예제 #1
0
    def analyze(self, input_string):
        # Parse statement into its component statements, then recursively analyze each one
        # and finally return a lambda function that evaluates all parameters and executes commands
        exprs = self.parse(input_string)
        exprs_processed = []
        for expr in exprs:
            if self.is_number(expr):
                # Numbers are our most basic primitive; we return a lambda function that replaces the string
                # with the corresponding number
                exprs_processed.append(lambda: self.get_number_value(expr))
            elif self.is_symbol(expr):
                # For strings ("symbols"), the returned function simply keeps them as is. Their value (if defined)
                # will be evaluated later as part of the execution
                exprs_processed.append(lambda: expr)
            elif self.is_command(expr):
                # For commands we first recursively analyze the arguments, then verify correct syntax,
                # and finally return a lambda function that evaluates all arguments and executes the command
                # (note that the arguments themselves may be commands, which get executed as part of the evaluation
                # process as well)
                cmd = self.get_cmd(expr)
                args_unevaluated = self.get_args(expr)
                args = []
                for arg in args_unevaluated:
                    args.append(self.analyze(arg))

                if cmd == "if":
                    cmd = "if_else"
                    args.append(lambda: 0)
                # Special case handling for "if" statement, which transforms it into an if_else statement with a
                # blank else clause. This saves us from having to implement both functions, as well as avoids problems
                # with the "if" command shadowing python's

                if cmd == "and":
                    cmd = "i_and"
                if cmd == "or":
                    cmd = "i_or"
                # Avoid shadowing with and/or commands
                CommandsInspector.verify_commands(self.commands, cmd, args)
                # self.commands.verify_command(cmd, args)
                exprs_processed.append(
                    (lambda xcmd, xargs: lambda: self.eval_and_exec(
                        xcmd, xargs))(cmd, args))
                # Important: This double-lambda construct is here because python evaluates variables on execution
                # and not on definition. Without this, other processed statements which return a lambda function will
                # refer to the same cmd and arg, which will only be evaluated in the end. In other words, instead of
                # analyzing all statements, only the last one will be analyzed (multiple times). To work our way around
                # this, we construct a lambda that constructs the lambda we actually want, and immediately call it.
                # Another way around this would be to define default arguments cmd=cmd, args=args since default
                # arguments are evaluated on definition.
            else:
                raise Exception("Syntax error in expression " + str(expr))

        return lambda: self.execute_multiple(exprs_processed)
예제 #2
0
 def eval_and_exec_if_else(self, cmd, args):
     # If statements are a special case since we only want to execute the lambdas of the arguments based on the
     # predicate. Therefore only the first argument (the predicate) is evaluated at this stage, and the relevant
     # result is evaluated within the function itself
     args_eval = args.copy()  # Avoid modifying the original arguments
     args_eval[0] = args_eval[0]()
     if self.is_symbol(args_eval[0]):
         args_eval[0] = self.get_symbol_value(args_eval[0])
     return CommandsInspector.execute_command(self.commands, cmd, args_eval)
예제 #3
0
 def eval_and_exec_general(self, cmd, args):
     # First execute all lambda functions for all arguments, reducing them all to either numbers or symbols
     # Then resolve all symbols by replacing them with their defined values
     args_eval = args.copy()  # Avoid modifying the original arguments
     for idx, arg in enumerate(args_eval):
         args_eval[idx] = arg()
         if self.is_symbol(args_eval[idx]):
             args_eval[idx] = self.get_symbol_value(args_eval[idx])
     return CommandsInspector.execute_command(self.commands, cmd, args_eval)
예제 #4
0
 def eval_and_exec_define(self, cmd, args):
     # Define is a special case since it expects the first argument to be an undefined symbol, so we must not
     # attempt to fully resolve it
     args_eval = args.copy()  # Avoid modifying the original arguments
     for idx, arg in enumerate(args_eval):
         args_eval[idx] = arg()
     if self.is_symbol(args_eval[1]):
         args_eval[1] = self.get_symbol_value(args_eval[1])
     return CommandsInspector.execute_command(self.commands, cmd, args_eval)