spice/lib/runtime/interp.ml

135 lines
3.9 KiB
OCaml
Raw Normal View History

2023-11-29 21:50:26 +00:00
exception Runtime_error of string
2023-12-02 22:02:40 +00:00
let runtime_error f =
Fmt.kstr (fun s -> raise (Runtime_error s)) f
2023-12-02 20:31:29 +00:00
module Prim = struct
let intop name ( ** ) v1 v2 =
2023-11-29 21:50:26 +00:00
match v1, v2 with
| Value.Int x, Value.Int y -> Value.Int (x ** y)
| _, _ -> runtime_error "cannot %s non integer values" name
2023-11-29 21:50:26 +00:00
let cmpop ( </> ) v1 v2 =
2023-11-29 21:50:26 +00:00
match v1, v2 with
| Value.Int x, Value.Int y -> Value.of_bool (Int64.compare x y </> 0)
2023-12-02 20:31:29 +00:00
| _, _ -> runtime_error "cannot compare non integer values"
let add = intop "add" Int64.add
let sub = intop "sub" Int64.sub
let mul = intop "mul" Int64.mul
let lst = cmpop ( < )
let grt = cmpop ( > )
let eql v1 v2 = Value.of_bool (Value.equal v1 v2)
let not v = Value.of_bool (not (Value.truthy v))
let loc obj name =
try
match obj with
| Value.Obj (vtable, _) ->
Value.of_elem (Hashtbl.find vtable.elems name)
| _ ->
(* TODO: vtable of primitive types *)
raise Not_found
with
Not_found -> runtime_error "no such element %S" name
let get (obj, loc) =
match obj, Value.to_elem loc with
| Value.Obj (_, s), Field i -> s.(i)
| Value.Obj (_, _), Method _ -> failwith "Interp.Prim.get: TODO: fcf"
2023-12-02 20:31:29 +00:00
| exception Invalid_argument _ -> runtime_error "invalid index"
| _ -> runtime_error "get field of non-object"
let set (obj, loc) v =
match obj, Value.to_elem loc with
| Value.Obj (_, s), Field i -> s.(i) <- v
2023-12-02 20:31:29 +00:00
| 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, loc) =
match obj, Value.to_elem loc with
2023-12-02 20:31:29 +00:00
| Value.Obj (vtable, _), Method i -> obj, vtable.mthds.(i)
| Value.Obj (_, _), Field _ -> failwith "Interp.Prim.mthd: TODO: fcf"
2023-12-02 20:31:29 +00:00
| exception Invalid_argument _ -> runtime_error "invalid index"
| _ ->
(* TODO: vtable of primitive types *)
runtime_error "call method of non-object"
2023-11-29 21:50:26 +00:00
end
type frame = {
rg : Value.t array;
2023-11-29 21:50:26 +00:00
mutable pc : Code.ins list;
mutable rv : Value.t;
2023-11-29 21:50:26 +00:00
}
let jmp fr b = fr.pc <- Code.instructions b
let get fr (`R a) = fr.rg.(a)
let set fr (`R a) b = fr.rg.(a) <- b
let arg fr = function
| #Code.reg as r -> get fr r
| `Cst v -> v
let opr = function
| Code.ADD -> Prim.add
| Code.SUB -> Prim.sub
| Code.MUL -> Prim.mul
| Code.DIV -> failwith "Interp: TODO(DIV)"
| Code.MOD -> Prim.add
| Code.NOT -> fun _ v -> Prim.not v
| Code.NEG -> failwith "Interp: TODO(NEG)"
| Code.Cmp EQ -> Prim.eql
| Code.Cmp GT -> Prim.grt
| Code.Cmp LT -> Prim.lst
let loc fr = function
| a, (#Code.reg as b) -> get fr a, get fr b
| a, (`Ofs ofs) -> get fr a, Value.of_int ofs
let rec exec fr = function
| Code.Mov (a, b) -> set fr a (arg fr b)
| Code.Opr (op, a, b) -> set fr a (opr op (get fr a) (arg fr b))
| Code.Get (a, bc) -> set fr a (Prim.get (loc fr bc))
| Code.Set (bc, a) -> Prim.set (loc fr bc) (arg fr a)
| Code.Loc (a, b, nam) -> set fr a (Prim.loc (get fr b) nam)
| Code.Con (a, vtb) -> set fr a (Value.make_obj vtb)
| Code.Jmp bl -> jmp fr bl
| Code.Btr (a, bl1, bl2) ->
jmp fr (if Value.truthy (arg fr a) then bl1 else bl2)
| Code.Ret a ->
fr.rv <- arg fr a
| Code.Cal (a, f, args) ->
let obj, mthd = Prim.mthd (loc fr f) in
let args = List.map (arg fr) args in
set fr a (call mthd obj args)
2023-12-07 03:03:49 +00:00
and call mthd self args =
match mthd with
| Code.Function fn -> run fn self args
| _ -> Value.call mthd self args
2023-12-07 03:03:49 +00:00
and step fr =
match fr.pc with
| [] -> ()
| i :: rest ->
fr.pc <- rest;
exec fr i;
step fr
and run fn self args =
let Code.{ n_args; frame_size; entry } = fn in
if List.length args <> n_args then
runtime_error "wrong number of arguments, expected %d, got %d"
n_args (List.length args);
let rg = Array.make frame_size Value.Nil in
rg.(0) <- self;
List.iteri (fun i v -> rg.(i + 1) <- v) args;
let fr = { rg; pc = []; rv = Nil } in
jmp fr entry;
step fr;
fr.rv
2023-12-07 03:03:49 +00:00