User:Marinus/Pirandello interpreter
Jump to navigation
Jump to search
Pirandello interpreter (escape (Interaction-/) does not work because there's no definition of what it should do.)
#!/usr/bin/env python import sys class Tape(object): def __init__(self): self.data = [0] * 100 self.idx = 3 def prev(self): if self.idx == 0: raise IndexError("The program went past the left end of the tape.") else: self.idx -= 1 def next(self): self.idx += 1 if self.idx >= len(self.data): self.data += [0] * 100 def get(self): return self.data[self.idx] def set(self, value): self.data[self.idx] = value def __getitem__(self, n): if n<0: raise IndexError("The program went past the left end of the tape.") try: return self.data[n] except IndexError: return 0 def __setitem__(self, n, v): if n<0: raise IndexError("The program went past the left end of the tape.") elif n>=len(self.data): self.data += [0] * (n - len(self.data) + 100) self.data[n] = v class Field(object): UP, DOWN, LEFT, RIGHT = (0,-1), (0,1), (-1,0), (1,0) def __init__(self, stream): lines = [x[:-1] for x in stream.readlines()] self.llen = maxlen = max(map(len, lines)) self.lines = [ x + ' ' * (maxlen - len(x)) for x in lines ] def __getitem__(self, (x, y)): return self.lines[y%len(self.lines)][x%self.llen] @staticmethod def addVectors((x1, y1), (x2, y2)): return (x1+x2, y1+y2) class Mode(object): def equals(self): pass def percent(self): self.interpreter.cycle() def do(self, char): try: { '+': self.plus, '-': self.minus, '/': self.slash, '*': self.star, '%': self.percent, '=': self.equals } [char] () except KeyError: raise KeyError("no such command: " + char) def __init__(self, interp): self.interpreter = interp class Flow(Mode): def star(self): self.interpreter.skip() def slash(self): self.interpreter.condturn() def plus(self): self.interpreter.left() def minus(self): self.interpreter.right() class Data(Mode): def plus(self): self.interpreter.advance() def minus(self): self.interpreter.retreat() def star(self): self.interpreter.increment() def slash(self): self.interpreter.decrement() class Interaction(Mode): def plus(self): self.interpreter.input() def minus(self): self.interpreter.output() def slash(self): self.interpreter.escape() def star(self): self.interpreter.exit() class Register(Mode): def plus(self): self.interpreter.store() def minus(self): self.interpreter.load() def slash(self): self.interpreter.regskip() def star(self): self.interpreter.add() class Cycle(object): def __init__(self, items): self.items = items self.current = 0 def next(self): self.current += 1 self.current %= len(self.items) def prev(self): self.current -= 1 self.current %= len(self.items) def get(self): return self.items[self.current] class Interpreter(object): def __init__(self, field, tape): self.field, self.tape = field, tape self.reg = 0 self.coords = (0, 0) self.mode = Cycle([x(self) for x in [Flow, Data, Interaction, Register]]) self.direction = Cycle([Field.DOWN, Field.LEFT, Field.UP, Field.RIGHT]) def cycle(self): self.mode.next() def skip(self): self.coords = Field.addVectors(self.coords, self.direction.get()) def condturn(self): self.left() if self.tape.get() > 0 else self.right() def left(self): self.direction.prev() def right(self): self.direction.next() def advance(self): self.tape.next() def retreat(self): try: self.tape.prev() except: pass def increment(self): self.tape.set(self.tape.get() + 1 % 256) def decrement(self): self.tape.set(self.tape.get() - 1 % 256) def input(self): ch = sys.stdin.read(1) if ch: self.tape[1] = ord(ch) else: self.tape[2] = 1 def output(self): sys.stdout.write(chr(self.tape[1])) def escape(self): raise NotImplementedError("found an escape: spec is unclear, so escape is not implemented") def exit(self): sys.exit() def store(self): self.reg = self.tape.get() def load(self): self.tape.set(self.reg) def regskip(self): if self.reg: self.skip() def add(self): self.tape.set(self.tape.get() + self.reg % 256) def step(self): self.mode.get().do(self.field[self.coords]) self.coords = Field.addVectors(self.coords, self.direction.get()) def main(argv): if len(argv) != 2: print "usage: pirandello.py filename" sys.exit() interp = None try: interp = Interpreter(Field(file(argv[1])), Tape()) while True: interp.step() except Exception, e: if interp: at = " at (%d, %d)" % interp.coords else: at = "" print "error%s: %s" % (at, str(e)) if __name__=='__main__': main(sys.argv)