"""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)