Files
Gwendolyn/gwendolyn/funcs/other/roll/parser.py
Nikolaj 2f4e606fbf
2024-10-30 15:31:46 +01:00

134 lines
4.7 KiB
Python

from rply import ParserGenerator
from lexer import TOKENS
from ast_nodes import Exp, ExpInt, ExpBinop, ExpLet, ExpVar, ExpMin, ExpMax, ExpRoll, Roll, RollKeepHighest, RollKeepLowest, RollMin, RollMax, RollExplode, ComparePoint, ExpIf, ExpTest, ExpApply, ExpLambda, ExpNeg
class Parser():
def __init__(self):
self.pg = ParserGenerator(
[i[0] for i in TOKENS],
precedence=[
('left', ["SYMBOL_BACKSLASH","SYMBOL_ARROW"]),
('left', ["KEYWORD_LET", "KEYWORD_IN", "KEYWORD_IF", "KEYWORD_THEN", "KEYWORD_ELSE"]),
('left', ["SYMBOL_EQUALS", "SYMBOL_LE"]),
('left', ["SYMBOL_PLUS", "SYMBOL_MINUS"]),
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]),
('left', ["ROLL_DIE"]),
('right', ["ROLL_KEEP_HIGHEST","ROLL_KEEP_LOWEST","ROLL_MIN","ROLL_MAX","ROLL_EXPLODE"])
]
)
self._get_parser()
def parse(self, token_input):
return self.parser.parse(token_input)
def _get_parser(self) -> Exp:
# Expressions
@self.pg.production('exp : exp SYMBOL_PLUS exp')
@self.pg.production('exp : exp SYMBOL_MINUS exp')
@self.pg.production('exp : exp SYMBOL_TIMES exp')
@self.pg.production('exp : exp SYMBOL_DIVIDE exp')
def exp_mul_div(tokens):
return ExpBinop(tokens[1].value, tokens[0], tokens[2])
@self.pg.production('exp : atom')
def exp_atom(tokens):
return tokens[0]
@self.pg.production("exp : SYMBOL_MINUS atom")
def exp_neg(tokens):
return ExpNeg(tokens[1])
@self.pg.production('exp : KEYWORD_LET ID SYMBOL_EQUALS exp KEYWORD_IN exp')
def exp_let(tokens):
return ExpLet(tokens[1].value, tokens[3], tokens[5])
@self.pg.production('exp : roll')
def exp_roll(tokens):
return ExpRoll(tokens[0])
@self.pg.production("exp : atom ROLL_MIN atom")
def exp_min(tokens):
return ExpMin(tokens[0], tokens[2])
@self.pg.production("exp : atom ROLL_MAX atom")
def exp_max(tokens):
return ExpMax(tokens[0], tokens[2])
@self.pg.production("exp : atom comp")
def exp_test(tokens):
return ExpTest(tokens[0], tokens[1])
@self.pg.production("exp : KEYWORD_IF exp KEYWORD_THEN exp KEYWORD_ELSE exp")
def exp_if(tokens):
return ExpIf(tokens[1],tokens[3],tokens[5])
@self.pg.production("exp : SYMBOL_BACKSLASH ID SYMBOL_ARROW exp")
def exp_lamda(tokens):
return ExpLambda(tokens[1].value, tokens[3])
@self.pg.production("exp : atom atom")
def apply(tokens):
return ExpApply(tokens[0],tokens[1])
# Rolls
@self.pg.production('roll : roll ROLL_KEEP_HIGHEST atom')
def roll_keep_highest(tokens):
return RollKeepHighest(tokens[0], tokens[2])
@self.pg.production('roll : roll ROLL_KEEP_LOWEST atom')
def roll_keep_lowest(tokens):
return RollKeepLowest(tokens[0], tokens[2])
@self.pg.production('roll : roll ROLL_MIN atom')
def roll_min(tokens):
return RollMin(tokens[0], tokens[2])
@self.pg.production('roll : roll ROLL_MAX atom')
def roll_max(tokens):
return RollMax(tokens[0], tokens[2])
@self.pg.production('roll : roll ROLL_EXPLODE ')
def roll_explode(tokens):
return RollExplode(tokens[0])
@self.pg.production('roll : roll ROLL_EXPLODE comp')
def roll_explode_comp(tokens):
return RollExplode(tokens[0], tokens[2])
@self.pg.production('roll : atom ROLL_DIE atom')
def roll(tokens):
return Roll(tokens[0], tokens[2])
@self.pg.production('roll : ROLL_DIE atom')
def roll_no_amount(tokens):
return Roll(ExpInt(1), tokens[1])
# Compare Points
@self.pg.production("comp : SYMBOL_EQUALS atom")
@self.pg.production("comp : SYMBOL_LE atom")
def comp_point(tokens):
return ComparePoint(tokens[0].value,tokens[1])
# Atoms
@self.pg.production("atom : INT")
def atom_int(tokens):
return ExpInt(int(tokens[0].value))
@self.pg.production("atom : SYMBOL_LPARENS exp SYMBOL_RPARENS")
def atom_exp(tokens):
return tokens[1]
@self.pg.production('atom : ID')
def atom_var(tokens):
return ExpVar(tokens[0].value)
## Error Handling ##
@self.pg.error
def error_handle(token):
raise Exception(f"Unexpected token '{token.value}' ({token.name}) at line {token.source_pos.lineno}, column {token.source_pos.colno}.")
## Finish ##
self.parser = self.pg.build()