| #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) | |
| { | |
| _PyTok_Off p_start = -1; | |
| _PyTok_Off p_end = -1; | |
| 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); | |
| } | |
| _PyTok_Off 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); | |
| } |