Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

added lazy interpreter & made interpreter closures indistinguishable …

…from python closures (i.e., built-in globals)
  • Loading branch information...
commit 9a1d1574d1e9e3115cd9eeaac871ea6fb1679b05 1 parent cdebe3a
Logan Kearsley authored

Showing 2 changed files with 211 additions and 12 deletions. Show diff stats Hide diff stats

  1. +208 0 tiddlazy.py
  2. +3 12 tiddlyvau.py
208 tiddlazy.py
... ... @@ -0,0 +1,208 @@
  1 +#### tiddlyvau.py
  2 +#
  3 +# Simple Lisp interpreter that uses vau expressions
  4 +# to remove all built-in functions and syntactic forms from eval
  5 +#
  6 +# Adapted from tiddlylisp by Michael Nielsen. See
  7 +# http://michaelnielsen.org/ddi/lisp-as-the-maxwells-equations-of-software/
  8 +
  9 +import sys
  10 +import traceback
  11 +from tiddlyparser import *
  12 +
  13 +class Env(dict):
  14 + "An environment: a dict of {'var':val} pairs, with an outer Env."
  15 + def __init__(self, bindings={}, outer=None):
  16 + self.update(bindings)
  17 + self.outer = outer
  18 +
  19 + def __getitem__(self, var):
  20 + loc = self.find(var)
  21 + val = super(Env,loc).__getitem__(var)
  22 + if isa(val, Deferral):
  23 + val = eval(val.expr, val.env)
  24 + loc[var] = val
  25 + return val
  26 +
  27 + def find(self, var):
  28 + "Find the innermost Env where var appears."
  29 + if var in self:
  30 + return self
  31 + elif not self.outer is None:
  32 + return self.outer.find(var)
  33 + else: raise ValueError("%s is not defined"%(var,))
  34 +
  35 +class Deferral():
  36 + def __init__(self, expr, env):
  37 + self.expr = expr
  38 + self.env = env
  39 +
  40 +def define(var,exp):
  41 + val = eval(exp)
  42 + if not isa(var.expr, Symbol):
  43 + raise Exception("Invalid Symbol %s" % (var.expr,))
  44 + exp.env[var.expr] = val
  45 + return val
  46 +
  47 +def setvar(var, exp):
  48 + val = eval(exp)
  49 + env.find(var)[var] = val
  50 + return val
  51 +
  52 +def quote(exp): return exp.expr
  53 +
  54 +def cond(*x):
  55 + for clause in x:
  56 + (p, e) = clause.expr
  57 + if eval(p,clause.env):
  58 + return eval(e,clause.env)
  59 + raise ValueError("No Branch Evaluates to True")
  60 +
  61 +def begin(*x):
  62 + val = 0
  63 + for exp in x:
  64 + val = eval(exp)
  65 + return val
  66 +
  67 +def vprint(x):
  68 + val = eval(x)
  69 + print to_string(val)
  70 + return val
  71 +
  72 +global_env = Env({
  73 + '+': lambda x,y:eval(x)+eval(y),
  74 + '-': lambda x,y:eval(x)-eval(y),
  75 + '*': lambda x,y:eval(x)*eval(y),
  76 + '/': lambda x,y:eval(x)/eval(y),
  77 + '>': lambda x,y:eval(x)>eval(y),
  78 + '<': lambda x,y:eval(x)<eval(y),
  79 + '>=': lambda x,y:eval(x)>=eval(y),
  80 + '<=': lambda x,y:eval(x)<=eval(y),
  81 + '=': lambda x,y:eval(x)==eval(y),
  82 + 'eq?': lambda x,y:
  83 + (lambda vx,vy: (not isa(vx, list)) and (vx == vy))(eval(x),eval(y)),
  84 + 'cons': lambda x,y:[eval(x)]+eval(y),
  85 + 'car': lambda x:eval(x)[0],
  86 + 'cdr': lambda x:eval(x)[1:],
  87 + 'list': lambda *x:[eval(exp) for exp in x],
  88 + 'append': lambda x,y:eval(x)+eval(y),
  89 + 'len': lambda x:len(eval(x)),
  90 + 'null?': lambda x:eval(x)==[],
  91 + 'symbol?': lambda x:isa(eval(x),Symbol),
  92 + 'list?': lambda x:isa(eval(x),list),
  93 + 'atom?': lambda x:not isa(eval(x), list),
  94 + 'exit': lambda:exit(),
  95 + 'True': True,
  96 + 'False': False,
  97 + 'if': lambda test,conseq,alt: eval((conseq if eval(test) else alt)),
  98 + 'cond': cond,
  99 + 'define': define,
  100 + 'set!': setvar,
  101 + 'lambda': lambda vars, body:
  102 + (lambda *args: eval(body.expr, Env(zip(vars.expr, args), body.env))),
  103 + 'q': quote,
  104 + 'quote': quote,
  105 + 'begin': begin,
  106 + 'print': vprint,
  107 + 'eval': lambda x,e: eval(x,e)
  108 +})
  109 +
  110 +#### eval
  111 +
  112 +def eval(x, env=global_env):
  113 + "Evaluate an expression in an environment."
  114 + if isa(x, Symbol): # variable reference
  115 + return env.find(x)[x]
  116 + if isa(x, Deferral):
  117 + return eval(x.expr, x.env)
  118 + if isa(x, list): # (proc exp*)
  119 + proc = eval(x[0], env)
  120 + args = [Deferral(expr,env) for expr in x[1:]]
  121 + if hasattr(proc, '__call__'): return proc(*args)
  122 + else: raise ValueError("%s = %s is not a procedure" % (to_string(x[0]),to_string(proc)))
  123 + return x
  124 +
  125 +#### Load from a file and run
  126 +
  127 +def load(filename):
  128 + """
  129 + Load the tiddlylisp program in filename, execute it, and start the
  130 + repl. If an error occurs, execution stops, and we are left in the
  131 + repl. Note that load copes with multi-line tiddlylisp code by
  132 + merging lines until the number of opening and closing parentheses
  133 + match.
  134 + """
  135 + print "Loading and executing %s" % filename
  136 + rps = 0
  137 + full_line = ""
  138 + for line in open(filename, "r"):
  139 + line = line.strip()
  140 + full_line += line
  141 + rps += line.count("(")-line.count(")")
  142 + if rps == 0 and full_line.strip() != "":
  143 + try:
  144 + tokens = tokenize(full_line)
  145 + while len(tokens) > 0:
  146 + val = eval(parse(tokens))
  147 + except SystemExit:
  148 + exit()
  149 + except:
  150 + handle_error()
  151 + print "\nThe line in which the error occurred:\n%s" % full_line
  152 + break
  153 + full_line = ""
  154 +
  155 +#### repl
  156 +
  157 +def repl(prompt='tiddlylisp> '):
  158 + """
  159 + A prompt-read-eval-print loop.
  160 + """
  161 + try:
  162 + while True:
  163 + full_line = raw_input(prompt)
  164 + rps = full_line.count("(")-full_line.count(")")
  165 + while rps != 0 or full_line == "":
  166 + line = raw_input(">\t")
  167 + full_line += line
  168 + rps += line.count("(")-line.count(")")
  169 + try:
  170 + tokens = tokenize(full_line)
  171 + while len(tokens) > 0:
  172 + val = eval(parse(tokens))
  173 + if val is not None: print to_string(val)
  174 + except (KeyboardInterrupt, SystemExit) as e:
  175 + raise e
  176 + except ValueError as e:
  177 + print e.message
  178 + except:
  179 + handle_error()
  180 + except (KeyboardInterrupt, SystemExit):
  181 + print "\nExiting tiddlylisp\n"
  182 + except:
  183 + print "\nFatal Error\n"
  184 + traceback.print_exc()
  185 + exit()
  186 +
  187 +#### error handling
  188 +
  189 +def handle_error():
  190 + """
  191 + Simple error handling for both the repl and load.
  192 + """
  193 + print "An error occurred. Here's the Python stack trace:\n"
  194 + traceback.print_exc()
  195 +
  196 +#### on startup from the command line
  197 +
  198 +if __name__ == "__main__":
  199 + if len(sys.argv) > 1:
  200 + load(sys.argv[1])
  201 + repl()
  202 +
  203 +#### on startup from the command line
  204 +
  205 +if __name__ == "__main__":
  206 + if len(sys.argv) > 1:
  207 + load(sys.argv[1])
  208 + repl()
15 tiddlyvau.py
@@ -24,16 +24,6 @@ def find(self, var):
24 24 return self.outer.find(var)
25 25 else: raise ValueError("%s is not defined"%(var,))
26 26
27   -class Closure():
28   - def __init__(self, body, vars, env):
29   - self.body = body
30   - self.vars = vars
31   - self.env = env
32   -
33   - def __call__(self, *args, **kwargs):
34   - exps = [eval(exp, kwargs['v']) for exp in args]
35   - return eval(self.body, Env(zip(self.vars, exps), self.env))
36   -
37 27 class Vau():
38 28 def __init__(self, body, vars, call_env_sym, clos_env):
39 29 self.body = body
@@ -105,7 +95,8 @@ def vprint(x,v=Env()):
105 95 'cond': cond,
106 96 'define': define,
107 97 'set!': set,
108   - 'lambda': lambda vars, body, v=Env(): Closure(body, vars, v),
  98 + 'lambda': lambda vars, body, v=Env():
  99 + (lambda *args, **kwargs: eval(body, Env(zip(vars, [eval(exp, kwargs['v']) for exp in args]), v))),
109 100 'vau': lambda vars, env, body, v=Env(): Vau(body, vars, env, v),
110 101 'q': quote,
111 102 'quote': quote,
@@ -210,4 +201,4 @@ def handle_error():
210 201 if __name__ == "__main__":
211 202 if len(sys.argv) > 1:
212 203 load(sys.argv[1])
213   - repl()
  204 + repl()

0 comments on commit 9a1d157

Please sign in to comment.
Something went wrong with that request. Please try again.