Skip to content

Commit

Permalink
evl(Parse(Scan('{2 10-x}3 4')))⇒[-1, 6]:I
Browse files Browse the repository at this point in the history
  • Loading branch information
hoosierEE committed May 7, 2024
1 parent ed5cdf1 commit a2fe7a9
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 63 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ __pycache__/

tests/doctest.h
a.out

.DS_Store
2 changes: 1 addition & 1 deletion prototype/Builtin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ADVERB = "'","/","\\"
ASSIGN = ":","::"
BS = "\\"
COPULA = ":","::"
CPAREN = ')','}',']'
ENDEXP = '',';','\n'
LF = "\n"
Expand Down
94 changes: 39 additions & 55 deletions prototype/Eval.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from Ast import Ast
from Builtin import COPULA,VERB
from Sema import lift_prj,formalize
from Builtin import ASSIGN,VERB
from Semantic import lift_prj,formalize
class Sym(str):pass
class Name(str):pass
#Vectors; strand/list notation, or construct with ops:
Expand Down Expand Up @@ -60,65 +60,49 @@ def v2val(op:str,a:Val,b:Val) -> Val:
case _: raise RuntimeError('nyi')

def v2(op:str,a,b):
if type(a)==type(b)==Val:
return v2val(op,a,b)
# if type(a)==list and type(b)==Val:
# raise 'nyi'
if type(a)==type(b)==Val: return v2val(op,a,b)
raise RuntimeError(f'nyi: {op} not defined for {a}{op}{b}')

def Eval(x:Ast|Val) -> Val:
def _Eval(e,x) -> Val:
if type(x)==Val: return x
if type(x)==str: return Val('v',x) if x in VERB else Val(Ty[t:=ty(x)],t(x))
if type(x)==Ast:
if not x.children:
r = _Eval(e,x.node)
if r.t=='n': return e[r.v]
return r
def evl(x:Ast,e=None) -> Val:
e = e or {}
return Eval(e,formalize(lift_prj(x)))

if x.node=='vec':
t = ty(x.children)
return Val(Ty[t].upper(),[t(c.node) for c in x.children])
def Eval(e,x) -> Val:
if type(x)==Val: return x
if type(x)==str: return Val('v',x) if x in VERB else Val(Ty[t:=ty(x)],t(x))
if type(x)==Ast:
if not x.children:
r = Eval(e,x.node)
if r.t=='n': return e[r.v]
return r

if x.node in COPULA:
n = x.children[0].node#name
r = _Eval(e,x.children[1])#value
e[n] = r#NOTE: reference semantics make this update visible to caller
return r
if x.node=='vec':
t = ty(x.children)
return Val(Ty[t].upper(),[t(c.node) for c in x.children])

if x.node in ('cmp','prj','{'): return x
if x.node=='app':
x = formalize(lift_prj(x))
b = _Eval(e,x.children[0])#body hm... should it use {**e} instead of e?
a = _Eval(e,x.children[1])#args
if type(b)==Ast and b.node=='{':
match a:
case Val(t,v): args = [v]
case Ast('(',c)|Ast('[',c): args = [_Eval({**e},ci) for ci in c]
case Ast(_,c): args = [c]
formal = b.childre[0].children
for k,v in zip(args,formal):
if v=='_':
raise "aaaaah"
if x.node in ASSIGN:
n = x.children[0].node#name
r = Eval(e,x.children[1])#value
e[n] = r#NOTE: reference semantics make this update visible to caller
return r

print(b.children[0].children)
#lambda application (app (lam (prg x) (x)) 5)
#lambda args: (prg x)
#lambda body: (x)
#environment: {'x':5}
#evaluate lambda body with environment where each (prg x) is replaced by applied argument
if x.node in ('cmp','prj','{'): return x

if type(b)==Val:
if b.t.isupper() and a.t=='a':
return v2('@',b,a)
if x.node in ('@','app'):#"app" always has 2 children TODO: what about "@"?
b = Eval(e,x.children[0])#body (...should it use {**e} instead of e?)
args = [Eval(e,xi) for xi in x.children[1].children] if x.children[1].node=='[' else [Eval(e,x.children[1])]
if type(b)==Ast and b.node=='{':
newenv = {a.node:v for a,v in zip(b.children[0].children,args)}
return Eval({**e,**newenv},b.children[1])
if type(b)==Val:
if b.t.isupper() and a.t=='a':
return v2('@',b,a)

raise RuntimeError(f'nyi: (app {x})')
raise RuntimeError(f'nyi: {x}')

if x.node[0] in VERB:
k = [_Eval(e,c) for c in x.children[::(-1,1)[x.node in '(;']]]
print('verb',k)
return (0,v1,v2)[len(x.children)](x.node,*k[::-1])
else:
raise RuntimeError(f'ast not recognized: {x}')
raise RuntimeError('wat?!')
return _Eval({},x)
if x.node[0] in VERB:
k = [Eval(e,c) for c in x.children[::(-1,1)[x.node in '(;']]]
return (0,v1,v2)[len(x.children)](x.node,*k[::-1])
else:
raise RuntimeError(f'ast not recognized: {x}')
raise RuntimeError('wat?!')
4 changes: 2 additions & 2 deletions prototype/Parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from Ast import Ast
from Builtin import ADVERB,COPULA,CPAREN,ENDEXP,LF,OPAREN,VERB,WHITESPACE
from Builtin import ADVERB,ASSIGN,CPAREN,ENDEXP,LF,OPAREN,VERB,WHITESPACE
import collections as C
NIL = Ast('NIL')
Op = C.namedtuple('Op','name arity')
Expand Down Expand Up @@ -88,7 +88,7 @@ def loop(i=0) -> int|None:#return error token index or None
else: d.append(Ast(s.pop().name)); s.append(Op(k,2))
else: s.append(Op(k,1))
if s[-1].arity==2: pad(n)
elif c in COPULA:
elif c in ASSIGN:
if n in CPAREN+ENDEXP: rq(Ast('prj',Ast(c),d.pop())); continue
else: s.append(Op(c,2))
elif c[0] in VERB:
Expand Down
4 changes: 2 additions & 2 deletions prototype/Sema.py → prototype/Semantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
- ⇒ (prj -) ⇒ (lam (prg x y) (- x y))
'''
from Ast import Ast
from Builtin import COPULA,VERB
from Builtin import ASSIGN,VERB

def lift_prj(a:Ast) -> Ast:
'''projection ⇒ lambda'''
Expand All @@ -28,7 +28,7 @@ def lift_prj(a:Ast) -> Ast:
def get_params(a:Ast) -> str:
'''get x y z arguments from lambdas'''
if a.node=='{': return ''
if a.node in COPULA and a.children[0].node in 'xyz': return ''
if a.node in ASSIGN and a.children[0].node in 'xyz': return ''
if a.node in [*'xyz']: return a.node
return ''.join(sorted(get_params(x) for x in a.children))

Expand Down
12 changes: 9 additions & 3 deletions prototype/Unit.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from Eval import *
from Parser import *
from Scanner import *
from Semantic import *
def test_expr(scan,parse):
x = """
input ⇒ expected output (in s-expr form)
Expand Down Expand Up @@ -137,17 +138,20 @@ def test_expr(scan,parse):
"""[1:-1].splitlines()[1:]

red,end = '\033[91m','\033[0m'
ok,total = 0,0
for i,o in (map(str.strip,a.split('⇒')) for a in x if not a.strip().startswith('#')):
total += 1
c = ''#comment
if '#' in o: o,c = o.split('#')
try: o,x = o.strip(),parse(scan(i.strip()),0)#verbosity=0 (silent)
try: o,x = o.strip(),parse(scan(i.strip()),0); ok += 1
except: print(f'Exception while parsing "{i}"'); continue
if str(x)==o: continue
wanted = f'{i}{o}{red}{end}'
actual = f'{"":<{len(i)}} {red}{x}{end}'
m = max(len(actual),len(wanted))
print(f'{wanted:<{m}} ⌈expected⌉ {c}')
print(f'{actual:<{m}}{red}actual{end} ⌋','\n')
print(f'{ok} of {total} passed')


def test_eval(scan,parse,_eval):
Expand All @@ -165,11 +169,13 @@ def test_eval(scan,parse,_eval):
4,a:1 2 3 ⇒ [4, 1, 2, 3]:L
4,a:1.0 2 3 ⇒ [4, 1.0, 2.0, 3.0]:L
"""[1:-1].splitlines()[1:]
ok,total = 0,0
for i,o in (map(str.strip,a.split('⇒')) for a in x if not a.strip().startswith('#')):
total += 1
c = ''
if '#' in o: o,c = o.split('#')
try: o,x = o.strip(),_eval(parse(scan(i.strip()),0))
try: o,x = o.strip(),_eval(parse(scan(i.strip()),0)); ok += 1
except: print(f'Exception evaluating "{i}"'); continue
if str(x)==o: continue
print(f'{i}{x} (actual)')
print('expected:',o,)
print('expected:',o)

0 comments on commit a2fe7a9

Please sign in to comment.