class HCS(object): def __init__(self, scope = None): self.scope = Scope() if scope is None else scope def parse(self, source): grammar = '\n'.join(v.__doc__ for k, v in vars(self.__class__).items() if '__' not in k and hasattr(v, '__doc__') and v.__doc__) return parsimonious.Grammar(grammar)['program'].parse(source) def evaluate(self, source): node = self.parse(source.replace("\n", " ")) if isinstance(source, str) else source method = getattr(self, node.expr_name, lambda node, children: children) if node.expr_name in ['formal_parameters_and_body', 'ifelse']: return method(node) return method(node, [self.evaluate(n) for n in node]) #################################### RULES #################################### def add_op(self, node, children): 'add_op = ~"[+-]"' add_operator = {"+": operator.add, "-": operator.sub} return add_operator[node.text] def anonymous_function(self, node, children): 'anonymous_function = "def" _ formal_parameters_and_body' _, _, formal_params_and_body = children return formal_params_and_body def anonymous_function_assignment(self, node, children): 'anonymous_function_assignment = identifier_name _ "=" _ anonymous_function' func_name, _, _, _, anonymous_func_result = children params, stmts, anonymous_func = anonymous_func_result self.scope.add_function(func_name, params, stmts, anonymous_func) return anonymous_func def anonymous_function_call(self, node, children): 'anonymous_function_call = "(" _ anonymous_function _ ")" _ "(" _ arguments _ ")"' _, _, anonymous_func_result, _, _, _, _, _, args, _, _ = children params, stmts, anonymous_func = anonymous_func_result return self.scope.direct_function_call(params, stmts, anonymous_func, args) def arguments(self, node, children): 'arguments = expression ( _ "," _ expression _ )*' expr, expr_list = children exprs = [expr] for _, _, _, expr, _ in expr_list: exprs.append(expr) return exprs def assignment(self, node, children): 'assignment = anonymous_function_assignment / variable_assignment' return children[0] def expression(self, node, children): 'expression = anonymous_function_call / function_call / assignment / prefix_expression / simple_expression' return children[0] def factor(self, node, children): 'factor = number / postfix_expression / identifier_value / parenthesized_expression / string' return children[0] def formal_parameters_and_body(self, node): 'formal_parameters_and_body = "(" _ parameters _ ")" _ "{" _ statements _ "}"' _, _, params, _, _, _, _, _, stmts, _, _ = node params = list(map(lambda x: x.strip(), ''.join([param.text.strip() for param in params]).split(','))) def anonymous_func(function_metadata, *args): function_metadata.scope.add_variable_list(dict(zip(function_metadata.formal_params, args))) return HCS(function_metadata.scope).evaluate(function_metadata.statements) return (params, stmts, anonymous_func) def function_call(self, node, children): 'function_call = identifier_name _ "(" _ arguments _ ")"' name, _, _, _, args, _, _ = children return self.scope.call(name, args) def identifier_name(self, node, children): 'identifier_name = ~"[a-z_][a-z_0-9]*"i _' return node.text.strip() def identifier_value(self, node, children): 'identifier_value = ~"[a-z_][a-z_0-9]*"i _' name = node.text.strip() return self.scope.get_variable_value(name) def statement_block(self, node, children): 'statement_block = "{" _ statements _ "}"' _, _, stmt, _, _ = children return stmt def ifelse(self, node): 'ifelse = "if" _ parenthesized_expression _ statement_block _ ( "else" _ statement_block )?' _, _, condition, _, if_true, _, if_false = node if self.evaluate(condition): return self.evaluate(if_true) elif len(if_false.children) > 0: return self.evaluate(if_false) def mul_op(self, node, children): 'mul_op = "*" / "//" / "/" / "%"' mul_operator = {"*": operator.mul, "/": operator.truediv, "//": operator.floordiv, "%": operator.mod} return mul_operator[node.text] def named_function(self, node, children): 'named_function = "def" ws identifier_name _ formal_parameters_and_body' _, _, func_name, _, func_result = children params, stmts, func = func_result self.scope.add_function(func_name, params, stmts, func) return func def number(self, node, children): 'number = ~"[+-]?\s*(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?"' try: return int(node.text) except ValueError: return float(node.text) def optional_semicolon(self, node, children): 'optional_semicolon = _ ";"? _' def parameters(self, node, children): 'parameters = identifier_name ( _ "," _ identifier_name)*' first_id, id_list = children ids = [first_id] for _, _, _, cur_id in id_list: ids.append(cur_id) return ids def parenthesized_expression(self, node, children): 'parenthesized_expression = "(" _ expression _ ")"' return children[2] def postfix_expression(self, node, children): 'postfix_expression = identifier_name pre_posfix_op' var_name, op = children current_value = self.scope.get_variable_value(var_name) self.scope.add_or_update_variable(var_name, op(current_value)) return current_value def prefix_expression(self, node, children): 'prefix_expression = pre_posfix_op identifier_name' op, var_name = children self.scope.add_or_update_variable(var_name, op(self.scope.get_variable_value(var_name))) return self.scope.get_variable_value(var_name) def pre_op(self, node, children): 'pre_op = ~"[+-]"' pre_operator = {"+": lambda x: x, "-": operator.neg} return pre_operator[node.text] def pre_posfix_op(self, node, children): 'pre_posfix_op = "++" / "--"' pre_posfix = {"++": lambda x: x + 1, "--": lambda x: x - 1,} return pre_posfix[node.text] def program(self, node, children): 'program = statements*' return children[0] def simple_expression(self, node, children): 'simple_expression = pre_op? _ term ( _ add_op _ term )*' p_op, _, term, term_list = children if len(p_op) > 0: result = p_op[0](term) else: result = term for _, a_op, _, term in term_list: result = a_op(result, term) return result def statement(self, node, children): 'statement = ifelse / named_function / expression' return children[0] def statements(self, node, children): 'statements = _ statement ( optional_semicolon statement )* optional_semicolon ' _, stmt, stmt_list, _, = children result = stmt for _, stmt in stmt_list: result = stmt return stmt def string(self, node, children): 'string = ~"\\".*?\\""' return node.text def term(self, node, children): 'term = factor ( _ mul_op _ factor )*' factor, factor_list = children result = factor for _, m_op, _, factor in factor_list: result = m_op(result, factor) return result def variable_assignment(self, node, children): 'variable_assignment = identifier_name _ "=" _ expression' var_name, _, _, _, expr = children self.scope.add_or_update_variable(var_name, expr) return expr def ws(self, node, children): 'ws = ~"\s+"' def _(self, node, children): '_ = ~"\s*"'