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)