git @ Cat's Eye Technologies Beatnik / master script / beatnik.py
master

Tree @master (Download .tar.gz)

beatnik.py @masterraw · history · blame

#!/usr/bin/env python

import sys
import re

SCRABBLE = {
    'A': 1,
    'B': 3,
    'C': 3,
    'D': 2,
    'E': 1,
    'F': 4,
    'G': 2,
    'H': 4,
    'I': 1,
    'J': 8,
    'K': 5,
    'L': 1,
    'M': 3,
    'N': 1,
    'O': 1,
    'P': 3,
    'Q': 10,
    'R': 1,
    'S': 1,
    'T': 1,
    'U': 1,
    'V': 4,
    'W': 4,
    'X': 8,
    'Y': 4,
    'Z': 10
}

ACTION = {
    5: 'PUSH',
    6: 'DISCARD',
    7: 'ADD',
    8: 'INPUT',
    9: 'OUTPUT',
    10: 'SUBTRACT',
    11: 'SWAP',
    12: 'DUP',
    13: 'SKIP_AHEAD_ZERO',
    14: 'SKIP_AHEAD_NONZERO',
    15: 'SKIP_BACK_ZERO',
    16: 'SKIP_BACK_NONZERO',
    17: 'STOP',
}

def scrabble(word):
    acc = 0
    for c in word.upper():
        acc += SCRABBLE[c]
    return acc

DEBUG = False

def debug(s, *args):
    if not DEBUG:
        return
    sys.stderr.write(s % args)
    sys.stderr.write("\n")


class Stack(object):
    def __init__(self):
        self._stack = []

    def push(self, value):
        self._stack.append(value % 256)

    def append(self, value):
        """for backwards compatibility"""
        self.push(value)

    def pop(self):
        return self._stack.pop()


def main(args):
    global DEBUG
    tokenize = False
    text = None

    while args and args[0].startswith('--'):
        switch = args.pop(0)
        if switch == '--debug':
            DEBUG = True
        elif switch == '--tokenize':
            tokenize = True
        elif switch == '--eval':
            text = args.pop(0)
        else:
            raise KeyError("Unknown command-line option '%s'" % switch)

    words = []
    if text is not None:
        for word in re.findall(r'[a-zA-Z]+', text):
            words.append(word)
    else:
        with open(args[0]) as f:
            for line in f:
                for word in re.findall(r'[a-zA-Z]+', line):
                    words.append(word)

    if tokenize:
        # A better version of this would not try to assign an ACTION
        # to constants (words that follow PUSH or SKIP instructions)
        for word in words:
            value = scrabble(word)
            print ("[%s:%s/%s]" % (value, ACTION.get(value, 'NOP'), word))
        sys.exit(0)

    words.append('FOXY')  # stop if you get to the end

    stack = Stack()
    pc = 0
    done = False

    while not done:
        value = scrabble(words[pc])
        debug("* '%s' = %s (%s)", words[pc], value, ACTION.get(value, '(none)'))
        if value == 5:
            pc += 1
            a = scrabble(words[pc])
            stack.append(a)
        elif value == 6:
            stack.pop()
        elif value == 7:
            a = stack.pop()
            b = stack.pop()
            debug("...ADD %s %s", a, b)
            stack.append(a + b)
        elif value == 8:
            c = sys.stdin.read(1)
            stack.append(ord(c))
        elif value == 9:
            c = stack.pop()
            debug("...WRITE %s", c)
            sys.stdout.write(chr(c))
        elif value == 10:
            a = stack.pop()
            b = stack.pop()
            stack.append(b - a)
        elif value == 11:
            a = stack.pop()
            b = stack.pop()
            stack.append(a)
            stack.append(b)
        elif value == 12:
            a = stack.pop()
            stack.append(a)
            stack.append(a)
        elif value == 13:
            a = stack.pop()
            pc += 1
            dist = scrabble(words[pc])
            if a == 0:
                pc += dist
        elif value == 14:
            a = stack.pop()
            pc += 1
            dist = scrabble(words[pc])
            if a != 0:
                pc += dist
        elif value == 15:
            a = stack.pop()
            dist = scrabble(words[pc + 1])
            if a == 0:
                pc -= dist
        elif value == 16:
            a = stack.pop()
            dist = scrabble(words[pc + 1])
            if a != 0:
                pc -= dist
        elif value == 17:
            done = True
        pc += 1


if __name__ == '__main__':
    main(sys.argv[1:])