User:Marinus/Pirandello interpreter

From Esolang
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)