108 lines
3.5 KiB
Python
108 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
import sys
|
|
import os
|
|
import pickle
|
|
|
|
# These opcode definitions must match those in compiler.py.
|
|
PUSH_INT = 0
|
|
PUSH_FLOAT = 1
|
|
LOAD_LOCAL = 2
|
|
STORE_LOCAL = 3
|
|
ADD_OP = 4
|
|
SUB_OP = 5
|
|
MUL_OP = 6
|
|
DIV_OP = 7
|
|
EQ_OP = 8
|
|
JMP_IF_FALSE= 9
|
|
JMP = 10
|
|
CALL_OP = 11
|
|
RET_OP = 12
|
|
LOAD_FIELD = 13
|
|
MAKE_STRUCT = 14
|
|
|
|
class Runtime:
|
|
def __init__(self, func_table):
|
|
# Preserve insertion order by converting to a list.
|
|
self.func_table = list(func_table.values())
|
|
|
|
def run_function(self, func_index, args):
|
|
func = self.func_table[func_index]
|
|
locals_ = [0] * func['n_locals']
|
|
for i, arg in enumerate(args):
|
|
locals_[i] = arg
|
|
stack = []
|
|
ip = 0
|
|
code = func['code']
|
|
while ip < len(code):
|
|
instr = code[ip]
|
|
op = instr[0]
|
|
if op == PUSH_INT:
|
|
stack.append(instr[1])
|
|
elif op == PUSH_FLOAT:
|
|
stack.append(instr[1])
|
|
elif op == LOAD_LOCAL:
|
|
stack.append(locals_[instr[1]])
|
|
elif op == STORE_LOCAL:
|
|
locals_[instr[1]] = stack.pop()
|
|
elif op == ADD_OP:
|
|
b = stack.pop(); a = stack.pop()
|
|
stack.append(a + b)
|
|
elif op == SUB_OP:
|
|
b = stack.pop(); a = stack.pop()
|
|
stack.append(a - b)
|
|
elif op == MUL_OP:
|
|
b = stack.pop(); a = stack.pop()
|
|
stack.append(a * b)
|
|
elif op == DIV_OP:
|
|
b = stack.pop(); a = stack.pop()
|
|
stack.append(a // b)
|
|
elif op == EQ_OP:
|
|
b = stack.pop(); a = stack.pop()
|
|
stack.append(1 if a == b else 0)
|
|
elif op == JMP_IF_FALSE:
|
|
offset = instr[1]
|
|
cond = stack.pop()
|
|
if cond == 0:
|
|
ip += offset
|
|
continue
|
|
elif op == JMP:
|
|
ip += instr[1]
|
|
continue
|
|
elif op == CALL_OP:
|
|
callee_index = instr[1]
|
|
num_args = instr[2]
|
|
call_args = [stack.pop() for _ in range(num_args)][::-1]
|
|
ret_val = self.run_function(callee_index, call_args)
|
|
stack.append(ret_val)
|
|
elif op == RET_OP:
|
|
return stack.pop() if stack else 0
|
|
elif op == LOAD_FIELD:
|
|
struct_val = stack.pop()
|
|
field_index = instr[1]
|
|
stack.append(struct_val[field_index])
|
|
elif op == MAKE_STRUCT:
|
|
n = instr[1]
|
|
fields = [stack.pop() for _ in range(n)][::-1]
|
|
stack.append(tuple(fields))
|
|
else:
|
|
raise RuntimeError("Unknown opcode: " + str(op))
|
|
ip += 1
|
|
return 0
|
|
|
|
if __name__ == '__main__':
|
|
bytecode_file = "program.bc"
|
|
if not os.path.exists(bytecode_file):
|
|
sys.exit("Error: Bytecode file not found. Please compile the source first.")
|
|
with open(bytecode_file, "rb") as f:
|
|
func_table = pickle.load(f)
|
|
main_index = None
|
|
for idx, (fname, fobj) in enumerate(func_table.items()):
|
|
if fname == 'main':
|
|
main_index = idx
|
|
break
|
|
if main_index is None:
|
|
sys.exit("Error: main function not found in bytecode.")
|
|
runtime = Runtime(func_table)
|
|
result = runtime.run_function(main_index, [0, 0])
|
|
print("Program finished with result:", result)
|