Logo Search packages:      
Sourcecode: malbolge version File versions  Download package

malbolge.py

#!/usr/bin/env python
#
# MSK (Malbolge Survival Kit) Malbolge Interpreter
# 2008, Toni Ruottu
#
# I hereby place this interpreter into the public domain.
#

import sys
from getopt import getopt

00012 class MBException(Exception): pass

00014 class Vm(object):

    def __init__(self, source=None, output=sys.stdout, input=sys.stdin, relax=False, book=False):
        self.decode_cipher = \
            r'''+b(29e*j1VMEKLyC})8&m#~W>qxdRp0wkrUo[D7,XTcA"lI''' + \
            r'''.v%{gJh4G\-=O@5`_3i<?Z';FNQuY]szf$!BS/|t:Pn6^Ha'''
        self.encode_cipher = [ord(c) for c in \
            r'''5z]&gqtyfr$(we4{WP)H-Zn,[%\3dL+Q;>U!pJS72FhOA1C''' + \
            r'''B6v^=I_0/8|jsb9m<.TVac`uY*MK'X~xDl}REokN:#?G"i@''' ]
        self.cipher_length = len(self.decode_cipher)
        self.WORD_SIZE = 10
        self.REG_MAX = self.tri2dec(self.WORD_SIZE * [2])
        self.MEM_SIZE = self.REG_MAX + 1
        self.operations = {
            'j': self.i_setdata,
            'i': self.i_setcode,
            '*': self.i_rotate,
            'p': self.i_op,
            '<': self.i_write,
            '/': self.i_read,
            'v': self.i_stop,
            'o': self.i_nop
        }
        if book:
            self.operations['<'] = self.i_read
            self.operations['/'] = self.i_write

        self.instructions = self.operations.keys()
        self.output = output
        self.input = input

        if source:
            self.load( source, relax )
            self.run()

    def is_graphical_ascii(self, c):
        return 33 <= c <= 126

    def dec2tri(self, d):
        i = self.WORD_SIZE
        t = i * [0]
        while d > 0:
            i -= 1
            t[i] = d % 3
            d = d / 3
        return t

    def tri2dec(self, t):
        d = 0
        i = 0
        while i < self.WORD_SIZE-1:
            d = (d + t[i]) * 3
            i += 1
        return d + t[i]

    def op(self, at, dt):
        table = [
            [1,0,0],
            [1,0,2],
            [2,2,1]
        ]
        o = self.WORD_SIZE * [0]
        for i in xrange(self.WORD_SIZE):
            o[i] = table[dt[i]][at[i]]
        return o

    def validate_source(self, length):
        if [c for c in self.mem[:length] if not self.is_graphical_ascii(c)]:
            raise MBException('source code contains invalid character(s)')
        valid = set(self.instructions)
        used = set([self.decode(_) for _ in xrange(length)])
        if len(used - valid) > 0:
            raise MBException('source code contains invalid instruction(s)')

    def load(self, source, relax=False):
        if len(source) < 2:
            raise MBException('source code too short')
        if len(source) > self.MEM_SIZE:
            raise MBException('source code too long')
        code = [ord(_) for _ in source if not _.isspace()]
        self.mem = ( code + (self.MEM_SIZE - len(code)) * [0])
        if not relax:
            self.validate_source(len(code))
        for i in xrange(len(code),self.MEM_SIZE):
            at = self.dec2tri(self.mem[i-1])
            dt = self.dec2tri(self.mem[i-2])
            self.mem[i] = self.tri2dec(self.op(at, dt))

    def i_setdata(self):
        self.d = self.mem[self.d]

    def i_setcode(self):
        self.c = self.mem[self.d]

    def i_rotate(self):
        t = self.dec2tri( self.mem[self.d])
        t = t[-1:] + t[:-1] 
        self.mem[self.d] = self.tri2dec(t)
        self.a = self.mem[self.d]

    def i_op(self):
        at = self.dec2tri(self.a)
        dt = self.dec2tri(self.mem[self.d])
        self.mem[self.d] = self.tri2dec(self.op(at, dt))
        self.a = self.mem[self.d]

    def i_write(self):
        self.output.write(chr(self.a & 0xff))

    def i_read(self):
        x = self.input.read(1)
        if (len(x) < 1): #EOF
            self.a = self.REG_MAX
        else:
            self.a = ord(x)

    def i_stop(self):
        pass

    def i_nop(self):
        pass

    def validate(self, addr):
        if not self.is_graphical_ascii(self.mem[self.c]):
            raise MBException('illegal code detected')

    def decode(self,addr):
        return self.decode_cipher[( self.mem[addr] - 33 + addr ) % self.cipher_length]

    def is_running(self):
        return self.i != 'v'

    def fetch(self):
        self.validate(self.mem[self.c])
        self.i = self.decode(self.c)

    def execute(self):
        if self.i in self.instructions:
            self.operations[self.i]()

    def modify(self):
        self.mem[self.c] = self.encode_cipher[self.mem[self.c] - 33]

    def increment_c(self):
        self.c += 1
        if self.c > self.REG_MAX:
            self.c = 0

    def increment_d(self):
        self.d += 1
        if self.d > self.REG_MAX:
            self.d = 0

    def run(self):
        self.a = 0  # accumulator
        self.c = 0  # code pointer
        self.d = 0  # data pointer
        self.i = '' # instruction
        while self.is_running():
            self.fetch()
            self.execute()
            self.modify()
            self.increment_c()
            self.increment_d()

def go(args):
    return getopt(args,'',['relaxed','by-the-book'])

def main():
    file = sys.stdin
    args = sys.argv[1:]
    opts, files = go(args)

    if files:
        file = open(files[0])

    source = file.read()

    if source[0] == '#':
        rows = source.split('\n')
        if len(rows) > 1 and len(rows[1]) > 0:
            moreopts, _ = go([_ for _ in rows[1][1:].split(' ') if _])
            opts += moreopts
        i = 0
        while rows[i] and rows[i][0] == '#':
            i += 1
        source = ''.join(rows [i:])

    relax = '--relaxed' in (x for x,_ in opts)
    book = '--by-the-book' in (x for x,_ in opts)

    try:
        Vm(source,relax=relax,book=book)
    except MBException, e:
        sys.stderr.write('ERROR: ' + str(e) + '\n')
        sys.exit(1)
    except KeyboardInterrupt:
        pass
if __name__ == '__main__':
    main()


Generated by  Doxygen 1.6.0   Back to index