Precedence but it doesn't work
Tree sitter doesn't let me do token-based precedence? I don't like tree-sitter's "make it inline but give it a number" system- seems like a bug farm to me.
This commit is contained in:
parent
98c4bb950f
commit
3012df4ac6
3 changed files with 35 additions and 14 deletions
15
grammar.py
15
grammar.py
|
|
@ -1,5 +1,5 @@
|
||||||
# This is an example grammar.
|
# This is an example grammar.
|
||||||
from parser import Assoc, Grammar, Nothing, rule, seq, Rule, Terminal, Re, Highlight, mark, opt
|
from parser import Assoc, Grammar, rule, seq, Rule, Terminal, Re, Highlight, mark, opt
|
||||||
|
|
||||||
|
|
||||||
class FineGrammar(Grammar):
|
class FineGrammar(Grammar):
|
||||||
|
|
@ -96,7 +96,11 @@ class FineGrammar(Grammar):
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
def export_list(self) -> Rule:
|
def export_list(self) -> Rule:
|
||||||
return Nothing | self.IDENTIFIER | seq(self.IDENTIFIER, self.COMMA, self.export_list)
|
return (
|
||||||
|
self.IDENTIFIER
|
||||||
|
| seq(self.IDENTIFIER, self.COMMA)
|
||||||
|
| seq(self.IDENTIFIER, self.COMMA, self.export_list)
|
||||||
|
)
|
||||||
|
|
||||||
# Functions
|
# Functions
|
||||||
@rule("FunctionDecl")
|
@rule("FunctionDecl")
|
||||||
|
|
@ -114,8 +118,9 @@ class FineGrammar(Grammar):
|
||||||
return seq(
|
return seq(
|
||||||
self.LPAREN,
|
self.LPAREN,
|
||||||
opt(
|
opt(
|
||||||
self._first_parameter,
|
self._first_parameter
|
||||||
opt(self.COMMA, self._parameter_list),
|
| seq(self._first_parameter, self.COMMA)
|
||||||
|
| seq(self._first_parameter, self.COMMA, self._parameter_list)
|
||||||
),
|
),
|
||||||
self.RPAREN,
|
self.RPAREN,
|
||||||
)
|
)
|
||||||
|
|
@ -126,7 +131,7 @@ class FineGrammar(Grammar):
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
def _parameter_list(self) -> Rule:
|
def _parameter_list(self) -> Rule:
|
||||||
return Nothing | self.parameter | seq(self.parameter, self.COMMA, self._parameter_list)
|
return self.parameter | seq(self.parameter, self.COMMA, self._parameter_list)
|
||||||
|
|
||||||
@rule("Parameter")
|
@rule("Parameter")
|
||||||
def parameter(self) -> Rule:
|
def parameter(self) -> Rule:
|
||||||
|
|
|
||||||
|
|
@ -2524,13 +2524,12 @@ class Grammar:
|
||||||
def terminals(self) -> list[Terminal]:
|
def terminals(self) -> list[Terminal]:
|
||||||
return self._terminals
|
return self._terminals
|
||||||
|
|
||||||
@property
|
|
||||||
def resolved_trivia(self) -> list[Terminal]:
|
|
||||||
return self._trivia
|
|
||||||
|
|
||||||
def non_terminals(self) -> list[NonTerminal]:
|
def non_terminals(self) -> list[NonTerminal]:
|
||||||
return [nt for _, nt in inspect.getmembers(self, lambda x: isinstance(x, NonTerminal))]
|
return [nt for _, nt in inspect.getmembers(self, lambda x: isinstance(x, NonTerminal))]
|
||||||
|
|
||||||
|
def get_precedence(self, name: str) -> None | tuple[Assoc, int]:
|
||||||
|
return self._precedence.get(name)
|
||||||
|
|
||||||
def generate_nonterminal_dict(
|
def generate_nonterminal_dict(
|
||||||
self, start: str | None = None
|
self, start: str | None = None
|
||||||
) -> typing.Tuple[dict[str, list[list[str | Terminal]]], set[str]]:
|
) -> typing.Tuple[dict[str, list[list[str | Terminal]]], set[str]]:
|
||||||
|
|
|
||||||
|
|
@ -99,9 +99,21 @@ def to_javascript_regex(re: parser.Re) -> str:
|
||||||
raise Exception(f"Regex node {re} not supported for tree-sitter")
|
raise Exception(f"Regex node {re} not supported for tree-sitter")
|
||||||
|
|
||||||
|
|
||||||
def convert_to_tree_sitter(rule: parser.Rule, grammar: parser.Grammar) -> str:
|
def apply_precedence(js: str, name: str, grammar: parser.Grammar) -> str:
|
||||||
# TODO: Precedence?
|
prec = grammar.get_precedence(name)
|
||||||
|
if prec is not None:
|
||||||
|
assoc, level = prec
|
||||||
|
if assoc == parser.Assoc.LEFT:
|
||||||
|
js = f"prec.left({level}, {js})"
|
||||||
|
elif assoc == parser.Assoc.RIGHT:
|
||||||
|
js = f"prec.right({level}, {js})"
|
||||||
|
else:
|
||||||
|
js = f"prec({level}, {js})"
|
||||||
|
|
||||||
|
return js
|
||||||
|
|
||||||
|
|
||||||
|
def convert_to_tree_sitter(rule: parser.Rule, grammar: parser.Grammar) -> str:
|
||||||
method = getattr(rule, "convert_to_tree_sitter", None)
|
method = getattr(rule, "convert_to_tree_sitter", None)
|
||||||
if method is not None:
|
if method is not None:
|
||||||
return method(grammar)
|
return method(grammar)
|
||||||
|
|
@ -109,10 +121,14 @@ def convert_to_tree_sitter(rule: parser.Rule, grammar: parser.Grammar) -> str:
|
||||||
if isinstance(rule, parser.Terminal):
|
if isinstance(rule, parser.Terminal):
|
||||||
if isinstance(rule.pattern, parser.Re):
|
if isinstance(rule.pattern, parser.Re):
|
||||||
regex = to_javascript_regex(rule.pattern)
|
regex = to_javascript_regex(rule.pattern)
|
||||||
return f"/{regex}/"
|
result = f"/{regex}/"
|
||||||
else:
|
else:
|
||||||
string = to_js_string(rule.pattern)
|
string = to_js_string(rule.pattern)
|
||||||
return f'"{string}"'
|
result = f'"{string}"'
|
||||||
|
|
||||||
|
if rule.name is not None:
|
||||||
|
result = apply_precedence(result, rule.name, grammar)
|
||||||
|
return result
|
||||||
|
|
||||||
elif isinstance(rule, parser.AlternativeRule):
|
elif isinstance(rule, parser.AlternativeRule):
|
||||||
final = []
|
final = []
|
||||||
|
|
@ -176,7 +192,6 @@ def convert_to_tree_sitter(rule: parser.Rule, grammar: parser.Grammar) -> str:
|
||||||
|
|
||||||
# https://tree-sitter.github.io/tree-sitter/creating-parsers
|
# https://tree-sitter.github.io/tree-sitter/creating-parsers
|
||||||
def emit_tree_sitter_grammar(grammar: parser.Grammar, path: pathlib.Path | str):
|
def emit_tree_sitter_grammar(grammar: parser.Grammar, path: pathlib.Path | str):
|
||||||
# TODO: PRECEDENCE
|
|
||||||
path = pathlib.Path(path) / "grammar.js"
|
path = pathlib.Path(path) / "grammar.js"
|
||||||
with open(path, "w", encoding="utf-8") as f:
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
f.write('/// <reference types="tree-sitter-cli/dsl" />\n')
|
f.write('/// <reference types="tree-sitter-cli/dsl" />\n')
|
||||||
|
|
@ -195,6 +210,8 @@ def emit_tree_sitter_grammar(grammar: parser.Grammar, path: pathlib.Path | str):
|
||||||
|
|
||||||
body = rule.fn(grammar)
|
body = rule.fn(grammar)
|
||||||
rule_definition = convert_to_tree_sitter(body, grammar)
|
rule_definition = convert_to_tree_sitter(body, grammar)
|
||||||
|
rule_definition = apply_precedence(rule_definition, rule.name, grammar)
|
||||||
|
|
||||||
f.write(f" '{rule_name}': $ => {rule_definition},")
|
f.write(f" '{rule_name}': $ => {rule_definition},")
|
||||||
|
|
||||||
f.write(" }\n")
|
f.write(" }\n")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue