◧◨/Interpreters

From Esolang
Jump to navigation Jump to search

Implementations for ◧◨.

Python (by User:stkptr)

import math
import sys


LEFT = "◧"
RIGHT = "◨"


# This assumes that the source does not have arbitrary other symbols in it
# Files with characters inside commands will not be parsed correctly
# Invalid programs are also not explicitly tested for
def parse(source):
    index = 0
    parsed = []

    def get(i):
        if i < len(source):
            return source[i]

    while index < len(source):
        if source[index] == LEFT:
            index += 2
            n = 0
            while get(index) == RIGHT:
                index += 1
                n += 1
            # overstep
            if get(index) == LEFT and get(index + 1) == RIGHT:
                index -= 1
                n -= 1
            parsed.append((-1)**n * math.ceil(n / 2))
        elif source[index] == RIGHT:
            index += 3
            parsed.append(None)
        else:
            index += 1

    return parsed


class BoundedTape:
    def __init__(self, default=0):
        self.cells = []
        self.default = default

    def __getitem__(self, index):
        if index >= len(self.cells):
            return self.default
        return self.cells[index]

    def __setitem__(self, index, value):
        extra = index + 1 - len(self.cells)
        if extra > 0:
            self.cells += [self.default] * extra
        self.cells[index] = value


class Tape:
    def __init__(self, default=0):
        self.left = BoundedTape(default)
        self.right = BoundedTape(default)

    def __getitem__(self, index):
        if index < 0:
            return self.left[abs(index) - 1]
        return self.right[index]

    def __setitem__(self, index, value):
        if index < 0:
            self.left[abs(index) - 1] = value
        else:
            self.right[index] = value


class Machine:
    def __init__(self, program):
        self.tape = Tape(False)
        self.pointer = 0
        self.ip = 0
        self.program = program

    def step(self):
        if self.ip >= len(self.program):
            return False
        if self.program[self.ip] is None:
            self.tape[self.pointer] = not self.tape[self.pointer]
            self.pointer += 1
            self.ip += 1
        else:
            jump = self.program[self.ip] if self.tape[self.pointer] else 1
            self.pointer -= 1
            self.ip += jump
        return True

    def run(self):
        running = True
        while running:
            running = self.step()


def run_source(source):
    program = parse(source)
    machine = Machine(program)
    machine.run()
    return machine


def main():
    if len(sys.argv) != 2:
        print("You must pass a single source filename")
        return

    with open(sys.argv[1]) as f:
        source = f.read()

    machine = run_source(source)
    touched = len(machine.tape.left.cells) + len(machine.tape.right.cells)
    print(f"Program ended with machine altering at most {touched} cell(s)")
    print(f"Final tape pointer was {machine.pointer}")


if __name__ == "__main__":
    main()