exception Runtime_error of string module Op = struct let add v1 v2 = match v1, v2 with | Value.Int x, Value.Int y -> Value.Int (Int64.add x y) | _, _ -> raise (Runtime_error "cannot add non integer values") let sub v1 v2 = match v1, v2 with | Value.Int x, Value.Int y -> Value.Int (Int64.sub x y) | _, _ -> raise (Runtime_error "cannot sub non integer values") let mul v1 v2 = match v1, v2 with | Value.Int x, Value.Int y -> Value.Int (Int64.mul x y) | _, _ -> raise (Runtime_error "cannot mul non integer values") let eql v1 v2 = match v1, v2 with | Value.Int x, Value.Int y -> Value.bool (Int64.equal x y) | _, _ -> Value.bool (v1 == v2) let lst v1 v2 = match v1, v2 with | Value.Int x, Value.Int y -> Value.bool (Int64.compare x y < 0) | _, _ -> raise (Runtime_error "cannot compare non integer values") let truthy = function | Value.False | Value.Nil -> false | _ -> true let not v = Value.bool (not (truthy v)) let get o s = match o, s with | Value.Obj (_, slots), Value.Int i -> slots.(Int64.to_int i) | _ -> raise (Runtime_error "get field of non-object") let set o s v = match o, s with | Value.Obj (_, slots), Value.Int i -> slots.(Int64.to_int i) <- v | _ -> raise (Runtime_error "set field of non-object") end type frame = { regs : Value.t array; mutable pc : Code.ins list; } let make_frame prog = { regs = Array.make (Code.frame_size prog) Value.Nil; pc = Code.instructions Code.(prog.entrypoint); } let return_value fr = fr.regs.(0) let eval fr = function | Code.Cst_nil -> Value.Nil | Code.Cst_true -> Value.Nil | Code.Cst_false -> Value.Nil | Code.Cst_int n -> Value.Int n | Code.Reg i -> fr.regs.(i) let exec fr = function | Code.MOV (l, r) -> fr.regs.(l) <- eval fr r | Code.ADD (l, r) -> fr.regs.(l) <- Op.add fr.regs.(l) (eval fr r) | Code.SUB (l, r) -> fr.regs.(l) <- Op.add fr.regs.(l) (eval fr r) | Code.MUL (l, r) -> fr.regs.(l) <- Op.mul fr.regs.(l) (eval fr r) | Code.EQL (l, r) -> fr.regs.(l) <- Op.eql fr.regs.(l) (eval fr r) | Code.LST (l, r) -> fr.regs.(l) <- Op.lst fr.regs.(l) (eval fr r) | Code.NOT l -> fr.regs.(l) <- Op.not fr.regs.(l) | Code.CON (l, vt) -> fr.regs.(l) <- Value.make_obj vt | Code.GET (o, s) -> fr.regs.(s) <- Op.get fr.regs.(o) fr.regs.(s) | Code.SET (o, s) -> Op.set fr.regs.(o) fr.regs.(s) fr.regs.(s + 1) | Code.RET -> fr.pc <- [] | Code.JMP l -> fr.pc <- Code.instructions l | Code.CBR (v, l1, l2) -> fr.pc <- Code.instructions (if Op.truthy (eval fr v) then l1 else l2) let rec run fr = match fr.pc with | [] -> return_value fr | is :: rest -> fr.pc <- rest; exec fr is; run fr let run_program pr = run (make_frame pr)