233 lines
5.3 KiB
Python
233 lines
5.3 KiB
Python
from parser import *
|
|
|
|
|
|
@rule
|
|
def expression():
|
|
return alt(
|
|
TRUE | FALSE | NUMBER | NAME,
|
|
LPAREN + expression + RPAREN,
|
|
binary_expression,
|
|
object_expression,
|
|
with_expression,
|
|
function_expression,
|
|
invoke_expression,
|
|
member_expression,
|
|
statement_block_expression,
|
|
)
|
|
|
|
|
|
@rule
|
|
def binary_expression():
|
|
return alt(
|
|
expression + PLUS + expression,
|
|
expression + STAR + expression,
|
|
expression + MINUS + expression,
|
|
expression + SLASH + expression,
|
|
expression + LESSER + expression,
|
|
expression + GREATER + expression,
|
|
expression + LESSER_EQUAL + expression,
|
|
expression + GREATER_EQUAL + expression,
|
|
expression + EQUAL_EQUAL + expression,
|
|
expression + AND + expression,
|
|
expression + OR + expression,
|
|
)
|
|
|
|
|
|
@rule
|
|
def object_expression():
|
|
return LCURLY + zero_or_more(value_pair + opt(COMMA)) + RCURLY
|
|
|
|
|
|
@rule
|
|
def value_pair():
|
|
return value_name + opt(EQUAL, expression)
|
|
|
|
|
|
@rule
|
|
def value_name():
|
|
return NAME | LSQUARE + expression + RSQUARE
|
|
|
|
|
|
@rule
|
|
def with_expression():
|
|
return WITH + object_expression + YIELD + expression
|
|
|
|
|
|
@rule
|
|
def function_expression():
|
|
return FN + opt(param_list) + FAT_ARROW + expression
|
|
|
|
|
|
@rule
|
|
def param_list():
|
|
return zero_or_more(NAME + COMMA) + NAME + opt(COMMA)
|
|
|
|
|
|
@rule
|
|
def invoke_expression():
|
|
return seq(
|
|
expression,
|
|
LPAREN,
|
|
opt(
|
|
zero_or_more(expression + COMMA),
|
|
expression + opt(COMMA),
|
|
),
|
|
RPAREN,
|
|
)
|
|
|
|
|
|
@rule
|
|
def member_expression():
|
|
return expression + DOT + NAME
|
|
|
|
|
|
@rule
|
|
def statement_block_expression():
|
|
return DO + statement_list + YIELD + expression
|
|
|
|
|
|
@rule
|
|
def statement_list():
|
|
return zero_or_more(_statement)
|
|
|
|
|
|
@rule
|
|
def _statement():
|
|
return alt(
|
|
assignment_statement,
|
|
if_statement,
|
|
declaration_statement,
|
|
while_loop,
|
|
)
|
|
|
|
|
|
@rule
|
|
def assignment_statement():
|
|
return (
|
|
NAME
|
|
+ alt(COLON_EQUAL, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL)
|
|
+ expression
|
|
+ SEMICOLON
|
|
)
|
|
|
|
|
|
@rule
|
|
def if_statement():
|
|
return IF + expression + THEN + statement_list + END
|
|
|
|
|
|
@rule
|
|
def declaration_statement():
|
|
return VAR + NAME + COLON_EQUAL + expression + SEMICOLON
|
|
|
|
|
|
@rule
|
|
def while_loop():
|
|
return WHILE + expression + DO + statement_list + END
|
|
|
|
|
|
BLANKS = Terminal("BLANKS", Re.set(" ", "\t", "\r", "\n").plus())
|
|
COMMENT = Terminal("COMMENT", Re.seq(Re.literal("//"), Re.set("\n").invert().star()))
|
|
|
|
TRUE = Terminal("TRUE", "true")
|
|
FALSE = Terminal("FALSE", "false")
|
|
|
|
NUMBER = Terminal(
|
|
"NUMBER",
|
|
Re.seq(
|
|
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,
|
|
)
|
|
|
|
FN = Terminal("FN", "fn")
|
|
ARROW = Terminal("ARROW", "->")
|
|
FAT_ARROW = Terminal("FAT_ARROW", "=>")
|
|
|
|
COMMA = Terminal("COMMA", ",")
|
|
LPAREN = Terminal("LPAREN", "(")
|
|
RPAREN = Terminal("RPAREN", ")")
|
|
LCURLY = Terminal("LCURLY", "{")
|
|
RCURLY = Terminal("RCURLY", "}")
|
|
LSQUARE = Terminal("LSQUARE", "[")
|
|
RSQUARE = Terminal("RSQUARE", "]")
|
|
COLON = Terminal("COLON", ":")
|
|
SEMICOLON = Terminal("SEMICOLON", ";")
|
|
LET = Terminal("LET", "let")
|
|
EQUAL = Terminal("EQUAL", "=")
|
|
RETURN = Terminal("RETURN", "return")
|
|
PLUS = Terminal("PLUS", "+")
|
|
PLUS_EQUAL = Terminal("PLUS_EQUAL", "+=")
|
|
MINUS = Terminal("MINUS", "-")
|
|
MINUS_EQUAL = Terminal("MINUS_EQUAL", "-=")
|
|
STAR = Terminal("STAR", "*")
|
|
STAR_EQUAL = Terminal("STAR_EQUAL", "*=")
|
|
SLASH = Terminal("SLASH", "/")
|
|
SLASH_EQUAL = Terminal("SLASH_EQUAL", "/=")
|
|
DOT = Terminal("DOT", ".")
|
|
|
|
# Design: A different assignment operator to make sure we
|
|
# don't get confused.
|
|
COLON_EQUAL = Terminal("COLON_EQUAL", ":=")
|
|
|
|
GREATER = Terminal("GREATER", ">")
|
|
GREATER_EQUAL = Terminal("GREATER_EQUAL", ">=")
|
|
LESSER = Terminal("LESSER", "<")
|
|
LESSER_EQUAL = Terminal("LESSER_EQUAL", "<=")
|
|
EQUAL_EQUAL = Terminal("EQUAL_EQUAL", "==")
|
|
AND = Terminal("AND", "and")
|
|
OR = Terminal("OR", "or")
|
|
NOT = Terminal("NOT", "not")
|
|
|
|
# I like `DO -> YIELD` instead of `BEGIN -> YIELD`
|
|
# A `YIELD` at the end instead of a generic "last expression"
|
|
# setup because I want to be explicit that blocks yield values
|
|
# and you don't get the wrong one by accident!
|
|
DO = Terminal("DO", "do")
|
|
IF = Terminal("IF", "if")
|
|
THEN = Terminal("THEN", "then")
|
|
END = Terminal("END", "end")
|
|
WHILE = Terminal("WHILE", "while")
|
|
|
|
# I just like explicit variable declaration, otherwise
|
|
# sometimes you don't mean to make a new variable but
|
|
# make a typeo and do. :(
|
|
#
|
|
# This also allows you to control shadowing.
|
|
VAR = Terminal("VAR", "var")
|
|
|
|
|
|
WITH = Terminal("WITH", "with")
|
|
YIELD = Terminal("YIELD", "yield")
|
|
|
|
NAME = Terminal(
|
|
"NAME",
|
|
Re.seq(
|
|
Re.set(("a", "z"), ("A", "Z"), "_"),
|
|
Re.set(("a", "z"), ("A", "Z"), ("0", "9"), "_").star(),
|
|
),
|
|
)
|
|
|
|
LensGrammar = Grammar(
|
|
name="Lens",
|
|
start=expression,
|
|
trivia=[BLANKS, COMMENT],
|
|
precedence=[
|
|
(Assoc.LEFT, [OR]),
|
|
(Assoc.LEFT, [AND]),
|
|
(Assoc.LEFT, [EQUAL_EQUAL, LESSER_EQUAL, LESSER, GREATER_EQUAL, GREATER]),
|
|
(Assoc.LEFT, [PLUS, MINUS]),
|
|
(Assoc.LEFT, [STAR, SLASH]),
|
|
(Assoc.LEFT, [LPAREN]),
|
|
(Assoc.LEFT, [DOT]),
|
|
],
|
|
)
|