Skip to content

Commit d9cca76

Browse files
committed
gh-153568: Use FIRST sets to speed up parser choices
1 parent 999a046 commit d9cca76

4 files changed

Lines changed: 6905 additions & 24 deletions

File tree

Lib/test/test_peg_generator/test_c_parser.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,57 @@ def test_soft_keywords_lookahead(self) -> None:
504504
"""
505505
self.run_test(grammar_source, test_source)
506506

507+
def test_first_set_dispatch(self) -> None:
508+
grammar = parse_string(
509+
"""
510+
start: expr NEWLINE
511+
expr: NAME | NUMBER | '(' NAME ')'
512+
""",
513+
GrammarParser,
514+
)
515+
parser_source = generate_c_parser_source(grammar)
516+
self.assertIn("switch (_current_token_type)", parser_source)
517+
518+
overlapping_grammar = parse_string(
519+
"start: expr NEWLINE $\nexpr: NAME '+' NAME | NAME\n",
520+
GrammarParser,
521+
)
522+
parser_source = generate_c_parser_source(overlapping_grammar)
523+
self.assertIn("_first_set_mask", parser_source)
524+
self.run_test(
525+
"start: expr NEWLINE $\nexpr: NAME '+' NAME | NAME\n",
526+
'self.check_input_strings_for_grammar(["a\\n", "a + b\\n"])',
527+
)
528+
529+
nullable_grammar = parse_string(
530+
"start: expr NEWLINE\nexpr: ['+'] | NUMBER\n",
531+
GrammarParser,
532+
)
533+
parser_source = generate_c_parser_source(nullable_grammar)
534+
self.assertNotIn("switch (_current_token_type)", parser_source)
535+
536+
def test_first_set_dispatch_soft_keywords(self) -> None:
537+
grammar_source = 'start: ("foo" | "bar") NEWLINE $\n'
538+
grammar = parse_string(grammar_source, GrammarParser)
539+
parser_source = generate_c_parser_source(grammar)
540+
self.assertIn("_first_set_mask", parser_source)
541+
self.run_test(
542+
grammar_source,
543+
'self.check_input_strings_for_grammar(["foo\\n", "bar\\n"], ["baz\\n"])',
544+
)
545+
546+
def test_first_set_dispatch_invalid_rules(self) -> None:
547+
grammar_source = """
548+
start: value NEWLINE $
549+
value: NAME '+' NAME | invalid_value
550+
invalid_value: NAME { RAISE_SYNTAX_ERROR("expected an addition") }
551+
"""
552+
test_source = """
553+
with self.assertRaisesRegex(SyntaxError, "expected an addition"):
554+
parse.parse_string("name\\n", mode=0)
555+
"""
556+
self.run_test(grammar_source, test_source)
557+
507558
def test_forced(self) -> None:
508559
grammar_source = """
509560
start: NAME &&':' | NAME

0 commit comments

Comments
 (0)