git @ Cat's Eye Technologies Jolverine / master script / jolverine
master

Tree @master (Download .tar.gz)

jolverine @masterraw · history · blame

#!/usr/bin/env python

# Reference interpreter for Jolverine 1.0
# Sept 11, 2012, Chris Pressey, Cat's Eye Technologies
# This program is in the public domain -- see the file UNLICENSE.

import sys


# check if running under Skulpt, and if so, apply appropriate modifications
if getattr(sys, 'resetTimeout', None) is not None:
    __name__ = '__skulpt__'


class Program(object):
    def __init__(self, debug=False, super_wimp_mode=False):
        self.wheel = [
            'left',
            'right',
            'rot',
            'adddx',
            'adddy',
            'input',
            'output',
        ]
        self.index = 0
        self.head = 0
        self.tape = {}
        self.playfield = {}
        self.insertpos = 'bottom'
        self._debug = debug
        self._super_wimp_mode = super_wimp_mode
        self.maxx = 0
        self.maxy = 0
        self.x = 0
        self.y = 0
        self.dx = 1
        self.dy = 0

    def load_string(self, text):
        y = 0
        for line in text.split('\n'):
            line = line.rstrip('\r\n')
            x = 0
            while x < len(line):
                self.playfield[(x, y)] = line[x]
                x += 1
            if x > self.maxx:
                self.maxx = x
            y += 1
        self.maxy = y

    def load_file(self, filename):
        self.load_string(open(filename, 'r').read())

    # ### helpers ### #

    def limit(self, x):
        if x == 2:
            return -1
        if x == -2:
            return 1
        return x

    # ### instructions ### #

    def left(self):
        self.head -= 1

    def right(self):
        self.head += 1

    def rot(self):
        val = self.tape.get(self.head, 0)
        val += 1
        val = self.limit(val)
        self.tape[self.head] = val

    def adddx(self):
        self.dx += self.tape.get(self.head, 0)
        self.dx = self.limit(self.dx)

    def adddy(self):
        self.dy += self.tape.get(self.head, 0)
        self.dy = self.limit(self.dy)

    def input(self):
        c = sys.stdin.read(1)
        if c == '0':
            pass
        elif c == '1':
            self.rot()
        elif c in (' ', '\n', '\r', '\t'):
            pass
        else:
            raise ValueError("Illegal binary input character '%s'" % c)

    def output(self):
        val = self.tape.get(self.head, 0)
        if val == 1:
            sys.stdout.write('1')
            sys.stdout.flush()
        elif val == 0:
            sys.stdout.write('0')
            sys.stdout.flush()
        else:
            raise ValueError("Illegal binary output value '%d'" % val)

    # ### execution ### #

    def cycle(self):
        self.index += 1
        self.index %= len(self.wheel)

    def execute(self):
        name = self.wheel[self.index]
        indent = ' ' * 20
        self.debug("*%sEXECUTING %s @ (%d,%d)" %
                   (indent, name, self.x, self.y))
        command = getattr(self, name)
        command()
        self.wheel.pop(self.index)
        if self.insertpos == 'bottom':
            self.wheel.insert(0, name)
            self.insertpos = 'top'
        else:
            self.wheel.append(name)
            self.insertpos = 'bottom'

    def run(self):
        if not self._super_wimp_mode:
            while True:
                instr = self.playfield.get((self.x, self.y), ' ')
                if instr == '*':
                    self.execute()
                self.cycle()
                self.x += self.dx
                self.y += self.dy
                self.dump()
                if (
                    self.x < 0 or self.y < 0 or
                    self.x > self.maxx or self.y > self.maxy
                ):
                    break
        else:
            table = {
              '<': 'left',
              '>': 'right',
              '+': 'rot',
              'x': 'adddx',
              'y': 'adddy',
              'i': 'input',
              'o': 'output',
            }
            while True:
                instr = self.playfield.get((self.x, self.y), ' ')
                name = table.get(instr, None)
                if name is not None:
                    indent = ' ' * 20
                    self.debug("*%sWIMPZECUTING %s @ (%d,%d)" %
                               (indent, name, self.x, self.y))
                    command = getattr(self, name)
                    command()
                self.x += self.dx
                self.y += self.dy
                self.dump()
                if (
                    self.x < 0 or self.y < 0 or
                    self.x > self.maxx or self.y > self.maxy
                ):
                    break

    # ### debugging ### #

    def dump(self):
        if not self._debug:
            return
        print("------------")
        print("x=%d,y=%d,dx=%d,dy=%d" % (self.x, self.y, self.dx, self.dy))
        print("head: %d cell: %d" % (self.head, self.tape.get(self.head, 0)))
        if not self._super_wimp_mode:
            print("Next reinsert: %s" % self.insertpos)
            print("Wheel:")
            print("------------")
            i = 0
            for x in self.wheel:
                arrow = "   "
                if i == self.index:
                    arrow = "-->"
                print("%s %d. %s" % (arrow, i, x))
                i += 1
        print("------------")

    def debug(self, msg):
        if self._debug:
            print(msg)

if __name__ == '__main__':
    debug = False
    super_wimp_mode = False
    if sys.argv[1] in ('-d', '--debug'):
        debug = True
        sys.argv.pop(1)
    if sys.argv[1] == '--super-wimp-mode':
        super_wimp_mode = True
        sys.argv.pop(1)
    p = Program(debug=debug, super_wimp_mode=super_wimp_mode)
    p.load_file(sys.argv[1])
    p.dump()
    p.run()