Added tail call optimizations
authorJorge Gorbe <slack@codemaniacs.com>
Fri, 10 Jan 2014 11:17:30 +0000 (12:17 +0100)
committerJorge Gorbe <slack@codemaniacs.com>
Fri, 10 Jan 2014 11:17:30 +0000 (12:17 +0100)
Added more int operators: -, *, /, mod
Added factorial test file

lisp0.py

index 3e5844428ca72d11d6c5e548dd4bffe323ffc733..abb63b7f11b9b833499b6c5f751f901832c08235 100644 (file)
--- 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