From: Jorge Gorbe Date: Fri, 10 Jan 2014 11:17:30 +0000 (+0100) Subject: Added tail call optimizations X-Git-Url: http://slack.codemaniacs.com/git/?a=commitdiff_plain;h=638c05b189c438dad8b15371c8eaa9a80f04956b;p=mylisp.git Added tail call optimizations Added more int operators: -, *, /, mod Added factorial test file --- diff --git a/lisp0.py b/lisp0.py index 3e58444..abb63b7 100644 --- a/lisp0.py +++ b/lisp0.py @@ -271,6 +271,35 @@ def plus_rec(args, env, result): def lisp_plus(args, env): return plus_rec(args, env, make_int(0)) +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) + +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) + +def lisp_times(args, env): + return times_rec(args, env, make_int(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) + +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) def lisp_display(args, env): args = eval_list(args, env) @@ -299,19 +328,66 @@ def should_eval_arguments(x): global_env = Environment() - +# inline apply into eval, to convert last call to eval in apply into a loop +# in order to optimize tail calls def lisp_eval(e, env): """evals an expression""" - result = e - if is_symbol(e): - result = env[e.v] - elif is_pair(e): - f = lisp_eval(car(e), env) - args = cdr(e) - if should_eval_arguments(f): - args = eval_list(args, env) - result = lisp_apply(f, args, env) - #print "evaluating %s\nresult: %s"%(e, result) + ended = False + while not ended: + #print "eval: %s"%str(e) + if is_symbol(e): + result = env[e.v] + ended = True + elif is_pair(e): + f = lisp_eval(car(e), env) + args = cdr(e) + # inlined body of the apply function + if should_eval_arguments(f): + args = eval_list(args, env) + if type(f) == types.FunctionType: + # TEST: special case for cond, the old lisp_cond function is only used as a marker, but evaluated inline + if f == lisp_cond: + action = None + while not is_nil(args): + c = car(args) + assert(is_pair(c)) + if is_true(lisp_eval(car(c), env)): + action = cadr(c) + break + args = cdr(args) + if action is not None: + e = action + else: + result = Value.NIL + ended = True + else: + result = f(args, env) + ended = True + elif f.t == "macro": + expansion = lisp_apply(f.v, args, env) + #print "macro expansion:", expansion + e = expansion + else: + formals, body, environment = f.v + new_env = Environment(environment) + #insert parameters into the environment + while not is_nil(formals): + name = car(formals) + formals = cdr(formals) + if not is_nil(args): + arg = car(args) + args = cdr(args) + else: + arg = Value.NIL + + new_env[name.v] = arg + + e = body + env = new_env + else: + result = e + ended = True + return result def lisp_apply(f, args, env): @@ -361,6 +437,10 @@ global_env["display"] = lisp_display global_env["list"] = lisp_list global_env["="] = lisp_equal global_env["+"] = lisp_plus +global_env["-"] = lisp_minus +global_env["*"] = lisp_times +global_env["/"] = lisp_div +global_env["%"] = lisp_mod global_env["nil"] = Value.NIL