This is very basic interpreter for the Human Resource Machine in-game instructions.
This code is far from complete and polished. Certainly there will be bugs as well.
If time permits in the future, I will refactor the code cleanly.
import re
import argparse
import os.path
verbose = False
def init_vm(memspace, consts):
print_log("init vm...")
print_log("memspace: %d" % memspace)
mem = [None] * memspace
index = memspace - 1
while consts:
const = consts.pop(0)
mem[index] = const
index = index - 1
print_log("mem: %s" % mem)
print_log("init done...")
return mem
def init_labels(commands):
labels = {}
p = re.compile("(?P<label>\w+):")
for index,cmd in enumerate(commands):
m = p.match(cmd)
if m:
lbl = m.group("label")
labels[lbl] = index
print_log("labels: %s" % labels)
print_log("init labels done...")
return labels
def run_commands(mem, commands, inputs):
print_log("start running commands...")
labels = init_labels(commands)
outputs = interpret(mem, labels, commands, inputs)
print_log("end running commands...")
return outputs
def interpret(mem, labels, commands, inputs):
outputs = []
steps = 0
print_log("interpreting...")
p = re.compile("\[(?P<addr>\w+)\]")
sub = re.compile("(\s)*SUB(\s)+\[(?P<addr>\w+)\]|(\s)*SUB(\s)+((?P<index>\w+))")
add = re.compile("(\s)*ADD(\s)+\[(?P<addr>\w+)\]|(\s)*ADD(\s)+((?P<index>\w+))")
jpz = re.compile("(\s)*JUMPZ(\s)+(?P<label>\w+)")
jpn = re.compile("(\s)*JUMPN(\s)+(?P<label>\w+)")
jmp = re.compile("(\s)*JUMP(\s)+(?P<label>\w+)")
bup = re.compile("(\s)*BUMPUP(\s)+\[(?P<addr>\w+)\]|(\s)*BUMPUP(\s)+(?P<index>\w+)")
bdn = re.compile("(\s)*BUMPDN(\s)+\[(?P<addr>\w+)\]|(\s)*BUMPDN(\s)+(?P<index>\w+)")
cpt = re.compile("(\s)*COPYTO(\s)+\[(?P<addr>\w+)\]|(\s)*COPYTO(\s)+(?P<index>\w+)")
cpf = re.compile("(\s)*COPYFROM(\s)+\[(?P<addr>\w+)\]|(\s)*COPYFROM(\s)+(?P<index>\w+)")
ptr = 0
x = None
while True:
print_log("mem: %s" % mem)
cmd = commands[ptr]
print_log("interpreting command: %s" % cmd)
if "INBOX" in cmd:
if not inputs:
break
x = inputs.pop(0)
elif "OUTBOX" in cmd:
outputs.append(x)
x = None
elif "ADD" in cmd:
m = add.match(cmd)
if m.group("addr"):
i = get_addr(m)
addr = get_val_from_mem(mem, i)
x = add_raw(x, get_val_from_mem(mem, addr))
else:
i = get_index(m)
x = add_raw(x, get_val_from_mem(mem, i))
elif "SUB" in cmd:
m = sub.match(cmd)
if m.group("addr"):
i = get_addr(m)
addr = get_val_from_mem(mem, i)
x = sub_raw(x, get_val_from_mem(mem, addr))
else:
i = get_index(m)
x = sub_raw(x, get_val_from_mem(mem, i))
elif "JUMPZ" in cmd:
if cmp_raw(x, "eq", 0):
m = jpz.match(cmd)
lbl = m.group("label")
ptr = labels[lbl]
elif "JUMPN" in cmd:
if cmp_raw(x, "lt", 0):
m = jpn.match(cmd)
lbl = m.group("label")
ptr = labels[lbl]
elif "JUMP" in cmd:
m = jmp.match(cmd)
lbl = m.group("label")
ptr = labels[lbl]
elif "BUMPUP" in cmd:
m = bup.match(cmd)
if m.group("addr"):
i = get_addr(m)
addr = get_val_from_mem(mem, i)
mem[addr] = add_raw(get_val_from_mem(mem, addr), 1)
x = get_val_from_mem(mem, addr)
else:
i = get_index(m)
mem[i] = add_raw(get_val_from_mem(mem, i), 1)
x = get_val_from_mem(mem, i)
elif "BUMPDN" in cmd:
m = bdn.match(cmd)
if m.group("addr"):
i = get_addr(m)
addr = get_val_from_mem(mem, i)
mem[addr] = sub_raw(get_val_from_mem(mem, addr), 1)
x = get_val_from_mem(mem, addr)
else:
i = get_index(m)
mem[i] = sub_raw(get_val_from_mem(mem, i), 1)
x = get_val_from_mem(mem, i)
elif "COPYTO" in cmd:
m = cpt.match(cmd)
if m.group("addr"):
i = get_addr(m)
addr = get_val_from_mem(mem, i)
mem[addr] = x
else:
i = get_index(m)
mem[i] = x
elif "COPYFROM" in cmd:
m = cpf.match(cmd)
if m.group("addr"):
i = get_addr(m)
addr = get_val_from_mem(mem, i)
x = get_val_from_mem(mem, addr)
else:
i = get_index(m)
x = get_val_from_mem(mem, i)
elif "END" in cmd:
break
ptr = ptr + 1
steps = steps + 1
print_log("interpreted in %d steps with %d commands" % (steps, len(commands)))
return outputs
def add_raw(left, right):
if type(left) != type(right):
print_error("Unable to add values of different types %s and %s" % (type(left), type(right)))
exit(1)
try:
return int(left) + int(right)
except ValueError:
print_error("Unable to add characters together")
exit(1)
def sub_raw(left, right):
if type(left) != type(right):
print_error("Unable to sub values of different types %s and %s" % (type(left), type(right)))
exit(1)
try:
return int(left) - int(right)
except ValueError:
return ord(left) - ord(right)
def cmp_raw(left, op, right):
try:
left = int(left)
except ValueError:
left = ord(left)
try:
right = int(right)
except ValueError:
right = ord(right)
return compare(left, op, right)
def compare(left, op, right):
if op == "eq":
return left == right
elif op == "ne":
return left != right
elif op == "lt":
return left < right
elif op == "gt":
return left > right
elif op == "lte":
return left <= right
elif op == "gte":
return left >= right
print_error("Invalid operation %s" % op)
exit(1)
def get_val_from_mem(mem, i):
val = mem[i]
return val
def get_index(m):
return int(m.group("index"))
def get_addr(m):
return int(m.group("addr"))
def get_raw_val(val):
if val is not None :
try:
return int(val)
except ValueError:
return val
return None
def read_commands(file_name):
commands = []
with open(file_name, "r") as f:
commands = f.readlines()
f.close()
commands = [cmd.replace("\t", " ") for cmd in commands]
return commands
def read_inputs(file_name):
line = ""
with open(file_name, "r") as f:
line = f.readline()
f.close()
inputs = [get_raw_val(val) for val in line.strip().split(" ")]
return inputs
def read_init(file_name):
lines = []
memspace = None
constants = []
with open(file_name, "r") as f:
lines = f.readlines()
f.close()
memspace = int(lines[0].strip())
memspace = get_raw_val(lines[0].strip())
if len(lines) == 2:
constants = [get_raw_val(val) for val in lines[1].strip().split(" ")]
return memspace, constants
def print_log(line):
if verbose:
print(line)
def print_error(line):
print("Error: %s" % line)
def check_file(filename):
if not os.path.isfile(filename):
print_error("%s not found" % filename)
exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="HRM Instructions Interpreter")
parser.add_argument("--init", help="file path to init VM data", default="init_vm.txt", required=True, dest="init_filename")
parser.add_argument("--cmd", help="file path to commands data", default="commands.txt", required=True, dest="cmd_filename")
parser.add_argument("--input", help="file path to input data", default="inputs.txt", required=True, dest="in_filename")
parser.add_argument("--verbose", "-v", help="print log", action="count")
args = parser.parse_args()
init_filename = args.init_filename
check_file(init_filename)
cmd_filename = args.cmd_filename
check_file(cmd_filename)
in_filename = args.in_filename
check_file(in_filename)
verbose = True if args.verbose else False
memspace, constants = read_init(init_filename)
mem = init_vm(memspace, constants)
commands = read_commands(cmd_filename)
inputs = read_inputs(in_filename)
print("inputs: %s" % inputs)
outputs = run_commands(mem, commands, inputs)
print("outputs: %s" % outputs)