Add document rendering to harness
To debug pretty-printing. Noticing some weirdness!
This commit is contained in:
parent
9cdfda6ec5
commit
265f07fd5a
2 changed files with 107 additions and 26 deletions
35
grammar.py
35
grammar.py
|
|
@ -85,6 +85,7 @@ class FineGrammar(Grammar):
|
||||||
indent(nl, mark(opt(self.class_body), field="body")),
|
indent(nl, mark(opt(self.class_body), field="body")),
|
||||||
nl,
|
nl,
|
||||||
self.RCURLY,
|
self.RCURLY,
|
||||||
|
nl, # Extra newline at the end of the class
|
||||||
)
|
)
|
||||||
|
|
||||||
@rule("ClassBody")
|
@rule("ClassBody")
|
||||||
|
|
@ -93,7 +94,7 @@ class FineGrammar(Grammar):
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
def _class_members(self) -> Rule:
|
def _class_members(self) -> Rule:
|
||||||
return self._class_member | seq(self._class_members, self._class_member)
|
return self._class_member | seq(self._class_members, nl, self._class_member)
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
def _class_member(self) -> Rule:
|
def _class_member(self) -> Rule:
|
||||||
|
|
@ -101,7 +102,7 @@ class FineGrammar(Grammar):
|
||||||
|
|
||||||
@rule("FieldDecl")
|
@rule("FieldDecl")
|
||||||
def field_declaration(self) -> Rule:
|
def field_declaration(self) -> Rule:
|
||||||
return group(self.IDENTIFIER, self.COLON, sp, self.type_expression, self.SEMICOLON) + nl
|
return group(self.IDENTIFIER, self.COLON, sp, self.type_expression, self.SEMICOLON)
|
||||||
|
|
||||||
# Types
|
# Types
|
||||||
@rule("TypeExpression")
|
@rule("TypeExpression")
|
||||||
|
|
@ -138,10 +139,14 @@ class FineGrammar(Grammar):
|
||||||
sp,
|
sp,
|
||||||
mark(self.IDENTIFIER, field="name", highlight=highlight.entity.name.function),
|
mark(self.IDENTIFIER, field="name", highlight=highlight.entity.name.function),
|
||||||
sp,
|
sp,
|
||||||
mark(self.function_parameters, field="parameters"),
|
group(
|
||||||
mark(opt(sp, self.ARROW, sp, self.type_expression), field="return_type"),
|
mark(self.function_parameters, field="parameters"),
|
||||||
|
mark(opt(sp, group(self.ARROW, sp, self.type_expression)), field="return_type"),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
sp,
|
||||||
mark(self.block, field="body"),
|
mark(self.block, field="body"),
|
||||||
|
nl,
|
||||||
)
|
)
|
||||||
|
|
||||||
@rule("ParamList")
|
@rule("ParamList")
|
||||||
|
|
@ -177,7 +182,7 @@ class FineGrammar(Grammar):
|
||||||
def block(self) -> Rule:
|
def block(self) -> Rule:
|
||||||
return alt(
|
return alt(
|
||||||
group(self.LCURLY, nl, self.RCURLY),
|
group(self.LCURLY, nl, self.RCURLY),
|
||||||
seq(self.LCURLY, indent(br, self.block_body), br, self.RCURLY),
|
seq(self.LCURLY, indent(nl, self.block_body), nl, self.RCURLY),
|
||||||
)
|
)
|
||||||
|
|
||||||
@rule("BlockBody")
|
@rule("BlockBody")
|
||||||
|
|
@ -185,7 +190,7 @@ class FineGrammar(Grammar):
|
||||||
return alt(
|
return alt(
|
||||||
self.expression,
|
self.expression,
|
||||||
self._statement_list,
|
self._statement_list,
|
||||||
seq(self._statement_list, br, self.expression),
|
seq(self._statement_list, nl, self.expression),
|
||||||
)
|
)
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
|
|
@ -338,15 +343,17 @@ class FineGrammar(Grammar):
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
def match_expression(self) -> Rule:
|
def match_expression(self) -> Rule:
|
||||||
return group(group(self.MATCH, sp, self.expression), sp, self.match_body)
|
return group(
|
||||||
|
group(self.MATCH, sp, self.expression, sp, self.LCURLY),
|
||||||
@rule("MatchBody")
|
indent(sp, self.match_arms),
|
||||||
def match_body(self) -> Rule:
|
sp,
|
||||||
return alt(
|
self.RCURLY,
|
||||||
group(self.LCURLY, nl, self.RCURLY),
|
|
||||||
group(self.LCURLY, indent(nl, self._match_arms), nl, self.RCURLY),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@rule("MatchArms")
|
||||||
|
def match_arms(self) -> Rule:
|
||||||
|
return self._match_arms
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
def _match_arms(self) -> Rule:
|
def _match_arms(self) -> Rule:
|
||||||
return (
|
return (
|
||||||
|
|
@ -381,7 +388,7 @@ class FineGrammar(Grammar):
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
def object_constructor_expression(self) -> Rule:
|
def object_constructor_expression(self) -> Rule:
|
||||||
return group(self.NEW, sp, self.type_identifier, self.field_list)
|
return group(self.NEW, sp, self.type_identifier, sp, self.field_list)
|
||||||
|
|
||||||
@rule
|
@rule
|
||||||
def field_list(self) -> Rule:
|
def field_list(self) -> Rule:
|
||||||
|
|
|
||||||
98
harness.py
98
harness.py
|
|
@ -18,6 +18,7 @@ import typing
|
||||||
|
|
||||||
import parser
|
import parser
|
||||||
from parser import runtime
|
from parser import runtime
|
||||||
|
from parser import wadler
|
||||||
|
|
||||||
# from parser import Token, Grammar, rule, seq
|
# from parser import Token, Grammar, rule, seq
|
||||||
|
|
||||||
|
|
@ -191,10 +192,28 @@ class DynamicLexerModule(DynamicModule[typing.Callable[[str], runtime.TokenStrea
|
||||||
return get_tokens
|
return get_tokens
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicPrinterModule(DynamicModule[wadler.Printer]):
|
||||||
|
def __init__(self, file_name, member_name):
|
||||||
|
super().__init__(file_name, member_name)
|
||||||
|
|
||||||
|
def _predicate(self, member) -> bool:
|
||||||
|
if not super()._predicate(member):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if getattr(member, "build_table", None):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _transform(self, value):
|
||||||
|
return wadler.Printer(value())
|
||||||
|
|
||||||
|
|
||||||
class DisplayMode(enum.Enum):
|
class DisplayMode(enum.Enum):
|
||||||
TREE = 0
|
TREE = 0
|
||||||
ERRORS = 1
|
ERRORS = 1
|
||||||
LOG = 2
|
LOG = 2
|
||||||
|
DOCUMENT = 3
|
||||||
|
|
||||||
|
|
||||||
class ListHandler(logging.Handler):
|
class ListHandler(logging.Handler):
|
||||||
|
|
@ -224,6 +243,7 @@ class Harness:
|
||||||
source: str | None
|
source: str | None
|
||||||
table: parser.ParseTable | None
|
table: parser.ParseTable | None
|
||||||
tree: runtime.Tree | None
|
tree: runtime.Tree | None
|
||||||
|
document: wadler.Document
|
||||||
mode: DisplayMode
|
mode: DisplayMode
|
||||||
log_handler: ListHandler
|
log_handler: ListHandler
|
||||||
|
|
||||||
|
|
@ -252,6 +272,7 @@ class Harness:
|
||||||
self.table = None
|
self.table = None
|
||||||
self.tokens = None
|
self.tokens = None
|
||||||
self.tree = None
|
self.tree = None
|
||||||
|
self.document = None
|
||||||
self.errors = []
|
self.errors = []
|
||||||
|
|
||||||
self.state_count = 0
|
self.state_count = 0
|
||||||
|
|
@ -267,6 +288,7 @@ class Harness:
|
||||||
)
|
)
|
||||||
|
|
||||||
self.lexer_module = DynamicLexerModule(self.lexer_file, self.grammar_member)
|
self.lexer_module = DynamicLexerModule(self.lexer_file, self.grammar_member)
|
||||||
|
self.printer_module = DynamicPrinterModule(self.grammar_file, self.grammar_member)
|
||||||
|
|
||||||
self.log_handler = ListHandler()
|
self.log_handler = ListHandler()
|
||||||
logging.basicConfig(level=logging.INFO, handlers=[self.log_handler])
|
logging.basicConfig(level=logging.INFO, handlers=[self.log_handler])
|
||||||
|
|
@ -287,6 +309,9 @@ class Harness:
|
||||||
elif k == "l":
|
elif k == "l":
|
||||||
self.mode = DisplayMode.LOG
|
self.mode = DisplayMode.LOG
|
||||||
self.lines = None
|
self.lines = None
|
||||||
|
elif k == "d":
|
||||||
|
self.mode = DisplayMode.DOCUMENT
|
||||||
|
self.lines = None
|
||||||
elif k == "j":
|
elif k == "j":
|
||||||
self.line_start = self.line_start - 1
|
self.line_start = self.line_start - 1
|
||||||
elif k == "k":
|
elif k == "k":
|
||||||
|
|
@ -301,6 +326,9 @@ class Harness:
|
||||||
def load_grammar(self) -> parser.ParseTable:
|
def load_grammar(self) -> parser.ParseTable:
|
||||||
return self.grammar_module.get()
|
return self.grammar_module.get()
|
||||||
|
|
||||||
|
def load_printer(self) -> wadler.Printer:
|
||||||
|
return self.printer_module.get()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
global VERSION
|
global VERSION
|
||||||
|
|
||||||
|
|
@ -337,6 +365,12 @@ class Harness:
|
||||||
self.average_entries = sum(len(row) for row in states) / len(states)
|
self.average_entries = sum(len(row) for row in states) / len(states)
|
||||||
self.max_entries = max(len(row) for row in states)
|
self.max_entries = max(len(row) for row in states)
|
||||||
|
|
||||||
|
printer = self.load_printer()
|
||||||
|
if self.tree is not None:
|
||||||
|
self.document = printer.convert_tree_to_document(self.tree)
|
||||||
|
else:
|
||||||
|
self.document = None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.errors = ["Error loading grammar:"] + [
|
self.errors = ["Error loading grammar:"] + [
|
||||||
|
|
@ -410,10 +444,14 @@ class Harness:
|
||||||
|
|
||||||
has_errors = "*" if self.errors else " "
|
has_errors = "*" if self.errors else " "
|
||||||
has_tree = "*" if self.tree else " "
|
has_tree = "*" if self.tree else " "
|
||||||
has_log = " " if self.log_handler.logs else " "
|
has_log = "*" if self.log_handler.logs else " "
|
||||||
|
has_document = "*" if self.document else " "
|
||||||
goto_cursor(0, rows - 1)
|
goto_cursor(0, rows - 1)
|
||||||
print(("\u2500" * cols) + "\r")
|
print(("\u2500" * cols) + "\r")
|
||||||
print(f"(e)rrors{has_errors} | (t)ree{has_tree} | (l)og{has_log} | (q)uit\r", end="")
|
print(
|
||||||
|
f"(e)rrors{has_errors} | (t)ree{has_tree} | (l)og{has_log} | (d)ocument{has_document} | (q)uit\r",
|
||||||
|
end="",
|
||||||
|
)
|
||||||
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
sys.stdout.buffer.flush()
|
sys.stdout.buffer.flush()
|
||||||
|
|
@ -433,6 +471,10 @@ class Harness:
|
||||||
case DisplayMode.LOG:
|
case DisplayMode.LOG:
|
||||||
lines.extend(line for line in self.log_handler.logs)
|
lines.extend(line for line in self.log_handler.logs)
|
||||||
|
|
||||||
|
case DisplayMode.DOCUMENT:
|
||||||
|
if self.document is not None:
|
||||||
|
self.format_document(lines, self.document)
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
typing.assert_never(self.mode)
|
typing.assert_never(self.mode)
|
||||||
|
|
||||||
|
|
@ -459,18 +501,50 @@ class Harness:
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def format_node(self, lines, node: runtime.Tree | runtime.TokenValue, indent=0):
|
def format_node(self, lines, node: runtime.Tree):
|
||||||
"""Print out an indented concrete syntax tree, from parse()."""
|
"""Print out an indented concrete syntax tree, from parse()."""
|
||||||
match node:
|
lines.extend(node.format_lines(self.source))
|
||||||
case runtime.Tree(name=name, start=start, end=end, children=children):
|
|
||||||
lines.append((" " * indent) + f"{name or '???'} [{start}, {end})")
|
|
||||||
for child in children:
|
|
||||||
self.format_node(lines, child, indent + 2)
|
|
||||||
|
|
||||||
case runtime.TokenValue(kind=kind, start=start, end=end):
|
def format_document(self, lines: list[str], doc: wadler.Document, indent: int = 0):
|
||||||
assert self.source is not None
|
def append(x: str):
|
||||||
value = self.source[start:end]
|
lines.append((" " * indent) + x)
|
||||||
lines.append((" " * indent) + f"{kind}:'{value}' [{start}, {end})")
|
|
||||||
|
match doc:
|
||||||
|
case wadler.NewLine(replace):
|
||||||
|
append(f"newline {repr(replace)}")
|
||||||
|
|
||||||
|
case wadler.ForceBreak():
|
||||||
|
append(f"forced break")
|
||||||
|
|
||||||
|
case wadler.Indent():
|
||||||
|
append(f"indent {doc.amount}")
|
||||||
|
self.format_document(lines, doc.doc, indent + 1)
|
||||||
|
|
||||||
|
case wadler.Text(start, end):
|
||||||
|
if self.source is not None:
|
||||||
|
append(f"< {self.source[start:end]}")
|
||||||
|
else:
|
||||||
|
append(f"< ??? {start}:{end}")
|
||||||
|
|
||||||
|
case wadler.Literal(text):
|
||||||
|
append(f"' {text}")
|
||||||
|
|
||||||
|
case wadler.Group():
|
||||||
|
append("group")
|
||||||
|
self.format_document(lines, doc.child, indent + 1)
|
||||||
|
|
||||||
|
case wadler.Lazy():
|
||||||
|
self.format_document(lines, doc.resolve(), indent)
|
||||||
|
|
||||||
|
case wadler.Cons():
|
||||||
|
self.format_document(lines, doc.left, indent)
|
||||||
|
self.format_document(lines, doc.right, indent)
|
||||||
|
|
||||||
|
case None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
case _:
|
||||||
|
typing.assert_never(doc)
|
||||||
|
|
||||||
|
|
||||||
def main(args: list[str]):
|
def main(args: list[str]):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue