Muck around with usability

This commit is contained in:
John Doty 2024-08-28 08:27:46 -07:00
parent cd62b65789
commit 02c1aa507e
2 changed files with 135 additions and 102 deletions

View file

@ -1,8 +1,5 @@
# This is an example grammar. # This is an example grammar.
import re from parser import Assoc, Grammar, Nothing, rule, seq, Rule, Terminal, Re, Highlight, mark, opt
import typing
from parser import Assoc, Grammar, Nothing, rule, seq, Rule, Terminal, Re, TerminalKind
class FineGrammar(Grammar): class FineGrammar(Grammar):
@ -53,7 +50,11 @@ class FineGrammar(Grammar):
@rule("ClassDeclaration") @rule("ClassDeclaration")
def class_declaration(self) -> Rule: def class_declaration(self) -> Rule:
return seq(self.CLASS, self.IDENTIFIER, self._class_body) return seq(
self.CLASS,
mark(self.IDENTIFIER, highlight=Highlight.Entity.Name.Type),
self._class_body,
)
@rule @rule
def _class_body(self) -> Rule: def _class_body(self) -> Rule:
@ -100,21 +101,23 @@ class FineGrammar(Grammar):
# Functions # Functions
@rule("FunctionDecl") @rule("FunctionDecl")
def function_declaration(self) -> Rule: def function_declaration(self) -> Rule:
return seq(self.FUN, self.IDENTIFIER, self.function_parameters, self.block) | seq( return seq(
self.FUN, self.FUN,
self.IDENTIFIER, mark(self.IDENTIFIER, highlight=Highlight.Entity.Name.Function),
self.function_parameters, self.function_parameters,
self.ARROW, opt(self.ARROW, self.type_expression),
self.type_expression,
self.block, self.block,
) )
@rule("ParamList") @rule("ParamList")
def function_parameters(self) -> Rule: def function_parameters(self) -> Rule:
return ( return seq(
seq(self.LPAREN, self.RPAREN) self.LPAREN,
| seq(self.LPAREN, self._first_parameter, self.RPAREN) opt(
| seq(self.LPAREN, self._first_parameter, self.COMMA, self._parameter_list, self.RPAREN) self._first_parameter,
opt(self.COMMA, self._parameter_list),
),
self.RPAREN,
) )
@rule @rule
@ -132,11 +135,10 @@ class FineGrammar(Grammar):
# Block # Block
@rule("Block") @rule("Block")
def block(self) -> Rule: def block(self) -> Rule:
return ( return seq(
seq(self.LCURLY, self.RCURLY) self.LCURLY,
| seq(self.LCURLY, self.expression, self.RCURLY) opt(opt(self._statement_list), self.expression)
| seq(self.LCURLY, self._statement_list, self.RCURLY) self.RCURLY,
| seq(self.LCURLY, self._statement_list, self.expression, self.RCURLY)
) )
@rule @rule
@ -326,32 +328,32 @@ class FineGrammar(Grammar):
BLANKS = Terminal(Re.set(" ", "\t", "\r", "\n").plus()) BLANKS = Terminal(Re.set(" ", "\t", "\r", "\n").plus())
COMMENT = Terminal( COMMENT = Terminal(
Re.seq(Re.literal("//"), Re.set("\n").invert().star()), Re.seq(Re.literal("//"), Re.set("\n").invert().star()),
kind=TerminalKind.Comment.Line, highlight=Highlight.Comment.Line,
) )
ARROW = Terminal("->", kind=TerminalKind.Keyword.Operator) ARROW = Terminal("->", highlight=Highlight.Keyword.Operator)
AS = Terminal("as", kind=TerminalKind.Keyword.Operator.Expression) AS = Terminal("as", highlight=Highlight.Keyword.Operator.Expression)
BAR = Terminal("|", kind=TerminalKind.Keyword.Operator.Expression) BAR = Terminal("|", highlight=Highlight.Keyword.Operator.Expression)
CLASS = Terminal("class", kind=TerminalKind.Storage.Type.Class) CLASS = Terminal("class", highlight=Highlight.Storage.Type.Class)
COLON = Terminal(":", kind=TerminalKind.Punctuation.Separator) COLON = Terminal(":", highlight=Highlight.Punctuation.Separator)
ELSE = Terminal("else", kind=TerminalKind.Keyword.Control.Conditional) ELSE = Terminal("else", highlight=Highlight.Keyword.Control.Conditional)
FOR = Terminal("for", kind=TerminalKind.Keyword.Control) FOR = Terminal("for", highlight=Highlight.Keyword.Control)
FUN = Terminal("fun", kind=TerminalKind.Storage.Type.Function) FUN = Terminal("fun", highlight=Highlight.Storage.Type.Function)
IDENTIFIER = Terminal( IDENTIFIER = Terminal(
Re.seq( Re.seq(
Re.set(("a", "z"), ("A", "Z"), "_"), Re.set(("a", "z"), ("A", "Z"), "_"),
Re.set(("a", "z"), ("A", "Z"), ("0", "9"), "_").star(), Re.set(("a", "z"), ("A", "Z"), ("0", "9"), "_").star(),
), ),
# kind=TerminalKind.Variable, #? # highlight=Highlight.Variable, #?
) )
IF = Terminal("if", kind=TerminalKind.Keyword.Control.Conditional) IF = Terminal("if", highlight=Highlight.Keyword.Control.Conditional)
IMPORT = Terminal("import", kind=TerminalKind.Keyword.Other) IMPORT = Terminal("import", highlight=Highlight.Keyword.Other)
IN = Terminal("in", kind=TerminalKind.Keyword.Operator) IN = Terminal("in", highlight=Highlight.Keyword.Operator)
LCURLY = Terminal("{", kind=TerminalKind.Punctuation.CurlyBrace.Open) LCURLY = Terminal("{", highlight=Highlight.Punctuation.CurlyBrace.Open)
RCURLY = Terminal("}", kind=TerminalKind.Punctuation.CurlyBrace.Close) RCURLY = Terminal("}", highlight=Highlight.Punctuation.CurlyBrace.Close)
LET = Terminal("let", kind=TerminalKind.Keyword.Other) LET = Terminal("let", highlight=Highlight.Keyword.Other)
RETURN = Terminal("return", kind=TerminalKind.Keyword.Control) RETURN = Terminal("return", highlight=Highlight.Keyword.Control)
SEMICOLON = Terminal(";", kind=TerminalKind.Punctuation.Separator) SEMICOLON = Terminal(";", highlight=Highlight.Punctuation.Separator)
STRING = Terminal( STRING = Terminal(
# Double-quoted string. # Double-quoted string.
Re.seq( Re.seq(
@ -365,27 +367,27 @@ class FineGrammar(Grammar):
(~Re.set("'", "\\") | (Re.set("\\") + Re.any())).star(), (~Re.set("'", "\\") | (Re.set("\\") + Re.any())).star(),
Re.literal("'"), Re.literal("'"),
), ),
kind=TerminalKind.String.Quoted, highlight=Highlight.String.Quoted,
) )
WHILE = Terminal("while", kind=TerminalKind.Keyword.Control) WHILE = Terminal("while", highlight=Highlight.Keyword.Control)
EQUAL = Terminal("=", kind=TerminalKind.Keyword.Operator.Expression) EQUAL = Terminal("=", highlight=Highlight.Keyword.Operator.Expression)
LPAREN = Terminal("(", kind=TerminalKind.Punctuation.Parenthesis.Open) LPAREN = Terminal("(", highlight=Highlight.Punctuation.Parenthesis.Open)
RPAREN = Terminal(")", kind=TerminalKind.Punctuation.Parenthesis.Close) RPAREN = Terminal(")", highlight=Highlight.Punctuation.Parenthesis.Close)
COMMA = Terminal(",", kind=TerminalKind.Punctuation.Separator) COMMA = Terminal(",", highlight=Highlight.Punctuation.Separator)
SELF = Terminal("self", name="SELFF", kind=TerminalKind.Variable.Language) SELF = Terminal("self", name="SELFF", highlight=Highlight.Variable.Language)
OR = Terminal("or", kind=TerminalKind.Keyword.Operator.Expression) OR = Terminal("or", highlight=Highlight.Keyword.Operator.Expression)
IS = Terminal("is", kind=TerminalKind.Keyword.Operator.Expression) IS = Terminal("is", highlight=Highlight.Keyword.Operator.Expression)
AND = Terminal("and", kind=TerminalKind.Keyword.Operator.Expression) AND = Terminal("and", highlight=Highlight.Keyword.Operator.Expression)
EQUALEQUAL = Terminal("==", kind=TerminalKind.Keyword.Operator.Expression) EQUALEQUAL = Terminal("==", highlight=Highlight.Keyword.Operator.Expression)
BANGEQUAL = Terminal("!=", kind=TerminalKind.Keyword.Operator.Expression) BANGEQUAL = Terminal("!=", highlight=Highlight.Keyword.Operator.Expression)
LESS = Terminal("<", kind=TerminalKind.Keyword.Operator.Expression) LESS = Terminal("<", highlight=Highlight.Keyword.Operator.Expression)
GREATER = Terminal(">", kind=TerminalKind.Keyword.Operator.Expression) GREATER = Terminal(">", highlight=Highlight.Keyword.Operator.Expression)
LESSEQUAL = Terminal("<=", kind=TerminalKind.Keyword.Operator.Expression) LESSEQUAL = Terminal("<=", highlight=Highlight.Keyword.Operator.Expression)
GREATEREQUAL = Terminal(">=", kind=TerminalKind.Keyword.Operator.Expression) GREATEREQUAL = Terminal(">=", highlight=Highlight.Keyword.Operator.Expression)
PLUS = Terminal("+", kind=TerminalKind.Keyword.Operator.Expression) PLUS = Terminal("+", highlight=Highlight.Keyword.Operator.Expression)
MINUS = Terminal("-", kind=TerminalKind.Keyword.Operator.Expression) MINUS = Terminal("-", highlight=Highlight.Keyword.Operator.Expression)
STAR = Terminal("*", kind=TerminalKind.Keyword.Operator.Expression) STAR = Terminal("*", highlight=Highlight.Keyword.Operator.Expression)
SLASH = Terminal("/", kind=TerminalKind.Keyword.Operator.Expression) SLASH = Terminal("/", highlight=Highlight.Keyword.Operator.Expression)
NUMBER = Terminal( NUMBER = Terminal(
Re.seq( Re.seq(
Re.set(("0", "9")).plus(), Re.set(("0", "9")).plus(),
@ -399,18 +401,18 @@ class FineGrammar(Grammar):
Re.set(("0", "9")).plus(), Re.set(("0", "9")).plus(),
).question(), ).question(),
), ),
kind=TerminalKind.Constant.Numeric, highlight=Highlight.Constant.Numeric,
) )
TRUE = Terminal("true", kind=TerminalKind.Constant.Language) TRUE = Terminal("true", highlight=Highlight.Constant.Language)
FALSE = Terminal("false", kind=TerminalKind.Constant.Language) FALSE = Terminal("false", highlight=Highlight.Constant.Language)
BANG = Terminal("!", kind=TerminalKind.Keyword.Operator.Expression) BANG = Terminal("!", highlight=Highlight.Keyword.Operator.Expression)
DOT = Terminal(".", kind=TerminalKind.Punctuation.Separator) DOT = Terminal(".", highlight=Highlight.Punctuation.Separator)
MATCH = Terminal("match", kind=TerminalKind.Keyword.Other) MATCH = Terminal("match", highlight=Highlight.Keyword.Other)
EXPORT = Terminal("export", kind=TerminalKind.Keyword.Other) EXPORT = Terminal("export", highlight=Highlight.Keyword.Other)
UNDERSCORE = Terminal("_", kind=TerminalKind.Variable.Language) UNDERSCORE = Terminal("_", highlight=Highlight.Variable.Language)
NEW = Terminal("new", kind=TerminalKind.Keyword.Operator) NEW = Terminal("new", highlight=Highlight.Keyword.Operator)
LSQUARE = Terminal("[", kind=TerminalKind.Punctuation.SquareBracket.Open) LSQUARE = Terminal("[", highlight=Highlight.Punctuation.SquareBracket.Open)
RSQUARE = Terminal("]", kind=TerminalKind.Punctuation.SquareBracket.Close) RSQUARE = Terminal("]", highlight=Highlight.Punctuation.SquareBracket.Close)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1715,6 +1715,19 @@ class NothingRule(Rule):
Nothing = NothingRule() Nothing = NothingRule()
class OptionalRule(Rule):
"""A rule that matches if one or another rule matches."""
def __init__(self, rule: Rule):
self.rule = rule
def flatten(self) -> typing.Generator[list[str | Terminal], None, None]:
# All the things from the left of the alternative, then all the things
# from the right, never intermingled.
yield from self.rule.flatten()
yield from Nothing.flatten()
def seq(*args: Rule) -> Rule: def seq(*args: Rule) -> Rule:
"""A rule that matches a sequence of rules. """A rule that matches a sequence of rules.
@ -1726,6 +1739,16 @@ def seq(*args: Rule) -> Rule:
return result return result
def opt(*args: Rule) -> Rule:
return OptionalRule(seq(*args))
def mark(rule: Rule, **kwargs) -> Rule:
# TODO: Figure out how to incorporate this into the world.
del kwargs
return rule
@typing.overload @typing.overload
def rule(f: typing.Callable, /) -> Rule: ... def rule(f: typing.Callable, /) -> Rule: ...
@ -2554,78 +2577,86 @@ def dump_lexer_table(table: LexerTable, name: str = "lexer.dot"):
# This here might be enough to produce extremely basic TextMate # This here might be enough to produce extremely basic TextMate
# grammars but anything more complicated will want tree patterns # grammars but anything more complicated will want tree patterns
# anyway, and we can only do tree patterns by influencing the grammar. # anyway, and we can only do tree patterns by influencing the grammar.
class TerminalMeta: class SyntaxMeta:
pass pass
class TerminalKind(TerminalMeta): class Highlight(SyntaxMeta):
class Comment(TerminalMeta): class Comment(SyntaxMeta):
class Block(TerminalMeta): class Block(SyntaxMeta):
pass pass
class Line(TerminalMeta): class Line(SyntaxMeta):
pass pass
class Constant(TerminalMeta): class Constant(SyntaxMeta):
class Language(TerminalMeta): class Language(SyntaxMeta):
pass pass
class Numeric(TerminalMeta): class Numeric(SyntaxMeta):
pass pass
class Keyword(TerminalMeta): class Entity(SyntaxMeta):
class Control(TerminalMeta): class Name(SyntaxMeta):
class Conditional(TerminalMeta): class Function(SyntaxMeta):
pass pass
class Operator(TerminalMeta): class Type(SyntaxMeta):
class Expression(TerminalMeta):
pass pass
class Other(TerminalMeta): class Keyword(SyntaxMeta):
class Control(SyntaxMeta):
class Conditional(SyntaxMeta):
pass
class Operator(SyntaxMeta):
class Expression(SyntaxMeta):
pass
class Other(SyntaxMeta):
pass pass
class Punctuation(TerminalMeta): class Punctuation(SyntaxMeta):
class Separator(TerminalMeta): class Separator(SyntaxMeta):
pass pass
class Parenthesis(TerminalMeta): class Parenthesis(SyntaxMeta):
class Open(TerminalMeta): class Open(SyntaxMeta):
pass pass
class Close(TerminalMeta): class Close(SyntaxMeta):
pass pass
class CurlyBrace(TerminalMeta): class CurlyBrace(SyntaxMeta):
class Open(TerminalMeta): class Open(SyntaxMeta):
pass pass
class Close(TerminalMeta): class Close(SyntaxMeta):
pass pass
class SquareBracket(TerminalMeta): class SquareBracket(SyntaxMeta):
class Open(TerminalMeta): class Open(SyntaxMeta):
pass pass
class Close(TerminalMeta): class Close(SyntaxMeta):
pass pass
class Storage(TerminalMeta): class Storage(SyntaxMeta):
class Type(TerminalMeta): class Type(SyntaxMeta):
class Class(TerminalMeta): class Class(SyntaxMeta):
pass pass
class Function(TerminalMeta): class Function(SyntaxMeta):
pass pass
class String(TerminalMeta): class String(SyntaxMeta):
class Quoted(TerminalMeta): class Quoted(SyntaxMeta):
class Single(TerminalMeta): class Single(SyntaxMeta):
pass pass
class Double(TerminalMeta): class Double(SyntaxMeta):
pass pass
class Variable(TerminalMeta): class Variable(SyntaxMeta):
class Language(TerminalMeta): class Language(SyntaxMeta):
pass pass