2 from logging import getLogger
4 _log = getLogger(__name__)
6 from collections import Counter
7 from itertools import imap, chain, product
8 from inspect import stack as inspect_stack
9 from joy.utils.stack import concat, expression_to_string, list_to_stack
10 from joy.parser import Symbol, text_to_expression
13 class AnyJoyType(object):
15 Joy type variable. Represents any Joy value.
18 accept = tuple, int, float, long, complex, str, unicode, bool, Symbol
21 def __init__(self, number):
25 return self.prefix + str(self.number)
27 def __eq__(self, other):
29 isinstance(other, self.__class__)
30 and other.prefix == self.prefix
31 and other.number == self.number
34 def __ge__(self, other):
36 issubclass(other.__class__, self.__class__)
37 or isinstance(other, self.accept)
40 def __le__(self, other):
41 # 'a string' >= AnyJoyType() should be False.
42 return issubclass(self.__class__, other.__class__)
44 def __add__(self, other):
45 return self.__class__(self.number + other)
49 return hash(repr(self))
52 class BooleanJoyType(AnyJoyType):
57 class NumberJoyType(AnyJoyType):
58 accept = bool, int, float, long, complex
62 class FloatJoyType(NumberJoyType):
67 class IntJoyType(FloatJoyType):
72 class TextJoyType(FloatJoyType):
77 class StackJoyType(AnyJoyType):
82 def __nonzero__(self):
83 # Imitate () at the end of cons list.
87 class KleeneStar(object):
89 A sequence of zero or more `AnyJoyType` variables would be:
93 The `A*` works by splitting the universe into two alternate histories:
99 The Kleene star variable disappears in one universe, and in the other
100 it turns into an `AnyJoyType` variable followed by itself again.
102 We have to return all universes (represented by their substitution
103 dicts, the "unifiers") that don't lead to type conflicts.
108 def __init__(self, number):
112 self.prefix = repr(self)
115 return '%s%i*' % (self.kind.prefix, self.number)
119 return self.kind(10000 * self.number + self.count)
121 def __eq__(self, other):
123 isinstance(other, self.__class__)
124 and other.number == self.number
127 def __ge__(self, other):
128 return self.kind >= other.kind
130 def __add__(self, other):
131 return self.__class__(self.number + other)
135 return hash(repr(self))
138 class AnyStarJoyType(KleeneStar): kind = AnyJoyType
139 class NumberStarJoyType(KleeneStar): kind = NumberJoyType
140 class FloatStarJoyType(KleeneStar): kind = FloatJoyType
141 class IntStarJoyType(KleeneStar): kind = IntJoyType
142 class StackStarJoyType(KleeneStar): kind = StackJoyType
143 class TextStarJoyType(KleeneStar): kind = TextJoyType
146 class FunctionJoyType(AnyJoyType):
148 def __init__(self, name, sec, number):
150 self.stack_effects = sec
153 def __add__(self, other):
161 class SymbolJoyType(FunctionJoyType):
163 Represent non-combinator functions.
165 These type variables carry the stack effect comments and can
166 appear in expressions (as in quoted programs.)
171 class CombinatorJoyType(FunctionJoyType):
173 Represent combinators.
175 These type variables carry Joy functions that implement the
176 behaviour of Joy combinators and they can appear in expressions.
177 For simple combinators the implementation functions can be the
178 combinators themselves.
180 These types can also specify a stack effect (input side only) to
181 guard against being used on invalid types.
186 def __init__(self, name, sec, number, expect=None):
187 super(CombinatorJoyType, self).__init__(name, sec, number)
190 def enter_guard(self, f):
191 if self.expect is None:
193 g = self.expect, self.expect
194 new_f = list(poly_compose(f, g, ()))
195 assert len(new_f) == 1, repr(new_f)
199 class JoyTypeError(Exception): pass
202 def reify(meaning, name, seen=None):
204 Apply substitution dict to term, returning new term.
206 if isinstance(name, tuple):
207 return tuple(reify(meaning, inner) for inner in name)
209 while name in meaning and safety:
213 raise ValueError('Cycle in substitution dict: %s' % (meaning,))
217 def relabel(left, right):
219 Re-number type variables to avoid collisions between stack effects.
221 return left, _1000(right)
225 if not isinstance(right, tuple):
227 return tuple(_1000(n) for n in right)
230 def delabel(f, seen=None, c=None):
232 Fix up type variable numbers after relabel().
236 seen, c = {}, Counter()
243 if not isinstance(f, tuple):
245 seen[f] = f.__class__(c[f.prefix] + 1)
246 except (TypeError, # FunctionJoyTypes break this.
247 AttributeError): # Symbol
253 return tuple(delabel(inner, seen, c) for inner in f)
256 def uni_unify(u, v, s=None):
258 Return a substitution dict representing a unifier for u and v.
266 if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
272 raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
274 elif isinstance(u, tuple) and isinstance(v, tuple):
275 if len(u) != len(v) != 2:
276 raise ValueError(repr((u, v))) # Bad input.
277 (a, b), (c, d) = u, v
278 s = uni_unify(b, d, uni_unify(a, c, s))
280 elif isinstance(v, tuple):
282 raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
285 elif isinstance(u, tuple):
287 raise JoyTypeError('Cannot unify %r and %r.' % (v, u))
291 raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
297 def inner(u, v, s=None):
300 len(inspect_stack()), u, v, s,
304 '%3i %s U %s w/ %s => %s',
305 len(inspect_stack()), u, v, s, res,
312 def unify(u, v, s=None):
314 Return a tuple of substitution dicts representing unifiers for u and v.
325 elif isinstance(u, tuple) and isinstance(v, tuple):
326 if len(u) != 2 or len(v) != 2:
327 if _that_one_special_case(u, v):
329 raise ValueError(repr((u, v))) # Bad input.
332 (a, b), (c, d) = v, u
333 if isinstance(a, KleeneStar):
334 if isinstance(c, KleeneStar):
335 s = _lil_uni(a, c, s) # Attempt to unify the two K-stars.
336 res = unify(d, b, s[0])
339 # Two universes, in one the Kleene star disappears and
340 # unification continues without it...
343 # In the other it spawns a new variable.
344 s1 = unify(u, (a.another(), v))
350 elif isinstance(c, KleeneStar):
351 res = unify(v, d) + unify(v, (c.another(), u))
356 res = tuple(flatten(unify(d, b, sn) for sn in unify(c, a, s)))
358 elif isinstance(v, tuple):
360 raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
364 elif isinstance(u, tuple):
366 raise JoyTypeError('Cannot unify %r and %r.' % (v, u))
371 res = _lil_uni(u, v, s)
376 def _that_one_special_case(u, v):
378 Handle e.g. ((), (n1*, s1)) when type-checking sum, product, etc...
383 and isinstance(v[0], KleeneStar)
384 and isinstance(v[1], StackJoyType)
389 return list(chain.from_iterable(g))
392 def _lil_uni(u, v, s):
399 raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
403 return thing.__class__ in {AnyJoyType, StackJoyType}
408 Return the stack effect of the composition of two stack effects.
410 # Relabel, unify, update, delabel.
411 (f_in, f_out), (g_in, g_out) = relabel(f, g)
412 fg = reify(uni_unify(g_in, f_out), (f_in, g_out))
416 def compose(*functions):
418 Return the stack effect of the composition of some of stack effects.
420 return reduce(_compose, functions)
425 Return True if a stack effect represents a function that can be
426 automatically compiled (to Python), False otherwise.
428 return isinstance(f, tuple) and all(imap(compilable, f)) or _stacky(f)
431 def doc_from_stack_effect(inputs, outputs):
433 Return a crude string representation of a stack effect.
435 switch = [False] # Do we need to display the '...' for the rest of the main stack?
436 i, o = _f(inputs, switch), _f(outputs, switch)
440 return '(%s--%s)' % (
441 ' '.join(reversed([''] + i)),
442 ' '.join(reversed(o + [''])),
446 def _f(term, switch):
448 while term and isinstance(term, tuple):
451 assert isinstance(term, (tuple, StackJoyType)), repr(term)
452 a = [_to_str(i, term, switch) for i in a]
456 def _to_str(term, stack, switch):
457 if not isinstance(term, tuple):
462 '[...%i]' % term.number
463 if isinstance(term, StackJoyType)
468 while term and isinstance(term, tuple):
470 a.append(_to_str(item, stack, switch))
471 assert isinstance(term, (tuple, StackJoyType)), repr(term)
474 end = '' if term == () else '...'
477 end = '' if term == () else '...%i' % term.number
479 return '[%s]' % ' '.join(a)
482 def compile_(name, f, doc=None):
484 Return a string of Python code implementing the function described
485 by the stack effect. If no doc string is passed doc_from_stack_effect()
486 is used to generate one.
490 doc = doc_from_stack_effect(i, o)
491 return '''def %s(stack):
499 return %s''' % (name, doc, i, o)
502 def _poly_compose(f, g, e):
503 (f_in, f_out), (g_in, g_out) = f, g
504 for s in unify(g_in, f_out):
505 yield reify(s, (e, (f_in, g_out)))
508 def poly_compose(f, g, e):
510 Yield the stack effects of the composition of two stack effects. An
511 expression is carried along and updated and yielded.
514 for fg in _poly_compose(f, g, e):
518 def _meta_compose(F, G, e):
519 for f, g in product(F, G):
521 for result in poly_compose(f, g, e): yield result
526 def meta_compose(F, G, e):
528 Yield the stack effects of the composition of two lists of stack
529 effects. An expression is carried along and updated and yielded.
531 res = sorted(set(_meta_compose(F, G, e)))
533 raise JoyTypeError('Cannot unify %r and %r.' % (F, G))
537 _S0 = StackJoyType(0)
538 ID = _S0, _S0 # Identity function.
548 if isinstance(n, SymbolJoyType):
549 eFG = meta_compose([F], n.stack_effects, e)
550 res = flatten(_infer(e, Fn) for e, Fn in eFG)
552 elif isinstance(n, CombinatorJoyType):
553 fi, fo = n.enter_guard(F)
554 res = flatten(_interpret(f, fi, fo, e) for f in n.stack_effects)
556 elif isinstance(n, Symbol):
558 res =_infer((FUNCTIONS[n], e), F)
562 # func = joy.library._dictionary[n]
563 # res = _interpret(func, F[0], F[1], e)
567 res = _infer(e, (fi, (n, fo)))
572 def _interpret(f, fi, fo, e):
573 new_fo, ee, _ = f(fo, e, {})
574 ee = reify(FUNCTIONS, ee) # Fix Symbols.
576 return _infer(ee, new_F)
582 len(inspect_stack()),
583 doc_from_stack_effect(*F),
584 expression_to_string(e),
588 def infer(*expression):
590 Return a list of stack effects for a Joy expression.
594 h = infer(pop, swap, rolldown, rest, rest, cons, cons)
596 print doc_from_stack_effect(fi, fo)
600 ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])
603 return sorted(set(_infer(list_to_stack(expression))))
606 def infer_string(string):
607 e = reify(FUNCTIONS, text_to_expression(string)) # Fix Symbols.
608 return sorted(set(_infer(e)))
611 def infer_expression(expression):
612 e = reify(FUNCTIONS, expression) # Fix Symbols.
613 return sorted(set(_infer(e)))
616 def type_check(name, stack):
618 Trinary predicate. True if named function type-checks, False if it
619 fails, None if it's indeterminate (because I haven't entered it into
620 the FUNCTIONS dict yet.)
623 func = FUNCTIONS[name]
625 return # None, indicating unknown
627 for fi, fo in infer(func):
630 except (JoyTypeError, ValueError), e:
631 #print >> sys.stderr, name, e, stack
638 FUNCTIONS = {} # Polytypes (lists of stack effects.)
639 _functions = {} # plain ol' stack effects.
643 stack = StackJoyType(23)
644 for item in seq: stack = item, stack
648 def stack_effect(*inputs):
649 def _stack_effect(*outputs):
650 def _apply_to(function):
651 i, o = _functions[function.name] = __(*inputs), __(*outputs)
652 function.__doc__ += (
653 '\nStack effect::\n\n ' # '::' for Sphinx docs.
654 + doc_from_stack_effect(i, o)
663 return __(*inputs), __(*outputs)
667 def combinator_effect(number, *expect):
668 def _combinator_effect(c):
669 C = FUNCTIONS[c.name] = CombinatorJoyType(c.name, [c], number)
670 if expect: C.expect = __(*expect)
672 return _combinator_effect
676 for name, stack_effect_comment in sorted(DEFS.iteritems()):
677 t = ' *'[compilable(stack_effect_comment)]
678 print name, '=', doc_from_stack_effect(*stack_effect_comment), t
681 def generate_library_code(DEFS, f=None):
685 print >> f, '# GENERATED FILE. DO NOT EDIT.\n'
686 for name, stack_effect_comment in sorted(DEFS.iteritems()):
687 if not compilable(stack_effect_comment):
690 print >> f, compile_(name, stack_effect_comment)
694 ##if __name__ == '__main__':