''' A crude compiler for a subset of Joy functions. I think I'm going about this the wrong way. The inference algorithm can "collapse" Yin function sequences into single stack effects which can then be written out as Python functions. Why not keep track of the new variables introduced as results of Yang functions, during inference? Could I write out better code that way? In any event, I am proceeding with this sort of ad hoc way for now. ''' from joy.parser import text_to_expression, Symbol from joy.utils.stack import concat, iter_stack, list_to_stack from joy.library import SimpleFunctionWrapper, YIN_STACK_EFFECTS def import_yin(): from joy.utils.generated_library import * return locals() class InfiniteStack(tuple): def _names(): n = 0 while True: m = yield Symbol('a' + str(n)) n = n + 1 if m is None else m _NAMES = _names() _NAMES.next() names = _NAMES.next reset = lambda _, _n=_NAMES: _n.send(-1) def __init__(self, code): self.reset() self.code = code def __iter__(self): if not self: new_var = self.names() self.code.append(('pop', new_var)) return iter((new_var, self)) def I(expression): code = [] stack = InfiniteStack(code) while expression: term, expression = expression if isinstance(term, Symbol): func = D[term] stack, expression, _ = func(stack, expression, code) else: stack = term, stack code.append(tuple(['ret'] + list(iter_stack(stack)))) return code strtup = lambda a, b: '(%s, %s)' % (b, a) strstk = lambda rest: reduce(strtup, rest, 'stack') def code_gen(code): #for p in code: print p coalesce_pops(code) lines = [] emit = lines.append for t in code: tag, rest = t[0], t[1:] if tag == 'pop': emit(strstk(rest) + ' = stack') elif tag == 'call': emit('%s = %s%s' % rest) elif tag == 'ret': emit('return ' + strstk(rest[::-1])) else: raise ValueError(tag) return '\n'.join(' ' + line for line in lines) def coalesce_pops(code): code.sort(key=lambda p: p[0] != 'pop') # All pops to the front. try: index = (i for i, t in enumerate(code) if t[0] != 'pop').next() except StopIteration: return code[:index] = [tuple(['pop'] + [t for _, t in code[:index][::-1]])] def compile_yinyang(name, text): return ''' def %s(stack): %s ''' % (name, code_gen(I(text_to_expression(text)))) def q(): memo = {} def bar(type_var): try: res = memo[type_var] except KeyError: res = memo[type_var] = InfiniteStack.names() return res return bar def type_vars_to_labels(thing, map_): if not thing: return thing if not isinstance(thing, tuple): return map_(thing) return tuple(type_vars_to_labels(inner, map_) for inner in thing) def remap_inputs(in_, stack, code): map_ = q() while in_: term, in_ = in_ arg0, stack = stack term = type_vars_to_labels(term, map_) code.append(('call', term, '', arg0)) return stack, map_ class BinaryBuiltin(object): def __init__(self, name): self.name = name def __call__(self, stack, expression, code): in1, (in0, stack) = stack out = InfiniteStack.names() code.append(('call', out, self.name, (in0, in1))) return (out, stack), expression, code YIN = import_yin() D = { name: SimpleFunctionWrapper(YIN[name]) for name in ''' ccons cons dup dupd dupdd over pop popd popdd popop popopd popopdd rolldown rollup swap swons tuck unit '''.split() } for name in ''' first first_two fourth rest rrest second third uncons unswons '''.split(): def foo(stack, expression, code, name=name): in_, out = YIN_STACK_EFFECTS[name] stack, map_ = remap_inputs(in_, stack, code) out = type_vars_to_labels(out, map_) return concat(out, stack), expression, code foo.__name__ = name D[name] = foo for name in ''' eq ge gt le lt ne xor lshift rshift and_ or_ add floordiv mod mul pow sub truediv '''.split(): D[name.rstrip('-')] = BinaryBuiltin(name) ''' stack stuncons stununcons swaack ''' for name in sorted(D): print name, ## print compile_yinyang(name, name) print '-' * 100 print compile_yinyang('mul_', 'mul') print compile_yinyang('pop', 'pop') print compile_yinyang('ppm', 'popop mul') print compile_yinyang('sqr', 'dup mul') print compile_yinyang('foo', 'dup 23 sub mul') print compile_yinyang('four_mul', 'mul mul mul mul') print compile_yinyang('baz', 'mul dup sub dup') print compile_yinyang('to_the_fifth_power', 'dup dup mul dup mul mul') print compile_yinyang('dup3', 'dup dup dup') print compile_yinyang('df2m', 'dup first_two mul') print compile_yinyang('sqr_first', 'uncons swap dup mul swons') print compile_yinyang('0BAD', 'uncons dup mul') print compile_yinyang('uncons', 'uncons')