From: Jorge Gorbe Date: Fri, 10 Jan 2014 13:53:13 +0000 (+0100) Subject: simplify quasiquote function X-Git-Url: http://slack.codemaniacs.com/git/?a=commitdiff_plain;h=57e130db28a3f909bbf7e0027a400e74a6751db7;p=mylisp.git simplify quasiquote function --- diff --git a/lisp0.py b/lisp0.py index abb63b7..18c6c86 100644 --- a/lisp0.py +++ b/lisp0.py @@ -1,5 +1,6 @@ import types import sys +from functools import reduce class Pair: def __init__(self, car, cdr): @@ -7,41 +8,18 @@ class Pair: 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) @@ -49,40 +27,19 @@ def make_function(params, body, env): return Value("function", [params, body, en 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" @@ -91,12 +48,9 @@ def is_symbol(v): return v.t == "symbol" 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: @@ -126,27 +80,21 @@ 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) @@ -166,10 +114,9 @@ def lisp_macro(args, env): 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): @@ -179,46 +126,35 @@ def lisp_quote(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): @@ -540,4 +476,29 @@ def eval_file(name): 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 +