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. :(
This commit is contained in:
John Doty 2024-09-01 07:37:54 -07:00
parent 0354fbf4a4
commit a99b3ecb70
2 changed files with 27 additions and 10 deletions

View file

@ -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

View file

@ -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):