def execute_len(self, exec_context): list_ = exec_context.symbol_table.get("list") if not isinstance(list_, List): return RuntimeResult().failure( ActiveRuntimeError("Argument must be list", self.start_pos, self.end_pos, exec_context)) return RuntimeResult().success(Number(len(list_.elements)))
def execute_append(self, exec_context): list_ = exec_context.symbol_table.get("list") value = exec_context.symbol_table.get("value") if not isinstance(list_, List): return RuntimeResult().failure( ActiveRuntimeError("First argument must be list", self.start_pos, self.end_pos, exec_context)) list_.elements.append(value) return RuntimeResult().success(Number(0))
def execute_extend(self, exec_context): first_list = exec_context.symbol_table.get("first_list") end_list = exec_context.symbol_table.get("second_list") if not isinstance(first_list, List): return RuntimeResult().failure( ActiveRuntimeError("First argument must be list", self.start_pos, self.end_pos, exec_context)) if not isinstance(end_list, List): return RuntimeResult().failure( ActiveRuntimeError("Second argument must be list", self.start_pos, self.end_pos, exec_context)) first_list.elements.extend(end_list.elements) return RuntimeResult().success(Number(0))
def visit_callnode(self, node, context): """ Visits the CallNode instance. :param node: The CallNode instance. :param context: The caller's context. :return: The resulting Node from the exec call. """ args = [] runtime_result = RuntimeResult() value_to_call = runtime_result.register( self.visit(node.node_to_call, context)) if runtime_result.should_return(): return runtime_result value_to_call = value_to_call.copy().set_position( node.start_pos, node.end_pos) for arg_node in node.arg_nodes: args.append(runtime_result.register(self.visit(arg_node, context))) if runtime_result.should_return(): return runtime_result return_value = runtime_result.register(value_to_call.execute(args)) if runtime_result.should_return(): return runtime_result return_value = return_value.copy().set_position( node.start_pos, node.end_pos).set_context(context) return runtime_result.success(return_value)
def visit_whilenode(self, node, context): """ Visits the WhileNode for while-loops in the stream. :param node: Node of the while-loop. :param context: Context of the caller. :return: List of all evaluated results. """ elements = [] runtime_result = RuntimeResult() while True: condition = runtime_result.register( self.visit(node.condition, context)) if runtime_result.should_return(): return runtime_result if not condition.is_true(): break current_value = runtime_result.register( self.visit(node.body_node, context)) if runtime_result.should_return() \ and runtime_result.loop_should_continue is False \ and runtime_result.loop_should_break is False: return runtime_result if runtime_result.loop_should_continue: continue if runtime_result.loop_should_break: break elements.append(current_value) return runtime_result.success( Number(0) if node.should_return_null else List(elements). set_context(context).set_position(node.start_pos, node.end_pos))
def visit_ifnode(self, node, context): """ Accesses the IfNode instance in the stream. :param node: IfNode instance we wish to visit. :param context: Context of the caller. :return: Value of the if-statement, or None. """ runtime_result = RuntimeResult() for condition, expr, should_return_null in node.cases: condition_value = runtime_result.register( self.visit(condition, context)) if runtime_result.should_return(): return runtime_result if condition_value.is_true(): expr_value = runtime_result.register(self.visit(expr, context)) if runtime_result.should_return(): return runtime_result return runtime_result.success( Number(0) if should_return_null else expr_value) if node.else_case: expr, should_return_null = node.else_case expr_value = runtime_result.register(self.visit(expr, context)) if runtime_result.should_return(): return runtime_result return runtime_result.success( Number(0) if should_return_null else expr_value) return runtime_result.success(Number(0))
def visit_continuenode(self, node, context): """ Visits the ContinueNode instance. :param node: The ContinueNode instance. :param context: The caller's context. :return: The RuntimeResult of a successful CONTINUE. """ return RuntimeResult().success_continue()
def visit_breaknode(self, node, context): """ Visits the BreakNode instance. :param node: The BreakNode instance. :param context: The caller's context. :return: The RuntimeResult of a successful BREAK. """ return RuntimeResult().success_break()
def execute_input_int(self, exec_context): while True: text = input() try: # Try converting to int number = int(text) break except ValueError: print("'{}' must be an integer. Try again!".format(text)) return RuntimeResult().success(Number(number))
def visit_stringnode(self, node, context): """ Visits the StringNode instance. :param node: The StringNode instance. :param context: The caller's Context instance. :return: A String instance. """ return RuntimeResult().success( String(node.token.value).set_context(context).set_position( node.start_pos, node.end_pos))
def execute_pop(self, exec_context): list_ = exec_context.symbol_table.get("list") index = exec_context.symbol_table.get("index") if not isinstance(list_, List): return RuntimeResult().failure( ActiveRuntimeError("First argument must be list", self.start_pos, self.end_pos, exec_context)) if not isinstance(index, Number): return RuntimeResult().failure( ActiveRuntimeError("Second argument must be number", self.start_pos, self.end_pos, exec_context)) try: # Try pop() command in Python element = list_.elements.pop(index.value) except IndexError: return RuntimeResult().failure( ActiveRuntimeError( 'Element at this index could not be removed from list because index is out of bounds', self.start_pos, self.end_pos, exec_context)) return RuntimeResult().success(element)
def visit_numbernode(self, node, context): """ Returns the value of the Node as a Number. :param node: The Node with the numeric value. :param context: Context of the caller. :return: Number instance with the Node value. """ return RuntimeResult().success( Number(node.token.value).set_context(context).set_position( node.start_pos, node.end_pos))
def visit_fornode(self, node, context): """ Visits the ForNode for for-loops in the stream. :param node: Node of the for-loop. :param context: Context of the caller. :return: List of evaluated values. """ elements = [] runtime_result = RuntimeResult() start_value = runtime_result.register( self.visit(node.start_value_node, context)) if runtime_result.should_return(): return runtime_result end_value = runtime_result.register( self.visit(node.end_value_node, context)) if runtime_result.should_return(): return runtime_result if node.step_value_node: step_value = runtime_result.register( self.visit(node.step_value_node, context)) if runtime_result.should_return(): return runtime_result else: # Default to one iteration step_value = Number(1) # Note: PEP 8 doesn't allow for lambda expressions to be assigned to # variables directly. They prefer a function definition. However, this # is the cleanest way to do this. Code is read more often than it's # written. This expression is easier to read and understand as an # inline lambda assignment. index = start_value.value if step_value.value >= 0: condition = lambda: index < end_value.value else: # Step value must be negative condition = lambda: index > end_value.value while condition(): context.symbol_table.set(node.var_name_token.value, Number(index)) index += step_value.value current_value = runtime_result.register( self.visit(node.body_node, context)) if runtime_result.should_return() \ and runtime_result.loop_should_continue is False \ and runtime_result.loop_should_break is False: return runtime_result if runtime_result.loop_should_continue: continue if runtime_result.loop_should_break: break elements.append(current_value) return runtime_result.success( Number(0) if node.should_return_null else List(elements). set_context(context).set_position(node.start_pos, node.end_pos))
def visit_binopnode(self, node, context): """ Returns the result of the binary operation. :param node: Node which houses two children Nodes. :param context: Context of the caller. :return: Result of the binary operation on both child Nodes. """ result, error = None, None runtime_result = RuntimeResult() left_node = runtime_result.register(self.visit(node.left_node, context)) right_node = runtime_result.register( self.visit(node.right_node, context)) if runtime_result.should_return(): return runtime_result if node.op_token.type == TP_PLUS: result, error = left_node.add_to(right_node) elif node.op_token.type == TP_MINUS: result, error = left_node.subtract_by(right_node) elif node.op_token.type == TP_POWER: result, error = left_node.power_by(right_node) elif node.op_token.type == TP_MUL: result, error = left_node.multiply_by(right_node) elif node.op_token.type == TP_DIV: result, error = left_node.divide_by(right_node) elif node.op_token.type == TP_MODULO: result, error = left_node.modulo_by(right_node) elif node.op_token.type == TP_CLEAN_DIV: result, error = left_node.divide_by(right_node, clean=True) elif node.op_token.type == TP_NE: result, error = left_node.get_comparison_ne(right_node) elif node.op_token.type == TP_EE: result, error = left_node.get_comparison_ee(right_node) elif node.op_token.type == TP_LT: result, error = left_node.get_comparison_lt(right_node) elif node.op_token.type == TP_LTE: result, error = left_node.get_comparison_lte(right_node) elif node.op_token.type == TP_GT: result, error = left_node.get_comparison_gt(right_node) elif node.op_token.type == TP_GTE: result, error = left_node.get_comparison_gte(right_node) elif node.op_token.matches(TP_KEYWORD, 'AND'): result, error = left_node.anded_by(right_node) elif node.op_token.matches(TP_KEYWORD, 'OR'): result, error = left_node.ored_by(right_node) if runtime_result.should_return(): return runtime_result.failure(runtime_result) if error: return runtime_result.failure(error) return runtime_result.success( result.set_position(node.start_pos, node.end_pos))
def check_and_populate_args(self, arg_names, args, exec_context): """ Checks args before populating the Context. :param arg_names: Names of all args. :param args: Values of all args. :param exec_context: Context we wish to update. :return: None, if there are no issues. """ runtime_result = RuntimeResult() runtime_result.register(self.check_args(arg_names, args)) if runtime_result.error: return runtime_result self.populate_args(arg_names, args, exec_context) return runtime_result.success(None)
def visit_varassignnode(self, node, context): """ Assigns the value of variables in the stream. :param node: Node of a variable to assign. :param context: Context of the caller. :return: Value of the variable. """ runtime_result = RuntimeResult() var_name = node.var_name.value var_value = runtime_result.register( self.visit(node.value_node, context)) if runtime_result.should_return(): return runtime_result context.symbol_table.set(var_name, var_value) return runtime_result.success(var_value)
def execute_run(self, exec_context): file_name = exec_context.symbol_table.get("fn") if not isinstance(file_name, String): return RuntimeResult().failure( ActiveRuntimeError("Second argument must be string", self.start_pos, self.end_pos, exec_context)) file_name = file_name.value try: with open(file_name, "r") as f: script = f.read() except Exception as exception: return RuntimeResult().failure( ActiveRuntimeError( "Failed to load script \"{}\"\n".format(file_name) + str(exception), self.start_pos, self.end_pos, exec_context)) _, error = run(file_name, script) if error: return RuntimeResult().failure( ActiveRuntimeError( "Failed to finish executing script \"{}\"\n".format( file_name) + error.as_string(), self.start_pos, self.end_pos, exec_context)) return RuntimeResult().success(Number(0))
def visit_funcdefnode(self, node, context): """ Visits a FuncDefNode instance. :param node: The FuncDefNode instance. :param context: The caller's context. :return: Function Node instance. """ runtime_result = RuntimeResult() func_name = node.var_name_token.value if node.var_name_token else None body_node = node.body_node arg_names = [arg_name.value for arg_name in node.arg_name_tokens] func_node = Function(func_name, body_node, arg_names, node.should_auto_return) \ .set_context(context).set_position(node.start_pos, node.end_pos) if node.var_name_token: context.symbol_table.set(func_name, func_node) return runtime_result.success(func_node)
def visit_returnnode(self, node, context): """ Visits the ReturnNode instance. :param node: The ReturnNode instance. :param context: The caller's context. :return: Value of the ReturnNode instance. """ runtime_result = RuntimeResult() if node.node_to_return: value = runtime_result.register( self.visit(node.node_to_return, context)) if runtime_result.should_return(): return runtime_result else: value = Number(0) return runtime_result.success_return(value)
def visit_varaccessnode(self, node, context): """ Accesses the value of variables in the stream. :param node: Node with which to fetch the variable. :param context: Context of the caller. :return: Value of fetching a variable's value and executing it. """ runtime_result = RuntimeResult() var_name = node.var_name.value var_value = context.symbol_table.get(var_name) if var_value is None: runtime_result.failure( ActiveRuntimeError('VAR "{}" not defined'.format(var_name), node.start_pos, node.end_pos, context)) var_value = var_value.copy().set_position( node.start_pos, node.end_pos).set_context(context) return runtime_result.success(var_value)
def visit_listnode(self, node, context): """ Visits the ListNode instance. :param node: The ListNode instance. :param context: The caller's context. :return: List instance with all values. """ elements = [] runtime_result = RuntimeResult() for element_node in node.element_nodes: elements.append( runtime_result.register(self.visit(element_node, context))) if runtime_result.should_return(): return runtime_result return runtime_result.success( List(elements).set_context(context).set_position( node.start_pos, node.end_pos))
def execute(self, args): """ Executes the BuiltInFunction instance. :param args: List of all arguments. :return: Value of whichever of the exec methods were called. """ runtime_result = RuntimeResult() exec_context = self.generate_new_context() method_name = 'execute_{}'.format(self.name.lower()) method = getattr(self, method_name, self.no_visit_method) runtime_result.register( self.check_and_populate_args(method.arg_names, args, exec_context)) if runtime_result.error: return runtime_result return_value = runtime_result.register(method(exec_context)) if runtime_result.error: return runtime_result return return_value
def check_args(self, arg_names, args): """ Checks that correct number of args are present. :param arg_names: List of argument names. :param args: List of arguments passed into func. :return: None, if there are no issues. """ runtime_result = RuntimeResult() if len(args) > len(arg_names): return runtime_result.failure( ActiveRuntimeError( 'Too many arguments'.format(len(args) - len(arg_names)), self.start_pos, self.end_pos, self.context)) if len(args) < len(arg_names): return runtime_result.failure( ActiveRuntimeError( 'Too few arguments'.format(len(arg_names) - len(args)), self.start_pos, self.end_pos, self.context)) return runtime_result.success(None)
def visit_unaryopnode(self, node, context): """ Returns result of the unary operator on the node. :param node: Node with which to perform a unary operation. :param context: Context of the caller. :return: Result of the unary operation on the node. """ error = None runtime_result = RuntimeResult() number = runtime_result.register(self.visit(node.right_node, context)) if runtime_result.should_return(): return runtime_result if node.op_token.type == TP_MINUS: number, error = number.multiply_by(Number(-1)) elif node.op_token.matches(TP_KEYWORD, 'NOT'): number, error = number.notted() if error: return runtime_result.failure(error) return runtime_result.success( number.set_position(node.start_pos, node.end_pos))
def execute(self, args): """ Execute a Function instance. :param args: Arguments being passed into the Function. :return: Value of the executed Function. """ runtime_result = RuntimeResult() interpreter = Interpreter() exec_context = self.generate_new_context() runtime_result.register( self.check_and_populate_args(self.arg_names, args, exec_context)) if runtime_result.should_return(): return runtime_result value = runtime_result.register( interpreter.visit(self.body_node, exec_context)) if runtime_result.should_return( ) and runtime_result.func_return_value is None: return runtime_result return_value \ = (value if self.should_auto_return else None) or runtime_result.func_return_value or Number(0) return runtime_result.success(return_value)
def execute_print(self, exec_context): print(str(exec_context.symbol_table.get('value'))) return RuntimeResult().success(Number(0))
def execute_input(self, exec_context): text = input() return RuntimeResult().success(String(text))
def execute_clear(self, exec_context): os.system('cls' if os.name == 'nt' else 'cls') return RuntimeResult().success(Number(0))
def execute_is_string(self, exec_context): is_number = isinstance(exec_context.symbol_table.get("value"), String) return RuntimeResult().success(Number(1) if is_number else Number(0))
def execute_is_function(self, exec_context): is_number = isinstance(exec_context.symbol_table.get("value"), BaseFunction) return RuntimeResult().success(Number(1) if is_number else Number(0))