Subleq/subleq.py

From Esolang
Jump to navigation Jump to search
Back to Subleq

This is a public domain interpreter and simple assembler for Subleq, written in Python 3. It can assemble and run the Hello, world! example on the Subleq article.

#!/usr/bin/env python3

import sys

class Subleq:
    def __init__(self):
        self.mem = []

    def assemble(self, source_text):
        labels = self.collect_labels(source_text)
        self.write_contents(source_text, labels)

    def collect_labels(self, source_text):
        labels = {}
        def label_it(s, addr):
            if ':' in s:
                label, contents = s.split(':')
                labels[label] = addr

        addr = 0
        for line in source_text.split("\n"):
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            [a, b, c] = line.split()
            label_it(a, addr)
            label_it(b, addr + 1)
            label_it(c, addr + 2)
            addr += 3

        return labels

    def write_contents(self, source_text, labels):
        def write_it(v, addr):
            if ':' in v:
                label, contents = v.split(':')
                v = contents
            if v in labels:
                v = labels[v]
            if v == '?+1':
                v = addr + 3
            self.mem.append(int(v))

        addr = 0
        for line in source_text.split("\n"):
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            [a, b, c] = line.split()
            write_it(a, addr)
            write_it(b, addr)
            write_it(c, addr)
            addr += 3

    def dump_triples(self, f):
        addr = 0
        while addr < len(self.mem):
            a = self.mem[addr]
            b = self.mem[addr + 1] if addr + 1 < len(self.mem) else 0
            c = self.mem[addr + 2] if addr + 2 < len(self.mem) else 0
            f.write('{} {} {}\n'.format(a, b, c))
            addr += 3

    def load_triples(self, f):
        for line in f:
            a, b, c = line.split()
            self.mem.append(int(a))
            self.mem.append(int(b))
            self.mem.append(int(c))

    def run(self, trace=False):
        pc = 0
        while pc >= 0:
            a = self.mem[pc]
            b = self.mem[pc + 1]
            c = self.mem[pc + 2]
            if trace:
                print('pc={}, a={}, b={}, c={}'.format(pc, a, b, c))
            at_a = self.mem[a]
            if b >= 0:
                at_b = self.mem[b]
                result = at_b - at_a
                self.mem[b] = result
            elif b == -1:
                sys.stdout.write(chr(at_a))
                result = 0
            if result <= 0:
                pc = c
            else:
                pc += 3


def main(args):
    if len(args) == 3 and args[0] == 'assemble':
        subleq = Subleq()
        subleq.assemble(open(args[1], 'r').read())
        with open(args[2], 'w') as f:
            subleq.dump_triples(f)
    elif len(args) == 2 and args[0] == 'run':
        subleq = Subleq()
        with open(args[1], 'r') as f:
            subleq.load_triples(f)
        subleq.run()
    elif len(args) == 2 and args[0] == 'trace':
        subleq = Subleq()
        with open(args[1], 'r') as f:
            subleq.load_triples(f)
        subleq.run(trace=True)
    else:
        print('usage: subleq.py assemble in.subleqasm out.triples | run in.triples | trace in.triples')
        sys.exit(1)


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