diff --git a/grammar.py b/grammar.py index 41c2064..1ecf61f 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") @@ -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]), 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..4ad5683 100644 --- a/parser.py +++ b/parser.py @@ -1850,12 +1850,30 @@ class Grammar: _precedence: dict[str, typing.Tuple[Assoc, int]] _start: str + _generator: type[GenerateLR0] + + def __init__( + self, + start: str | None = None, + precedence: PrecedenceList | None = None, + 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." + ) - def __init__(self, start: str, precedence: PrecedenceList | None = None): 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: @@ -1870,6 +1888,7 @@ class Grammar: self._precedence = precedence_table self._start = start + self._generator = generator def generate_nonterminal_dict( self, start: str | None = None @@ -1940,7 +1959,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 +1967,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