1 from collections import Counter
2 from itertools import imap
3 from joy.utils.stack import concat
4 from joy.parser import Symbol
7 class AnyJoyType(object):
9 Joy type variable. Represents any Joy value.
12 accept = tuple, int, float, long, complex, str, unicode, bool, Symbol
15 def __init__(self, number):
19 return self.prefix + str(self.number)
21 def __eq__(self, other):
23 isinstance(other, self.__class__)
24 and other.prefix == self.prefix
25 and other.number == self.number
28 def __ge__(self, other):
30 issubclass(other.__class__, self.__class__)
31 or isinstance(other, self.accept)
34 def __le__(self, other):
35 # 'a string' >= AnyJoyType() should be False.
36 return issubclass(self.__class__, other.__class__)
38 def __add__(self, other):
39 return self.__class__(self.number + other)
43 return hash(repr(self))
46 class BooleanJoyType(AnyJoyType):
51 class NumberJoyType(AnyJoyType):
52 accept = int, float, long, complex
56 class FloatJoyType(NumberJoyType):
61 class IntJoyType(FloatJoyType):
66 class TextJoyType(FloatJoyType):
71 class StackJoyType(AnyJoyType):
76 def __nonzero__(self):
77 # Imitate () at the end of cons list.
81 class JoyTypeError(Exception): pass
84 def reify(meaning, name, seen=None):
86 Apply substitution dict to term, returning new term.
88 if isinstance(name, tuple):
89 return tuple(reify(meaning, inner) for inner in name)
91 while name in meaning and safety:
95 raise ValueError('Cycle in substitution dict: %s' % (meaning,))
99 def relabel(left, right):
101 Re-number type variables to avoid collisions between stack effects.
103 return left, _1000(right)
107 if not isinstance(right, tuple):
109 return tuple(_1000(n) for n in right)
112 def delabel(f, seen=None, c=None):
114 Fix up type variable numbers after relabel().
118 seen, c = {}, Counter()
125 if not isinstance(f, tuple):
127 seen[f] = f.__class__(c[f.prefix] + 1)
128 except (TypeError, # FunctionJoyTypes break this.
129 AttributeError): # Symbol
135 return tuple(delabel(inner, seen, c) for inner in f)
138 def unify(u, v, s=None):
140 Return a substitution dict representing a unifier for u and v.
148 if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
154 raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
156 elif isinstance(u, tuple) and isinstance(v, tuple):
157 if len(u) != len(v) != 2:
158 raise ValueError(repr((u, v))) # Bad input.
159 (a, b), (c, d) = u, v
160 s = unify(b, d, unify(a, c, s))
162 elif isinstance(v, tuple):
164 raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
167 elif isinstance(u, tuple):
169 raise JoyTypeError('Cannot unify %r and %r.' % (v, u))
173 raise JoyTypeError('Cannot unify %r and %r.' % (u, v))
179 return thing.__class__ in {AnyJoyType, StackJoyType}
184 Return the stack effect of the composition of two stack effects.
186 # Relabel, unify, update, delabel.
187 (f_in, f_out), (g_in, g_out) = relabel(f, g)
188 fg = reify(unify(g_in, f_out), (f_in, g_out))
192 def compose(*functions):
194 Return the stack effect of the composition of some of stack effects.
196 return reduce(_compose, functions)
201 Return True if a stack effect represents a function that can be
202 automatically compiled (to Python), False otherwise.
204 return isinstance(f, tuple) and all(imap(compilable, f)) or _stacky(f)
207 def doc_from_stack_effect(inputs, outputs):
209 Return a crude string representation of a stack effect.
211 switch = [False] # Do we need to display the '...' for the rest of the main stack?
212 i, o = _f(inputs, switch), _f(outputs, switch)
216 return '(%s--%s)' % (
217 ' '.join(reversed([''] + i)),
218 ' '.join(reversed(o + [''])),
222 def _f(term, switch):
224 while term and isinstance(term, tuple):
227 assert isinstance(term, (tuple, StackJoyType)), repr(term)
228 a = [_to_str(i, term, switch) for i in a]
232 def _to_str(term, stack, switch):
233 if not isinstance(term, tuple):
238 '[...%i]' % term.number
239 if isinstance(term, StackJoyType)
244 while term and isinstance(term, tuple):
246 a.append(_to_str(item, stack, switch))
247 assert isinstance(term, (tuple, StackJoyType)), repr(term)
250 end = '' if term == () else '...'
253 end = '' if term == () else '...%i' % term.number
255 return '[%s]' % ' '.join(a)
258 def compile_(name, f, doc=None):
260 Return a string of Python code implementing the function described
261 by the stack effect. If no doc string is passed doc_from_stack_effect()
262 is used to generate one.
266 doc = doc_from_stack_effect(i, o)
267 return '''def %s(stack):
275 return %s''' % (name, doc, i, o)
282 stack = StackJoyType(23)
283 for item in seq: stack = item, stack
287 def stack_effect(*inputs):
288 def _stack_effect(*outputs):
289 def _apply_to(function):
290 i, o = _functions[function.name] = __(*inputs), __(*outputs)
291 function.__doc__ += (
292 '\nStack effect::\n\n ' # '::' for Sphinx docs.
293 + doc_from_stack_effect(i, o)
302 return __(*inputs), __(*outputs)
307 for name, stack_effect_comment in sorted(DEFS.iteritems()):
308 t = ' *'[compilable(stack_effect_comment)]
309 print name, '=', doc_from_stack_effect(*stack_effect_comment), t
312 def generate_library_code(DEFS, f=None):
316 print >> f, '# GENERATED FILE. DO NOT EDIT.\n'
317 for name, stack_effect_comment in sorted(DEFS.iteritems()):
318 if not compilable(stack_effect_comment):
321 print >> f, compile_(name, stack_effect_comment)
325 ##if __name__ == '__main__':