Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

added lazy interpreter & made interpreter closures indistinguishable …

…from python closures (i.e., built-in globals)
  • Loading branch information...
commit 9a1d1574d1e9e3115cd9eeaac871ea6fb1679b05 1 parent cdebe3a
@gliese1337 authored
Showing with 211 additions and 12 deletions.
  1. +208 −0 tiddlazy.py
  2. +3 −12 tiddlyvau.py
View
208 tiddlazy.py
@@ -0,0 +1,208 @@
+#### tiddlyvau.py
+#
+# Simple Lisp interpreter that uses vau expressions
+# to remove all built-in functions and syntactic forms from eval
+#
+# Adapted from tiddlylisp by Michael Nielsen. See
+# http://michaelnielsen.org/ddi/lisp-as-the-maxwells-equations-of-software/
+
+import sys
+import traceback
+from tiddlyparser import *
+
+class Env(dict):
+ "An environment: a dict of {'var':val} pairs, with an outer Env."
+ def __init__(self, bindings={}, outer=None):
+ self.update(bindings)
+ self.outer = outer
+
+ def __getitem__(self, var):
+ loc = self.find(var)
+ val = super(Env,loc).__getitem__(var)
+ if isa(val, Deferral):
+ val = eval(val.expr, val.env)
+ loc[var] = val
+ return val
+
+ def find(self, var):
+ "Find the innermost Env where var appears."
+ if var in self:
+ return self
+ elif not self.outer is None:
+ return self.outer.find(var)
+ else: raise ValueError("%s is not defined"%(var,))
+
+class Deferral():
+ def __init__(self, expr, env):
+ self.expr = expr
+ self.env = env
+
+def define(var,exp):
+ val = eval(exp)
+ if not isa(var.expr, Symbol):
+ raise Exception("Invalid Symbol %s" % (var.expr,))
+ exp.env[var.expr] = val
+ return val
+
+def setvar(var, exp):
+ val = eval(exp)
+ env.find(var)[var] = val
+ return val
+
+def quote(exp): return exp.expr
+
+def cond(*x):
+ for clause in x:
+ (p, e) = clause.expr
+ if eval(p,clause.env):
+ return eval(e,clause.env)
+ raise ValueError("No Branch Evaluates to True")
+
+def begin(*x):
+ val = 0
+ for exp in x:
+ val = eval(exp)
+ return val
+
+def vprint(x):
+ val = eval(x)
+ print to_string(val)
+ return val
+
+global_env = Env({
+ '+': lambda x,y:eval(x)+eval(y),
+ '-': lambda x,y:eval(x)-eval(y),
+ '*': lambda x,y:eval(x)*eval(y),
+ '/': lambda x,y:eval(x)/eval(y),
+ '>': lambda x,y:eval(x)>eval(y),
+ '<': lambda x,y:eval(x)<eval(y),
+ '>=': lambda x,y:eval(x)>=eval(y),
+ '<=': lambda x,y:eval(x)<=eval(y),
+ '=': lambda x,y:eval(x)==eval(y),
+ 'eq?': lambda x,y:
+ (lambda vx,vy: (not isa(vx, list)) and (vx == vy))(eval(x),eval(y)),
+ 'cons': lambda x,y:[eval(x)]+eval(y),
+ 'car': lambda x:eval(x)[0],
+ 'cdr': lambda x:eval(x)[1:],
+ 'list': lambda *x:[eval(exp) for exp in x],
+ 'append': lambda x,y:eval(x)+eval(y),
+ 'len': lambda x:len(eval(x)),
+ 'null?': lambda x:eval(x)==[],
+ 'symbol?': lambda x:isa(eval(x),Symbol),
+ 'list?': lambda x:isa(eval(x),list),
+ 'atom?': lambda x:not isa(eval(x), list),
+ 'exit': lambda:exit(),
+ 'True': True,
+ 'False': False,
+ 'if': lambda test,conseq,alt: eval((conseq if eval(test) else alt)),
+ 'cond': cond,
+ 'define': define,
+ 'set!': setvar,
+ 'lambda': lambda vars, body:
+ (lambda *args: eval(body.expr, Env(zip(vars.expr, args), body.env))),
+ 'q': quote,
+ 'quote': quote,
+ 'begin': begin,
+ 'print': vprint,
+ 'eval': lambda x,e: eval(x,e)
+})
+
+#### eval
+
+def eval(x, env=global_env):
+ "Evaluate an expression in an environment."
+ if isa(x, Symbol): # variable reference
+ return env.find(x)[x]
+ if isa(x, Deferral):
+ return eval(x.expr, x.env)
+ if isa(x, list): # (proc exp*)
+ proc = eval(x[0], env)
+ args = [Deferral(expr,env) for expr in x[1:]]
+ if hasattr(proc, '__call__'): return proc(*args)
+ else: raise ValueError("%s = %s is not a procedure" % (to_string(x[0]),to_string(proc)))
+ return x
+
+#### Load from a file and run
+
+def load(filename):
+ """
+ Load the tiddlylisp program in filename, execute it, and start the
+ repl. If an error occurs, execution stops, and we are left in the
+ repl. Note that load copes with multi-line tiddlylisp code by
+ merging lines until the number of opening and closing parentheses
+ match.
+ """
+ print "Loading and executing %s" % filename
+ rps = 0
+ full_line = ""
+ for line in open(filename, "r"):
+ line = line.strip()
+ full_line += line
+ rps += line.count("(")-line.count(")")
+ if rps == 0 and full_line.strip() != "":
+ try:
+ tokens = tokenize(full_line)
+ while len(tokens) > 0:
+ val = eval(parse(tokens))
+ except SystemExit:
+ exit()
+ except:
+ handle_error()
+ print "\nThe line in which the error occurred:\n%s" % full_line
+ break
+ full_line = ""
+
+#### repl
+
+def repl(prompt='tiddlylisp> '):
+ """
+ A prompt-read-eval-print loop.
+ """
+ try:
+ while True:
+ full_line = raw_input(prompt)
+ rps = full_line.count("(")-full_line.count(")")
+ while rps != 0 or full_line == "":
+ line = raw_input(">\t")
+ full_line += line
+ rps += line.count("(")-line.count(")")
+ try:
+ tokens = tokenize(full_line)
+ while len(tokens) > 0:
+ val = eval(parse(tokens))
+ if val is not None: print to_string(val)
+ except (KeyboardInterrupt, SystemExit) as e:
+ raise e
+ except ValueError as e:
+ print e.message
+ except:
+ handle_error()
+ except (KeyboardInterrupt, SystemExit):
+ print "\nExiting tiddlylisp\n"
+ except:
+ print "\nFatal Error\n"
+ traceback.print_exc()
+ exit()
+
+#### error handling
+
+def handle_error():
+ """
+ Simple error handling for both the repl and load.
+ """
+ print "An error occurred. Here's the Python stack trace:\n"
+ traceback.print_exc()
+
+#### on startup from the command line
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ load(sys.argv[1])
+ repl()
+
+#### on startup from the command line
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ load(sys.argv[1])
+ repl()
View
15 tiddlyvau.py
@@ -24,16 +24,6 @@ def find(self, var):
return self.outer.find(var)
else: raise ValueError("%s is not defined"%(var,))
-class Closure():
- def __init__(self, body, vars, env):
- self.body = body
- self.vars = vars
- self.env = env
-
- def __call__(self, *args, **kwargs):
- exps = [eval(exp, kwargs['v']) for exp in args]
- return eval(self.body, Env(zip(self.vars, exps), self.env))
-
class Vau():
def __init__(self, body, vars, call_env_sym, clos_env):
self.body = body
@@ -105,7 +95,8 @@ def vprint(x,v=Env()):
'cond': cond,
'define': define,
'set!': set,
- 'lambda': lambda vars, body, v=Env(): Closure(body, vars, v),
+ 'lambda': lambda vars, body, v=Env():
+ (lambda *args, **kwargs: eval(body, Env(zip(vars, [eval(exp, kwargs['v']) for exp in args]), v))),
'vau': lambda vars, env, body, v=Env(): Vau(body, vars, env, v),
'q': quote,
'quote': quote,
@@ -210,4 +201,4 @@ def handle_error():
if __name__ == "__main__":
if len(sys.argv) > 1:
load(sys.argv[1])
- repl()
+ repl()
Please sign in to comment.
Something went wrong with that request. Please try again.