Skip to content

Commit 1e7bca6

Browse files
committed
RFC: Number lexer lookahead restriction
Implements and adds the tests described by graphql/graphql-spec#601 Replicates graphql/graphql-js@ca1c1df
1 parent 51a3bd5 commit 1e7bca6

File tree

3 files changed

+36
-5
lines changed

3 files changed

+36
-5
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The current stable version 3.0.1 of GraphQL-core is up-to-date
1616
with GraphQL.js version 14.5.8.
1717

1818
All parts of the API are covered by an extensive test suite
19-
of currently 1992 unit tests.
19+
of currently 1993 unit tests.
2020

2121

2222
## Documentation

src/graphql/language/lexer.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,8 @@ def read_number(
215215
position = self.read_digits(position, char)
216216
char = body[position : position + 1]
217217

218-
# Numbers cannot be followed by . or e
219-
if char and char in ".eE":
218+
# Numbers cannot be followed by . or NameStart
219+
if char and (char == "." or is_name_start(char)):
220220
raise GraphQLSyntaxError(
221221
source,
222222
position,
@@ -436,3 +436,8 @@ def char2hex(a: str):
436436
elif "a" <= a <= "f": # a-f
437437
return ord(a) - 87
438438
return -1
439+
440+
441+
def is_name_start(char: str) -> bool:
442+
"""Check whether char is an underscore or a plain ASCII letter"""
443+
return char == "_" or "A" <= char <= "Z" or "a" <= char <= "z"

tests/language/test_lexer.py

+28-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ def lex_second(s: str) -> Token:
1919
return lexer.advance()
2020

2121

22-
def assert_syntax_error(text, message, location):
22+
def assert_syntax_error(text, message, location, second=False):
2323
with raises(GraphQLSyntaxError) as exc_info:
24-
lex_one(text)
24+
lex_second(text) if second else lex_one(text)
2525
error = exc_info.value
2626
assert error.message == f"Syntax Error: {message}"
2727
assert error.locations == [location]
@@ -330,6 +330,32 @@ def lex_reports_useful_number_errors():
330330
"1.23.4", "Invalid number, expected digit but got: '.'.", (1, 5)
331331
)
332332

333+
def lex_does_not_allow_name_start_after_a_number():
334+
assert_syntax_error(
335+
"0xF1", "Invalid number, expected digit but got: 'x'.", (1, 2)
336+
)
337+
assert_syntax_error(
338+
"0b10", "Invalid number, expected digit but got: 'b'.", (1, 2)
339+
)
340+
assert_syntax_error(
341+
"123abc", "Invalid number, expected digit but got: 'a'.", (1, 4)
342+
)
343+
assert_syntax_error(
344+
"1_1234", "Invalid number, expected digit but got: '_'.", (1, 2)
345+
)
346+
assert_syntax_error(
347+
"1_1234", "Invalid number, expected digit but got: '_'.", (1, 2)
348+
)
349+
assert_syntax_error(
350+
"1ß", "Cannot parse the unexpected character 'ß'.", (1, 2), second=True,
351+
)
352+
assert_syntax_error(
353+
"1.23f", "Invalid number, expected digit but got: 'f'.", (1, 5)
354+
)
355+
assert_syntax_error(
356+
"12ß", "Cannot parse the unexpected character 'ß'.", (1, 3), second=True,
357+
)
358+
333359
# noinspection PyArgumentEqualDefault
334360
def lexes_punctuation():
335361
assert lex_one("!") == Token(TokenKind.BANG, 0, 1, 1, 1, None, None)

0 commit comments

Comments
 (0)