def evaluate(ast, env): if is_list(ast): first_exp = ast[0] if first_exp == "atom": return is_atom(evaluate(ast[1],env)) elif first_exp == "define": return eval_define(ast[1:], env) elif first_exp == "if": return eval_if_statement(ast[1:], env) elif first_exp == "lambda": return eval_lambda(ast[1:], env) elif first_exp == "quote": return ast[1]; elif first_exp == "eq": return eval_equation(ast[1:], env) elif first_exp == "cons": return eval_list_cons(ast[1:], env) elif first_exp == "head": return eval_list_head(ast[1:], env) elif first_exp == "tail": return eval_list_tail(ast[1:], env) elif first_exp == "empty": return eval_list_empty(ast[1:], env) elif first_exp in math_operands: return eval_math_operation(first_exp, ast[1:], env) elif first_exp == "<": return evaluate(ast[1], env) < evaluate(ast[2], env) elif first_exp == "<=": return evaluate(ast[1], env) <= evaluate(ast[2], env) elif first_exp == ">": return evaluate(ast[1], env) > evaluate(ast[2], env) elif first_exp == ">=": return evaluate(ast[1], env) >= evaluate(ast[2], env) elif is_list(first_exp) or is_symbol(first_exp): eval_first = evaluate(first_exp, env) return evaluate([eval_first]+ast[1:], env) elif is_closure(first_exp): arguments = ast[1:] return evaluate(first_exp.body, first_exp.env.extend(evaluate_function_arguments(first_exp, arguments, env))) else: raise LispError('{0} not a function'.format(first_exp)) elif is_symbol(ast): return env.lookup(ast) elif is_atom(ast): return ast
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" math_operators = ["+", "-", "*", "/", "mod", ">"] exprs = { "quote" : lambda ast: ast[1], "atom" : lambda ast: is_atom(evaluate(ast[1], env)), "eq" : lambda ast: (evaluate(["atom", ast[1]], env) and evaluate(["atom", ast[2]], env) and evaluate(ast[1], env) == evaluate(ast[2], env)), "if" : lambda ast: eval_if_statement(ast, env), "define" : lambda ast: eval_define(ast, env), "lambda" : lambda ast: eval_lambda(ast, env), "cons" : lambda ast: eval_cons(ast, env), "head" : lambda ast: eval_head(ast, env), "tail" : lambda ast: eval_tail(ast, env), "empty" : lambda ast: eval_empty(ast, env), "env" : lambda ast: eval_in_env(ast, env) } exprs.update(exprs.fromkeys(math_operators, lambda ast: eval_math_operators(ast, env))) if is_symbol(ast): return env.lookup(ast) elif is_atom(ast): return ast elif is_list(ast): expr = ast[0] if not(is_list(ast[0])) else "env" return exprs.get(expr, exprs["env"])(ast)
def _define_closure(ast, env): if len(ast) != 2: raise LispError("Wrong number of arguments") if not is_list(ast[0]): raise LispError("Params of lambda must be a list") return Closure(env, ast[0], ast[1])
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment. """ print ast if is_atom(ast) or ast == []: if is_symbol(ast): return evaluate(env.lookup(ast), env) else: return ast if is_list(ast[0]): ast[0] = evaluate(ast[0], env) if ast[0] in MATH_EXP.keys(): return _evaluate_math(ast, env) for _map in (CONTROL_EXP, EVAL_EXP, VAR_EXP, CLOSURE_EXP, LIST_EXP): if ast[0] in _map.keys(): func = _map[ast[0]] return evaluate(func(ast[1:], env), env) if is_symbol(ast[0]): ast[0] = env.lookup(ast[0]) if is_closure(ast[0]): if len(ast[1:]) != len(ast[0].params): raise LispError('wrong number of arguments, expected %s got %s' % (len(ast[0].params), len(ast[1:]))) arguments = [evaluate(arg, env) for arg in ast[1:]] parameters = dict(zip(ast[0].params, arguments)) env = ast[0].env.extend(parameters) return evaluate(ast[0].body, env) return [evaluate(a, env) for a in ast]
def assert_list(rest, env): rest = evaluate(rest, env) if is_list(rest): return rest else: raise LispError("Expected list, got: %s" % unparse(rest))
def build_evaluator(ast): if is_list(ast): if is_closure(ast[0]): return ClosureEvaluator(ast) if ast[0] == "quote": return QuoteEvaluator(ast) if ast[0] == "if": return IfEvaluator(ast) if ast[0] == "define": return DefineEvaluator(ast) if ast[0] == "lambda": return LambdaEvaluator(ast) if ast[0] == "atom": return AtomEvaluator(ast) if ast[0] == "eq": return EqEvaluator(ast) if ast[0] in ["+", "-", "/", "*", "mod", ">"]: return MathEvaluator(ast) if ast[0] == "cons": return ConsEvaluator(ast) if ast[0] == "head": return HeadEvaluator(ast) if ast[0] == "tail": return TailEvaluator(ast) if ast[0] == "empty": return EmptyEvaluator(ast) return Evaluator(ast)
def eval_lambda(ast, env): # arguments should be type list validate_num_args(ast, 2) if not(is_list(ast[1])): err_syntax(ast) result = Closure(env, ast[1], ast[2]) return result
def eval_lambda(ast, env): if len(ast) != 3: raise LispError('Wrong number of arguments to lambda form') if not is_list(ast[1]): raise LispError('The parameters of lambda should be a list.') return Closure(env, ast[1], ast[2])
def eval_lambda(args, env): if not len(args) == 2: raise LispError("number of arguments") params = args[0] body = args[-1] if not (is_list(params)): raise LispError('arguments of lambda must be a list') return Closure(env, params, body)
def eval_rhead(ast, env): assert_exp_length(ast, 2) ls = evaluate(ast[1], env) if not is_list(ls): raise LispError('The argument of rhead should be a list.') elif len(ls) == 0: raise LispError('And empty list has no reverse head.') return ls[-1]
def evaluate(self, env): if is_list(self.ast[1]): if len(self.ast) == 3: return Closure(env, self.ast[1], self.ast[2]) else: raise LispError("number of arguments") else: raise LispError
def __init__(self, env, params, body): from ast import is_list if not is_list(params): raise LispError("param %s is not a list") self.env = env self.params = params self.body = body
def eval_lambda(ast, env): """Evaluate a 'lambda' statement in the specified environment. """ if len(ast) != 3: raise LispError("Wrong number of arguments: %d" % (len(ast) - 2)) elif not is_list(ast[1]): raise LispError("The parameters to lambda need to be a list!") else: return Closure(env, ast[1], ast[2])
def evaluate_function(ast, env): if is_list(ast[0]): function = evaluate(ast[0], env) return do_function(function, ast, env) elif ast[0] in env.variables: function = env.lookup(ast[0]) return do_function(function, ast, env) else: raise LispError("not a function")
def eval_cons(ast, env): """Evaluate a 'cons' statement in the specified environment. Cons an element onto a list. """ if len(ast) != 3: raise LispError("Wrong number of arguments to 'cons'") s2 = evaluate(ast[2], env) if not is_list(s2): raise LispError("Cannot cons to a non-list!") else: return [evaluate(ast[1], env)] + s2
def evalEmptyCommand(args, env): if len(args) != 1: raise LispError("tail command expects exactly one argument") arg = evaluate(args[0], env) if not is_list(arg): raise LispError("tail command expects a list as argument") return len(arg) == 0
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" if is_list(ast): if ast[0] == "quote": return ast[1] if ast[0] == "define": if not len(ast) == 3: raise LispError("Wrong number of arguments") if not is_symbol(ast[1]): raise LispError("non-symbol") env.set(ast[1], evaluate(ast[2], env)) return if ast[0] == "if": if evaluate(ast[1], env): return evaluate(ast[2], env) else: return evaluate(ast[3], env) if ast[0] == "atom": return is_atom(evaluate(ast[1], env)) if ast[0] == "eq": if not is_atom(evaluate(ast[1], env)) or not is_atom(evaluate(ast[2], env)): return False return evaluate(ast[1], env) == evaluate(ast[2], env) try: if ast[0] == "+": return evaluate(ast[1], env) + evaluate(ast[2], env) if ast[0] == "-": return evaluate(ast[1], env) - evaluate(ast[2], env) if ast[0] == "/": return evaluate(ast[1], env) / evaluate(ast[2], env) if ast[0] == "*": return evaluate(ast[1], env) * evaluate(ast[2], env) if ast[0] == "mod": return evaluate(ast[1], env) % evaluate(ast[2], env) if ast[0] == "<": return evaluate(ast[1], env) < evaluate(ast[2], env) if ast[0] == ">": return evaluate(ast[1], env) > evaluate(ast[2], env) except TypeError: raise LispError("TypeError") if is_symbol(ast): return env.lookup(ast) else: return ast
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" if is_symbol(ast): if ast[0] == '"': return ast return env.lookup(ast) elif is_atom(ast): return ast elif is_list(ast): return eval_list(ast, env) else: raise LispError('Syntax error: %s' % unparse(ast))
def evalHeadCommand(args, env): if len(args) != 1: raise LispError("head command expects exactly one argument") arg = evaluate(args[0], env) if not is_list(arg): raise LispError("head command expects a list as argument") if len(arg) == 0: raise LispError("head command expect non-empty list") return arg[0]
def unparse(ast): """Turns an AST back into lisp program source""" if is_boolean(ast): return 'True' if ast else 'False' elif is_list(ast): if len(ast) > 0 and ast[0] == "quote": return "'%s" % unparse(ast[1]) else: return "(%s)" % " ".join([unparse(x) for x in ast]) else: # integers or symbols (or lambdas) return str(ast)
def unparse(ast): """Turns an AST back into lisp program source""" if is_boolean(ast): return "#t" if ast else "#f" elif is_list(ast): if len(ast) > 0 and ast[0] == "quote": return "'%s" % unparse(ast[1]) else: return "(%s)" % " ".join([unparse(x) for x in ast]) else: # integers or symbols (or lambdas) return str(ast)
def eval_list(ast, env): """evaluate all the built-in functions""" if ast[0] == 'quote': return eval_quote(ast, env) elif ast[0] == 'exit': return eval_exit(ast) elif ast[0] == 'print': return eval_print(ast, env) elif ast[0] == 'atom': return eval_atom(ast, env) elif ast[0] == 'let': return eval_let(ast, env) elif ast[0] == 'def': return eval_def(ast, env) elif ast[0] == 'lambda': return eval_lambda(ast, env) elif ast[0] == 'eq': return eval_eq(ast, env) elif ast[0] == 'set': return eval_set(ast, env) elif ast[0] == 'if': return eval_if(ast, env) elif ast[0] == 'cons': return eval_cons(ast, env) elif ast[0] == 'head': return eval_head(ast, env) elif ast[0] == 'tail': return eval_tail(ast, env) elif ast[0] == 'rhead': return eval_rhead(ast, env) elif ast[0] == 'rtail': return eval_rtail(ast, env) elif ast[0] == 'empty': return eval_empty(ast, env) elif ast[0] in [ '+', '-', '*', '**', '/', 'mod', '<', '<=', '=', '!=', '>=', '>' ]: return eval_math(ast, env) elif ast[0] == 'random': return eval_random() elif ast[0] in ['int', 'float', 'str', 'type']: return eval_types(ast, env) elif ast[0] in ['str_append', 'str_split']: return eval_string(ast, env) elif is_closure(ast[0]): return apply(ast, env) elif is_symbol(ast[0]) or is_list(ast[0]): return evaluate([evaluate(ast[0], env)] + ast[1:], env) else: raise LispError('%s is not a function' % unparse(ast[0]))
def evalConsCommand(args, env): if len(args) != 2: raise LispError("cons command expects exactly two arguments") head = evaluate(args[0], env) rest = evaluate(args[1], env) if not is_list(rest): raise LispError("cons command expects a list as second argument") output = list(rest) output.insert(0, head) return output
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment. """ if is_boolean(ast) or is_integer(ast): # evaluate booleans and integers return ast elif is_symbol(ast): # evaluate symbols return env.lookup(ast) elif is_list(ast): # evaluate lists if is_closure(ast[0]): # evaluate closure return eval_closure(ast, env) elif ast[0] == 'quote': # evaluate quotes return ast[1] elif ast[0] == 'atom': # evaluate atoms return is_atom(evaluate(ast[1], env)) elif ast[0] == 'eq': # evaluate equality return eval_eq(ast, env) # evaluate basic math operators: elif ast[0] in ['+', '-', '/', '*', 'mod', '>', '<', '=']: return eval_math(ast, env) elif ast[0] == 'if': # evaluate if expression return eval_if(ast, env) elif ast[0] == 'define': # evaluate define statement eval_define(ast, env) elif ast[0] == 'lambda': # evaluate lambda statement return eval_lambda(ast, env) elif ast[0] == 'cons': # evaluate cons statement return eval_cons(ast, env) elif ast[0] == 'head': # evaluate head statement return eval_head(ast, env) elif ast[0] == 'tail': # evaluate tail statement return eval_tail(ast, env) elif ast[0] == 'empty': # evaluate empty statement return eval_empty(ast, env) elif is_symbol(ast[0]) or is_list(ast[0]): # evaluate closure from env return eval_closure_env(ast, env) else: raise LispError('Argument is not a function!')
def evalLambdaCommand(args, env): if len(args) != 2: raise LispError("lambda command expects exactly two arguments") params = args[0] body = args[1] if not is_list(params): raise LispError("lambda first parameter is expected to be a list") # In case the lambda has no parameters, evaluate its body and return the result if len(params) == 0: return evaluate(body, env) else: return Closure(env, params, body)
def eval_list(ast, env): """evaluate all the built-in functions""" if ast[0] == 'quote': return eval_quote(ast, env) elif ast[0] == 'exit': return eval_exit(ast) elif ast[0] == 'print': return eval_print(ast, env) elif ast[0] == 'atom': return eval_atom(ast, env) elif ast[0] == 'let': return eval_let(ast, env) elif ast[0] == 'def': return eval_def(ast, env) elif ast[0] == 'lambda': return eval_lambda(ast, env) elif ast[0] == 'eq': return eval_eq(ast, env) elif ast[0] == 'set': return eval_set(ast, env) elif ast[0] == 'if': return eval_if(ast, env) elif ast[0] == 'cons': return eval_cons(ast, env) elif ast[0] == 'head': return eval_head(ast, env) elif ast[0] == 'tail': return eval_tail(ast, env) elif ast[0] == 'rhead': return eval_rhead(ast, env) elif ast[0] == 'rtail': return eval_rtail(ast, env) elif ast[0] == 'empty': return eval_empty(ast, env) elif ast[0] in ['+', '-', '*', '**', '/', 'mod', '<', '<=', '=', '!=', '>=', '>']: return eval_math(ast, env) elif ast[0] == 'random': return eval_random() elif ast[0] in ['int', 'float', 'str', 'type']: return eval_types(ast, env) elif ast[0] in ['str_append', 'str_split']: return eval_string(ast, env) elif is_closure(ast[0]): return apply(ast, env) elif is_symbol(ast[0]) or is_list(ast[0]): return evaluate([evaluate(ast[0], env)] + ast[1:], env) else: raise LispError('%s is not a function' % unparse(ast[0]))
def evaluate(self, env): if is_list(self.ast): if is_symbol(self.ast[0]): # self.ast[0] is possibly a function name block = [env.lookup(self.ast[0])] if is_closure(evaluate(self.ast[0], env)): block = [evaluate(self.ast[0], env)] else: raise LispError("not a function") block.extend(self.ast[1:]) return evaluate(block, env) try: return env.lookup(self.ast) except Exception as e: if is_symbol(self.ast): raise e return self.ast
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" if is_integer(ast): return ast elif ast[0] == "quote": return ast[1] elif ast[0] == "atom": return atom(ast[1], env) elif ast[0] == "eq": first = evaluate(ast[1], env) second = evaluate(ast[2], env) return first == second elif ast[0] == "+": return do_math(ast, op.add, env) elif ast[0] == "-": return do_math(ast, op.sub, env) elif ast[0] == "/": return do_math(ast, op.div, env) elif ast[0] == "*": return do_math(ast, op.mul, env) elif ast[0] == "mod": return do_math(ast, op.mod, env) elif ast[0] == ">": return do_math(ast, op.gt, env) elif ast[0] == "if": return do_if(ast, env) elif ast[0] == "define": define(ast, env) elif ast[0] == "lambda": return closure(ast, env) elif ast[0] == "cons": return cons(ast, env) elif ast[0] == "car": return car(ast, env) elif ast[0] == "cdr": return cdr(ast, env) elif is_closure(ast[0]): return evaluate_closure(ast, env) elif is_list(ast): return evaluate_function(ast, env) else: return env.lookup(ast)
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" if is_symbol(ast): return env.lookup(ast) elif is_boolean(ast) or is_integer(ast): return ast elif is_closure(ast): return evalClosure(ast, [], env) elif is_list(ast) and len(ast) > 0: first = ast[0] # handle string commands if isinstance(first, basestring): if first in commands: return evalCommand(first, ast[1:], env) # When a non-keyword symbol is the first element of the AST list, it is resolved to its value in # the environment (which should be a function closure). An AST with the variables # replaced with its value should then be evaluated instead. else: func = env.lookup(first) if not is_closure(func): raise LispError("Symbol %s must evaluate to a function" % first) ast[0] = func return evaluate(ast, env) # handle closure objects elif is_closure(first): return evalClosure(first, ast[1:], env) else: # otherwise - evaluate the first expression on the list firstEval = evaluate(first, env); if is_closure(firstEval): # if closure - treat the rest of the list as arguments return evalClosure(first, ast[1:], env) else : # not a closure, just evaluate the rest of the list and return the last expression if len(ast) > 1: return evaluate(ast[1:], env) else: return firstEval raise LispError("Invalid AST: %s" % ast)
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" if is_boolean(ast): return ast elif is_integer(ast): return ast elif ast[0] in ['+', '-', '*', '/']: return eval_math(ast, env) elif is_list(ast): if ast[0] == "atom": return is_atom(evaluate(ast[1], env)) #return is_atom(ast[1]) elif ast[0] == "quote": return ast[1] elif ast[0] == "eq": assert_exp_length(ast, 3) v1 = evaluate(ast[1], env) v2 = evaluate(ast[2], env) if not is_atom(v1) or not is_atom(v2): return False else: return (v1 == v2)
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" if is_atom(ast): if is_symbol(ast): return forms[ast] if ast in forms else env.lookup(ast) else: return ast else: if (len(ast) == 0): return [] fn = evaluate(ast[0], env) args = ast[1:] if len(ast) > 1 else [] if callable(fn): return fn(args, env) if is_closure(fn): return apply(fn, args, env) elif is_list(fn): return evaluate([evaluate(fn, env)] + args, env) else: raise LispError("%s is not a function." % unparse(fn))
def evaluate(ast, env): """Evaluate an Abstract Syntax Tree in the specified environment.""" # evaluating atoms if is_symbol(ast): return env.lookup(ast) if is_boolean(ast): return ast if is_integer(ast): return ast if is_list(ast): # lists if ast[0] == "cons": if len(ast) != 3: raise LispError("expected 2 arguments") else: value = evaluate(ast[1], env) list = evaluate(ast[2], env) new_list = [value] for i in list: new_list.append(i) return new_list if ast[0] == "head": list = evaluate(ast[1], env) if list == []: raise LispError else: return list[0] if ast[0] == "tail": list = evaluate(ast[1], env) if list == []: raise LispError else: return list[1:] if ast[0] == "empty": list = evaluate(ast[1], env) if list == []: return True else: return False if ast[0] == "quote": return ast[1] # functions if is_closure(ast[0]): closure = ast[0] arguments = ast[1:] params = closure.params number_of_arguments = len(arguments) number_of_params = len(params) if number_of_arguments != number_of_params: raise LispError("wrong number of arguments, expected %(param)d got %(arg)d" % {"arg": number_of_arguments, "param": number_of_params}) variables = {} for i in range(number_of_arguments): arg = evaluate(arguments[i], env) param = params[i] variables.update({param: arg}) environment = closure.env.extend(variables) return evaluate(closure.body, environment) if ast[0] == "lambda": if not is_list(ast[1]): raise LispError if len(ast) == 3: return Closure(env, ast[1], ast[2]) else: raise LispError("number of arguments") # defining variables if ast[0] == "define": if is_symbol(ast[1]): if len(ast) == 3: return env.set(ast[1], evaluate(ast[2], env)) else: raise LispError("Wrong number of arguments") else: raise LispError("non-symbol") #typechecks if ast[0] == "atom": return is_atom(evaluate(ast[1], env)) if ast[0] == "eq": return evaluate(ast[1], env) == evaluate(ast[2], env) and \ is_atom(evaluate(ast[1], env)) and is_atom(evaluate(ast[2], env)) #arithmetic: # elif is_arith_op(ast[0]): # try: # return arith_ops[ast[0]](evaluate(ast[1], env), evaluate(ast[2], env)) # make dicitonary of these operators if ast[0] == "+": if is_integer(evaluate(ast[1], env)) and is_integer(evaluate(ast[2], env)): return evaluate(ast[1], env) + evaluate(ast[2], env) else: raise LispError if ast[0] == "-": if is_integer(evaluate(ast[1], env)) and is_integer(evaluate(ast[2], env)): return evaluate(ast[1], env) - evaluate(ast[2], env) else: raise LispError if ast[0] == "*": if is_integer(evaluate(ast[1], env)) and is_integer(evaluate(ast[2], env)): return evaluate(ast[1], env) * evaluate(ast[2], env) else: raise LispError if ast[0] == "/": if is_integer(evaluate(ast[1], env)) and is_integer(evaluate(ast[2], env)): return evaluate(ast[1], env) / evaluate(ast[2], env) else: raise LispError if ast[0] == "mod": if is_integer(evaluate(ast[1], env)) and is_integer(evaluate(ast[2], env)): return evaluate(ast[1], env) % evaluate(ast[2], env) else: raise LispError # boolean operators if ast[0] == ">": return evaluate(ast[1], env) > evaluate(ast[2], env) if ast[0] == "<": return evaluate(ast[1], env) < evaluate(ast[2], env) # control-flow if ast[0] == 'if': pred = ast[1] then = ast[2] elsee = ast[3] if evaluate(pred, env): return evaluate(then, env) else: return evaluate(elsee, env) if is_symbol(ast[0]) or is_list(ast[0]): closure = evaluate(ast[0], env) return evaluate([closure] + ast[1:], env) else: raise LispError("not a function")
def eval_cons(ast, env): assert_exp_length(ast, 3) ls = evaluate(ast[2], env) if not is_list(ls): raise LispError('The second argument of cons should be a list.') return [evaluate(ast[1], env)] + ls
def eval_empty(ast, env): assert_exp_length(ast, 2) ls = evaluate(ast[1], env) if not is_list(ls): raise LispError('The argument of empty should be a list.') return len(ls) == 0