Skip to content

Commit

Permalink
eval could be simpler
Browse files Browse the repository at this point in the history
  • Loading branch information
hoosierEE committed May 13, 2024
1 parent 018d7ab commit 042cf94
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 134 deletions.
4 changes: 2 additions & 2 deletions prototype/Eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# use plain Python values instead of Val class
from Ast import Ast
from Builtin import ASSIGN,VERB
from Semantic import lamp,formalize
from Semantic import lam_from_prj,formalize
class Sym(str):pass
class Name(str):pass
class Num(float):pass
Expand Down Expand Up @@ -56,7 +56,7 @@ def v2(op,x,y):

def evl(x:Ast,e=None) -> object:
e = e or {}
x = formalize(lamp(x))
x = formalize(lam_from_prj(x))
return Eval(e,x)

def Eval(e:dict,x:Ast) -> object:
Expand Down
12 changes: 4 additions & 8 deletions prototype/Semantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,17 @@
from Ast import Ast
from Builtin import ASSIGN,VERB

def lamc(a:Ast) -> Ast:
'''composition ⇒ lambda'''
...

def lamp(a:Ast) -> Ast:
'''projection ⇒ lambda'''
def lam_from_prj(a:Ast) -> Ast:
'''convert projection to lambda'''
ax,ay = Ast('x'),Ast('y')
match a.node,len(a.children):
case 'prj',1:
if (v:=a.children[0].node)[0] in VERB and v.endswith(':'):
return Ast('{',Ast('[',ax), Ast(v,ax))
return Ast('{',Ast('[',ax,ay), Ast(v,ax,ay))
case 'prj',2:
return Ast('{',Ast('[',ax), Ast(a.children[0].node,lamp(a.children[1]),ax))
return Ast(a.node, *map(lamp,a.children))
return Ast('{',Ast('[',ax), Ast(a.children[0].node,lam_from_prj(a.children[1]),ax))
return Ast(a.node, *map(lam_from_prj,a.children))

def get_params(a:Ast) -> str:
'''get x y z arguments from lambdas'''
Expand Down
File renamed without changes.
124 changes: 0 additions & 124 deletions prototype/_eval1.py

This file was deleted.

44 changes: 44 additions & 0 deletions prototype/lisp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# lisp.py: λ-calculus with integers, subtraction, variables, conditionals, and closures.
# NOTE: Split and Parse courtesy of https://www.norvig.com/lispy.html
def Split(x:str)->list:
return x.replace('(',' ( ').replace(')',' ) ').split()

def Parse(x:list)->list:
t = x.pop(0)
if t=='(':
l = []
while x[0]!=')': l.append(Parse(x))
x.pop(0)
return l
elif t==')':
raise SyntaxError('unexpected ")"')
else:
return int(t) if t.isnumeric() else t

def Eval(e:dict,x:list|int|str)->int|list:#Eval({},Parse(Split('(if 1 42 43)')))
match x:
case int()|('lam',_,_):return x
case str(): return e[x]
case ('-',a,b): return Eval(e,a)-Eval(e,b)
case ('let',a,b,c): return Eval({**e,a:Eval(e,b)},c)
case ('if',a,b,c): return Eval(e,b) if Eval(e,a) else Eval(e,c)
case (('lam',a,b),*c): return Eval({**e,**dict(zip(a,(Eval(e,i) for i in c)))},b)
case list(): return Eval(e,[Eval(e,i) for i in x])

if __name__=="__main__":#unit tests
x = [a.strip() for a in '''
42 ⇒ 42
(- 1 4) ⇒ -3
(let x 1 (- 43 x)) ⇒ 42
(lam x (- 0 x)) ⇒ ['lam', 'x', ['-', 0, 'x']]
(if (- 3 3) 1 2) ⇒ 2
(if 42 1 2) ⇒ 1
((lam x x) 3) ⇒ 3
(let f (lam x 3) (f 1)) ⇒ 3
'''.splitlines()[1:-1]]
sz = max(map(len,x))
for t in x:
i,o = t.split(' ⇒ ')
try: r = Eval({},Parse(Split(i)))
except Exception as e: print(f'failing test: {i}','\n',f'exception: {e}'); continue
print(f'{t:<{sz}}','✅' if str(r)==o else f'❌ got: {r}')

0 comments on commit 042cf94

Please sign in to comment.