"""The AST definition""" import operator class Equality(object): """ A baseclass for all AST nodes. It implements basic checks for equality. """ def __eq__(self, o): """ Checks for equality. Returns True when the class dictionaries are the same and the classes are based on the same baseclass. Otherwise returns False. o -- the other object returns -- a Boolean """ return isinstance(o, self.__class__) and self.__dict__ == o.__dict__ def __ne__(self, o): """ Checks for inequality. Simply negates the equality check. o -- the other object returns -- a Boolean """ return not self.__eq__(o) class Statement(Equality): """The baseclass of all statements.""" pass class ArithmeticExp(Equality): """The baseclass of all arithmetic expressions.""" pass class BooleanExp(Equality): """The baseclass of all boolean expressions.""" pass class IntArithmeticExp(ArithmeticExp): """The AST node for integer arithmetic expressions.""" def __init__(self, i): """ The initialization method. i -- the integer value """ self.i = i def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "IntArithmeticStatement(i)" """ return 'IntArithmeticExp(%d)' % self.i def eval(self, env): """ Evaluates the node. Returns the value. env -- the environment in which to evaluate the node returns -- an Integer """ return self.i class VarArithmeticExp(ArithmeticExp): """The AST node for all variable arithmetic expressions.""" def __init__(self, name): """ The initialization method. name -- the variable name """ self.name = name def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "VarArithmeticStatement(name)" """ return 'VarArithmeticExp(%s)' % self.name def eval(self, env): """ Evaluates the node. Returns the variable's value. env -- the environment in which to evaluate the node returns -- the variable's value """ return env.get(self.name, 0) class BinArithmeticExp(ArithmeticExp): """The AST node for all binary arithmetic expressions.""" operators = { '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv, '**': operator.pow, } def __init__(self, op, left, right): """ The initialization method. op -- the operation to perform left -- the left operand right -- the right operand """ self.op = op self.left = left self.right = right def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "BinArithmeticStatement(operation, left, right)" """ return 'BinArithmeticExp(%s, %s, %s)' % (self.op, self.left, self.right) def eval(self, env): """ Evaluates the node. Evaluates the left and right operands and performs the arithmetic epxression on them. env -- the environment in which to evaluate the node returns -- the evaluated value """ left_value = self.left.eval(env) right_value = self.right.eval(env) try: return self.operators[self.op](left_value, right_value) except KeyError: raise RuntimeError('unknown operator: ' + self.op) class RelationExp(BooleanExp): """The AST node for relational boolean expressions.""" operators = { '<': operator.lt, '<=': operator.le, '>': operator.gt, '>=': operator.ge, '=': operator.eq, '!=': operator.ne, } def __init__(self, op, left, right): """ The initialization method. op -- the operation to perform left -- the left operand right -- the right operand """ self.op = op self.left = left self.right = right def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "RelationExp(operation, left, right)" """ return 'RelationExp(%s, %s, %s)' % (self.op, self.left, self.right) def eval(self, env): """ Evaluates the node. Evaluates the left and right operands and performs the relational epxression on them. env -- the environment in which to evaluate the node returns -- the evaluated value """ left_value = self.left.eval(env) right_value = self.right.eval(env) try: return self.operators[self.op](left_value, right_value) except KeyError: raise RuntimeError('unknown operator: ' + self.op) class AndExp(BooleanExp): """The AST node for and expressions""" def __init__(self, left, right): """ The initializer method. left -- the left operand right -- the right operand """ self.left = left self.right = right def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "AndExp(left, right)" """ return 'AndExp(%s, %s)' % (self.left, self.right) def eval(self, env): """ Evaluates the node. Evaluates the left and right operands and performs a logical and on them. Supports short circuiting of logic. env -- the environment in which to evaluate the node returns -- the evaluated value """ return self.left.eval(env) and self.right.eval(env) class OrExp(BooleanExp): """The AST node for or expressions""" def __init__(self, left, right): """ The initializer method. left -- the left operand right -- the right operand """ self.left = left self.right = right def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "OrExp(left, right)" """ return 'OrExp(%s, %s)' % (self.left, self.right) def eval(self, env): """ Evaluates the node. Evaluates the left and right operands and performs a logical or on them. Supports short circuiting of logic. env -- the environment in which to evaluate the node returns -- the evaluated value """ return self.left.eval(env) or self.right.eval(env) class NotExp(BooleanExp): """The AST node for not expressions""" def __init__(self, exp): """ The initializer method. exp -- the operand to negate """ self.exp = exp def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "NotExp(operand)" """ return 'NotExp(%s)' % self.exp def eval(self, env): """ Evaluates the node. Evaluates the operand and performs a logical not on it. env -- the environment in which to evaluate the node returns -- the evaluated value """ return not self.exp.eval(env) class AssignStatement(Statement): """The AST node for assignments""" def __init__(self, name, rexp): """ The initialization method. name -- the variable name rexp -- the expression that evaluates to the name """ self.name = name self.rexp = rexp def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "AssignStatement(name, expression)" """ return 'AssignStatement(%s, %s)' % (self.name, self.rexp) def eval(self, env): """ Evaluates the node. Evaluates the statement for the expression and saves it within the environment (under the given name). env -- the environment in which to evaluate the node """ env[self.name] = self.rexp.eval(env) class CompoundStatement(Statement): """The AST node for compound statements""" def __init__(self, first, second): """ The initialization method. first -- the first statement second -- the second statement """ self.first = first self.second = second def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "CompoundStatement(first, second)" """ return 'CompoundStatement(%s, %s)' % (self.first, self.second) def eval(self, env): """ Evaluates the node. Evaluates the statements one after the other. env -- the environment in which to evaluate the node """ self.first.eval(env) self.second.eval(env) class IfStatement(Statement): """The AST node for if statements""" def __init__(self, condition, on_true, on_false): """ The initialization method. condition -- the condition that needs to be evaluated on_true -- the statement that is to be executed when the condition is met on_false -- the statement that is to be executed when the condition is not met """ self.condition = condition self.on_true = on_true self.on_false = on_false def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "IfStatement(condition, on_true, on_false)" """ return 'IfStatement(%s, %s, %s)' % (self.condition, self.on_true, self.on_false) def eval(self, env): """ Evaluates the node. Evaluates the condition. If it is met, the on_true block is executed. If not and the on_false block exists, it is executed. env -- the environment in which to evaluate the node """ if self.condition.eval(env): self.on_true.eval(env) else: if self.on_false: self.on_false.eval(env) class ReadStatement(Statement): def __repr__(self): return 'ReadStatement()' def eval(self, env): try: return int(input()) except ValueError: return 0 class WriteStatement(Statement): """The AST node for write statements""" def __init__(self, message): """ The initialization method. message -- the message that should be printed """ self.message = message def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "WriteStatement(message)" """ return 'WriteStatement(%s)' % message def eval(self, env): """ Evaluates the node. Prints the message. returns -- None """ return print(self.message.eval(env)) class WhileStatement(Statement): """The AST node for while statements""" def __init__(self, condition, body): """ The initialization method. condition -- the condition that needs to be evaluated body -- the statement that is to be executed while the condition is met """ self.condition = condition self.body = body def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "WhileStatement(condition, body)" """ return 'WhileStatement(%s, %s)' % (self.condition, self.body) def eval(self, env): """ Evaluates the node. Evaluates the condition. While it is met, the body block is executed. env -- the environment in which to evaluate the node """ while self.condition.eval(env): self.body.eval(env) class ForStatement(Statement): """The AST node for for statements""" def __init__(self, count, body): """ The initialization method. count -- the number of times the body should be executed body -- the statement that is to be executed while inside the loop """ self.count = count self.body = body def __repr__(self): """ A representation of the node for debug purposes. returns -- a String of the form "ForStatement(count, body)" """ return 'ForStatement(%s, %s)' % (self.count, self.body) def eval(self, env): """ Evaluates the node. Executes the body block count times. env -- the environment in which to evaluate the node """ for _ in range(self.count.eval(env)): self.body.eval(env)