#!/usr/bin/env python """Horrlang Interpreter.""" import sys import io from collections import defaultdict class Machine: def __init__(self, code, read=sys.stdin.read, write=sys.stdout.write, max_cycles=None): self.read = read self.write = write self.mem = defaultdict(int) self.mem.update({i: ord(char) for i, char in enumerate(code)}) self.exc_ptr = 0 self.mem_ptr = 0 self.max_cycles = max_cycles self.cycles = 0 def cycle(self): self.cycles += 1 if self.max_cycles is not None: if self.cycles >= self.max_cycles: return False return True def get_integer(self): parameter = 0 while self.mem[self.exc_ptr + 1] >= 0x30 and self.mem[self.exc_ptr + 1] <= 0x39: self.exc_ptr += 1 if not self.cycle(): return parameter parameter = parameter * 10 + (self.mem[self.exc_ptr] - ord("0")) if parameter == 0: parameter = 1 return parameter def L(self): """Move memory pointer to the left.""" self.mem_ptr -= self.get_integer() if self.mem_ptr < 0: self.mem_ptr = 0 def R(self): """Move memory pointer to the right.""" self.mem_ptr += self.get_integer() def O(self): """Output.""" char = self.mem[self.mem_ptr] if 0 < char < 256: self.write(chr(char)) def I(self): """Input.""" self.mem[self.mem_ptr] = ord(self.read(1)) def N(self): """Forward jump.""" self.exc_ptr += 1 search = self.mem[self.exc_ptr] while self.mem[self.exc_ptr] != search: self.exc_ptr += 1 if not self.cycle(): return def P(self): """Backward jump.""" search = self.mem[self.exc_ptr + 1] self.exc_ptr -= 1 while self.mem[self.exc_ptr] != search: self.exc_ptr -= 1 if not self.cycle(): return def J(self): """Conditional jump.""" if self.mem[self.mem_ptr] != ord("0"): search = self.mem[self.exc_ptr + 1] self.exc_ptr -= 1 while self.mem[self.exc_ptr] != search: self.exc_ptr -= 1 if not self.cycle(): return def H(self): """Increment.""" self.mem[self.mem_ptr] = self.mem[self.mem_ptr] + 1 def K(self): """Decrement.""" self.mem[self.mem_ptr] = self.mem[self.mem_ptr] - 1 def _debug(self): print(f"# Cycle {self.cycles}\n") for i, value in sorted(self.mem.items()): marker = "" if i == self.exc_ptr: marker += "← exc ptr" control = chr(self.mem[self.exc_ptr]) if hasattr(self, control): marker += " (" + getattr(self, control).__doc__ + ")" if i == self.mem_ptr: marker += "← mem ptr" printable = "" if chr(value).isprintable(): printable = f"{chr(value)}" print(f"{i:3d} | {value:5d} | {printable:5s}", marker) print() def run(self, debug=False): while True: if self.mem[self.exc_ptr] == 0: return char = self.mem[self.exc_ptr] if 0 < char < 256: control = chr(char) if debug: self._debug() if hasattr(self, control): getattr(self, control)() self.exc_ptr += 1 if not self.cycle(): return def eval(instructions, input="", max_cycles=None): """Convenience, non-interactive wrapper around HorrlangMachine.""" stdin = io.StringIO(input) stdout = io.StringIO() machine = Machine(instructions, stdin.read, stdout.write, max_cycles=max_cycles) machine.run() return stdout.getvalue() import argparse def parse_args(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("code", type=str) parser.add_argument("--debug", action="store_true") parser.add_argument("--max-cycles", type=int, default=None) return parser.parse_args() def main(): args = parse_args() Machine(args.code, max_cycles=args.max_cycles,).run(debug=args.debug) if __name__ == '__main__': main()