Moving right along.
A little clunky but it seems to work so far.
This commit is contained in:
parent
c2dd7cca0a
commit
73d19b1f3d
|
|
@ -25,11 +25,12 @@ function.
|
||||||
'''
|
'''
|
||||||
from inspect import getdoc
|
from inspect import getdoc
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from itertools import count
|
||||||
from inspect import getmembers, isfunction
|
from inspect import getmembers, isfunction
|
||||||
import operator, math
|
import operator, math
|
||||||
|
|
||||||
from .parser import text_to_expression, Symbol
|
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.brutal_hackery import rename_code_object
|
||||||
|
|
||||||
from .utils import generated_library as genlib
|
from .utils import generated_library as genlib
|
||||||
|
|
@ -38,16 +39,28 @@ from .utils.types import (
|
||||||
ef,
|
ef,
|
||||||
stack_effect,
|
stack_effect,
|
||||||
AnyJoyType,
|
AnyJoyType,
|
||||||
|
AnyStarJoyType,
|
||||||
BooleanJoyType,
|
BooleanJoyType,
|
||||||
NumberJoyType,
|
NumberJoyType,
|
||||||
|
NumberStarJoyType,
|
||||||
StackJoyType,
|
StackJoyType,
|
||||||
|
StackStarJoyType,
|
||||||
FloatJoyType,
|
FloatJoyType,
|
||||||
IntJoyType,
|
IntJoyType,
|
||||||
|
SymbolJoyType,
|
||||||
TextJoyType,
|
TextJoyType,
|
||||||
_functions,
|
_functions,
|
||||||
|
FUNCTIONS,
|
||||||
|
infer,
|
||||||
|
JoyTypeError,
|
||||||
|
combinator_effect,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_SYM_NUMS = count().next
|
||||||
|
_COMB_NUMS = count().next
|
||||||
|
|
||||||
|
|
||||||
_R = range(10)
|
_R = range(10)
|
||||||
A = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = map(AnyJoyType, _R)
|
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)
|
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)
|
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)()
|
sec0 = stack_effect(t1)()
|
||||||
sec1 = stack_effect(s0, i1)(s1)
|
sec1 = stack_effect(s0, i1)(s1)
|
||||||
sec2 = stack_effect(s0, i1)(a1)
|
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_binary_math = stack_effect(n1, n2)(n3)
|
||||||
sec_unary_logic = stack_effect(a1)(b1)
|
sec_unary_logic = stack_effect(a1)(b1)
|
||||||
sec_unary_math = stack_effect(n1)(n2)
|
sec_unary_math = stack_effect(n1)(n2)
|
||||||
|
sec_Ns_math = stack_effect((Ns[1], s1),)(n0)
|
||||||
|
|
||||||
_dictionary = {}
|
_dictionary = {}
|
||||||
|
|
||||||
|
|
@ -350,6 +369,14 @@ class DefinitionWrapper(object):
|
||||||
Add the definition to the dictionary.
|
Add the definition to the dictionary.
|
||||||
'''
|
'''
|
||||||
F = class_.parse_definition(definition)
|
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
|
dictionary[F.name] = F
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -357,23 +384,11 @@ def _text_to_defs(text):
|
||||||
return (line.strip() for line in text.splitlines() if '==' in line)
|
return (line.strip() for line in text.splitlines() if '==' in line)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## eh = compose(dup, bool_)
|
|
||||||
## sqr = compose(dup, mul)
|
|
||||||
## of = compose(swap, at)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Functions
|
# Functions
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
# Load the auto-generated primitives into the dictionary.
|
|
||||||
_functions.update(yin_functions())
|
|
||||||
for name, primitive in getmembers(genlib, isfunction):
|
|
||||||
inscribe(SimpleFunctionWrapper(primitive))
|
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
@sec0
|
@sec0
|
||||||
@FunctionWrapper
|
@FunctionWrapper
|
||||||
|
|
@ -528,6 +543,7 @@ def select(stack):
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
@sec_Ns_math
|
||||||
@SimpleFunctionWrapper
|
@SimpleFunctionWrapper
|
||||||
def max_(S):
|
def max_(S):
|
||||||
'''Given a list find the maximum.'''
|
'''Given a list find the maximum.'''
|
||||||
|
|
@ -536,6 +552,7 @@ def max_(S):
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
@sec_Ns_math
|
||||||
@SimpleFunctionWrapper
|
@SimpleFunctionWrapper
|
||||||
def min_(S):
|
def min_(S):
|
||||||
'''Given a list find the minimum.'''
|
'''Given a list find the minimum.'''
|
||||||
|
|
@ -544,6 +561,7 @@ def min_(S):
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
@sec_Ns_math
|
||||||
@SimpleFunctionWrapper
|
@SimpleFunctionWrapper
|
||||||
def sum_(S):
|
def sum_(S):
|
||||||
'''Given a quoted sequence of numbers return the sum.
|
'''Given a quoted sequence of numbers return the sum.
|
||||||
|
|
@ -872,6 +890,7 @@ S_truthy = Symbol('truthy')
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
@combinator_effect(_COMB_NUMS(), s1)
|
||||||
@FunctionWrapper
|
@FunctionWrapper
|
||||||
def i(stack, expression, dictionary):
|
def i(stack, expression, dictionary):
|
||||||
'''
|
'''
|
||||||
|
|
@ -889,6 +908,7 @@ def i(stack, expression, dictionary):
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
@combinator_effect(_COMB_NUMS(), s1)
|
||||||
@FunctionWrapper
|
@FunctionWrapper
|
||||||
def x(stack, expression, dictionary):
|
def x(stack, expression, dictionary):
|
||||||
'''
|
'''
|
||||||
|
|
@ -906,6 +926,7 @@ def x(stack, expression, dictionary):
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
#@combinator_effect(_COMB_NUMS(), s7, s6)
|
||||||
@FunctionWrapper
|
@FunctionWrapper
|
||||||
def b(stack, expression, dictionary):
|
def b(stack, expression, dictionary):
|
||||||
'''
|
'''
|
||||||
|
|
@ -941,6 +962,7 @@ def dupdip(stack, expression, dictionary):
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
#@combinator_effect(_COMB_NUMS(), s7, s6)
|
||||||
@FunctionWrapper
|
@FunctionWrapper
|
||||||
def infra(stack, expression, dictionary):
|
def infra(stack, expression, dictionary):
|
||||||
'''
|
'''
|
||||||
|
|
@ -1150,6 +1172,7 @@ def _cond(conditions, expression):
|
||||||
|
|
||||||
|
|
||||||
@inscribe
|
@inscribe
|
||||||
|
@combinator_effect(_COMB_NUMS(), a1, s1)
|
||||||
@FunctionWrapper
|
@FunctionWrapper
|
||||||
def dip(stack, expression, dictionary):
|
def dip(stack, expression, dictionary):
|
||||||
'''
|
'''
|
||||||
|
|
@ -1437,7 +1460,31 @@ for F in (
|
||||||
del F # Otherwise Sphinx autodoc will pick it up.
|
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(_dictionary, ALIASES)
|
||||||
|
add_aliases(_functions, ALIASES)
|
||||||
|
add_aliases(FUNCTIONS, ALIASES)
|
||||||
|
|
||||||
|
|
||||||
DefinitionWrapper.add_definitions(definitions, _dictionary)
|
DefinitionWrapper.add_definitions(definitions, _dictionary)
|
||||||
|
|
||||||
|
#sec_Ns_math(_dictionary['product'])
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,6 @@ an unbounded sequence of other types.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
import sys
|
import sys
|
||||||
from inspect import stack as inspect_stack
|
|
||||||
from itertools import chain, product
|
|
||||||
from logging import getLogger
|
|
||||||
|
|
||||||
_log = getLogger(__name__)
|
|
||||||
|
|
||||||
import joy.library
|
import joy.library
|
||||||
from joy.parser import Symbol, text_to_expression
|
from joy.parser import Symbol, text_to_expression
|
||||||
from joy.utils.stack import (
|
from joy.utils.stack import (
|
||||||
|
|
@ -23,391 +17,22 @@ from joy.utils.stack import (
|
||||||
expression_to_string,
|
expression_to_string,
|
||||||
list_to_stack,
|
list_to_stack,
|
||||||
)
|
)
|
||||||
from joy.utils.types import (
|
# from joy.utils.types import (
|
||||||
AnyJoyType, A,
|
# AnyJoyType, A,
|
||||||
BooleanJoyType, B,
|
# BooleanJoyType, B,
|
||||||
DEFS,
|
# DEFS,
|
||||||
doc_from_stack_effect,
|
# doc_from_stack_effect,
|
||||||
FloatJoyType, F,
|
# FloatJoyType, F,
|
||||||
JoyTypeError,
|
# JoyTypeError,
|
||||||
NumberJoyType, N,
|
# NumberJoyType, N,
|
||||||
StackJoyType, S,
|
# StackJoyType, S,
|
||||||
_stacky,
|
# _stacky,
|
||||||
_R,
|
# _R,
|
||||||
relabel, delabel,
|
# relabel, delabel,
|
||||||
reify,
|
# 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?'''
|
'''Docstring for functions in Sphinx?'''
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -452,6 +77,7 @@ FUNCTIONS.update({
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def branch_true(stack, expression, dictionary):
|
def branch_true(stack, expression, dictionary):
|
||||||
(then, (else_, (flag, stack))) = stack
|
(then, (else_, (flag, stack))) = stack
|
||||||
return stack, CONCAT(then, expression), dictionary
|
return stack, CONCAT(then, expression), dictionary
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
|
# -*- coding: utf_8
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
_log = getLogger(__name__)
|
||||||
|
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from itertools import imap
|
from itertools import imap, chain, product
|
||||||
from joy.utils.stack import concat
|
from inspect import stack as inspect_stack
|
||||||
from joy.parser import Symbol
|
from joy.utils.stack import concat, expression_to_string, list_to_stack
|
||||||
|
from joy.parser import Symbol, text_to_expression
|
||||||
|
|
||||||
|
|
||||||
class AnyJoyType(object):
|
class AnyJoyType(object):
|
||||||
|
|
@ -49,7 +55,7 @@ class BooleanJoyType(AnyJoyType):
|
||||||
|
|
||||||
|
|
||||||
class NumberJoyType(AnyJoyType):
|
class NumberJoyType(AnyJoyType):
|
||||||
accept = int, float, long, complex
|
accept = bool, int, float, long, complex
|
||||||
prefix = 'n'
|
prefix = 'n'
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,6 +84,118 @@ class StackJoyType(AnyJoyType):
|
||||||
return False
|
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
|
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)
|
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.
|
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:
|
if len(u) != len(v) != 2:
|
||||||
raise ValueError(repr((u, v))) # Bad input.
|
raise ValueError(repr((u, v))) # Bad input.
|
||||||
(a, b), (c, d) = u, v
|
(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):
|
elif isinstance(v, tuple):
|
||||||
if not _stacky(u):
|
if not _stacky(u):
|
||||||
|
|
@ -175,6 +293,112 @@ def unify(u, v, s=None):
|
||||||
return s
|
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):
|
def _stacky(thing):
|
||||||
return thing.__class__ in {AnyJoyType, StackJoyType}
|
return thing.__class__ in {AnyJoyType, StackJoyType}
|
||||||
|
|
||||||
|
|
@ -185,7 +409,7 @@ def _compose(f, g):
|
||||||
'''
|
'''
|
||||||
# Relabel, unify, update, delabel.
|
# Relabel, unify, update, delabel.
|
||||||
(f_in, f_out), (g_in, g_out) = relabel(f, g)
|
(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)
|
return delabel(fg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -275,7 +499,144 @@ def compile_(name, f, doc=None):
|
||||||
return %s''' % (name, doc, i, o)
|
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):
|
def __(*seq):
|
||||||
|
|
@ -303,6 +664,14 @@ def ef(*inputs):
|
||||||
return _ef
|
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):
|
def show(DEFS):
|
||||||
for name, stack_effect_comment in sorted(DEFS.iteritems()):
|
for name, stack_effect_comment in sorted(DEFS.iteritems()):
|
||||||
t = ' *'[compilable(stack_effect_comment)]
|
t = ' *'[compilable(stack_effect_comment)]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue