233 lines
6.0 KiB
Python
233 lines
6.0 KiB
Python
"""The parser"""
|
|
|
|
from functools import reduce
|
|
|
|
from .ast import *
|
|
from .parser_combinator import *
|
|
from .tokenize import *
|
|
|
|
ARITHMETIC_PRECEDENCE = [
|
|
['*', '/'],
|
|
['+', '-'],
|
|
]
|
|
|
|
BOOLEAN_PRECEDENCE = [
|
|
['and'],
|
|
['or'],
|
|
]
|
|
|
|
#Helper
|
|
def precedence(value_parser, precedences, combine):
|
|
def op_parser(precedence):
|
|
return any_op_in_list(precedence) ^ combine
|
|
parser = value_parser * op_parser(precedences[0])
|
|
for precedence in precedences[1:]:
|
|
parser = parser * op_parser(precedence)
|
|
return parser
|
|
|
|
def process_binop(op):
|
|
"""
|
|
Takes a binary operation as input and
|
|
returns a function that build AST nodes
|
|
given the operands.
|
|
|
|
op -- the operation
|
|
|
|
returns -- a function that takes two operands
|
|
and returns a Binary Arithmetic AST
|
|
node
|
|
"""
|
|
return lambda l, r: BinArithmeticExp(op, l, r)
|
|
|
|
def process_relop(parsed):
|
|
""""
|
|
Takes a parsed relational operation and returns
|
|
an AST node.
|
|
|
|
parsed -- a tuple of the form ((left, operation), right)
|
|
|
|
returns -- an AST node representing the expression
|
|
"""
|
|
((left, op), right) = parsed
|
|
return RelationExp(op, left, right)
|
|
|
|
def process_logic(op):
|
|
"""
|
|
Takes a logical operation and returns a function
|
|
consuming two operands, building an AST node from them.
|
|
|
|
op -- the operation
|
|
|
|
returns -- a function that builds Logical Expression
|
|
AST nodes
|
|
|
|
throws -- RuntimeError on unknown operator
|
|
"""
|
|
if op == 'and':
|
|
return lambda l, r: AndExp(l, r)
|
|
elif op == 'or':
|
|
return lambda l, r: OrExp(l, r)
|
|
else:
|
|
raise RuntimeError('unknown logic operator: ' + op)
|
|
|
|
def process_group(parsed):
|
|
((_, p), _) = parsed
|
|
return p
|
|
|
|
def any_op_in_list(ops):
|
|
"""
|
|
Builds a keyword parser from all given operations.
|
|
|
|
ops -- a list of operators
|
|
|
|
returns -- a parser matching all operators
|
|
"""
|
|
op_parsers = [keyword(op) for op in ops]
|
|
return reduce(lambda l, r: l | r, op_parsers)
|
|
|
|
#Parser
|
|
num = Tag(INT) ^ (lambda i: int(i))
|
|
imp_id = Tag(ID)
|
|
|
|
def keyword(kw):
|
|
"""
|
|
The keyword parser. Takes a keyword
|
|
and returns a Reserved AST node.
|
|
|
|
kw -- the operation as string
|
|
|
|
returns -- a corresponding Reserved AST node
|
|
"""
|
|
return Reserved(kw, RESERVED)
|
|
|
|
def arithmetic_group():
|
|
"""
|
|
Matches groups of arithmetic operations.
|
|
|
|
returns -- a parser matching grouped arithmetic operations
|
|
"""
|
|
return keyword('(') + Lazy(arithmetic_exp) + keyword(')') ^ process_group
|
|
|
|
def arithmetic_value():
|
|
"""
|
|
Matches numbers and variables.
|
|
|
|
returns -- a parser matching all kinds of values
|
|
"""
|
|
return ((num ^ (lambda i: IntArithmeticExp(i))) |
|
|
(imp_id ^ (lambda v: VarArithmeticExp(v))))
|
|
|
|
def arithmetic_term():
|
|
"""
|
|
Matches an arithmetic term.
|
|
|
|
returns -- a parser matching an arithmetic value or group
|
|
"""
|
|
return arithmetic_value() | arithmetic_group()
|
|
|
|
def arithmetic_exp():
|
|
"""
|
|
Matches arithmetic expressions, valuing precedence.
|
|
|
|
returns -- a parser correctly matching arithmetic expressions
|
|
"""
|
|
return precedence(arithmetic_term(),
|
|
ARITHMETIC_PRECEDENCE,
|
|
process_binop)
|
|
|
|
def boolean_not():
|
|
"""
|
|
Matches the boolean not with its arguments.
|
|
|
|
returns -- a parser matching the boolean not
|
|
"""
|
|
return keyword('not') + Lazy(boolean_term) ^ (lambda parsed: NotExp(parsed[1]))
|
|
|
|
def boolean_relop():
|
|
"""
|
|
Matches boolean relational operations.
|
|
|
|
returns -- a parser matching all relations
|
|
"""
|
|
relops = ['<', '<=', '>', '>=', '=', '!=']
|
|
return arithmetic_exp() + any_op_in_list(relops) + arithmetic_exp() ^ process_relop
|
|
|
|
def boolean_group():
|
|
"""
|
|
Matches grouped boolean expressions
|
|
|
|
returns -- a parser matching groups of boolean expressions
|
|
"""
|
|
return keyword('(') + Lazy(boolean_exp) + keyword(')') ^ process_group
|
|
|
|
def boolean_term():
|
|
"""
|
|
Matches boolean terms.
|
|
|
|
returns -- a parser matching boolean terms
|
|
"""
|
|
return boolean_not() | boolean_relop() | boolean_group()
|
|
|
|
def boolean_exp():
|
|
"""
|
|
Matches boolean expressing, valuing precedence.
|
|
|
|
returns -- a parser correctly matching arithmetic expressions
|
|
"""
|
|
return precedence(boolean_term(),
|
|
BOOLEAN_PRECEDENCE,
|
|
process_logic)
|
|
|
|
def assign_statement():
|
|
def internal(parsed):
|
|
((name, _), exp) = parsed
|
|
return AssignStatement(name, exp)
|
|
return imp_id + keyword(':=') + arithmetic_exp() ^ internal
|
|
|
|
def if_statement():
|
|
def internal(parsed):
|
|
(((((_, condition), _), on_true), false_parsed), _) = parsed
|
|
if false_parsed:
|
|
(_, on_false) = false_parsed
|
|
else:
|
|
on_false = None
|
|
return IfStatement(condition, on_true, on_false)
|
|
return (keyword('if') + boolean_exp()
|
|
+ keyword('then') + Lazy(statements)
|
|
+ Opt(keyword('else') + Lazy(statements))
|
|
+ keyword('end') ^ internal)
|
|
|
|
def while_statement():
|
|
def internal(parsed):
|
|
((((_, condition), _), body), _) = parsed
|
|
return WhileStatement(condition, body)
|
|
return (keyword('while') + boolean_exp()
|
|
+ keyword('do') + Lazy(statements)
|
|
+ keyword('end') ^ internal)
|
|
|
|
def statement():
|
|
return assign_statement() | if_statement() | while_statement()
|
|
|
|
def statements():
|
|
sep = keyword(';') ^ (lambda x: lambda l, r: CompoundStatement(l, r))
|
|
return Exp(statement(), sep)
|
|
|
|
def parser():
|
|
"""
|
|
Builds a parser for Imp.
|
|
|
|
returns -- a parser for the Imp language
|
|
"""
|
|
return Phrase(statements())
|
|
|
|
def parse(tokens):
|
|
"""
|
|
The parser entry point, takes a list of tokens and
|
|
transforms them into an AST.
|
|
|
|
tokens -- the token list
|
|
|
|
returns -- the AST
|
|
"""
|
|
return parser()(tokens, 0)
|