Simple type inference and compiler.
The compiler works for the subset of Joy functions that deal strictly in manipulating stacks and their contents.
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
# GENERATED FILE. DO NOT EDIT.
|
||||
|
||||
|
||||
def _Tree_add_Ee(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a3 a4 ...0] a2 a1 a0 -- [a1 a2 ...0])
|
||||
|
||||
"""
|
||||
(a0, (a1, (a2, ((a3, (a4, s0)), s1)))) = stack
|
||||
return ((a1, (a2, s0)), s1)
|
||||
|
||||
|
||||
def _Tree_delete_R0(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a1 ...0] a0 -- [a1 ...0] a1 a0 a0)
|
||||
|
||||
"""
|
||||
(a0, ((a1, s0), s1)) = stack
|
||||
return (a0, (a0, (a1, ((a1, s0), s1))))
|
||||
|
||||
|
||||
def _Tree_delete_clear_stuff(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a2 a1 [a0 ...0] -- [...0])
|
||||
|
||||
"""
|
||||
((a0, s0), (a1, (a2, s1))) = stack
|
||||
return (s0, s1)
|
||||
|
||||
|
||||
def _Tree_get_E(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a2 a3 ...0] a1 a0 -- a3)
|
||||
|
||||
"""
|
||||
(a0, (a1, ((a2, (a3, s0)), s1))) = stack
|
||||
return (a3, s1)
|
||||
|
||||
|
||||
def ccons(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a1 a0 [...0] -- [a1 a0 ...0])
|
||||
|
||||
"""
|
||||
(s0, (a0, (a1, s1))) = stack
|
||||
return ((a1, (a0, s0)), s1)
|
||||
|
||||
|
||||
def cons(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a1 [...0] -- [a1 ...0])
|
||||
|
||||
"""
|
||||
(s0, (a1, s23)) = stack
|
||||
return ((a1, s0), s23)
|
||||
|
||||
|
||||
def dup(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a1 -- a1 a1)
|
||||
|
||||
"""
|
||||
(a1, s23) = stack
|
||||
return (a1, (a1, s23))
|
||||
|
||||
|
||||
def dupd(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a2 a1 -- a2 a2 a1)
|
||||
|
||||
"""
|
||||
(a1, (a2, s23)) = stack
|
||||
return (a1, (a2, (a2, s23)))
|
||||
|
||||
|
||||
def dupdd(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a3 a2 a1 -- a3 a3 a2 a1)
|
||||
|
||||
"""
|
||||
(a1, (a2, (a3, s23))) = stack
|
||||
return (a1, (a2, (a3, (a3, s23))))
|
||||
|
||||
|
||||
def first(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a1 ...1] -- a1)
|
||||
|
||||
"""
|
||||
((a1, s1), s23) = stack
|
||||
return (a1, s23)
|
||||
|
||||
|
||||
def first_two(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a0 a1 ...0] -- a0 a1)
|
||||
|
||||
"""
|
||||
((a0, (a1, s0)), s1) = stack
|
||||
return (a1, (a0, s1))
|
||||
|
||||
|
||||
def fourth(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a0 a1 a2 a3 ...0] -- a3)
|
||||
|
||||
"""
|
||||
((a0, (a1, (a2, (a3, s0)))), s1) = stack
|
||||
return (a3, s1)
|
||||
|
||||
|
||||
def over(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a2 a1 -- a2 a1 a2)
|
||||
|
||||
"""
|
||||
(a1, (a2, s23)) = stack
|
||||
return (a2, (a1, (a2, s23)))
|
||||
|
||||
|
||||
def pop(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a1 --)
|
||||
|
||||
"""
|
||||
(a1, s23) = stack
|
||||
return s23
|
||||
|
||||
|
||||
def popd(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a2 a1 -- a1)
|
||||
|
||||
"""
|
||||
(a1, (a2, s23)) = stack
|
||||
return (a1, s23)
|
||||
|
||||
|
||||
def popdd(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a3 a2 a1 -- a2 a1)
|
||||
|
||||
"""
|
||||
(a1, (a2, (a3, s23))) = stack
|
||||
return (a1, (a2, s23))
|
||||
|
||||
|
||||
def popop(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a2 a1 --)
|
||||
|
||||
"""
|
||||
(a1, (a2, s23)) = stack
|
||||
return s23
|
||||
|
||||
|
||||
def popopd(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a3 a2 a1 -- a1)
|
||||
|
||||
"""
|
||||
(a1, (a2, (a3, s23))) = stack
|
||||
return (a1, s23)
|
||||
|
||||
|
||||
def popopdd(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a4 a3 a2 a1 -- a2 a1)
|
||||
|
||||
"""
|
||||
(a1, (a2, (a3, (a4, s23)))) = stack
|
||||
return (a1, (a2, s23))
|
||||
|
||||
|
||||
def rest(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a1 ...0] -- [...0])
|
||||
|
||||
"""
|
||||
((a1, s0), s23) = stack
|
||||
return (s0, s23)
|
||||
|
||||
|
||||
def rolldown(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a1 a2 a3 -- a2 a3 a1)
|
||||
|
||||
"""
|
||||
(a3, (a2, (a1, s23))) = stack
|
||||
return (a1, (a3, (a2, s23)))
|
||||
|
||||
|
||||
def rollup(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a1 a2 a3 -- a3 a1 a2)
|
||||
|
||||
"""
|
||||
(a3, (a2, (a1, s23))) = stack
|
||||
return (a2, (a1, (a3, s23)))
|
||||
|
||||
|
||||
def rrest(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a0 a1 ...0] -- [...0])
|
||||
|
||||
"""
|
||||
((a0, (a1, s0)), s1) = stack
|
||||
return (s0, s1)
|
||||
|
||||
|
||||
def second(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a0 a1 ...0] -- a1)
|
||||
|
||||
"""
|
||||
((a0, (a1, s0)), s1) = stack
|
||||
return (a1, s1)
|
||||
|
||||
|
||||
def stack(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(... -- ... [...])
|
||||
|
||||
"""
|
||||
s0 = stack
|
||||
return (s0, s0)
|
||||
|
||||
|
||||
def stuncons(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(... a0 -- ... a0 a0 [...])
|
||||
|
||||
"""
|
||||
(a0, s0) = stack
|
||||
return (s0, (a0, (a0, s0)))
|
||||
|
||||
|
||||
def stununcons(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(... a1 a0 -- ... a1 a0 a0 a1 [...])
|
||||
|
||||
"""
|
||||
(a0, (a1, s0)) = stack
|
||||
return (s0, (a1, (a0, (a0, (a1, s0)))))
|
||||
|
||||
|
||||
def swap(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a1 a2 -- a2 a1)
|
||||
|
||||
"""
|
||||
(a2, (a1, s23)) = stack
|
||||
return (a1, (a2, s23))
|
||||
|
||||
|
||||
def swons(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([...0] a0 -- [a0 ...0])
|
||||
|
||||
"""
|
||||
(a0, (s0, s1)) = stack
|
||||
return ((a0, s0), s1)
|
||||
|
||||
|
||||
def third(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a0 a1 a2 ...0] -- a2)
|
||||
|
||||
"""
|
||||
((a0, (a1, (a2, s0))), s1) = stack
|
||||
return (a2, s1)
|
||||
|
||||
|
||||
def tuck(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
(a2 a1 -- a1 a2 a1)
|
||||
|
||||
"""
|
||||
(a1, (a2, s23)) = stack
|
||||
return (a1, (a2, (a1, s23)))
|
||||
|
||||
|
||||
def uncons(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a1 ...0] -- a1 [...0])
|
||||
|
||||
"""
|
||||
((a1, s0), s23) = stack
|
||||
return (s0, (a1, s23))
|
||||
|
||||
|
||||
def unswons(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
([a0 ...0] -- [...0] a0)
|
||||
|
||||
"""
|
||||
((a0, s0), s1) = stack
|
||||
return (a0, (s0, s1))
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
from collections import Counter
|
||||
from itertools import imap
|
||||
|
||||
|
||||
class AnyJoyType(object):
|
||||
|
||||
prefix = 'a'
|
||||
|
||||
def __init__(self, number):
|
||||
self.number = number
|
||||
|
||||
def __repr__(self):
|
||||
return self.prefix + str(self.number)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
isinstance(other, self.__class__)
|
||||
and other.prefix == self.prefix
|
||||
and other.number == self.number
|
||||
)
|
||||
|
||||
def __ge__(self, other):
|
||||
return issubclass(other.__class__, self.__class__)
|
||||
|
||||
def __add__(self, other):
|
||||
return self.__class__(self.number + other)
|
||||
__radd__ = __add__
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
|
||||
class NumberJoyType(AnyJoyType): prefix = 'n'
|
||||
class FloatJoyType(NumberJoyType): prefix = 'f'
|
||||
class IntJoyType(FloatJoyType): prefix = 'i'
|
||||
|
||||
class StackJoyType(AnyJoyType): prefix = 's'
|
||||
|
||||
_R = range(10)
|
||||
A = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = map(AnyJoyType, _R)
|
||||
N = n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 = map(NumberJoyType, _R)
|
||||
S = s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 = map(StackJoyType, _R)
|
||||
|
||||
|
||||
def update(s, term):
|
||||
if not isinstance(term, tuple):
|
||||
return s.get(term, term)
|
||||
return tuple(update(s, inner) for inner in term)
|
||||
|
||||
|
||||
def relabel(left, right):
|
||||
return left, _1000(right)
|
||||
|
||||
|
||||
def _1000(right):
|
||||
if not isinstance(right, tuple):
|
||||
return 1000 + right
|
||||
return tuple(_1000(n) for n in right)
|
||||
|
||||
|
||||
def delabel(f, seen=None, c=None):
|
||||
if seen is None:
|
||||
assert c is None
|
||||
seen, c = {}, Counter()
|
||||
|
||||
try:
|
||||
return seen[f]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if not isinstance(f, tuple):
|
||||
seen[f] = f.__class__(c[f.prefix])
|
||||
c[f.prefix] += 1
|
||||
return seen[f]
|
||||
|
||||
return tuple(delabel(inner, seen, c) for inner in f)
|
||||
|
||||
|
||||
def stack_concat(q, e):
|
||||
return (q[0], stack_concat(q[1], e)) if q else e
|
||||
|
||||
|
||||
def unify(u, v, s=None):
|
||||
if s is None:
|
||||
s = {}
|
||||
elif s:
|
||||
u = update(s, u)
|
||||
v = update(s, v)
|
||||
|
||||
if u == v:
|
||||
return s
|
||||
|
||||
if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
|
||||
if u >= v:
|
||||
s[u] = v
|
||||
return s
|
||||
if v >= u:
|
||||
s[v] = u
|
||||
return s
|
||||
raise ValueError('Cannot unify %r and %r.' % (u, v))
|
||||
|
||||
if isinstance(u, tuple) and isinstance(v, tuple):
|
||||
if len(u) != len(v) != 2:
|
||||
raise ValueError(repr((u, v)))
|
||||
for uu, vv in zip(u, v):
|
||||
s = unify(uu, vv, s)
|
||||
if s == False: # (instead of a substitution dict.)
|
||||
break
|
||||
return s
|
||||
|
||||
if isinstance(v, tuple):
|
||||
if not stacky(u):
|
||||
raise ValueError('Cannot unify %r and %r.' % (u, v))
|
||||
s[u] = v
|
||||
return s
|
||||
|
||||
if isinstance(u, tuple):
|
||||
if not stacky(v):
|
||||
raise ValueError('Cannot unify %r and %r.' % (v, u))
|
||||
s[v] = u
|
||||
return s
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def stacky(thing):
|
||||
return thing.__class__ in {AnyJoyType, StackJoyType}
|
||||
|
||||
|
||||
def compose(f, g):
|
||||
(f_in, f_out), (g_in, g_out) = f, g
|
||||
|
||||
if not g_in:
|
||||
fg_in, fg_out = f_in, stack_concat(g_out, f_out)
|
||||
elif not f_out:
|
||||
fg_in, fg_out = stack_concat(f_in, g_in), g_out
|
||||
else:
|
||||
s = unify(g_in, f_out)
|
||||
if s == False: # s can also be the empty dict, which is ok.
|
||||
raise TypeError('Cannot unify %r and %r.' % (fo, gi))
|
||||
fg_in, fg_out = update(s, (f_in, g_out))
|
||||
|
||||
return fg_in, fg_out
|
||||
|
||||
|
||||
def _C(f, g):
|
||||
f, g = relabel(f, g)
|
||||
fg = compose(f, g)
|
||||
return delabel(fg)
|
||||
|
||||
|
||||
def C(*functions):
|
||||
return reduce(_C, functions)
|
||||
|
||||
|
||||
def compilable(f):
|
||||
return isinstance(f, tuple) and all(imap(compilable, f)) or stacky(f)
|
||||
|
||||
|
||||
def doc_from_stack_effect(inputs, outputs):
|
||||
switch = [False] # Do we need to display the '...' for the rest of the main stack?
|
||||
i, o = _f(inputs, switch), _f(outputs, switch)
|
||||
if switch[0]:
|
||||
i.append('...')
|
||||
o.append('...')
|
||||
return '(%s--%s)' % (
|
||||
' '.join(reversed([''] + i)),
|
||||
' '.join(reversed(o + [''])),
|
||||
)
|
||||
|
||||
|
||||
def _f(term, switch):
|
||||
a = []
|
||||
while term and isinstance(term, tuple):
|
||||
item, term = term
|
||||
a.append(item)
|
||||
assert isinstance(term, StackJoyType), repr(term)
|
||||
a = [_to_str(i, term, switch) for i in a]
|
||||
return a
|
||||
|
||||
|
||||
def _to_str(term, stack, switch):
|
||||
if not isinstance(term, tuple):
|
||||
if term == stack:
|
||||
switch[0] = True
|
||||
return '[...]'
|
||||
return (
|
||||
'[...%i]' % term.number
|
||||
if isinstance(term, StackJoyType)
|
||||
else str(term)
|
||||
)
|
||||
|
||||
a = []
|
||||
while term and isinstance(term, tuple):
|
||||
item, term = term
|
||||
a.append(_to_str(item, stack, switch))
|
||||
assert isinstance(term, StackJoyType), repr(term)
|
||||
if term == stack:
|
||||
switch[0] = True
|
||||
end = '...'
|
||||
else:
|
||||
end = '...%i' % term.number
|
||||
a.append(end)
|
||||
return '[%s]' % ' '.join(a)
|
||||
|
||||
|
||||
def compile_(name, f, doc=None):
|
||||
i, o = f
|
||||
if doc is None:
|
||||
doc = doc_from_stack_effect(i, o)
|
||||
return '''def %s(stack):
|
||||
"""
|
||||
::
|
||||
|
||||
%s
|
||||
|
||||
"""
|
||||
%s = stack
|
||||
return %s''' % (name, doc, i, o)
|
||||
|
||||
|
||||
def __(*seq):
|
||||
stack = StackJoyType(23)
|
||||
for item in seq: stack = item, stack
|
||||
return stack
|
||||
|
||||
|
||||
def defs():
|
||||
rolldown = __(a1, a2, a3), __(a2, a3, a1)
|
||||
rollup = __(a1, a2, a3), __(a3, a1, a2)
|
||||
pop = __(a1), __()
|
||||
popop = __(a2, a1,), __()
|
||||
popd = __(a2, a1,), __(a1)
|
||||
popdd = __(a3, a2, a1,), __(a2, a1,)
|
||||
popopd = __(a3, a2, a1,), __(a1)
|
||||
popopdd = __(a4, a3, a2, a1,), __(a2, a1)
|
||||
swap = __(a1, a2), __(a2, a1)
|
||||
rest = __((a1, s0),), __(s0,)
|
||||
rrest = C(rest, rest)
|
||||
cons = __(a1, s0), __((a1, s0),)
|
||||
ccons = C(cons, cons)
|
||||
uncons = __((a1, s0),), __(a1, s0)
|
||||
unswons = C(uncons, swap)
|
||||
swons = C(swap, cons)
|
||||
dup = __(a1,), __(a1, a1)
|
||||
dupd = __(a2, a1), __(a2, a2, a1)
|
||||
dupdd = __(a3, a2, a1), __(a3, a3, a2, a1)
|
||||
first = __((a1, s1),), __(a1,)
|
||||
second = C(rest, first)
|
||||
third = C(rest, second)
|
||||
fourth = C(rest, third)
|
||||
tuck = __(a2, a1), __(a1, a2, a1)
|
||||
over = __(a2, a1), __(a2, a1, a2)
|
||||
stack = s0, (s0, s0)
|
||||
stuncons = C(stack, uncons)
|
||||
stununcons = C(stack, uncons, uncons)
|
||||
first_two = C(uncons, uncons, pop)
|
||||
|
||||
mul = __(n1, n2), __(n3,)
|
||||
sqrt = C(dup, mul)
|
||||
succ = pred = __(n1,), __(n2,)
|
||||
divmod_ = pm = __(n2, n1), __(n4, n3)
|
||||
|
||||
_Tree_add_Ee = C(pop, swap, rolldown, rrest, ccons)
|
||||
_Tree_get_E = C(popop, second)
|
||||
_Tree_delete_clear_stuff = C(rollup, popop, rest)
|
||||
_Tree_delete_R0 = C(over, first, swap, dup)
|
||||
|
||||
return locals()
|
||||
|
||||
|
||||
DEFS = defs()
|
||||
|
||||
#globals().update(DEFS)
|
||||
|
||||
|
||||
def show():
|
||||
for name, stack_effect_comment in sorted(DEFS.iteritems()):
|
||||
t = ' *'[compilable(stack_effect_comment)]
|
||||
print name, '=', doc_from_stack_effect(*stack_effect_comment), t
|
||||
|
||||
|
||||
def generate_library_code(f=None):
|
||||
if f is None:
|
||||
import sys
|
||||
f = sys.stdout
|
||||
print >> f, '# GENERATED FILE. DO NOT EDIT.\n'
|
||||
for name, stack_effect_comment in sorted(DEFS.iteritems()):
|
||||
if not compilable(stack_effect_comment):
|
||||
continue
|
||||
print >> f
|
||||
print >> f, compile_(name, stack_effect_comment)
|
||||
print >> f
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
show()
|
||||
Reference in New Issue
Block a user