Last active
April 1, 2023 05:46
-
-
Save conf8o/bdfbf9f682f70ce1a92c85000db72e17 to your computer and use it in GitHub Desktop.
lisp by python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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