#!/usr/bin/env python ''' stacker is a stack-based programming language interpreter factory. Example: """ import stacker class MyTranslator: def __init__(self, raw_source): ... def get(self, n): ... def value(self, n): ... stacker.magic(MyTranslator) """ ''' import os, sys, threading, time, urllib, re from math import sin, cos, asin, acos, pi if True: def DEBUG_PRINT(x): ''' print x and return it useful inside interesting expressions. ''' print x return x else: DEBUG_PRINT = lambda x: x try: any and all except: def any(seq): for elem in seq: if elem: return True return False def all(seq): for elem in seq: if not elem: return False return True read = sys.stdin.read def write(s): sys.stdout.write(s) sys.stdout.flush() class InterpreterExit(Exception): pass class Done(Exception): pass class Translator: ''' basic stack-based assembly language translator. Translator('42 dup 1 sub bit_xor') ''' FIBONACCI_CONSTRUCTIVE = ( ('1 dup print_num 10 print_char ' * 2) + '2 ' + 'dup print_num 10 print_char ' + 'dup 3 1 rot dup 4 -1 rot add ' + '11 jump' ) FIBONACCI_DESTRUCTIVE = ( '1 dup print_num 10 print_char 1 ' + 'dup print_num 10 print_char ' + 'dup 3 1 rot add ' + '6 jump' ) FIBONACCI = FIBONACCI_DESTRUCTIVE HELLO = ''.join([ (str(ord(c)) + ' print_char ') for c in 'Hello, world!\n' ]) @staticmethod def test(): Interpreter(raw=Translator.HELLO)() Interpreter(raw=Translator.FIBONACCI)() def __init__(self, raw): self.tkns = [] self.values = {} tkn = None for i, match in enumerate(re.finditer('\S+', raw)): tkn = match.string[match.start() : match.end()] if re.match('\-?\d+', tkn): self.values[i] = int(tkn) self.tkns.append('push') elif re.match('\-?\d+\.\d+', tkn): self.values[i] = float(tkn) self.tkns.append('push') else: self.tkns.append(tkn) if tkn != 'exit': self.tkns.append('exit') def get(self, n): return self.tkns[n] def value(self, n): return self.values[n] class Interpreter: Translator = Translator def __init__(self, fn=None, f=None, trace=False, translator=None, raw=None): ''' read all file(fn), set up the stack and current_frame. ''' self.trace = trace if fn: raw = file(fn).read() elif f: raw = f.read() if translator: self.translator = translator else: self.translator = self.Translator(raw) self.stack = [] self.current = 0 def print_trace(self): write('\n\t<%d %s %r>\n' % (self.current, self.translator.get(self.current), self.stack)) def __call__(self): ''' interpret self.raw until something raises InterpreterExit ''' try: while True: try: op = self.translator.get(self.current) except: raise InterpreterExit # assume end of program self.current += 1 try: self.ops[op](self) except KeyError: pass # ignore bad op codes if self.trace: try: self.print_trace() except: pass except InterpreterExit: pass def nop(self): pass def push(self): ''' append to the stack the length of the current word ''' if hasattr(self.translator, 'value') and hasattr(self.translator.value, '__call__'): try: self.stack.append(self.translator.value(self.current - 1)) except: pass else: self.stack.append(self.translator.get(self.current)) self.current += 1 def pop(self): ''' discard the top of the stack ''' self.stack.pop() def dup(self): ''' duplicate the top of the stack ''' self.stack.append(self.stack[-1]) def nth(self): ''' pop the nth value from the top, and push it back to the top. ''' self.stack.append(self.stack.pop(-int(self.stack.pop()))) def swap(self): ''' swap the top two values of the stack ''' self.stack[-2], self.stack[-1] = self.stack[-1], self.stack[-2] def rot(self): roll = self.stack.pop() depth = self.stack.pop() roll %= depth top_depth = self.stack[-depth:] top_depth = top_depth[roll:] + top_depth[:roll] self.stack[-depth:] = top_depth def jump(self): self.current = self.stack.pop() def thread(self): new = Interpreter(raw=self.raw, translator=self.translator) new.stack = self.stack[ : ] new.current_word = self.stack.pop() threading.Thread(target=new).start() def exit(self): raise InterpreterExit def eval(self): self.stack.append(Interpreter.ops[self.stack.pop()](self)) def cast_int(self): self.stack.append(int(self.stack.pop())) def cast_float(self): self.stack.append(float(self.stack.pop())) def add(self): self.stack.append(self.stack.pop() + self.stack.pop()) def sub(self): self.stack.append(-self.stack.pop() + self.stack.pop()) def mul(self): self.stack.append(self.stack.pop() * self.stack.pop()) def div(self): b = self.stack.pop() a = self.stack.pop() self.stack.append(a / b) def mod(self): b = self.stack.pop() a = self.stack.pop() self.stack.append(a % b) def pow(self): self.stack.append(self.stack.pop() ** self.stack.pop()) def cos(self): self.stack.append(cos(self.stack.pop())) def sin(self): self.stack.append(sin(self.stack.pop())) def exp(self): self.stack.append(exp(self.stack.pop())) def log(self): self.stack.append(log(self.stack.pop())) def log_not(self): self.stack.append(int(not self.stack.pop())) def log_and(self): self.stack.append(self.stack.pop() and self.stack.pop()) def log_or(self): self.stack.append(self.stack.pop() or self.stack.pop()) def log_nor(self): self.stack.append(not (self.stack.pop() or self.stack.pop())) def log_xor(self): a = self.stack.pop() b = self.stack.pop() self.stack.append((a and (not b)) or ((not a) and b)) def bit_not(self): self.stack.append(~int(self.stack.pop())) def bit_and(self): self.stack.append(int(self.stack.pop()) & int(self.stack.pop())) def bit_or(self): self.stack.append(int(self.stack.pop()) | int(self.stack.pop())) def bit_nor(self): self.stack.append(~int(self.stack.pop()) | int(self.stack.pop())) def bit_xor(self): self.stack.append(int(self.stack.pop()) ^ int(self.stack.pop())) def gt(self): self.stack.append(int(self.stack.pop() > self.stack.pop())) def lt(self): self.stack.append(int(self.stack.pop() < self.stack.pop())) def eq(self): self.stack.append(int(self.stack.pop() == self.stack.pop())) def leq(self): self.stack.append(int(self.stack.pop() <= self.stack.pop())) def geq(self): self.stack.append(int(self.stack.pop() >= self.stack.pop())) def print_char(self): write(chr(self.stack.pop())) def print_num(self): write(str(self.stack.pop())) def read_char(self): self.stack.append(read(1)) def read_num(self): self.stack.append(raw_input()) max_ops_list = [ nop, # don't do anything push, # push the tone number of the following tone to the stack pop, # discard the top of the stack dup, # duplicate the top of the stack nth, # push(pop(-pop())) swap, # swap the top 2 values of the stack rot, # rotate the top pop() of the stack by pop() places jump, # self.current += pop() thread, # start a thread at the pop()th frame, and continue this thread at the next frame exit, # raise InterpreterExit eval, # evaluate pop() as if it were a tone cast_int, # push(int(pop())/pop()) cast_float, # push(float(pop())) add, # push(pop()+pop()) sub, # push(pop()-pop()) mul, # push(pop()*pop()) div, # push(pop()/pop()) mod, # push(pop()%pop()) pow, # push(pop()**pop()) cos, # push(cos(pop())) sin, # push(sin(pop())) exp, # push(exp(pop())) log, # push(log(pop())) log_not, # push(not pop()) log_and, # push(pop() and pop()) log_or, # push(pop() or pop()) log_xor, # logical exclusive or bit_not, # push(~ pop()) bit_and, # push(pop() & pop()) bit_or, # push(pop() | pop()) bit_xor, # push(pop() ^ pop()) gt, # push(pop() > pop()) lt, # push(pop() < pop()) eq, # push(pop() == pop()) print_char, # write(chr(pop())) print_num, # write(str(pop())) read_char, # push(ord(read(1))) read_num, # push(int(raw_input())) ] max_ops = dict([ (f.func_name, f) for f in max_ops_list ]) min_ops_list = [ push, pop, dup, nth, jump, sub, log_nor, bit_nor, leq, print_char, read_char, ] min_ops = dict([ (f.func_name, f) for f in min_ops_list ]) ops = max_ops @staticmethod def main(argv): ''' ''' if argv: if argv[0] == '-': Interpreter(f=sys.stdin)() else: for fn in argv: Interpreter(fn=fn)() else: Interpreter.Translator.test() def magic(translator=None, interpreter=Interpreter): if __name__ == '__main__': if translator: interpreter.Translator = translator sys.exit(interpreter.main(sys.argv[1:])) magic()