diff --git a/src/graphql/language/ast.py b/src/graphql/language/ast.py index e9935504..57f871ae 100644 --- a/src/graphql/language/ast.py +++ b/src/graphql/language/ast.py @@ -161,10 +161,13 @@ def __init__( value: str | None = None, ) -> None: self.kind = kind - self.start, self.end = start, end - self.line, self.column = line, column + self.start = start + self.end = end + self.line = line + self.column = column self.value = value - self.prev = self.next = None + self.prev = None + self.next = None def __str__(self) -> str: return self.desc diff --git a/src/graphql/language/lexer.py b/src/graphql/language/lexer.py index 97ad9e23..0efaa743 100644 --- a/src/graphql/language/lexer.py +++ b/src/graphql/language/lexer.py @@ -2,12 +2,13 @@ from __future__ import annotations +import re from typing import TYPE_CHECKING, NamedTuple from ..error import GraphQLSyntaxError from .ast import Token from .block_string import dedent_block_string_lines -from .character_classes import is_digit, is_name_continue, is_name_start +from .character_classes import is_digit, is_name_start from .token_kind import TokenKind if TYPE_CHECKING: @@ -69,6 +70,8 @@ def __init__(self, source: Source) -> None: self.line, self.line_start = 1, 0 self._max_tokens = self._comment_budget = self._max_comments = None self._comment_count = 0 + # NameContinue is ASCII letters, digits and underscore only + self._match_name_continue = re.compile("[0-9A-Z_a-z]*").match def advance(self) -> Token: """Advance the token stream to the next non-ignored token. @@ -163,9 +166,7 @@ def create_token( :meta private: """ - line = self.line - col = 1 + start - self.line_start - return Token(kind, start, end, line, col, value) + return Token(kind, start, end, self.line, 1 + start - self.line_start, value) def read_next_token(self, start: int) -> Token: """Get the next token from the source starting at the given position. @@ -663,14 +664,8 @@ def read_name(self, start: int) -> Token: :meta private: """ body = self.source.body - body_length = len(body) - position = start + 1 - - while position < body_length: - char = body[position] - if not is_name_continue(char): - break - position += 1 + # the pattern can match an empty string, so the match is never None + position = self._match_name_continue(body, start + 1).end() # type: ignore[union-attr] return self.create_token(TokenKind.NAME, start, position, body[start:position])