173 lines
5.2 KiB
OCaml
173 lines
5.2 KiB
OCaml
exception Runtime_error of string
|
|
|
|
let runtime_error f =
|
|
Fmt.kstr (fun s -> raise (Runtime_error s)) f
|
|
|
|
module Op = struct
|
|
let add v1 v2 =
|
|
match v1, v2 with
|
|
| Value.Int x, Value.Int y -> Value.Int (Int64.add x y)
|
|
| _, _ -> 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)
|
|
| _, _ -> 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)
|
|
| _, _ -> 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)
|
|
| _, _ -> runtime_error "cannot compare non integer values"
|
|
|
|
let grt v1 v2 =
|
|
match v1, v2 with
|
|
| Value.Int x, Value.Int y -> Value.bool (Int64.compare x y > 0)
|
|
| _, _ -> runtime_error "cannot compare non integer values"
|
|
|
|
let is_truthy = function
|
|
| Value.False | Value.Nil -> false
|
|
| _ -> true
|
|
|
|
let not v = Value.bool (not (is_truthy v))
|
|
|
|
let slt obj name =
|
|
match obj with
|
|
| Value.Obj (vtable, _) ->
|
|
begin
|
|
try Value.of_elem (Hashtbl.find vtable.elems name)
|
|
with Not_found -> runtime_error "no such element %S" name
|
|
end
|
|
| _ -> runtime_error "get element of non-object"
|
|
|
|
let get obj el =
|
|
match obj, Value.to_elem el with
|
|
| Value.Obj (_, slots), Field i -> slots.(i)
|
|
| Value.Obj (_, _), Method _ -> failwith "Interp.Op.get: TODO: method reification"
|
|
| exception Invalid_argument _ -> runtime_error "invalid index"
|
|
| _ -> runtime_error "get field of non-object"
|
|
|
|
let set obj el v =
|
|
match obj, Value.to_elem el with
|
|
| Value.Obj (_, slots), Field i -> slots.(i) <- v
|
|
| Value.Obj (_, _), Method _ -> runtime_error "cannot reassign method"
|
|
| exception Invalid_argument _ -> runtime_error "invalid index"
|
|
| _ -> runtime_error "set field of non-object"
|
|
|
|
let mthd obj el =
|
|
match obj, Value.to_elem el with
|
|
| Value.Obj (vtable, _), Method i -> obj, vtable.mthds.(i)
|
|
| Value.Obj (_, _), Field _ -> failwith "Interp.Op.get: TODO: fcf calls"
|
|
| exception Invalid_argument _ -> runtime_error "invalid index"
|
|
| _ ->
|
|
(* TODO: create vtable from primitive types *)
|
|
runtime_error "call field of non-object"
|
|
end
|
|
|
|
type frame = {
|
|
regs : Value.t array;
|
|
mutable pc : Code.ins list;
|
|
}
|
|
|
|
let eval fr = function
|
|
| Code.Cst_nil -> Value.Nil
|
|
| Code.Cst_true -> Value.True
|
|
| Code.Cst_false -> Value.False
|
|
| Code.Cst_int n -> Value.Int n
|
|
| Code.Reg i -> fr.regs.(i)
|
|
|
|
let rec 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.GRT (l, r) -> fr.regs.(l) <- Op.grt 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.SLT (o, e, nm) -> fr.regs.(e) <- Op.slt fr.regs.(o) nm
|
|
| Code.GET (o, e) -> fr.regs.(e) <- Op.get fr.regs.(o) fr.regs.(e)
|
|
| Code.SET (o, e) -> Op.set fr.regs.(o) fr.regs.(e) fr.regs.(e + 1)
|
|
| Code.RET -> fr.pc <- []
|
|
| Code.CLL (o, m, k) ->
|
|
let self, mthd = Op.mthd fr.regs.(o) fr.regs.(m) in
|
|
let args = List.init k (fun i -> fr.regs.(m + i + 1)) in
|
|
fr.regs.(m) <- call mthd self args
|
|
| Code.JMP l -> fr.pc <- Code.instructions l
|
|
| Code.CBR (v, l1, l2) ->
|
|
fr.pc <- Code.instructions (if Op.is_truthy (eval fr v) then l1 else l2)
|
|
|
|
and call mthd self args =
|
|
match mthd with
|
|
| Code.Method pr ->
|
|
if args <> [] then failwith "Interp.call: TODO: method arguments";
|
|
run pr self
|
|
| _ -> Value.call mthd self args
|
|
|
|
and run prog self =
|
|
let frame_size = 1 in
|
|
let frame_size = max frame_size (Code.frame_size prog) in
|
|
let fr = {
|
|
regs = Array.make frame_size Value.Nil;
|
|
pc = Code.instructions prog.entrypoint;
|
|
} in
|
|
let rec run_loop () =
|
|
match fr.pc with
|
|
| [] -> ()
|
|
| ins :: rest ->
|
|
fr.pc <- rest;
|
|
exec fr ins;
|
|
run_loop ()
|
|
in
|
|
fr.regs.(0) <- self;
|
|
run_loop ();
|
|
fr.regs.(0)
|
|
|
|
let stdlib =
|
|
let println vs =
|
|
let pp ppf vs =
|
|
List.iteri
|
|
(fun i v ->
|
|
if i > 0 then Fmt.pf ppf " ";
|
|
Value.pp ppf v)
|
|
vs
|
|
in
|
|
Fmt.pr "%a\n" pp vs;
|
|
Value.Nil
|
|
in
|
|
let min = function
|
|
| [] -> runtime_error "zero arguments to min()"
|
|
| [ v ] -> v
|
|
| v :: vs ->
|
|
List.fold_left
|
|
(fun v1 v2 ->
|
|
match Op.lst v1 v2 with
|
|
| Value.True -> v1
|
|
| _ -> v2)
|
|
v
|
|
vs
|
|
in
|
|
let max = function
|
|
| [] -> runtime_error "zero arguments to max()"
|
|
| [ v ] -> v
|
|
| v :: vs ->
|
|
List.fold_left
|
|
(fun v1 v2 ->
|
|
match Op.grt v1 v2 with
|
|
| Value.True -> v1
|
|
| _ -> v2)
|
|
v
|
|
vs
|
|
in
|
|
[ "println", println; "min", min; "max", max ]
|