[all] A whole new style for grammars
Say good by to the sea of `self.`!
This commit is contained in:
parent
d6f1e7aba1
commit
5064a768e7
10 changed files with 1097 additions and 1318 deletions
915
grammar.py
915
grammar.py
|
|
@ -20,503 +20,498 @@ from parser import (
|
|||
sp,
|
||||
)
|
||||
|
||||
@rule("File")
|
||||
def file() -> Rule:
|
||||
return _file_statement_list
|
||||
|
||||
class FineGrammar(Grammar):
|
||||
# generator = parser.GenerateLR1
|
||||
# generator = parser.GeneratePager
|
||||
start = "File"
|
||||
@rule
|
||||
def _file_statement_list() -> Rule:
|
||||
return alt(
|
||||
_file_statement,
|
||||
_file_statement_list + nl + _file_statement,
|
||||
)
|
||||
|
||||
trivia = ["BLANKS", "LINE_BREAK", "COMMENT"]
|
||||
@rule
|
||||
def _file_statement() -> Rule:
|
||||
return (
|
||||
import_statement | class_declaration | export_statement | _statement
|
||||
)
|
||||
|
||||
pretty_indent = " "
|
||||
@rule
|
||||
def import_statement() -> Rule:
|
||||
return group(
|
||||
IMPORT, sp, STRING, sp, AS, sp, IDENTIFIER, sp, SEMICOLON
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
precedence=[
|
||||
(Assoc.RIGHT, [self.EQUAL]),
|
||||
(Assoc.LEFT, [self.OR]),
|
||||
(Assoc.LEFT, [self.IS]),
|
||||
(Assoc.LEFT, [self.AND]),
|
||||
(Assoc.LEFT, [self.EQUALEQUAL, self.BANGEQUAL]),
|
||||
(Assoc.LEFT, [self.LESS, self.GREATER, self.GREATEREQUAL, self.LESSEQUAL]),
|
||||
(Assoc.LEFT, [self.PLUS, self.MINUS]),
|
||||
(Assoc.LEFT, [self.STAR, self.SLASH]),
|
||||
(Assoc.LEFT, [self.primary_expression]),
|
||||
(Assoc.LEFT, [self.LPAREN]),
|
||||
(Assoc.LEFT, [self.DOT]),
|
||||
#
|
||||
# If there's a confusion about whether to make an IF
|
||||
# statement or an expression, prefer the statement.
|
||||
#
|
||||
(Assoc.NONE, [self.if_statement]),
|
||||
],
|
||||
)
|
||||
@rule("ClassDeclaration")
|
||||
def class_declaration() -> Rule:
|
||||
return seq(
|
||||
group(
|
||||
CLASS,
|
||||
sp,
|
||||
mark(IDENTIFIER, field="name", highlight=highlight.entity.name.type),
|
||||
sp,
|
||||
LCURLY,
|
||||
),
|
||||
indent(nl, mark(opt(class_body), field="body")),
|
||||
nl,
|
||||
RCURLY,
|
||||
nl, # Extra newline at the end of the class
|
||||
)
|
||||
|
||||
@rule("File")
|
||||
def file(self) -> Rule:
|
||||
return self._file_statement_list
|
||||
@rule("ClassBody")
|
||||
def class_body() -> Rule:
|
||||
return _class_members
|
||||
|
||||
@rule
|
||||
def _file_statement_list(self) -> Rule:
|
||||
return alt(
|
||||
self._file_statement,
|
||||
self._file_statement_list + nl + self._file_statement,
|
||||
)
|
||||
@rule
|
||||
def _class_members() -> Rule:
|
||||
return _class_member | seq(_class_members, nl, _class_member)
|
||||
|
||||
@rule
|
||||
def _file_statement(self) -> Rule:
|
||||
return (
|
||||
self.import_statement | self.class_declaration | self.export_statement | self._statement
|
||||
)
|
||||
@rule
|
||||
def _class_member() -> Rule:
|
||||
return field_declaration | function_declaration
|
||||
|
||||
@rule
|
||||
def import_statement(self) -> Rule:
|
||||
return group(
|
||||
self.IMPORT, sp, self.STRING, sp, self.AS, sp, self.IDENTIFIER, sp, self.SEMICOLON
|
||||
)
|
||||
@rule("FieldDecl")
|
||||
def field_declaration() -> Rule:
|
||||
return group(IDENTIFIER, COLON, sp, type_expression, SEMICOLON)
|
||||
|
||||
@rule("ClassDeclaration")
|
||||
def class_declaration(self) -> Rule:
|
||||
return seq(
|
||||
group(
|
||||
self.CLASS,
|
||||
sp,
|
||||
mark(self.IDENTIFIER, field="name", highlight=highlight.entity.name.type),
|
||||
sp,
|
||||
self.LCURLY,
|
||||
),
|
||||
indent(nl, mark(opt(self.class_body), field="body")),
|
||||
nl,
|
||||
self.RCURLY,
|
||||
nl, # Extra newline at the end of the class
|
||||
)
|
||||
# Types
|
||||
@rule("TypeExpression")
|
||||
def type_expression() -> Rule:
|
||||
return alternate_type | type_identifier
|
||||
|
||||
@rule("ClassBody")
|
||||
def class_body(self) -> Rule:
|
||||
return self._class_members
|
||||
@rule("AlternateType")
|
||||
def alternate_type() -> Rule:
|
||||
return group(type_expression, sp, OR, sp, type_identifier)
|
||||
|
||||
@rule
|
||||
def _class_members(self) -> Rule:
|
||||
return self._class_member | seq(self._class_members, nl, self._class_member)
|
||||
@rule("TypeIdentifier")
|
||||
def type_identifier() -> Rule:
|
||||
return mark(IDENTIFIER, field="id", highlight=highlight.entity.name.type)
|
||||
|
||||
@rule
|
||||
def _class_member(self) -> Rule:
|
||||
return self.field_declaration | self.function_declaration
|
||||
@rule
|
||||
def export_statement() -> Rule:
|
||||
return alt(
|
||||
group(EXPORT, sp, class_declaration),
|
||||
group(EXPORT, sp, function_declaration),
|
||||
group(EXPORT, sp, let_statement),
|
||||
group(EXPORT, sp, export_list, SEMICOLON),
|
||||
)
|
||||
|
||||
@rule("FieldDecl")
|
||||
def field_declaration(self) -> Rule:
|
||||
return group(self.IDENTIFIER, self.COLON, sp, self.type_expression, self.SEMICOLON)
|
||||
@rule
|
||||
def export_list() -> Rule:
|
||||
return IDENTIFIER | seq(IDENTIFIER, COMMA, sp, export_list)
|
||||
|
||||
# Types
|
||||
@rule("TypeExpression")
|
||||
def type_expression(self) -> Rule:
|
||||
return self.alternate_type | self.type_identifier
|
||||
|
||||
@rule("AlternateType")
|
||||
def alternate_type(self) -> Rule:
|
||||
return group(self.type_expression, sp, self.OR, sp, self.type_identifier)
|
||||
|
||||
@rule("TypeIdentifier")
|
||||
def type_identifier(self) -> Rule:
|
||||
return mark(self.IDENTIFIER, field="id", highlight=highlight.entity.name.type)
|
||||
|
||||
@rule
|
||||
def export_statement(self) -> Rule:
|
||||
return alt(
|
||||
group(self.EXPORT, sp, self.class_declaration),
|
||||
group(self.EXPORT, sp, self.function_declaration),
|
||||
group(self.EXPORT, sp, self.let_statement),
|
||||
group(self.EXPORT, sp, self.export_list, self.SEMICOLON),
|
||||
)
|
||||
|
||||
@rule
|
||||
def export_list(self) -> Rule:
|
||||
return self.IDENTIFIER | seq(self.IDENTIFIER, self.COMMA, sp, self.export_list)
|
||||
|
||||
# Functions
|
||||
@rule("FunctionDecl")
|
||||
def function_declaration(self) -> Rule:
|
||||
return seq(
|
||||
# Functions
|
||||
@rule("FunctionDecl")
|
||||
def function_declaration() -> Rule:
|
||||
return seq(
|
||||
group(
|
||||
group(
|
||||
group(
|
||||
group(
|
||||
self.FUN,
|
||||
sp,
|
||||
mark(
|
||||
self.IDENTIFIER,
|
||||
field="name",
|
||||
highlight=highlight.entity.name.function,
|
||||
),
|
||||
FUN,
|
||||
sp,
|
||||
mark(
|
||||
IDENTIFIER,
|
||||
field="name",
|
||||
highlight=highlight.entity.name.function,
|
||||
),
|
||||
nl,
|
||||
mark(self.function_parameters, field="parameters"),
|
||||
),
|
||||
mark(
|
||||
opt(indent(sp, group(self.ARROW, sp, self.type_expression))),
|
||||
field="return_type",
|
||||
),
|
||||
),
|
||||
sp,
|
||||
mark(self.block, field="body"),
|
||||
nl,
|
||||
)
|
||||
|
||||
@rule("ParamList")
|
||||
def function_parameters(self) -> Rule:
|
||||
return group(
|
||||
self.LPAREN,
|
||||
indent(
|
||||
nl,
|
||||
opt(
|
||||
self._first_parameter
|
||||
| seq(self._first_parameter, self.COMMA)
|
||||
| group(self._first_parameter, self.COMMA, sp, self._parameter_list)
|
||||
),
|
||||
mark(function_parameters, field="parameters"),
|
||||
),
|
||||
mark(
|
||||
opt(indent(sp, group(ARROW, sp, type_expression))),
|
||||
field="return_type",
|
||||
),
|
||||
),
|
||||
sp,
|
||||
mark(block, field="body"),
|
||||
nl,
|
||||
)
|
||||
|
||||
@rule("ParamList")
|
||||
def function_parameters() -> Rule:
|
||||
return group(
|
||||
LPAREN,
|
||||
indent(
|
||||
nl,
|
||||
self.RPAREN,
|
||||
)
|
||||
|
||||
@rule
|
||||
def _first_parameter(self) -> Rule:
|
||||
return self.SELF | self.parameter
|
||||
|
||||
@rule
|
||||
def _parameter_list(self) -> Rule:
|
||||
return self.parameter | seq(self.parameter, self.COMMA, sp, self._parameter_list)
|
||||
|
||||
@rule("Parameter")
|
||||
def parameter(self) -> Rule:
|
||||
return group(self.IDENTIFIER, self.COLON, sp, self.type_expression)
|
||||
|
||||
# Block
|
||||
@rule("Block")
|
||||
def block(self) -> Rule:
|
||||
return alt(
|
||||
group(self.LCURLY, nl, self.RCURLY),
|
||||
group(self.LCURLY, indent(br, self.block_body), sp, self.RCURLY),
|
||||
)
|
||||
|
||||
@rule("BlockBody")
|
||||
def block_body(self) -> Rule:
|
||||
return alt(
|
||||
self.expression,
|
||||
self._statement_list,
|
||||
seq(self._statement_list, br, self.expression),
|
||||
)
|
||||
|
||||
@rule
|
||||
def _statement_list(self) -> Rule:
|
||||
return self._statement | seq(self._statement_list, br, self._statement)
|
||||
|
||||
@rule
|
||||
def _statement(self) -> Rule:
|
||||
return (
|
||||
self.function_declaration
|
||||
| self.let_statement
|
||||
| self.return_statement
|
||||
| self.for_statement
|
||||
| self.if_statement
|
||||
| self.while_statement
|
||||
| self.expression_statement
|
||||
)
|
||||
|
||||
@rule("LetStatement")
|
||||
def let_statement(self) -> Rule:
|
||||
return group(
|
||||
group(
|
||||
self.LET,
|
||||
sp,
|
||||
self.IDENTIFIER,
|
||||
sp,
|
||||
self.EQUAL,
|
||||
opt(
|
||||
_first_parameter
|
||||
| seq(_first_parameter, COMMA)
|
||||
| group(_first_parameter, COMMA, sp, _parameter_list)
|
||||
),
|
||||
indent(sp, self.expression, self.SEMICOLON),
|
||||
)
|
||||
),
|
||||
nl,
|
||||
RPAREN,
|
||||
)
|
||||
|
||||
@rule("ReturnStatement")
|
||||
def return_statement(self) -> Rule:
|
||||
return alt(
|
||||
group(self.RETURN, indent(sp, group(self.expression, self.SEMICOLON))),
|
||||
group(self.RETURN, self.SEMICOLON),
|
||||
)
|
||||
@rule
|
||||
def _first_parameter() -> Rule:
|
||||
return SELF | parameter
|
||||
|
||||
@rule("ForStatement")
|
||||
def for_statement(self) -> Rule:
|
||||
return group(
|
||||
group(self.FOR, sp, self.iterator_variable, sp, self.IN, sp, group(self.expression)),
|
||||
self.block,
|
||||
)
|
||||
@rule
|
||||
def _parameter_list() -> Rule:
|
||||
return parameter | seq(parameter, COMMA, sp, _parameter_list)
|
||||
|
||||
@rule("IteratorVariable")
|
||||
def iterator_variable(self) -> Rule:
|
||||
return self.IDENTIFIER
|
||||
@rule("Parameter")
|
||||
def parameter() -> Rule:
|
||||
return group(IDENTIFIER, COLON, sp, type_expression)
|
||||
|
||||
@rule("IfStatement")
|
||||
def if_statement(self) -> Rule:
|
||||
return self.conditional_expression
|
||||
# Block
|
||||
@rule("Block")
|
||||
def block() -> Rule:
|
||||
return alt(
|
||||
group(LCURLY, nl, RCURLY),
|
||||
group(LCURLY, indent(br, block_body), sp, RCURLY),
|
||||
)
|
||||
|
||||
@rule
|
||||
def while_statement(self) -> Rule:
|
||||
return group(group(self.WHILE, sp, self.expression), sp, self.block)
|
||||
@rule("BlockBody")
|
||||
def block_body() -> Rule:
|
||||
return alt(
|
||||
expression,
|
||||
_statement_list,
|
||||
seq(_statement_list, br, expression),
|
||||
)
|
||||
|
||||
@rule
|
||||
def expression_statement(self) -> Rule:
|
||||
return seq(self.expression, self.SEMICOLON)
|
||||
@rule
|
||||
def _statement_list() -> Rule:
|
||||
return _statement | seq(_statement_list, br, _statement)
|
||||
|
||||
# Expressions
|
||||
@rule(transparent=True)
|
||||
def expression(self) -> Rule:
|
||||
return self.binary_expression | self.is_expression | self.primary_expression
|
||||
@rule
|
||||
def _statement() -> Rule:
|
||||
return (
|
||||
function_declaration
|
||||
| let_statement
|
||||
| return_statement
|
||||
| for_statement
|
||||
| if_statement
|
||||
| while_statement
|
||||
| expression_statement
|
||||
)
|
||||
|
||||
@rule("BinaryExpression")
|
||||
def binary_expression(self) -> Rule:
|
||||
return alt(
|
||||
# Assignment gets special indentation.
|
||||
group(group(self.expression, sp, self.EQUAL), indent(sp, self.expression)),
|
||||
# Other ones do not.
|
||||
group(group(self.expression, sp, self.OR), sp, self.expression),
|
||||
group(group(self.expression, sp, self.AND), sp, self.expression),
|
||||
group(group(self.expression, sp, self.EQUALEQUAL), sp, self.expression),
|
||||
group(group(self.expression, sp, self.BANGEQUAL), sp, self.expression),
|
||||
group(group(self.expression, sp, self.LESS), sp, self.expression),
|
||||
group(group(self.expression, sp, self.LESSEQUAL), sp, self.expression),
|
||||
group(group(self.expression, sp, self.GREATER), sp, self.expression),
|
||||
group(group(self.expression, sp, self.GREATEREQUAL), sp, self.expression),
|
||||
group(group(self.expression, sp, self.PLUS), sp, self.expression),
|
||||
group(group(self.expression, sp, self.MINUS), sp, self.expression),
|
||||
group(group(self.expression, sp, self.STAR), sp, self.expression),
|
||||
group(group(self.expression, sp, self.SLASH), sp, self.expression),
|
||||
)
|
||||
|
||||
@rule("IsExpression")
|
||||
def is_expression(self) -> Rule:
|
||||
return group(self.expression, sp, self.IS, indent(sp, self.pattern))
|
||||
|
||||
@rule
|
||||
def primary_expression(self) -> Rule:
|
||||
return (
|
||||
self.identifier_expression
|
||||
| self.literal_expression
|
||||
| self.SELF
|
||||
| seq(self.BANG, self.primary_expression)
|
||||
| seq(self.MINUS, self.primary_expression)
|
||||
| self.block
|
||||
| self.conditional_expression
|
||||
| self.list_constructor_expression
|
||||
| self.object_constructor_expression
|
||||
| self.match_expression
|
||||
| seq(self.primary_expression, self.LPAREN, self.RPAREN)
|
||||
| group(
|
||||
self.primary_expression,
|
||||
self.LPAREN,
|
||||
indent(nl, self._expression_list),
|
||||
nl,
|
||||
self.RPAREN,
|
||||
)
|
||||
| group(self.primary_expression, indent(nl, self.DOT, self.IDENTIFIER))
|
||||
| group(self.LPAREN, indent(nl, self.expression), nl, self.RPAREN)
|
||||
)
|
||||
|
||||
@rule("IdentifierExpression")
|
||||
def identifier_expression(self):
|
||||
return self.IDENTIFIER
|
||||
|
||||
@rule("Literal")
|
||||
def literal_expression(self):
|
||||
return self.NUMBER | self.STRING | self.TRUE | self.FALSE
|
||||
|
||||
@rule("ConditionalExpression")
|
||||
def conditional_expression(self) -> Rule:
|
||||
return (
|
||||
seq(group(self.IF, sp, self.expression), sp, self.block)
|
||||
| seq(
|
||||
group(self.IF, sp, self.expression),
|
||||
sp,
|
||||
self.block,
|
||||
sp,
|
||||
self.ELSE,
|
||||
sp,
|
||||
self.conditional_expression,
|
||||
)
|
||||
| seq(
|
||||
group(self.IF, sp, self.expression), sp, self.block, sp, self.ELSE, sp, self.block
|
||||
)
|
||||
)
|
||||
|
||||
@rule
|
||||
def list_constructor_expression(self) -> Rule:
|
||||
return alt(
|
||||
group(self.LSQUARE, nl, self.RSQUARE),
|
||||
group(self.LSQUARE, indent(nl, self._expression_list), nl, self.RSQUARE),
|
||||
)
|
||||
|
||||
@rule
|
||||
def _expression_list(self) -> Rule:
|
||||
return (
|
||||
self.expression
|
||||
| seq(self.expression, self.COMMA)
|
||||
| seq(self.expression, self.COMMA, sp, self._expression_list)
|
||||
)
|
||||
|
||||
@rule
|
||||
def match_expression(self) -> Rule:
|
||||
return group(
|
||||
group(self.MATCH, sp, self.expression, sp, self.LCURLY),
|
||||
indent(sp, self.match_arms),
|
||||
@rule("LetStatement")
|
||||
def let_statement() -> Rule:
|
||||
return group(
|
||||
group(
|
||||
LET,
|
||||
sp,
|
||||
self.RCURLY,
|
||||
)
|
||||
|
||||
@rule("MatchArms")
|
||||
def match_arms(self) -> Rule:
|
||||
return self._match_arms
|
||||
|
||||
@rule
|
||||
def _match_arms(self) -> Rule:
|
||||
return (
|
||||
self.match_arm
|
||||
| seq(self.match_arm, self.COMMA)
|
||||
| seq(self.match_arm, self.COMMA, br, self._match_arms)
|
||||
)
|
||||
|
||||
@rule("MatchArm")
|
||||
def match_arm(self) -> Rule:
|
||||
return group(self.pattern, sp, self.ARROW, sp, self.expression)
|
||||
|
||||
@rule("Pattern")
|
||||
def pattern(self) -> Rule:
|
||||
return (
|
||||
group(self.variable_binding, self._pattern_core, sp, self.AND, sp, self.expression)
|
||||
| group(self.variable_binding, self._pattern_core)
|
||||
| self._pattern_core
|
||||
)
|
||||
|
||||
@rule
|
||||
def _pattern_core(self) -> Rule:
|
||||
return self.type_expression | self.wildcard_pattern
|
||||
|
||||
@rule("WildcardPattern")
|
||||
def wildcard_pattern(self) -> Rule:
|
||||
return self.UNDERSCORE
|
||||
|
||||
@rule("VariableBinding")
|
||||
def variable_binding(self) -> Rule:
|
||||
return seq(self.IDENTIFIER, self.COLON)
|
||||
|
||||
@rule
|
||||
def object_constructor_expression(self) -> Rule:
|
||||
return group(self.NEW, sp, self.type_identifier, sp, self.field_list)
|
||||
|
||||
@rule
|
||||
def field_list(self) -> Rule:
|
||||
return alt(
|
||||
seq(self.LCURLY, self.RCURLY),
|
||||
group(self.LCURLY, indent(nl, self.field_values), nl, self.RCURLY),
|
||||
)
|
||||
|
||||
@rule
|
||||
def field_values(self) -> Rule:
|
||||
return (
|
||||
self.field_value
|
||||
| seq(self.field_value, self.COMMA)
|
||||
| seq(self.field_value, self.COMMA, sp, self.field_values)
|
||||
)
|
||||
|
||||
@rule
|
||||
def field_value(self) -> Rule:
|
||||
return self.IDENTIFIER | group(self.IDENTIFIER, self.COLON, indent(sp, self.expression))
|
||||
|
||||
BLANKS = Terminal(Re.set(" ", "\t").plus())
|
||||
LINE_BREAK = Terminal(Re.set("\r", "\n"), trivia_mode=TriviaMode.NewLine)
|
||||
COMMENT = Terminal(
|
||||
Re.seq(Re.literal("//"), Re.set("\n").invert().star()),
|
||||
highlight=highlight.comment.line,
|
||||
trivia_mode=TriviaMode.LineComment,
|
||||
)
|
||||
|
||||
ARROW = Terminal("->", highlight=highlight.keyword.operator)
|
||||
AS = Terminal("as", highlight=highlight.keyword.operator.expression)
|
||||
BAR = Terminal("|", highlight=highlight.keyword.operator.expression)
|
||||
CLASS = Terminal("class", highlight=highlight.storage.type.klass)
|
||||
COLON = Terminal(":", highlight=highlight.punctuation.separator)
|
||||
ELSE = Terminal("else", highlight=highlight.keyword.control.conditional)
|
||||
FOR = Terminal("for", highlight=highlight.keyword.control)
|
||||
FUN = Terminal("fun", highlight=highlight.storage.type.function)
|
||||
IDENTIFIER = Terminal(
|
||||
Re.seq(
|
||||
Re.set(("a", "z"), ("A", "Z"), "_"),
|
||||
Re.set(("a", "z"), ("A", "Z"), ("0", "9"), "_").star(),
|
||||
IDENTIFIER,
|
||||
sp,
|
||||
EQUAL,
|
||||
),
|
||||
indent(sp, expression, SEMICOLON),
|
||||
)
|
||||
IF = Terminal("if", highlight=highlight.keyword.control.conditional)
|
||||
IMPORT = Terminal("import", highlight=highlight.keyword.other)
|
||||
IN = Terminal("in", highlight=highlight.keyword.operator)
|
||||
LCURLY = Terminal("{", highlight=highlight.punctuation.curly_brace.open)
|
||||
RCURLY = Terminal("}", highlight=highlight.punctuation.curly_brace.close)
|
||||
LET = Terminal("let", highlight=highlight.keyword.other)
|
||||
RETURN = Terminal("return", highlight=highlight.keyword.control)
|
||||
SEMICOLON = Terminal(";", highlight=highlight.punctuation.separator)
|
||||
STRING = Terminal(
|
||||
# Double-quoted string.
|
||||
Re.seq(
|
||||
Re.literal('"'),
|
||||
(~Re.set('"', "\\") | (Re.set("\\") + Re.any())).star(),
|
||||
Re.literal('"'),
|
||||
|
||||
@rule("ReturnStatement")
|
||||
def return_statement() -> Rule:
|
||||
return alt(
|
||||
group(RETURN, indent(sp, group(expression, SEMICOLON))),
|
||||
group(RETURN, SEMICOLON),
|
||||
)
|
||||
|
||||
@rule("ForStatement")
|
||||
def for_statement() -> Rule:
|
||||
return group(
|
||||
group(FOR, sp, iterator_variable, sp, IN, sp, group(expression)),
|
||||
block,
|
||||
)
|
||||
|
||||
@rule("IteratorVariable")
|
||||
def iterator_variable() -> Rule:
|
||||
return IDENTIFIER
|
||||
|
||||
@rule("IfStatement")
|
||||
def if_statement() -> Rule:
|
||||
return conditional_expression
|
||||
|
||||
@rule
|
||||
def while_statement() -> Rule:
|
||||
return group(group(WHILE, sp, expression), sp, block)
|
||||
|
||||
@rule
|
||||
def expression_statement() -> Rule:
|
||||
return seq(expression, SEMICOLON)
|
||||
|
||||
# Expressions
|
||||
@rule(transparent=True)
|
||||
def expression() -> Rule:
|
||||
return binary_expression | is_expression | primary_expression
|
||||
|
||||
@rule("BinaryExpression")
|
||||
def binary_expression() -> Rule:
|
||||
return alt(
|
||||
# Assignment gets special indentation.
|
||||
group(group(expression, sp, EQUAL), indent(sp, expression)),
|
||||
# Other ones do not.
|
||||
group(group(expression, sp, OR), sp, expression),
|
||||
group(group(expression, sp, AND), sp, expression),
|
||||
group(group(expression, sp, EQUALEQUAL), sp, expression),
|
||||
group(group(expression, sp, BANGEQUAL), sp, expression),
|
||||
group(group(expression, sp, LESS), sp, expression),
|
||||
group(group(expression, sp, LESSEQUAL), sp, expression),
|
||||
group(group(expression, sp, GREATER), sp, expression),
|
||||
group(group(expression, sp, GREATEREQUAL), sp, expression),
|
||||
group(group(expression, sp, PLUS), sp, expression),
|
||||
group(group(expression, sp, MINUS), sp, expression),
|
||||
group(group(expression, sp, STAR), sp, expression),
|
||||
group(group(expression, sp, SLASH), sp, expression),
|
||||
)
|
||||
|
||||
@rule("IsExpression")
|
||||
def is_expression() -> Rule:
|
||||
return group(expression, sp, IS, indent(sp, pattern))
|
||||
|
||||
@rule
|
||||
def primary_expression() -> Rule:
|
||||
return (
|
||||
identifier_expression
|
||||
| literal_expression
|
||||
| SELF
|
||||
| seq(BANG, primary_expression)
|
||||
| seq(MINUS, primary_expression)
|
||||
| block
|
||||
| conditional_expression
|
||||
| list_constructor_expression
|
||||
| object_constructor_expression
|
||||
| match_expression
|
||||
| seq(primary_expression, LPAREN, RPAREN)
|
||||
| group(
|
||||
primary_expression,
|
||||
LPAREN,
|
||||
indent(nl, _expression_list),
|
||||
nl,
|
||||
RPAREN,
|
||||
)
|
||||
# Single-quoted string.
|
||||
| Re.seq(
|
||||
Re.literal("'"),
|
||||
(~Re.set("'", "\\") | (Re.set("\\") + Re.any())).star(),
|
||||
Re.literal("'"),
|
||||
),
|
||||
highlight=highlight.string.quoted,
|
||||
| group(primary_expression, indent(nl, DOT, IDENTIFIER))
|
||||
| group(LPAREN, indent(nl, expression), nl, RPAREN)
|
||||
)
|
||||
WHILE = Terminal("while", highlight=highlight.keyword.control)
|
||||
EQUAL = Terminal("=", highlight=highlight.keyword.operator.expression)
|
||||
LPAREN = Terminal("(", highlight=highlight.punctuation.parenthesis.open)
|
||||
RPAREN = Terminal(")", highlight=highlight.punctuation.parenthesis.close)
|
||||
COMMA = Terminal(",", highlight=highlight.punctuation.separator)
|
||||
SELF = Terminal("self", name="SELFF", highlight=highlight.variable.language)
|
||||
OR = Terminal("or", highlight=highlight.keyword.operator.expression)
|
||||
IS = Terminal("is", highlight=highlight.keyword.operator.expression)
|
||||
AND = Terminal("and", highlight=highlight.keyword.operator.expression)
|
||||
EQUALEQUAL = Terminal("==", highlight=highlight.keyword.operator.expression)
|
||||
BANGEQUAL = Terminal("!=", highlight=highlight.keyword.operator.expression)
|
||||
LESS = Terminal("<", highlight=highlight.keyword.operator.expression)
|
||||
GREATER = Terminal(">", highlight=highlight.keyword.operator.expression)
|
||||
LESSEQUAL = Terminal("<=", highlight=highlight.keyword.operator.expression)
|
||||
GREATEREQUAL = Terminal(">=", highlight=highlight.keyword.operator.expression)
|
||||
PLUS = Terminal("+", highlight=highlight.keyword.operator.expression)
|
||||
MINUS = Terminal("-", highlight=highlight.keyword.operator.expression)
|
||||
STAR = Terminal("*", highlight=highlight.keyword.operator.expression)
|
||||
SLASH = Terminal("/", highlight=highlight.keyword.operator.expression)
|
||||
NUMBER = Terminal(
|
||||
|
||||
@rule("IdentifierExpression")
|
||||
def identifier_expression():
|
||||
return IDENTIFIER
|
||||
|
||||
@rule("Literal")
|
||||
def literal_expression():
|
||||
return NUMBER | STRING | TRUE | FALSE
|
||||
|
||||
@rule("ConditionalExpression")
|
||||
def conditional_expression() -> Rule:
|
||||
return (
|
||||
seq(group(IF, sp, expression), sp, block)
|
||||
| seq(
|
||||
group(IF, sp, expression),
|
||||
sp,
|
||||
block,
|
||||
sp,
|
||||
ELSE,
|
||||
sp,
|
||||
conditional_expression,
|
||||
)
|
||||
| seq(
|
||||
group(IF, sp, expression), sp, block, sp, ELSE, sp, block
|
||||
)
|
||||
)
|
||||
|
||||
@rule
|
||||
def list_constructor_expression() -> Rule:
|
||||
return alt(
|
||||
group(LSQUARE, nl, RSQUARE),
|
||||
group(LSQUARE, indent(nl, _expression_list), nl, RSQUARE),
|
||||
)
|
||||
|
||||
@rule
|
||||
def _expression_list() -> Rule:
|
||||
return (
|
||||
expression
|
||||
| seq(expression, COMMA)
|
||||
| seq(expression, COMMA, sp, _expression_list)
|
||||
)
|
||||
|
||||
@rule
|
||||
def match_expression() -> Rule:
|
||||
return group(
|
||||
group(MATCH, sp, expression, sp, LCURLY),
|
||||
indent(sp, match_arms),
|
||||
sp,
|
||||
RCURLY,
|
||||
)
|
||||
|
||||
@rule("MatchArms")
|
||||
def match_arms() -> Rule:
|
||||
return _match_arms
|
||||
|
||||
@rule
|
||||
def _match_arms() -> Rule:
|
||||
return (
|
||||
match_arm
|
||||
| seq(match_arm, COMMA)
|
||||
| seq(match_arm, COMMA, br, _match_arms)
|
||||
)
|
||||
|
||||
@rule("MatchArm")
|
||||
def match_arm() -> Rule:
|
||||
return group(pattern, sp, ARROW, sp, expression)
|
||||
|
||||
@rule("Pattern")
|
||||
def pattern() -> Rule:
|
||||
return (
|
||||
group(variable_binding, _pattern_core, sp, AND, sp, expression)
|
||||
| group(variable_binding, _pattern_core)
|
||||
| _pattern_core
|
||||
)
|
||||
|
||||
@rule
|
||||
def _pattern_core() -> Rule:
|
||||
return type_expression | wildcard_pattern
|
||||
|
||||
@rule("WildcardPattern")
|
||||
def wildcard_pattern() -> Rule:
|
||||
return UNDERSCORE
|
||||
|
||||
@rule("VariableBinding")
|
||||
def variable_binding() -> Rule:
|
||||
return seq(IDENTIFIER, COLON)
|
||||
|
||||
@rule
|
||||
def object_constructor_expression() -> Rule:
|
||||
return group(NEW, sp, type_identifier, sp, field_list)
|
||||
|
||||
@rule
|
||||
def field_list() -> Rule:
|
||||
return alt(
|
||||
seq(LCURLY, RCURLY),
|
||||
group(LCURLY, indent(nl, field_values), nl, RCURLY),
|
||||
)
|
||||
|
||||
@rule
|
||||
def field_values() -> Rule:
|
||||
return (
|
||||
field_value
|
||||
| seq(field_value, COMMA)
|
||||
| seq(field_value, COMMA, sp, field_values)
|
||||
)
|
||||
|
||||
@rule
|
||||
def field_value() -> Rule:
|
||||
return IDENTIFIER | group(IDENTIFIER, COLON, indent(sp, expression))
|
||||
|
||||
BLANKS = Terminal("BLANKS", Re.set(" ", "\t").plus())
|
||||
LINE_BREAK = Terminal("LINE_BREAK", Re.set("\r", "\n"), trivia_mode=TriviaMode.NewLine)
|
||||
COMMENT = Terminal(
|
||||
"COMMENT",
|
||||
Re.seq(Re.literal("//"), Re.set("\n").invert().star()),
|
||||
highlight=highlight.comment.line,
|
||||
trivia_mode=TriviaMode.LineComment,
|
||||
)
|
||||
|
||||
ARROW = Terminal("ARROW", "->", highlight=highlight.keyword.operator)
|
||||
AS = Terminal("AS", "as", highlight=highlight.keyword.operator.expression)
|
||||
BAR = Terminal("BAR", "|", highlight=highlight.keyword.operator.expression)
|
||||
CLASS = Terminal("CLASS", "class", highlight=highlight.storage.type.klass)
|
||||
COLON = Terminal("COLON", ":", highlight=highlight.punctuation.separator)
|
||||
ELSE = Terminal("ELSE", "else", highlight=highlight.keyword.control.conditional)
|
||||
FOR = Terminal("FOR", "for", highlight=highlight.keyword.control)
|
||||
FUN = Terminal("FUN", "fun", highlight=highlight.storage.type.function)
|
||||
IDENTIFIER = Terminal(
|
||||
"IDENTIFIER",
|
||||
Re.seq(
|
||||
Re.set(("a", "z"), ("A", "Z"), "_"),
|
||||
Re.set(("a", "z"), ("A", "Z"), ("0", "9"), "_").star(),
|
||||
),
|
||||
)
|
||||
IF = Terminal("IF", "if", highlight=highlight.keyword.control.conditional)
|
||||
IMPORT = Terminal("IMPORT", "import", highlight=highlight.keyword.other)
|
||||
IN = Terminal("IN", "in", highlight=highlight.keyword.operator)
|
||||
LCURLY = Terminal("LCURLY", "{", highlight=highlight.punctuation.curly_brace.open)
|
||||
RCURLY = Terminal("RCURLY", "}", highlight=highlight.punctuation.curly_brace.close)
|
||||
LET = Terminal("LET", "let", highlight=highlight.keyword.other)
|
||||
RETURN = Terminal("RETURN", "return", highlight=highlight.keyword.control)
|
||||
SEMICOLON = Terminal("SEMICOLON", ";", highlight=highlight.punctuation.separator)
|
||||
STRING = Terminal(
|
||||
"STRING",
|
||||
# Double-quoted string.
|
||||
Re.seq(
|
||||
Re.literal('"'),
|
||||
(~Re.set('"', "\\") | (Re.set("\\") + Re.any())).star(),
|
||||
Re.literal('"'),
|
||||
)
|
||||
# Single-quoted string.
|
||||
| Re.seq(
|
||||
Re.literal("'"),
|
||||
(~Re.set("'", "\\") | (Re.set("\\") + Re.any())).star(),
|
||||
Re.literal("'"),
|
||||
),
|
||||
highlight=highlight.string.quoted,
|
||||
)
|
||||
WHILE = Terminal("WHILE", "while", highlight=highlight.keyword.control)
|
||||
EQUAL = Terminal("EQUAL", "=", highlight=highlight.keyword.operator.expression)
|
||||
LPAREN = Terminal("LPAREN", "(", highlight=highlight.punctuation.parenthesis.open)
|
||||
RPAREN = Terminal("RPAREN", ")", highlight=highlight.punctuation.parenthesis.close)
|
||||
COMMA = Terminal("COMMA", ",", highlight=highlight.punctuation.separator)
|
||||
SELF = Terminal("SELFF", "self", highlight=highlight.variable.language)
|
||||
OR = Terminal("OR", "or", highlight=highlight.keyword.operator.expression)
|
||||
IS = Terminal("IS", "is", highlight=highlight.keyword.operator.expression)
|
||||
AND = Terminal("AND", "and", highlight=highlight.keyword.operator.expression)
|
||||
EQUALEQUAL = Terminal("EQUALEQUAL", "==", highlight=highlight.keyword.operator.expression)
|
||||
BANGEQUAL = Terminal("BANGEQUAL", "!=", highlight=highlight.keyword.operator.expression)
|
||||
LESS = Terminal("LESS", "<", highlight=highlight.keyword.operator.expression)
|
||||
GREATER = Terminal("GREATER", ">", highlight=highlight.keyword.operator.expression)
|
||||
LESSEQUAL = Terminal("LESSEQUAL", "<=", highlight=highlight.keyword.operator.expression)
|
||||
GREATEREQUAL = Terminal("GREATEREQUAL", ">=", highlight=highlight.keyword.operator.expression)
|
||||
PLUS = Terminal("PLUS", "+", highlight=highlight.keyword.operator.expression)
|
||||
MINUS = Terminal("MINUS", "-", highlight=highlight.keyword.operator.expression)
|
||||
STAR = Terminal("STAR", "*", highlight=highlight.keyword.operator.expression)
|
||||
SLASH = Terminal("SLASH", "/", highlight=highlight.keyword.operator.expression)
|
||||
NUMBER = Terminal(
|
||||
"NUMBER",
|
||||
Re.seq(
|
||||
Re.set(("0", "9")).plus(),
|
||||
Re.seq(
|
||||
Re.literal("."),
|
||||
Re.set(("0", "9")).plus(),
|
||||
Re.seq(
|
||||
Re.literal("."),
|
||||
Re.set(("0", "9")).plus(),
|
||||
).question(),
|
||||
Re.seq(
|
||||
Re.set("e", "E"),
|
||||
Re.set("+", "-").question(),
|
||||
Re.set(("0", "9")).plus(),
|
||||
).question(),
|
||||
),
|
||||
highlight=highlight.constant.numeric,
|
||||
)
|
||||
TRUE = Terminal("true", highlight=highlight.constant.language)
|
||||
FALSE = Terminal("false", highlight=highlight.constant.language)
|
||||
BANG = Terminal("!", highlight=highlight.keyword.operator.expression)
|
||||
DOT = Terminal(".", highlight=highlight.punctuation.separator)
|
||||
MATCH = Terminal("match", highlight=highlight.keyword.other)
|
||||
EXPORT = Terminal("export", highlight=highlight.keyword.other)
|
||||
UNDERSCORE = Terminal("_", highlight=highlight.variable.language)
|
||||
NEW = Terminal("new", highlight=highlight.keyword.operator)
|
||||
LSQUARE = Terminal("[", highlight=highlight.punctuation.square_bracket.open)
|
||||
RSQUARE = Terminal("]", highlight=highlight.punctuation.square_bracket.close)
|
||||
).question(),
|
||||
Re.seq(
|
||||
Re.set("e", "E"),
|
||||
Re.set("+", "-").question(),
|
||||
Re.set(("0", "9")).plus(),
|
||||
).question(),
|
||||
),
|
||||
highlight=highlight.constant.numeric,
|
||||
)
|
||||
TRUE = Terminal("TRUE", "true", highlight=highlight.constant.language)
|
||||
FALSE = Terminal("FALSE", "false", highlight=highlight.constant.language)
|
||||
BANG = Terminal("BANG", "!", highlight=highlight.keyword.operator.expression)
|
||||
DOT = Terminal("DOT", ".", highlight=highlight.punctuation.separator)
|
||||
MATCH = Terminal("MATCH", "match", highlight=highlight.keyword.other)
|
||||
EXPORT = Terminal("EXPORT", "export", highlight=highlight.keyword.other)
|
||||
UNDERSCORE = Terminal("UNDERSCORE", "_", highlight=highlight.variable.language)
|
||||
NEW = Terminal("NEW", "new", highlight=highlight.keyword.operator)
|
||||
LSQUARE = Terminal("LSQUARE", "[", highlight=highlight.punctuation.square_bracket.open)
|
||||
RSQUARE = Terminal("RSQUARE", "]", highlight=highlight.punctuation.square_bracket.close)
|
||||
|
||||
FineGrammar=Grammar(
|
||||
start=file,
|
||||
trivia=[BLANKS, LINE_BREAK, COMMENT],
|
||||
pretty_indent=" ",
|
||||
precedence=[
|
||||
(Assoc.RIGHT, [EQUAL]),
|
||||
(Assoc.LEFT, [OR]),
|
||||
(Assoc.LEFT, [IS]),
|
||||
(Assoc.LEFT, [AND]),
|
||||
(Assoc.LEFT, [EQUALEQUAL, BANGEQUAL]),
|
||||
(Assoc.LEFT, [LESS, GREATER, GREATEREQUAL, LESSEQUAL]),
|
||||
(Assoc.LEFT, [PLUS, MINUS]),
|
||||
(Assoc.LEFT, [STAR, SLASH]),
|
||||
(Assoc.LEFT, [primary_expression]),
|
||||
(Assoc.LEFT, [LPAREN]),
|
||||
(Assoc.LEFT, [DOT]),
|
||||
#
|
||||
# If there's a confusion about whether to make an IF
|
||||
# statement or an expression, prefer the statement.
|
||||
#
|
||||
(Assoc.NONE, [if_statement]),
|
||||
],
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from pathlib import Path
|
||||
|
|
@ -525,7 +520,7 @@ if __name__ == "__main__":
|
|||
from parser.tree_sitter import emit_tree_sitter_grammar, emit_tree_sitter_queries
|
||||
|
||||
# TODO: Actually generate a lexer/parser for some runtime.
|
||||
grammar = FineGrammar()
|
||||
grammar = FineGrammar
|
||||
|
||||
table = grammar.build_table()
|
||||
# print(table.format())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue