-
-
Notifications
You must be signed in to change notification settings - Fork 140
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Optimize performance of character class predicates
Note that because letters are contiguous blocks in ASCII, we can compare with lower and upper boundaries, which is more efficient than checking containment in a set or string.
- Loading branch information
Showing
5 changed files
with
149 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,68 @@ | ||
__all__ = ["is_digit", "is_letter", "is_name_start", "is_name_continue"] | ||
|
||
try: | ||
"string".isascii() | ||
except AttributeError: # Python < 3.7 | ||
|
||
def is_digit(char: str) -> bool: | ||
"""Check whether char is a digit | ||
def is_digit(char: str) -> bool: | ||
"""Check whether char is a digit | ||
For internal use by the lexer only. | ||
""" | ||
return "0" <= char <= "9" | ||
For internal use by the lexer only. | ||
""" | ||
return "0" <= char <= "9" | ||
|
||
def is_letter(char: str) -> bool: | ||
"""Check whether char is a plain ASCII letter | ||
def is_letter(char: str) -> bool: | ||
"""Check whether char is a plain ASCII letter | ||
For internal use by the lexer only. | ||
""" | ||
return "a" <= char <= "z" or "A" <= char <= "Z" | ||
|
||
For internal use by the lexer only. | ||
""" | ||
return "A" <= char <= "Z" or "a" <= char <= "z" | ||
def is_name_start(char: str) -> bool: | ||
"""Check whether char is allowed at the beginning of a GraphQL name | ||
For internal use by the lexer only. | ||
""" | ||
return "a" <= char <= "z" or "A" <= char <= "Z" or char == "_" | ||
|
||
def is_name_start(char: str) -> bool: | ||
"""Check whether char is allowed at the beginning of a GraphQL name | ||
def is_name_continue(char: str) -> bool: | ||
"""Check whether char is allowed in the continuation of a GraphQL name | ||
For internal use by the lexer only. | ||
""" | ||
return is_letter(char) or char == "_" | ||
For internal use by the lexer only. | ||
""" | ||
return ( | ||
"a" <= char <= "z" | ||
or "A" <= char <= "Z" | ||
or "0" <= char <= "9" | ||
or char == "_" | ||
) | ||
|
||
else: | ||
|
||
def is_name_continue(char: str) -> bool: | ||
"""Check whether char is allowed in the continuation of a GraphQL name | ||
def is_digit(char: str) -> bool: | ||
"""Check whether char is a digit | ||
For internal use by the lexer only. | ||
""" | ||
return is_letter(char) or is_digit(char) or char == "_" | ||
For internal use by the lexer only. | ||
""" | ||
return char.isascii() and char.isdigit() | ||
|
||
def is_letter(char: str) -> bool: | ||
"""Check whether char is a plain ASCII letter | ||
For internal use by the lexer only. | ||
""" | ||
return char.isascii() and char.isalpha() | ||
|
||
def is_name_start(char: str) -> bool: | ||
"""Check whether char is allowed at the beginning of a GraphQL name | ||
For internal use by the lexer only. | ||
""" | ||
return char.isascii() and (char.isalpha() or char == "_") | ||
|
||
def is_name_continue(char: str) -> bool: | ||
"""Check whether char is allowed in the continuation of a GraphQL name | ||
For internal use by the lexer only. | ||
""" | ||
return char.isascii() and (char.isalnum() or char == "_") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
from string import ascii_letters as letters, digits, punctuation | ||
|
||
from graphql.language.character_classes import ( | ||
is_digit, | ||
is_letter, | ||
is_name_start, | ||
is_name_continue, | ||
) | ||
|
||
non_ascii = "¯_±¹²³½£ºµÄäÖöØø×〇᧐〸αΑωΩ" | ||
|
||
|
||
def describe_digit(): | ||
def accepts_digits(): | ||
assert all(is_digit(char) for char in digits) | ||
|
||
def rejects_letters(): | ||
assert not any(is_digit(char) for char in letters) | ||
|
||
def rejects_underscore(): | ||
assert not is_digit("_") | ||
|
||
def rejects_punctuation(): | ||
assert not any(is_digit(char) for char in punctuation) | ||
|
||
def rejects_non_ascii(): | ||
assert not any(is_digit(char) for char in non_ascii) | ||
|
||
def rejects_empty_string(): | ||
assert not is_digit("") | ||
|
||
|
||
def describe_letter(): | ||
def accepts_letters(): | ||
assert all(is_letter(char) for char in letters) | ||
|
||
def rejects_digits(): | ||
assert not any(is_letter(char) for char in digits) | ||
|
||
def rejects_underscore(): | ||
assert not is_letter("_") | ||
|
||
def rejects_punctuation(): | ||
assert not any(is_letter(char) for char in punctuation) | ||
|
||
def rejects_non_ascii(): | ||
assert not any(is_letter(char) for char in non_ascii) | ||
|
||
def rejects_empty_string(): | ||
assert not is_letter("") | ||
|
||
|
||
def describe_name_start(): | ||
def accepts_letters(): | ||
assert all(is_name_start(char) for char in letters) | ||
|
||
def accepts_underscore(): | ||
assert is_name_start("_") | ||
|
||
def rejects_digits(): | ||
assert not any(is_name_start(char) for char in digits) | ||
|
||
def rejects_punctuation(): | ||
assert not any(is_name_start(char) for char in punctuation if char != "_") | ||
|
||
def rejects_non_ascii(): | ||
assert not any(is_name_start(char) for char in non_ascii) | ||
|
||
def rejects_empty_string(): | ||
assert not is_name_start("") | ||
|
||
|
||
def describe_name_continue(): | ||
def accepts_letters(): | ||
assert all(is_name_continue(char) for char in letters) | ||
|
||
def accepts_digits(): | ||
assert all(is_name_continue(char) for char in digits) | ||
|
||
def accepts_underscore(): | ||
assert is_name_continue("_") | ||
|
||
def rejects_punctuation(): | ||
assert not any(is_name_continue(char) for char in punctuation if char != "_") | ||
|
||
def rejects_non_ascii(): | ||
assert not any(is_name_continue(char) for char in non_ascii) | ||
|
||
def rejects_empty_string(): | ||
assert not is_name_continue("") |