173 lines
5.8 KiB
OCaml
173 lines
5.8 KiB
OCaml
module Env = Map.Make (String)
|
|
|
|
type binding =
|
|
| Var_arg of Ir.arg
|
|
| Var_ele of Ir.arg * Ir.name
|
|
| Var_fun of Ir.arg * Ir.name
|
|
|
|
let ( @@@ ) f g x = f (g x)
|
|
let fcf_mthd_name = "call"
|
|
|
|
let anf modl =
|
|
let next_uid = ref 0 in
|
|
let gen_id name =
|
|
let uid = !next_uid in
|
|
incr next_uid;
|
|
Ir.Id.{ name; uid }
|
|
in
|
|
|
|
let lift_rhs name rhs =
|
|
let tmp = gen_id name in
|
|
let ctx rest = Ir.Let (tmp, rhs, rest) in
|
|
Ir.Arg tmp, ctx
|
|
in
|
|
|
|
let rec lower_exp env = function
|
|
| Syn.Literal l -> lift_rhs "lit" (Ir.Literal l)
|
|
| Syn.Path p -> lower_path env p
|
|
| Syn.Call (fn, args) ->
|
|
let (ob_v, ob_ctx), mthd =
|
|
match fn with
|
|
| Syn.Ele (ob, mthd) -> lower_exp env ob, mthd
|
|
| Syn.Var f -> (
|
|
match Env.find f env with
|
|
| Var_fun (ob, mthd) -> (ob, Fun.id), mthd
|
|
| _ -> lower_path env fn, fcf_mthd_name)
|
|
in
|
|
let arg_vs_rev, ctx =
|
|
List.fold_left
|
|
(fun (vs, ctx) arg ->
|
|
let v, v_ctx = lower_exp env arg in
|
|
v :: vs, ctx @@@ v_ctx)
|
|
([], ob_ctx)
|
|
args
|
|
in
|
|
let fn_e = Ir.Ele (ob_v, mthd) in
|
|
let arg_vs = List.rev arg_vs_rev in
|
|
let v, call_ctx = lift_rhs mthd (Ir.Call (fn_e, arg_vs)) in
|
|
v, ctx @@@ call_ctx
|
|
| Syn.Binop (op, e1, e2) ->
|
|
let v1, v1_ctx = lower_exp env e1 in
|
|
let v2, v2_ctx = lower_exp env e2 in
|
|
let v, op_ctx = lift_rhs "op" (Ir.Binop (op, v1, v2)) in
|
|
v, v1_ctx @@@ v2_ctx @@@ op_ctx
|
|
| Syn.If (e1, e2, e3) ->
|
|
let v1, v1_ctx = lower_exp env e1 in
|
|
let v2, v2_ctx = lower_exp env e2 in
|
|
let v3, v3_ctx = lower_exp env e3 in
|
|
let jn = gen_id "jn" in
|
|
let rv = gen_id "rv" in
|
|
let ctx rest =
|
|
let cont = Ir.{ params = [ rv ]; body = rest } in
|
|
let e2 = v2_ctx (Ir.Jump (jn, [ v2 ])) in
|
|
let e3 = v3_ctx (Ir.Jump (jn, [ v3 ])) in
|
|
v1_ctx (Ir.Let (jn, Cont cont, If (v1, e2, e3)))
|
|
in
|
|
Ir.Arg rv, ctx
|
|
| Syn.Obj items -> lower_obj env items
|
|
(* | Syn.Fun (xs, e) -> () *)
|
|
(* | Syn.Scope items -> () *)
|
|
| _ -> failwith "..."
|
|
and lower_path env = function
|
|
| Syn.Var x -> (
|
|
match Env.find x env with
|
|
| Var_arg v -> v, Fun.id
|
|
| Var_ele (ob, el) -> lift_rhs x (Ir.Get (Ele (ob, el)))
|
|
| Var_fun (ob, el) ->
|
|
(* TODO: special treatment for known FCF's? *)
|
|
lift_rhs x (Ir.Get (Ele (ob, el)))
|
|
| exception Not_found -> failwith (Fmt.str "undefined variable %S" x))
|
|
| Syn.Ele (ob, el) ->
|
|
let ob_v, ob_ctx = lower_exp env ob in
|
|
let el_v, el_ctx = lift_rhs el (Ir.Get (Ele (ob_v, el))) in
|
|
el_v, ob_ctx @@@ el_ctx
|
|
and lower_obj env items =
|
|
(* TODO: detect duplicate item names *)
|
|
let ob = gen_id "ob" in
|
|
let ob_v = Ir.Arg ob in
|
|
(* TODO: get last expression in block *)
|
|
let env, slots, mthds, inits =
|
|
List.fold_left
|
|
(fun (env, slots, mthds, inits) -> function
|
|
| Syn.Item_exp e ->
|
|
let inits = `Exp e :: inits in
|
|
env, slots, mthds, inits
|
|
| Syn.Item_val (name, rhs) ->
|
|
let env = Env.add name (Var_ele (ob_v, name)) env in
|
|
let slots = name :: slots in
|
|
let inits = `Val (name, rhs) :: inits in
|
|
env, slots, mthds, inits
|
|
| Syn.Item_obj (name, body) ->
|
|
let env = Env.add name (Var_ele (ob_v, name)) env in
|
|
let slots = name :: slots in
|
|
let inits = `Obj (name, body) :: inits in
|
|
env, slots, mthds, inits
|
|
| Syn.Item_fun (name, param_names, body) ->
|
|
let env = Env.add name (Var_fun (ob_v, name)) env in
|
|
let mthds = (name, param_names, body) :: mthds in
|
|
env, slots, mthds, inits)
|
|
(env, [], [], [])
|
|
items
|
|
in
|
|
(* mthds was built in reverse order, so rev_map fixes the order *)
|
|
let mthds =
|
|
List.rev_map
|
|
(fun (name, param_names, body) ->
|
|
(* TODO: detect duplicate param names *)
|
|
let params = List.map gen_id param_names in
|
|
let env' =
|
|
List.fold_left2
|
|
(fun env name id -> Env.add name (Var_arg (Arg id)) env)
|
|
env
|
|
param_names
|
|
params
|
|
in
|
|
let body_v, body_ctx = lower_exp env' body in
|
|
let body = body_ctx (Ir.Ret body_v) in
|
|
Ir.{ name; defn = { params; body } })
|
|
mthds
|
|
in
|
|
(* inits was built in reverse order, so make sure to *prepend* contexts
|
|
* when building the final context *)
|
|
let init_ctx =
|
|
List.fold_left
|
|
(fun ctx' -> function
|
|
| `Exp e ->
|
|
let _, ctx = lower_exp env e in
|
|
ctx @@@ ctx'
|
|
| `Val (name, rhs) ->
|
|
let rhs_v, rhs_ctx = lower_exp env rhs in
|
|
let set_ctx rest = Ir.Set (Ele (ob_v, name), rhs_v, rest) in
|
|
rhs_ctx @@@ set_ctx @@@ ctx'
|
|
| `Obj (name, body) ->
|
|
let ob_v, ob_ctx = lower_obj env body in
|
|
let set_ctx rest = Ir.Set (Ele (ob_v, name), ob_v, rest) in
|
|
ob_ctx @@@ set_ctx @@@ ctx')
|
|
Fun.id
|
|
inits
|
|
in
|
|
let slots = List.rev slots in
|
|
let ob_ctx rest = Ir.Let (ob, Obj { mthds; slots }, rest) in
|
|
ob_v, ob_ctx @@@ init_ctx
|
|
in
|
|
|
|
let std = gen_id "std" in
|
|
let std_env = Env.empty in
|
|
let std_env =
|
|
List.fold_left
|
|
(fun env fn -> Env.add fn (Var_fun (Arg std, fn)) env)
|
|
std_env
|
|
[ "read"; "print"; "itoa" ]
|
|
in
|
|
let std_env =
|
|
List.fold_left
|
|
(fun env var -> Env.add var (Var_ele (Arg std, var)) env)
|
|
std_env
|
|
[ "fs"; "rand" ]
|
|
in
|
|
|
|
let ret_v, ctx = lower_obj std_env modl.Syn.items in
|
|
let params = [ std ] in
|
|
let body = ctx (Ir.Ret ret_v) in
|
|
Ir.{ params; body }
|