Skip to content

Instantly share code, notes, and snippets.

@conf8o
Last active April 1, 2023 05:46
Show Gist options
  • Save conf8o/bdfbf9f682f70ce1a92c85000db72e17 to your computer and use it in GitHub Desktop.
Save conf8o/bdfbf9f682f70ce1a92c85000db72e17 to your computer and use it in GitHub Desktop.
lisp by python
import operator as op
from itertools import chain
def car(l):
l = iter(l)
try:
return next(l)
except StopIteration:
return None
def cdr(l):
l = iter(l)
try:
next(l)
return l
except StopIteration:
return None
class Special:
def __init__(self, env):
self.env = env
def __call__(self, *args):
pass
def is_special(obj):
return type(obj) is type and issubclass(obj, Special)
class EvaluateError(Exception):
pass
def lisp_list(*s):
return s
class Sym:
def __init__(self, sym):
self.sym = sym
def __hash__(self):
return hash(self.sym)
def __eq__(self, other):
return hash(self) == hash(other)
class S:
def __init__(self, *s):
self.car = car(s)
self.cdr = cdr(s)
def eval(self, env):
f = evaluate(self.car, env)
if is_special(f):
sp = f(env)
return sp(*self.cdr)
elif callable(f):
return f(*evaluate_list(self.cdr, env))
else:
raise EvaluateError()
class SymError(Exception):
pass
def evaluate(obj, env):
if type(obj) is S:
return obj.eval(env)
elif type(obj) is Sym:
for e in reversed(env):
x = e.get(obj)
if x is not None:
return x
raise SymError(obj)
else:
return obj
def evaluate_list(l, env):
for x in l:
yield evaluate(x, env)
class Define(Special):
def __call__(self, label, value):
self.env[-1][label] = evaluate(value, self.env)
class LetBindingError(Exception):
pass
class Let(Special):
def __call__(self, bindings, expr):
self.env.append({})
try:
bindings = iter(bindings)
for label in bindings:
value_s = next(bindings)
value = evaluate(value_s, self.env)
self.env[-1][label] = value
ret = evaluate(expr, self.env)
self.env.pop()
return ret
except StopIteration:
raise LetBindingError()
class Fn(Special):
def __call__(self, _args, expr):
def fn(*args):
bindings = chain.from_iterable(zip(_args, args))
return evaluate(S(Let, bindings, expr), self.env)
return fn
class If(Special):
def __call__(self, pred, t_expr, f_expr):
if evaluate(pred, self.env):
return evaluate(t_expr, self.env)
else:
return evaluate(f_expr, self.env)
environment = [{
Sym("+"): op.add,
Sym("-"): op.sub,
Sym("*"): op.mul,
Sym("/"): op.truediv,
Sym(">"): op.gt,
Sym("<"): op.lt,
Sym("="): op.eq,
Sym("def"): Define,
Sym("let"): Let,
Sym("fn"): Fn,
Sym("if"): If,
Sym("true"): True,
Sym("false"): False
}]
class ReadExprError(Exception):
pass
def append_atom(expr_buf, atom_buf):
sym = "".join(atom_buf)
if sym:
try:
num = int(sym)
expr_buf.append(num)
except ValueError:
expr_buf.append(Sym(sym))
def read_expr(s_expr, expr_buf, return_type):
atom_buf = []
for c in s_expr:
if return_type is str:
if c != "\"":
expr_buf.append(c)
else:
return "".join(expr_buf)
elif c == " " or c == "\n":
append_atom(expr_buf, atom_buf)
atom_buf = []
elif c in ["(", "[", "\""]:
append_atom(expr_buf, atom_buf)
atom_buf = []
_return_type = S if c == "(" else lisp_list if c == "[" else str
inner_expr = read_expr(s_expr, [], _return_type)
expr_buf.append(inner_expr)
elif c in [")", "]"]:
append_atom(expr_buf, atom_buf)
return return_type(*expr_buf)
else:
atom_buf.append(c)
return return_type(*expr_buf)
ss = [
S(Define, Sym("x"), S(Sym("*"), 120, 50)),
S(Let, [Sym("a"), 80,
Sym("b"), S(Sym("*"), 2, 10),
Sym("square"), S(Fn, [Sym("x")],
S(Sym("*"), Sym("x"), Sym("x")))],
S(Sym("/"), Sym("x"), S(Sym("square"), S(Sym("+"), Sym("a"), Sym("b")))))
]
for s in ss:
res = evaluate(s, environment)
print(res)
def read(expr):
return read_expr(iter(expr), [], lisp_list)
s = """
(def x
(* 120 50))
(def s "lisp")
(let [a 80
b (* 2 10)
square (fn [x] (* x x))]
(/ x (square (+ a b))))
(let [z 10]
(if (< x 11)
(* 10 20)
(+ 10 20)))
"""
ss = read(s)
for s in ss:
res = evaluate(s, environment)
print(res)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment