Intcode/Interpreters

From Esolang
Jump to navigation Jump to search

This page is for interpreters of Intcode that don't have their own website or external repository.

Python

arities = {
    1: 4, #Take x, y, z write x+y to pos z
    2: 4, #" " x*y to pos z
    3: 2, #Write input to pos x
    4: 2, #Print item at pos x
    5: 3, #If pos x == 0: jump to pos y
    6: 3, #If pos x != 0: jump to pos y
    7: 4, #Write x < y to pos z
    8: 4, #Write x == y to pos z
    9: 2, #Change relative position
    99:0 #End of execution
} #Default arity is 1

functions = {
    1: lambda stack, lhs, rhs, location, modes: stack.set_item(location,
    stack.get_data(lhs, modes.pop()) + stack.get_data(rhs, modes.pop())),

    2: lambda stack, lhs, rhs, location, modes: stack.set_item(location,
    stack.get_data(lhs, modes.pop()) * stack.get_data(rhs, modes.pop())),

    3: lambda stack, location, modes: stack.set_item(location, int(input())),

    4: lambda stack, location, modes: print(stack.get_data(location,
                                                           modes.pop())),

}


def extract_command(integer):
    command = str(integer)
    if len(command) <= 2:
        return (integer, Stack())
    else:
        op = command[-2:]
        modes = command[:-2]
        return (int(op), Stack([int(char) for char in modes]))
 
 
class Stack:
    def __init__(self, iterable=[]):
        if iterable:
            self.stack = iterable
        else:
            self.stack = []

        self.rel_base = 0
        self.ip = 0

    def __len__(self):
        return len(self.stack)

    def __setitem__(self, index, value):
        if type(index) == slice:
            idx = index.stop + 1
        else:
            idx = index + 1
        if idx > len(self.stack):
            for _ in range(idx - len(self.stack)):
                self.stack.append(0)
        self.stack[index] = value

    def __getitem__(self, index):
        if type(index) == slice:
            idx = index.stop + 1
        else:
            idx = index + 1
        if idx >= len(self.stack):
            for _ in range(idx - len(self.stack)):
                self.stack.append(0)
        return self.stack[index]

    def __delitem__(self, what):
        del self.stack[what]

    def get_data(self, pos, mode):
        if mode == 0: #Position mode:
            return self.__getitem__(pos)
        elif mode == 1: #Immediate
            return pos
        elif mode == 2:
            x = self.rel_base + pos
            return self.__getitem__(x)

    def opcode_9(self, value):
        self.rel_base += value

    def pop(self):
        if len(self.stack):
            return self.stack.pop()
        else:
            return 0

    def set_item(self, location, value):
        self.__setitem__(location, value)

def main(program):
    prog = Stack([int(tkn) for tkn in program.split(",")])
    ip = 0

    while ip < len(prog):
        opcode, modes = extract_command(prog[ip])
        arity = arities[opcode]

        if opcode in [1, 2]:
            command = functions[opcode]
            lhs = prog[ip + 1]
            rhs = prog[ip + 2]
            location = prog[ip + 3]
            
            if len(modes) == 3 and modes[0] == 2:
                location += prog.rel_base

            command(prog, lhs, rhs, location, modes)
            ip += arity

        elif opcode == 3:
            command = functions[opcode]
            if len(modes) and modes[0] == 2:
                command(prog, prog[ip + 1] + prog.rel_base, modes)
            else:
                command(prog, *prog[ip+1 : ip + arity], modes)
            ip += arity
        elif opcode == 4:
            command = functions[opcode]
            command(prog, *prog[ip+1 : ip + arity], modes)
            ip += arity

        elif opcode == 5:
            condition = prog.get_data(prog[ip + 1], modes.pop())
            if condition:
                jump_position = prog.get_data(prog[ip + 2], modes.pop())
                ip = jump_position
            else:
                ip += arity

        elif opcode == 6:
            condition = prog.get_data(prog[ip + 1], modes.pop())
            if not condition:
                jump_position = prog.get_data(prog[ip + 2], modes.pop())
                ip = jump_position
            else:
                ip += arity

        elif opcode == 7:
            lhs = prog.get_data(prog[ip + 1], modes.pop())
            rhs = prog.get_data(prog[ip + 2], modes.pop())
            location = prog[ip + 3]

            if modes.pop() == 2:
                location += prog.rel_base

            if lhs < rhs:
                prog[location] = 1
            else:
                prog[location] = 0

            ip += arity

        elif opcode == 8:
            lhs = prog.get_data(prog[ip + 1], modes.pop())
            rhs = prog.get_data(prog[ip + 2], modes.pop())
            location = prog[ip + 3]

            if modes.pop() == 2:
                location += prog.rel_base

            if lhs == rhs:
                prog[location] = 1
            else:
                prog[location] = 0

            ip += arity

        elif opcode == 9:
            value = prog.get_data(prog[ip + 1], modes.pop())
            prog.opcode_9(value)
            ip += arity

        elif opcode == 99:
            break

    return "Final value: ", prog.stack

if __name__ == "__main__":
    program = input()
    print(main(program))