/
githubmirror
/
cpython
Обзор
Документация
Войти
/
githubmirror
/
cpython
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
Parser/lexer/number.c
313 строк
10 KB
Pablo Galindo Salgado
gh-153569: Split tokenizer lexer into focused files (#153585)
11 июл 2026, 21:53
Не верифицирован
11 июл 2026, 21:53
a2d3787
Код
Авторство
О чём код?
#include "Python.h" #include "pycore_token.h" #include "lexer_internal.h" #include "../tokenizer/helpers.h" #define MAKE_TOKEN(token_type) _PyLexer_token_setup(tok, token, token_type, p_start, p_end) static int lookahead(struct tok_state *tok, const char *test) { const char *s = test; int res = 0; while (1) { int c = tok_nextc(tok); if (*s == 0) { res = !is_potential_identifier_char(c); } else if (c == *s) { s++; continue; } tok_backup(tok, c); while (s != test) { tok_backup(tok, *--s); } return res; } } static int verify_end_of_number(struct tok_state *tok, int c, const char *kind) { if (tok->tok_extra_tokens) { // When we are parsing extra tokens, we don't want to emit warnings // about invalid literals, because we want to be a bit more liberal. return 1; } /* Emit a deprecation warning only if the numeric literal is immediately * followed by one of keywords which can occur after a numeric literal * in valid code: "and", "else", "for", "if", "in", "is" and "or". * It allows to gradually deprecate existing valid code without adding * warning before error in most cases of invalid numeric literal (which * would be confusing and break existing tests). * Raise a syntax error with slightly better message than plain * "invalid syntax" if the numeric literal is immediately followed by * other keyword or identifier. */ int r = 0; if (c == 'a') { r = lookahead(tok, "nd"); } else if (c == 'e') { r = lookahead(tok, "lse"); } else if (c == 'f') { r = lookahead(tok, "or"); } else if (c == 'i') { int c2 = tok_nextc(tok); if (c2 == 'f' || c2 == 'n' || c2 == 's') { r = 1; } tok_backup(tok, c2); } else if (c == 'o') { r = lookahead(tok, "r"); } else if (c == 'n') { r = lookahead(tok, "ot"); } if (r) { tok_backup(tok, c); if (_PyTokenizer_parser_warn(tok, PyExc_SyntaxWarning, "invalid %s literal", kind)) { return 0; } tok_nextc(tok); } else /* In future releases, only error will remain. */ if (c < 128 && is_potential_identifier_char(c)) { tok_backup(tok, c); _PyTokenizer_syntaxerror(tok, "invalid %s literal", kind); return 0; } return 1; } static int tok_decimal_tail(struct tok_state *tok) { int c; while (1) { do { c = tok_nextc(tok); } while (Py_ISDIGIT(c)); if (c != '_') { break; } c = tok_nextc(tok); if (!Py_ISDIGIT(c)) { tok_backup(tok, c); _PyTokenizer_syntaxerror(tok, "invalid decimal literal"); return 0; } } return c; } int _PyLexer_scan_number(struct tok_state *tok, struct token *token, int c, int leading_dot) { const char *p_start = NULL; const char *p_end = NULL; if (leading_dot) { goto fraction; } if (c == '0') { /* Hex, octal or binary -- maybe. */ c = tok_nextc(tok); if (c == 'x' || c == 'X') { /* Hex */ c = tok_nextc(tok); do { if (c == '_') { c = tok_nextc(tok); } if (!Py_ISXDIGIT(c)) { tok_backup(tok, c); return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid hexadecimal literal")); } do { c = tok_nextc(tok); } while (Py_ISXDIGIT(c)); } while (c == '_'); if (!verify_end_of_number(tok, c, "hexadecimal")) { return MAKE_TOKEN(ERRORTOKEN); } } else if (c == 'o' || c == 'O') { /* Octal */ c = tok_nextc(tok); do { if (c == '_') { c = tok_nextc(tok); } if (c < '0' || c >= '8') { if (Py_ISDIGIT(c)) { return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in octal literal", c)); } else { tok_backup(tok, c); return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid octal literal")); } } do { c = tok_nextc(tok); } while ('0' <= c && c < '8'); } while (c == '_'); if (Py_ISDIGIT(c)) { return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in octal literal", c)); } if (!verify_end_of_number(tok, c, "octal")) { return MAKE_TOKEN(ERRORTOKEN); } } else if (c == 'b' || c == 'B') { /* Binary */ c = tok_nextc(tok); do { if (c == '_') { c = tok_nextc(tok); } if (c != '0' && c != '1') { if (Py_ISDIGIT(c)) { return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c)); } else { tok_backup(tok, c); return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid binary literal")); } } do { c = tok_nextc(tok); } while (c == '0' || c == '1'); } while (c == '_'); if (Py_ISDIGIT(c)) { return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c)); } if (!verify_end_of_number(tok, c, "binary")) { return MAKE_TOKEN(ERRORTOKEN); } } else { int nonzero = 0; /* maybe old-style octal; c is first char of it */ /* in any case, allow '0' as a literal */ while (1) { if (c == '_') { c = tok_nextc(tok); if (!Py_ISDIGIT(c)) { tok_backup(tok, c); return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid decimal literal")); } } if (c != '0') { break; } c = tok_nextc(tok); } char* zeros_end = tok->cur; if (Py_ISDIGIT(c)) { nonzero = 1; c = tok_decimal_tail(tok); if (c == 0) { return MAKE_TOKEN(ERRORTOKEN); } } if (c == '.') { c = tok_nextc(tok); goto fraction; } else if (c == 'e' || c == 'E') { goto exponent; } else if (c == 'j' || c == 'J') { goto imaginary; } else if (nonzero && !tok->tok_extra_tokens) { /* Old-style octal: now disallowed. */ tok_backup(tok, c); return MAKE_TOKEN(_PyTokenizer_syntaxerror_known_range( tok, (int)(tok->start + 1 - tok->line_start), (int)(zeros_end - tok->line_start), "leading zeros in decimal integer " "literals are not permitted; " "use an 0o prefix for octal integers")); } if (!verify_end_of_number(tok, c, "decimal")) { return MAKE_TOKEN(ERRORTOKEN); } } } else { /* Decimal */ c = tok_decimal_tail(tok); if (c == 0) { return MAKE_TOKEN(ERRORTOKEN); } { /* Accept floating-point numbers. */ if (c == '.') { c = tok_nextc(tok); fraction: /* Fraction */ if (Py_ISDIGIT(c)) { c = tok_decimal_tail(tok); if (c == 0) { return MAKE_TOKEN(ERRORTOKEN); } } } if (c == 'e' || c == 'E') { int e; exponent: e = c; /* Exponent part */ c = tok_nextc(tok); if (c == '+' || c == '-') { c = tok_nextc(tok); if (!Py_ISDIGIT(c)) { tok_backup(tok, c); return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid decimal literal")); } } else if (!Py_ISDIGIT(c)) { tok_backup(tok, c); if (!verify_end_of_number(tok, e, "decimal")) { return MAKE_TOKEN(ERRORTOKEN); } tok_backup(tok, e); p_start = tok->start; p_end = tok->cur; return MAKE_TOKEN(NUMBER); } c = tok_decimal_tail(tok); if (c == 0) { return MAKE_TOKEN(ERRORTOKEN); } } if (c == 'j' || c == 'J') { /* Imaginary part */ imaginary: c = tok_nextc(tok); if (!verify_end_of_number(tok, c, "imaginary")) { return MAKE_TOKEN(ERRORTOKEN); } } else if (!verify_end_of_number(tok, c, "decimal")) { return MAKE_TOKEN(ERRORTOKEN); } } } tok_backup(tok, c); p_start = tok->start; p_end = tok->cur; return MAKE_TOKEN(NUMBER); }