From e203e27407fc00da3c77f52fb0326fa183f6b4be Mon Sep 17 00:00:00 2001 From: John Doty Date: Sat, 1 Jun 2024 05:42:40 -0700 Subject: [PATCH 1/2] Allow the grammar to specify a preference for a generator Overridable, like start production --- grammar.py | 3 ++- harness.py | 7 +++---- parser.py | 13 +++++++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/grammar.py b/grammar.py index 41c2064..1801bfd 100644 --- a/grammar.py +++ b/grammar.py @@ -1,7 +1,7 @@ # This is an example grammar. import re -from parser import Assoc, Grammar, Nothing, Terminal, rule, seq, Rule +from parser import Assoc, GenerateLALR, GenerateLR1, Grammar, Nothing, Terminal, rule, seq, Rule ARROW = Terminal("Arrow") AS = Terminal("As") @@ -81,6 +81,7 @@ class FineGrammar(Grammar): # (Assoc.NONE, [self.is_expression]), ], + generator=GenerateLALR, ) @rule("File") diff --git a/harness.py b/harness.py index 268b6cd..acf48ff 100644 --- a/harness.py +++ b/harness.py @@ -258,11 +258,10 @@ class DynamicModule: class DynamicGrammarModule(DynamicModule): - def __init__(self, file_name, member_name, start_rule, generator): + def __init__(self, file_name, member_name, start_rule): super().__init__(file_name, member_name) self.start_rule = start_rule - self.generator = generator def _predicate(self, member) -> bool: if not super()._predicate(member): @@ -274,7 +273,7 @@ class DynamicGrammarModule(DynamicModule): return False def _transform(self, value): - return value().build_table(start=self.start_rule, generator=self.generator) + return value().build_table(start=self.start_rule) class DynamicLexerModule(DynamicModule): @@ -319,7 +318,7 @@ class Harness: self.max_entries = 0 self.grammar_module = DynamicGrammarModule( - self.grammar_file, self.grammar_member, self.start_rule, generator=parser.GenerateLALR + self.grammar_file, self.grammar_member, self.start_rule ) self.lexer_module = DynamicLexerModule(self.lexer_file, self.lexer_member) diff --git a/parser.py b/parser.py index 583d8bd..813d91d 100644 --- a/parser.py +++ b/parser.py @@ -1850,8 +1850,14 @@ class Grammar: _precedence: dict[str, typing.Tuple[Assoc, int]] _start: str + _generator: type[GenerateLR0] - def __init__(self, start: str, precedence: PrecedenceList | None = None): + def __init__( + self, + start: str, + precedence: PrecedenceList | None = None, + generator: type[GenerateLR0] = GenerateLALR, + ): if precedence is None: precedence = getattr(self, "precedence", []) assert precedence is not None @@ -1870,6 +1876,7 @@ class Grammar: self._precedence = precedence_table self._start = start + self._generator = generator def generate_nonterminal_dict( self, start: str | None = None @@ -1940,7 +1947,7 @@ class Grammar: return grammar, transparents - def build_table(self, start: str | None, generator=GenerateLALR): + def build_table(self, start: str | None, generator=None): """Construct a parse table for this grammar, starting at the named nonterminal rule. """ @@ -1948,6 +1955,8 @@ class Grammar: start = self._start desugared, transparents = self.desugar(start) + if generator is None: + generator = self._generator gen = generator(start, desugared, precedence=self._precedence, transparents=transparents) table = gen.gen_table() return table From 03937e62e61d7da1f3d41a3f58c3def033391be4 Mon Sep 17 00:00:00 2001 From: John Doty Date: Sat, 1 Jun 2024 05:57:16 -0700 Subject: [PATCH 2/2] Allow the generator to be a little more declarative --- grammar.py | 4 ++-- parser.py | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/grammar.py b/grammar.py index 1801bfd..1ecf61f 100644 --- a/grammar.py +++ b/grammar.py @@ -54,10 +54,11 @@ RSQUARE = Terminal("RightBracket") class FineGrammar(Grammar): + generator = GenerateLALR + start = "File" def __init__(self): super().__init__( - start="File", precedence=[ (Assoc.RIGHT, [EQUAL]), (Assoc.LEFT, [OR]), @@ -81,7 +82,6 @@ class FineGrammar(Grammar): # (Assoc.NONE, [self.is_expression]), ], - generator=GenerateLALR, ) @rule("File") diff --git a/parser.py b/parser.py index 813d91d..4ad5683 100644 --- a/parser.py +++ b/parser.py @@ -1854,14 +1854,26 @@ class Grammar: def __init__( self, - start: str, + start: str | None = None, precedence: PrecedenceList | None = None, - generator: type[GenerateLR0] = GenerateLALR, + generator: type[GenerateLR0] | None = None, ): + if start is None: + start = getattr(self, "start", None) + if start is None: + raise ValueError( + "The default start rule must either be specified in the constructor or as an " + "attribute in the class." + ) + if precedence is None: precedence = getattr(self, "precedence", []) assert precedence is not None + if generator is None: + generator = getattr(self, "generator", GenerateLALR) + assert generator is not None + precedence_table = {} for prec, (associativity, symbols) in enumerate(precedence): for symbol in symbols: