From ae32029453d05d0515ea0c1346f9fafa015e748d Mon Sep 17 00:00:00 2001 From: Jorge Gorbe Date: Mon, 13 Jan 2014 13:47:07 +0100 Subject: [PATCH] moved argument checking and evaluation in lisp_* functions to a common decorator --- lisp0.py | 153 +++++++++++++++++++++++++------------------------------ repl.py | 7 ++- 2 files changed, 73 insertions(+), 87 deletions(-) diff --git a/lisp0.py b/lisp0.py index 18c6c86..edce63a 100644 --- a/lisp0.py +++ b/lisp0.py @@ -1,5 +1,7 @@ import types import sys +import operator +import inspect from functools import reduce class Pair: @@ -26,6 +28,7 @@ def make_string(s): return Value("string", s) def make_function(params, body, env): return Value("function", [params, body, env]) def make_macro(f): return Value("macro", f) def make_symbol(s): return Value("symbol", s) +def make_boolean(b): return Value.TRUE if b else Value.FALSE def cons(x,y): return Value("pair", Pair(x,y)) def flipcons(x,y): return cons(y,x) def make_list(*args): return reduce(flipcons, reversed(args), Value.NIL) @@ -92,13 +95,11 @@ def foldr(func, initial, l): if is_nil(l): return initial else: return func(car(l), foldr(func, initial, cdr(l))) -def reverse_list(l): return foldl(flipcons, Value.NIL, l) -def map_list(func, l): return foldr(lambda x,y: cons(func(x), y), Value.NIL, l) -def do_list(func, l): return foldl(lambda x,y: func(y), None, l) - -def eval_list(args, env): - return map_list(lambda x: lisp_eval(x, env), args) - +def reverse_list(l): return foldl(flipcons, Value.NIL, l) +def map_list(func, l): return foldr(lambda x,y: cons(func(x), y), Value.NIL, l) +def do_list(func, l): return foldl(lambda x,y: func(y), None, l) +def eval_list(args, env): return map_list(lambda x: lisp_eval(x, env), args) +def list_length(l): return foldl(lambda x,y: x+1, 0, l) # Built-in functions and special forms # ------------------------------------ @@ -118,7 +119,6 @@ def lisp_define(args, env): env[name.v] = lisp_eval(value, env) return Value.NIL - def lisp_list(args, env): return eval_list(args, env) @@ -126,6 +126,20 @@ def lisp_quote(args, env): assert(is_nil(cdr(args))) return car(args) +def lisp_cond(args, env): + while not is_nil(args): + c = car(args) + assert(is_pair(c)) + if is_true(lisp_eval(car(c), env)): + actions = cdr(c) + while not is_nil(actions): + result = lisp_eval(car(actions), env) + actions = cdr(actions) + return result + args = cdr(args) + return Value.NIL + + def quasiquote_expression(expr, env): """returns a tuple with: - the expression with unquotes recursively expanded as needed @@ -152,98 +166,66 @@ def quasiquote_expression(expr, env): return expr, "normal" def lisp_quasiquote(args, env): - assert(is_nil(cdr(args))) + assert(list_length(args) == 1) e = car(args) return quasiquote_expression(e, env)[0] +############################## +# functions exported to lisp # +############################## + +# a decorator which checks the number of arguments, evals them, +# and forwards them to a python function as positional arguments +def lisp_function(f): + def result(args, env): + argspec = inspect.getargspec(f) + if (argspec.varargs is None): + expected_args = len(argspec.args) + given_args = list_length(args) + if (given_args != expected_args): + raise Exception("function %s takes %d arguments (%d given)"%(f.func_name, expected_args, given_args)) + def eval_and_append(l, x): + l.append(lisp_eval(x, env)) + return l + python_args = foldl(eval_and_append, [], args) + return f(*python_args) + return result -def lisp_car(args, env): - args = eval_list(args, env) - assert(is_nil(cdr(args))) - return car(car(args)) +@lisp_function +def lisp_car(l): return car(l) -def lisp_cdr(args, env): - args = eval_list(args, env) - assert(is_nil(cdr(args))) - return cdr(car(args)) +@lisp_function +def lisp_cdr(l): return cdr(l) -def lisp_cons(args, env): - args = eval_list(args, env) - assert(is_nil(cddr(args))) - return cons(car(args), cadr(args)) +@lisp_function +def lisp_cons(a, b): return cons(a, b) - -def lisp_cond(args, env): - while not is_nil(args): - c = car(args) - assert(is_pair(c)) - if is_true(lisp_eval(car(c), env)): - actions = cdr(c) - while not is_nil(actions): - result = lisp_eval(car(actions), env) - actions = cdr(actions) - return result - args = cdr(args) - return Value.NIL - # defined only for atoms -def lisp_equal(args, env): - args = eval_list(args, env) - e1 = car(args) - e2 = cadr(args) - result = (not is_pair(e1)) and e1.t == e2.t and e1.v == e2.v - if result: - return Value.TRUE - else: - return Value.FALSE - - -def plus_rec(args, env, result): - if is_nil(args): return result - else: - result.v += lisp_eval(car(args), env).v - return plus_rec(cdr(args), env, result) - -def lisp_plus(args, env): - return plus_rec(args, env, make_int(0)) +@lisp_function +def lisp_equal(e1, e2): return make_boolean((not is_pair(e1)) and e1.t == e2.t and e1.v == e2.v) -def lisp_minus(args, env): - args = eval_list(args, env) - e1 = car(args) - e2 = cadr(args) - assert(e1.t == "int" and e2.t == "int") - return make_int(e1.v - e2.v) +@lisp_function +def lisp_plus(*args): return make_int(sum((i.v for i in args))) -def times_rec(args, env, result): - if is_nil(args): return result - else: - result.v *= lisp_eval(car(args), env).v - return times_rec(cdr(args), env, result) +@lisp_function +def lisp_minus(e1, e2): return make_int(e1.v - e2.v) -def lisp_times(args, env): - return times_rec(args, env, make_int(1)) +@lisp_function +def lisp_times(*args): return make_int(reduce(operator.mul, (i.v for i in args), 1)) -def lisp_div(args, env): - args = eval_list(args, env) - e1 = car(args) - e2 = cadr(args) - assert(e1.t == "int" and e2.t == "int") - return make_int(e1.v / e2.v) +@lisp_function +def lisp_div(e1, e2): return make_int(e1.v / e2.v) -def lisp_mod(args, env): - args = eval_list(args, env) - e1 = car(args) - e2 = cadr(args) - assert(e1.t == "int" and e2.t == "int") - return make_int(e1.v % e2.v) +@lisp_function +def lisp_mod(e1, e2): return make_int(e1.v % e2.v) -def lisp_display(args, env): - args = eval_list(args, env) - do_list(lambda x: sys.stdout.write(str(x)), args) +@lisp_function +def lisp_display(*args): + for i in args: + sys.stdout.write(str(i)) sys.stdout.write("\n") return Value.NIL - # Helper functions for the evaluator # ---------------------------------- @@ -268,9 +250,10 @@ global_env = Environment() # in order to optimize tail calls def lisp_eval(e, env): """evals an expression""" + result = None ended = False while not ended: - #print "eval: %s"%str(e) + # print "eval: %s"%str(e) if is_symbol(e): result = env[e.v] ended = True @@ -323,7 +306,7 @@ def lisp_eval(e, env): else: result = e ended = True - + # print "result: %s"%result return result def lisp_apply(f, args, env): diff --git a/repl.py b/repl.py index eb06d6c..96122d7 100644 --- a/repl.py +++ b/repl.py @@ -10,7 +10,10 @@ else: while True: in_str = raw_input() e,s = parse(in_str) - result = lisp_eval(e, global_env) - print(result) + try: + result = lisp_eval(e, global_env) + print(result) + except Exception as e: + print '%s: %s' % (type(e).__name__, e) -- 2.34.1