From a99b3ecb7035cd0f54904007fa72e2b115daa368 Mon Sep 17 00:00:00 2001 From: John Doty Date: Sun, 1 Sep 2024 07:37:54 -0700 Subject: [PATCH] Interpret precedence the way tree-sitter does, kinda This allows most of our precedence to be re-used. There are some cases still where tree-sitter gets confused (and we don't), see the corresponding change to grammar.py. I wish I knew how to fix this but I don't. :( --- grammar.py | 6 +----- parser/tree_sitter.py | 31 ++++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/grammar.py b/grammar.py index 474d575..6fed257 100644 --- a/grammar.py +++ b/grammar.py @@ -290,15 +290,11 @@ class FineGrammar(Grammar): @rule("Pattern") def pattern(self) -> Rule: return ( - seq(self.variable_binding, self._pattern_core, self._pattern_predicate) + seq(self.variable_binding, self._pattern_core, self.AND, self.expression) | seq(self.variable_binding, self._pattern_core) | self._pattern_core ) - @rule - def _pattern_predicate(self) -> Rule: - return seq(self.AND, self.expression) - @rule def _pattern_core(self) -> Rule: return self.type_expression | self.wildcard_pattern diff --git a/parser/tree_sitter.py b/parser/tree_sitter.py index 6cd7303..6ef7631 100644 --- a/parser/tree_sitter.py +++ b/parser/tree_sitter.py @@ -126,8 +126,6 @@ def convert_to_tree_sitter(rule: parser.Rule, grammar: parser.Grammar) -> str: string = to_js_string(rule.pattern) result = f'"{string}"' - if rule.name is not None: - result = apply_precedence(result, rule.name, grammar) return result elif isinstance(rule, parser.AlternativeRule): @@ -172,9 +170,32 @@ def convert_to_tree_sitter(rule: parser.Rule, grammar: parser.Grammar) -> str: if len(final) == 0: raise Exception("Unsupported rule: empty sequence") - result = ", ".join([convert_to_tree_sitter(r, grammar) for r in final]) - if len(final) > 1: - result = f"seq({result})" + # OK so there's a weird thing here? If we see a terminal in our + # sequence that has a precedence then we need to group with the + # previous element somehow. + # + # This is an incredible comment that explains how tree-sitter + # actually thinks about conflicts: + # + # https://github.com/tree-sitter/tree-sitter/issues/372 + # + pieces = [convert_to_tree_sitter(r, grammar) for r in final] + + def make_seq(pieces: list[str]): + if len(pieces) == 1: + return pieces[0] + + return "seq({})".format(", ".join(pieces)) + + for i, r in reversed(list(enumerate(final))): + if isinstance(r, parser.Terminal) and r.name is not None: + if grammar.get_precedence(r.name) is not None: + cut = max(i - 1, 0) + js = make_seq(pieces[cut:]) + js = apply_precedence(js, r.name, grammar) + pieces = pieces[:cut] + [js] + + result = make_seq(pieces) return result elif isinstance(rule, parser.NonTerminal):