import types
import sys
+from functools import reduce
class Pair:
def __init__(self, car, cdr):
self.cdr = cdr
class Value:
+ """A class encapsulating a tagged lisp value. self.t is a string with the type name, self.v is the value"""
def __init__(self, t, v=None):
self.t = t
self.v = v
def __str__(self):
- result = "" # self.t+"["
- if is_pair(self):
- result += "("
- l = self
- while is_pair(cdr(l)):
- result += "%s " % car(l)
- l = cdr(l)
- result += "%s" % car(l)
- if not is_nil(cdr(l)):
- result += " . %s" % cdr(l)
- result += ")"
- elif is_function(self):
- result += "(lambda %s %s), environment: %s"%(self.v[0], self.v[1], self.v[2])
- elif is_macro(self):
- result += "macro: %s"%str(self.v)
- elif is_nil(self):
- result += "nil"
- elif is_string(self):
- result += '"' + self.v + '"'
- else:
- result += str(self.v)
-
- #result += "]"
- return result
+ return Value2String(self)
Value.TRUE = Value("symbol", "#t")
Value.FALSE = Value("symbol", "#f")
Value.NIL = Value("nil", None)
-
# value constructors
def make_int(i): return Value("int", i)
def make_string(s): return Value("string", s)
def make_macro(f): return Value("macro", f)
def make_symbol(s): return Value("symbol", s)
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)
-def make_list(*args):
- if len(args) == 0: return Value.NIL
- result = cons(args[0], Value.NIL)
- last = result
- for i in args[1:]:
- new_last = cons(i, Value.NIL)
- set_cdr(last, new_last)
- last = new_last
- return result
-
-
-# value_accessors
-def car(p):
- assert(p.t == "pair")
- return p.v.car
-
-def cdr(p):
- assert(p.t == "pair")
- return p.v.cdr
+# pair value accessors
+def car(p): return p.v.car
+def cdr(p): return p.v.cdr
+def set_car(p, v): p.v.car = v
+def set_cdr(p, v): p.v.cdr = v
+# list helper accessors
def cadr(p): return car(cdr(p))
def cddr(p): return cdr(cdr(p))
-def set_car(p, v):
- assert(p.t == "pair")
- p.v.car = v
-
-def set_cdr(p, v):
- assert(p.t == "pair")
- p.v.cdr = v
-
-
-
# type predicates for values
def is_pair(v): return v.t == "pair"
def is_function(v): return v.t == "function"
def is_string(v): return v.t == "string"
def is_nil(v): return v.t == "nil"
def is_int(v): return v.t == "int"
-
-
def is_false(v): return is_symbol(v) and v.v == "#f"
def is_true(v): return not is_false(v)
-
# Environment is a dictionary wrapper with "inheritance": if a key is not found
# the parent environment is searched, and so on.
class Environment:
# helper lisp-list functions
+def foldl(func, initial, l):
+ result = initial
+ cur = l
+ while not is_nil(cur):
+ result = func(result, car(cur))
+ cur = cdr(cur)
+ return result
-def reverse_list_rec(l, result):
- if l.t == "nil": return result
- else: return reverse_list_rec(l.v.cdr, cons(l.v.car, result))
-
-def reverse_list(l):
- return reverse_list_rec(l, Value.NIL)
-
-def map_list_rec(func, l, result):
- if l.t == "nil": return result
- else: return map_list_rec(func, l.v.cdr, cons(func(l.v.car), result))
-
-def map_list(func, l):
- return reverse_list(map_list_rec(func, l, Value.NIL))
-
+def foldr(func, initial, l):
+ if is_nil(l): return initial
+ else: return func(car(l), foldr(func, initial, cdr(l)))
-def do_list(func, l):
- if is_nil(l): return
- else:
- func(car(l))
- do_list(func, 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 lisp_define(args, env):
name = car(args)
- assert(is_symbol(name))
value = cadr(args)
env[name.v] = lisp_eval(value, env)
- return None
+ return Value.NIL
def lisp_list(args, env):
assert(is_nil(cdr(args)))
return car(args)
-def quasiquote_expression(e, env):
- """returns an expression with unquotes recursively expanded as needed. If
+def quasiquote_expression(expr, env):
+ """returns a tuple with:
+ - the expression with unquotes recursively expanded as needed
+ - a string telling how the value should be inserted into the parent sequence: "normal" or "splicing"
called with an unquote-splicing expression, it will return a python list of
expressions, which will be inserted into the containing list"""
- # if e is a list, look for unquotes
- if is_pair(e):
- first = car(e)
- if is_symbol(first) and first.v == "unquote":
- assert(is_nil(cddr(e)))
- result = lisp_eval(cadr(e), env)
- elif is_symbol(first) and first.v == "unquote-splicing":
- second = lisp_eval(cadr(e), env)
- assert(is_nil(cddr(e)))
- assert(is_pair(second))
- result = []
- while not is_nil(second):
- result.append(car(second))
- second = cdr(second)
-
+ if is_pair(expr):
+ # if first in list is an unquote, eval the second and set mode accordingly
+ first = car(expr)
+ if is_symbol(first) and first.v in ["unquote", "unquote-splicing"]:
+ assert(is_nil(cddr(expr)))
+ mode = "splicing" if first.v == "unquote-splicing" else "normal"
+ return lisp_eval(cadr(expr), env), mode
else:
- result = make_list()
- cur = e
- while not is_nil(cur):
- r = quasiquote_expression(car(cur), env)
- if type(r) != list:
- r = [r]
- for i in r:
- result = cons(i, result)
-
- cur = cdr(cur)
-
- result = reverse_list(result)
+ def quasiquote_and_push_front(result, element):
+ r, mode = quasiquote_expression(element, env)
+ if mode == "splicing":
+ return foldl(flipcons, result, r) # push elements of r, in reverse order, at the head of result
+ else:
+ return cons(r, result)
+ result = foldl(quasiquote_and_push_front, make_list(), expr)
+ return reverse_list(result), "normal"
else:
- result = e
- return result
+ return expr, "normal"
def lisp_quasiquote(args, env):
assert(is_nil(cdr(args)))
e = car(args)
- return quasiquote_expression(e, env)
+ return quasiquote_expression(e, env)[0]
def lisp_car(args, env):
if e is not None:
lisp_eval(e, global_env)
+# value pretty-printing
+def Value2String(x):
+ result = ""
+ if is_pair(x):
+ result += "("
+ l = x
+ while is_pair(cdr(l)):
+ result += "%s " % car(l)
+ l = cdr(l)
+ result += "%s" % car(l)
+ if not is_nil(cdr(l)):
+ result += " . %s" % cdr(l)
+ result += ")"
+ elif is_function(x):
+ result += "(lambda %s %s), environment: %s"%(x.v[0], x.v[1], x.v[2])
+ elif is_macro(x):
+ result += "macro: %s"%str(x.v)
+ elif is_nil(x):
+ result += "nil"
+ elif is_string(x):
+ result += '"' + x.v + '"'
+ else:
+ result += str(x.v)
+ return result
+