Moving right along.

A little clunky but it seems to work so far.
This commit is contained in:
Simon Forman 2018-07-17 17:12:27 -07:00
parent c2dd7cca0a
commit 73d19b1f3d
3 changed files with 453 additions and 411 deletions

View File

@ -25,11 +25,12 @@ function.
'''
from inspect import getdoc
from functools import wraps
from itertools import count
from inspect import getmembers, isfunction
import operator, math
from .parser import text_to_expression, Symbol
from .utils.stack import list_to_stack, iter_stack, pick, concat
from .utils.stack import expression_to_string, list_to_stack, iter_stack, pick, concat
from .utils.brutal_hackery import rename_code_object
from .utils import generated_library as genlib
@ -38,16 +39,28 @@ from .utils.types import (
ef,
stack_effect,
AnyJoyType,
AnyStarJoyType,
BooleanJoyType,
NumberJoyType,
NumberStarJoyType,
StackJoyType,
StackStarJoyType,
FloatJoyType,
IntJoyType,
SymbolJoyType,
TextJoyType,
_functions,
FUNCTIONS,
infer,
JoyTypeError,
combinator_effect,
)
_SYM_NUMS = count().next
_COMB_NUMS = count().next
_R = range(10)
A = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = map(AnyJoyType, _R)
B = b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = map(BooleanJoyType, _R)
@ -58,6 +71,12 @@ I = i0, i1, i2, i3, i4, i5, i6, i7, i8, i9 = map(IntJoyType, _R)
T = t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 = map(TextJoyType, _R)
_R = range(1, 11)
As = map(AnyStarJoyType, _R)
Ns = map(NumberStarJoyType, _R)
Ss = map(StackStarJoyType, _R)
sec0 = stack_effect(t1)()
sec1 = stack_effect(s0, i1)(s1)
sec2 = stack_effect(s0, i1)(a1)
@ -67,7 +86,7 @@ sec_binary_logic = stack_effect(b1, b2)(b3)
sec_binary_math = stack_effect(n1, n2)(n3)
sec_unary_logic = stack_effect(a1)(b1)
sec_unary_math = stack_effect(n1)(n2)
sec_Ns_math = stack_effect((Ns[1], s1),)(n0)
_dictionary = {}
@ -350,6 +369,14 @@ class DefinitionWrapper(object):
Add the definition to the dictionary.
'''
F = class_.parse_definition(definition)
try:
#print F._body
secs = infer(*F._body)
except JoyTypeError:
pass
print F.name, '==', expression_to_string(F.body), ' --failed to infer stack effect.'
else:
FUNCTIONS[F.name] = SymbolJoyType(F.name, secs, _SYM_NUMS())
dictionary[F.name] = F
@ -357,23 +384,11 @@ def _text_to_defs(text):
return (line.strip() for line in text.splitlines() if '==' in line)
## eh = compose(dup, bool_)
## sqr = compose(dup, mul)
## of = compose(swap, at)
#
# Functions
#
# Load the auto-generated primitives into the dictionary.
_functions.update(yin_functions())
for name, primitive in getmembers(genlib, isfunction):
inscribe(SimpleFunctionWrapper(primitive))
@inscribe
@sec0
@FunctionWrapper
@ -528,6 +543,7 @@ def select(stack):
@inscribe
@sec_Ns_math
@SimpleFunctionWrapper
def max_(S):
'''Given a list find the maximum.'''
@ -536,6 +552,7 @@ def max_(S):
@inscribe
@sec_Ns_math
@SimpleFunctionWrapper
def min_(S):
'''Given a list find the minimum.'''
@ -544,6 +561,7 @@ def min_(S):
@inscribe
@sec_Ns_math
@SimpleFunctionWrapper
def sum_(S):
'''Given a quoted sequence of numbers return the sum.
@ -872,6 +890,7 @@ S_truthy = Symbol('truthy')
@inscribe
@combinator_effect(_COMB_NUMS(), s1)
@FunctionWrapper
def i(stack, expression, dictionary):
'''
@ -889,6 +908,7 @@ def i(stack, expression, dictionary):
@inscribe
@combinator_effect(_COMB_NUMS(), s1)
@FunctionWrapper
def x(stack, expression, dictionary):
'''
@ -906,6 +926,7 @@ def x(stack, expression, dictionary):
@inscribe
#@combinator_effect(_COMB_NUMS(), s7, s6)
@FunctionWrapper
def b(stack, expression, dictionary):
'''
@ -941,6 +962,7 @@ def dupdip(stack, expression, dictionary):
@inscribe
#@combinator_effect(_COMB_NUMS(), s7, s6)
@FunctionWrapper
def infra(stack, expression, dictionary):
'''
@ -1150,6 +1172,7 @@ def _cond(conditions, expression):
@inscribe
@combinator_effect(_COMB_NUMS(), a1, s1)
@FunctionWrapper
def dip(stack, expression, dictionary):
'''
@ -1437,7 +1460,31 @@ for F in (
del F # Otherwise Sphinx autodoc will pick it up.
YIN_STACK_EFFECTS = yin_functions()
# Load the auto-generated primitives into the dictionary.
_functions.update(YIN_STACK_EFFECTS)
# exec '''
# eh = compose(dup, bool)
# sqr = compose(dup, mul)
# of = compose(swap, at)
# ''' in dict(compose=compose), _functions
FUNCTIONS.update(
(name, SymbolJoyType(name, [_functions[name]], _SYM_NUMS()))
for name in sorted(_functions)
)
for name, primitive in getmembers(genlib, isfunction):
inscribe(SimpleFunctionWrapper(primitive))
add_aliases(_dictionary, ALIASES)
add_aliases(_functions, ALIASES)
add_aliases(FUNCTIONS, ALIASES)
DefinitionWrapper.add_definitions(definitions, _dictionary)
#sec_Ns_math(_dictionary['product'])

View File

@ -10,12 +10,6 @@ an unbounded sequence of other types.
'''
import sys
from inspect import stack as inspect_stack
from itertools import chain, product
from logging import getLogger
_log = getLogger(__name__)
import joy.library
from joy.parser import Symbol, text_to_expression
from joy.utils.stack import (
@ -23,391 +17,22 @@ from joy.utils.stack import (
expression_to_string,
list_to_stack,
)
from joy.utils.types import (
AnyJoyType, A,
BooleanJoyType, B,
DEFS,
doc_from_stack_effect,
FloatJoyType, F,
JoyTypeError,
NumberJoyType, N,
StackJoyType, S,
_stacky,
_R,
relabel, delabel,
reify,
)
# from joy.utils.types import (
# AnyJoyType, A,
# BooleanJoyType, B,
# DEFS,
# doc_from_stack_effect,
# FloatJoyType, F,
# JoyTypeError,
# NumberJoyType, N,
# StackJoyType, S,
# _stacky,
# _R,
# relabel, delabel,
# reify,
# )
# We no longer want FloatJoyType to accept IntJoyType.
class IntJoyType(NumberJoyType): prefix = 'i'
class KleeneStar(object):
u'''
A sequence of zero or more `AnyJoyType` variables would be:
A*
The `A*` works by splitting the universe into two alternate histories:
A*
A* A A*
The Kleene star variable disappears in one universe, and in the other
it turns into an `AnyJoyType` variable followed by itself again.
We have to return all universes (represented by their substitution
dicts, the "unifiers") that don't lead to type conflicts.
'''
kind = AnyJoyType
def __init__(self, number):
assert number
self.number = number
self.count = 0
self.prefix = repr(self)
def __repr__(self):
return '%s%i*' % (self.kind.prefix, self.number)
def another(self):
self.count += 1
return self.kind(10000 * self.number + self.count)
def __eq__(self, other):
return (
isinstance(other, self.__class__)
and other.number == self.number
)
def __ge__(self, other):
return self.kind >= other.kind
def __add__(self, other):
return self.__class__(self.number + other)
__radd__ = __add__
def __hash__(self):
return hash(repr(self))
class AnyStarJoyType(KleeneStar): kind = AnyJoyType
class NumberStarJoyType(KleeneStar): kind = NumberJoyType
#class FloatStarJoyType(KleeneStar): kind = FloatJoyType
#class IntStarJoyType(KleeneStar): kind = IntJoyType
class StackStarJoyType(KleeneStar): kind = StackJoyType
class FunctionJoyType(AnyJoyType):
def __init__(self, name, sec, number):
self.name = name
self.stack_effects = sec
self.number = number
def __add__(self, other):
return self
__radd__ = __add__
def __repr__(self):
return self.name
class SymbolJoyType(FunctionJoyType):
'''
Represent non-combinator functions.
These type variables carry the stack effect comments and can
appear in expressions (as in quoted programs.)
'''
prefix = 'F'
class CombinatorJoyType(FunctionJoyType):
'''
Represent combinators.
These type variables carry Joy functions that implement the
behaviour of Joy combinators and they can appear in expressions.
For simple combinators the implementation functions can be the
combinators themselves.
These types can also specify a stack effect (input side only) to
guard against being used on invalid types.
'''
prefix = 'C'
def __init__(self, name, sec, number, expect=None):
super(CombinatorJoyType, self).__init__(name, sec, number)
self.expect = expect
def enter_guard(self, f):
if self.expect is None:
return f
g = self.expect, self.expect
new_f = list(compose(f, g, ()))
assert len(new_f) == 1, repr(new_f)
return new_f[0][1]
def _log_uni(U):
def inner(u, v, s=None):
_log.debug(
'%3i %s U %s w/ %s',
len(inspect_stack()), u, v, s,
)
res = U(u, v, s)
_log.debug(
'%3i %s U %s w/ %s => %s',
len(inspect_stack()), u, v, s, res,
)
return res
return inner
@_log_uni
def unify(u, v, s=None):
'''
Return a tuple of substitution dicts representing unifiers for u and v.
'''
if s is None:
s = {}
elif s:
u = reify(s, u)
v = reify(s, v)
if u == v:
res = s,
elif isinstance(u, tuple) and isinstance(v, tuple):
if len(u) != 2 or len(v) != 2:
if _that_one_special_case(u, v):
return s,
raise ValueError(repr((u, v))) # Bad input.
(a, b), (c, d) = v, u
if isinstance(a, KleeneStar):
if isinstance(c, KleeneStar):
s = _lil_uni(a, c, s) # Attempt to unify the two K-stars.
res = unify(d, b, s[0])
else:
# Two universes, in one the Kleene star disappears and
# unification continues without it...
s0 = unify(u, b)
# In the other it spawns a new variable.
s1 = unify(u, (a.another(), v))
res = s0 + s1
for sn in res:
sn.update(s)
elif isinstance(c, KleeneStar):
res = unify(v, d) + unify(v, (c.another(), u))
for sn in res:
sn.update(s)
else:
res = tuple(flatten(unify(d, b, sn) for sn in unify(c, a, s)))
elif isinstance(v, tuple):
if not _stacky(u):
raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
s[u] = v
res = s,
elif isinstance(u, tuple):
if not _stacky(v):
raise JoyTypeError('Cannot unify %r and %r.' % (v, u))
s[v] = u
res = s,
else:
res = _lil_uni(u, v, s)
return res
def _that_one_special_case(u, v):
'''
Handle e.g. ((), (n1*, s1)) when type-checking sum, product, etc...
'''
return (
u == ()
and len(v) == 2
and isinstance(v[0], KleeneStar)
and isinstance(v[1], StackJoyType)
)
def _lil_uni(u, v, s):
if u >= v:
s[u] = v
return s,
if v >= u:
s[v] = u
return s,
raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
def _compose(f, g, e):
(f_in, f_out), (g_in, g_out) = f, g
for s in unify(g_in, f_out):
yield reify(s, (e, (f_in, g_out)))
def compose(f, g, e):
'''
Yield the stack effects of the composition of two stack effects. An
expression is carried along and updated and yielded.
'''
f, g = relabel(f, g)
for fg in _compose(f, g, e):
yield delabel(fg)
def _meta_compose(F, G, e):
for f, g in product(F, G):
try:
for result in compose(f, g, e): yield result
except JoyTypeError:
pass
def meta_compose(F, G, e):
'''
Yield the stack effects of the composition of two lists of stack
effects. An expression is carried along and updated and yielded.
'''
res = sorted(set(_meta_compose(F, G, e)))
if not res:
raise JoyTypeError('Cannot unify %r and %r.' % (F, G))
return res
def flatten(g):
return list(chain.from_iterable(g))
ID = S[0], S[0] # Identity function.
def _infer(e, F=ID):
_log_it(e, F)
if not e:
return [F]
n, e = e
if isinstance(n, SymbolJoyType):
eFG = meta_compose([F], n.stack_effects, e)
res = flatten(_infer(e, Fn) for e, Fn in eFG)
elif isinstance(n, CombinatorJoyType):
fi, fo = n.enter_guard(F)
res = flatten(_interpret(f, fi, fo, e) for f in n.stack_effects)
elif isinstance(n, Symbol):
assert n not in FUNCTIONS, repr(n)
func = joy.library._dictionary[n]
res = _interpret(func, F[0], F[1], e)
else:
fi, fo = F
res = _infer(e, (fi, (n, fo)))
return res
def _interpret(f, fi, fo, e):
new_fo, ee, _ = f(fo, e, {})
ee = reify(FUNCTIONS, ee) # Fix Symbols.
new_F = fi, new_fo
return _infer(ee, new_F)
def _log_it(e, F):
_log.info(
u'%3i %s%s',
len(inspect_stack()),
doc_from_stack_effect(*F),
expression_to_string(e),
)
def infer(*expression):
'''
Return a list of stack effects for a Joy expression.
For example::
h = infer(pop, swap, rolldown, rest, rest, cons, cons)
for fi, fo in h:
print doc_from_stack_effect(fi, fo)
Prints::
([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])
'''
return sorted(set(_infer(list_to_stack(expression))))
def infer_string(string):
e = reify(FUNCTIONS, text_to_expression(string)) # Fix Symbols.
return sorted(set(_infer(e)))
def infer_expression(expression):
e = reify(FUNCTIONS, expression) # Fix Symbols.
return sorted(set(_infer(e)))
def type_check(name, stack):
'''
Trinary predicate. True if named function type-checks, False if it
fails, None if it's indeterminate (because I haven't entered it into
the FUNCTIONS dict yet.)
'''
try:
func = FUNCTIONS[name]
except KeyError:
return # None, indicating unknown
for fi, fo in infer(func):
try:
U = unify(fi, stack)
except (JoyTypeError, ValueError), e:
#print >> sys.stderr, name, e, stack
continue
#print U
return True
return False
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = A
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = B
n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 = N
f0, f1, f2, f3, f4, f5, f6, f7, f8, f9 = F
i0, i1, i2, i3, i4, i5, i6, i7, i8, i9 = I = map(IntJoyType, _R)
s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 = S
_R = range(1, 11)
As = map(AnyStarJoyType, _R)
Ns = map(NumberStarJoyType, _R)
Ss = map(StackStarJoyType, _R)
FUNCTIONS = {
name: SymbolJoyType(name, [DEFS[name]], i)
for i, name in enumerate(sorted(DEFS))
}
'''Docstring for functions in Sphinx?'''
@ -452,6 +77,7 @@ FUNCTIONS.update({
))
})
def branch_true(stack, expression, dictionary):
(then, (else_, (flag, stack))) = stack
return stack, CONCAT(then, expression), dictionary

View File

@ -1,7 +1,13 @@
# -*- coding: utf_8
from logging import getLogger
_log = getLogger(__name__)
from collections import Counter
from itertools import imap
from joy.utils.stack import concat
from joy.parser import Symbol
from itertools import imap, chain, product
from inspect import stack as inspect_stack
from joy.utils.stack import concat, expression_to_string, list_to_stack
from joy.parser import Symbol, text_to_expression
class AnyJoyType(object):
@ -49,7 +55,7 @@ class BooleanJoyType(AnyJoyType):
class NumberJoyType(AnyJoyType):
accept = int, float, long, complex
accept = bool, int, float, long, complex
prefix = 'n'
@ -78,6 +84,118 @@ class StackJoyType(AnyJoyType):
return False
class KleeneStar(object):
u'''
A sequence of zero or more `AnyJoyType` variables would be:
A*
The `A*` works by splitting the universe into two alternate histories:
A*
A* A A*
The Kleene star variable disappears in one universe, and in the other
it turns into an `AnyJoyType` variable followed by itself again.
We have to return all universes (represented by their substitution
dicts, the "unifiers") that don't lead to type conflicts.
'''
kind = AnyJoyType
def __init__(self, number):
assert number
self.number = number
self.count = 0
self.prefix = repr(self)
def __repr__(self):
return '%s%i*' % (self.kind.prefix, self.number)
def another(self):
self.count += 1
return self.kind(10000 * self.number + self.count)
def __eq__(self, other):
return (
isinstance(other, self.__class__)
and other.number == self.number
)
def __ge__(self, other):
return self.kind >= other.kind
def __add__(self, other):
return self.__class__(self.number + other)
__radd__ = __add__
def __hash__(self):
return hash(repr(self))
class AnyStarJoyType(KleeneStar): kind = AnyJoyType
class NumberStarJoyType(KleeneStar): kind = NumberJoyType
class FloatStarJoyType(KleeneStar): kind = FloatJoyType
class IntStarJoyType(KleeneStar): kind = IntJoyType
class StackStarJoyType(KleeneStar): kind = StackJoyType
class TextStarJoyType(KleeneStar): kind = TextJoyType
class FunctionJoyType(AnyJoyType):
def __init__(self, name, sec, number):
self.name = name
self.stack_effects = sec
self.number = number
def __add__(self, other):
return self
__radd__ = __add__
def __repr__(self):
return self.name
class SymbolJoyType(FunctionJoyType):
'''
Represent non-combinator functions.
These type variables carry the stack effect comments and can
appear in expressions (as in quoted programs.)
'''
prefix = 'F'
class CombinatorJoyType(FunctionJoyType):
'''
Represent combinators.
These type variables carry Joy functions that implement the
behaviour of Joy combinators and they can appear in expressions.
For simple combinators the implementation functions can be the
combinators themselves.
These types can also specify a stack effect (input side only) to
guard against being used on invalid types.
'''
prefix = 'C'
def __init__(self, name, sec, number, expect=None):
super(CombinatorJoyType, self).__init__(name, sec, number)
self.expect = expect
def enter_guard(self, f):
if self.expect is None:
return f
g = self.expect, self.expect
new_f = list(poly_compose(f, g, ()))
assert len(new_f) == 1, repr(new_f)
return new_f[0][1]
class JoyTypeError(Exception): pass
@ -135,7 +253,7 @@ def delabel(f, seen=None, c=None):
return tuple(delabel(inner, seen, c) for inner in f)
def unify(u, v, s=None):
def uni_unify(u, v, s=None):
'''
Return a substitution dict representing a unifier for u and v.
'''
@ -157,7 +275,7 @@ def unify(u, v, s=None):
if len(u) != len(v) != 2:
raise ValueError(repr((u, v))) # Bad input.
(a, b), (c, d) = u, v
s = unify(b, d, unify(a, c, s))
s = uni_unify(b, d, uni_unify(a, c, s))
elif isinstance(v, tuple):
if not _stacky(u):
@ -175,6 +293,112 @@ def unify(u, v, s=None):
return s
def _log_uni(U):
def inner(u, v, s=None):
_log.debug(
'%3i %s U %s w/ %s',
len(inspect_stack()), u, v, s,
)
res = U(u, v, s)
_log.debug(
'%3i %s U %s w/ %s => %s',
len(inspect_stack()), u, v, s, res,
)
return res
return inner
@_log_uni
def unify(u, v, s=None):
'''
Return a tuple of substitution dicts representing unifiers for u and v.
'''
if s is None:
s = {}
elif s:
u = reify(s, u)
v = reify(s, v)
if u == v:
res = s,
elif isinstance(u, tuple) and isinstance(v, tuple):
if len(u) != 2 or len(v) != 2:
if _that_one_special_case(u, v):
return s,
raise ValueError(repr((u, v))) # Bad input.
(a, b), (c, d) = v, u
if isinstance(a, KleeneStar):
if isinstance(c, KleeneStar):
s = _lil_uni(a, c, s) # Attempt to unify the two K-stars.
res = unify(d, b, s[0])
else:
# Two universes, in one the Kleene star disappears and
# unification continues without it...
s0 = unify(u, b)
# In the other it spawns a new variable.
s1 = unify(u, (a.another(), v))
res = s0 + s1
for sn in res:
sn.update(s)
elif isinstance(c, KleeneStar):
res = unify(v, d) + unify(v, (c.another(), u))
for sn in res:
sn.update(s)
else:
res = tuple(flatten(unify(d, b, sn) for sn in unify(c, a, s)))
elif isinstance(v, tuple):
if not _stacky(u):
raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
s[u] = v
res = s,
elif isinstance(u, tuple):
if not _stacky(v):
raise JoyTypeError('Cannot unify %r and %r.' % (v, u))
s[v] = u
res = s,
else:
res = _lil_uni(u, v, s)
return res
def _that_one_special_case(u, v):
'''
Handle e.g. ((), (n1*, s1)) when type-checking sum, product, etc...
'''
return (
u == ()
and len(v) == 2
and isinstance(v[0], KleeneStar)
and isinstance(v[1], StackJoyType)
)
def flatten(g):
return list(chain.from_iterable(g))
def _lil_uni(u, v, s):
if u >= v:
s[u] = v
return s,
if v >= u:
s[v] = u
return s,
raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
def _stacky(thing):
return thing.__class__ in {AnyJoyType, StackJoyType}
@ -185,7 +409,7 @@ def _compose(f, g):
'''
# Relabel, unify, update, delabel.
(f_in, f_out), (g_in, g_out) = relabel(f, g)
fg = reify(unify(g_in, f_out), (f_in, g_out))
fg = reify(uni_unify(g_in, f_out), (f_in, g_out))
return delabel(fg)
@ -275,7 +499,144 @@ def compile_(name, f, doc=None):
return %s''' % (name, doc, i, o)
_functions = {}
def _poly_compose(f, g, e):
(f_in, f_out), (g_in, g_out) = f, g
for s in unify(g_in, f_out):
yield reify(s, (e, (f_in, g_out)))
def poly_compose(f, g, e):
'''
Yield the stack effects of the composition of two stack effects. An
expression is carried along and updated and yielded.
'''
f, g = relabel(f, g)
for fg in _poly_compose(f, g, e):
yield delabel(fg)
def _meta_compose(F, G, e):
for f, g in product(F, G):
try:
for result in poly_compose(f, g, e): yield result
except JoyTypeError:
pass
def meta_compose(F, G, e):
'''
Yield the stack effects of the composition of two lists of stack
effects. An expression is carried along and updated and yielded.
'''
res = sorted(set(_meta_compose(F, G, e)))
if not res:
raise JoyTypeError('Cannot unify %r and %r.' % (F, G))
return res
_S0 = StackJoyType(0)
ID = _S0, _S0 # Identity function.
def _infer(e, F=ID):
_log_it(e, F)
if not e:
return [F]
n, e = e
if isinstance(n, SymbolJoyType):
eFG = meta_compose([F], n.stack_effects, e)
res = flatten(_infer(e, Fn) for e, Fn in eFG)
elif isinstance(n, CombinatorJoyType):
fi, fo = n.enter_guard(F)
res = flatten(_interpret(f, fi, fo, e) for f in n.stack_effects)
elif isinstance(n, Symbol):
if n in FUNCTIONS:
res =_infer((FUNCTIONS[n], e), F)
else:
raise JoyTypeError
# print n
# func = joy.library._dictionary[n]
# res = _interpret(func, F[0], F[1], e)
else:
fi, fo = F
res = _infer(e, (fi, (n, fo)))
return res
def _interpret(f, fi, fo, e):
new_fo, ee, _ = f(fo, e, {})
ee = reify(FUNCTIONS, ee) # Fix Symbols.
new_F = fi, new_fo
return _infer(ee, new_F)
def _log_it(e, F):
_log.info(
u'%3i %s%s',
len(inspect_stack()),
doc_from_stack_effect(*F),
expression_to_string(e),
)
def infer(*expression):
'''
Return a list of stack effects for a Joy expression.
For example::
h = infer(pop, swap, rolldown, rest, rest, cons, cons)
for fi, fo in h:
print doc_from_stack_effect(fi, fo)
Prints::
([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])
'''
return sorted(set(_infer(list_to_stack(expression))))
def infer_string(string):
e = reify(FUNCTIONS, text_to_expression(string)) # Fix Symbols.
return sorted(set(_infer(e)))
def infer_expression(expression):
e = reify(FUNCTIONS, expression) # Fix Symbols.
return sorted(set(_infer(e)))
def type_check(name, stack):
'''
Trinary predicate. True if named function type-checks, False if it
fails, None if it's indeterminate (because I haven't entered it into
the FUNCTIONS dict yet.)
'''
try:
func = FUNCTIONS[name]
except KeyError:
return # None, indicating unknown
for fi, fo in infer(func):
try:
U = unify(fi, stack)
except (JoyTypeError, ValueError), e:
#print >> sys.stderr, name, e, stack
continue
#print U
return True
return False
FUNCTIONS = {} # Polytypes (lists of stack effects.)
_functions = {} # plain ol' stack effects.
def __(*seq):
@ -303,6 +664,14 @@ def ef(*inputs):
return _ef
def combinator_effect(number, *expect):
def _combinator_effect(c):
C = FUNCTIONS[c.name] = CombinatorJoyType(c.name, [c], number)
if expect: C.expect = __(*expect)
return c
return _combinator_effect
def show(DEFS):
for name, stack_effect_comment in sorted(DEFS.iteritems()):
t = ' *'[compilable(stack_effect_comment)]