|
|
- open import "prelude.ml"
- module Src = import "../lang.ml"
- open Src
-
- module Map = import "data/map.ml"
- module Set = import "data/set.ml"
- module Stg = import "./stg.ml"
-
- let spine f =
- let rec spine = function
- | App (f, x) ->
- let (f, args) = spine f
- (f, x :: args)
- | e -> (e, [])
- let (f, args) = spine f
- (f, reverse args)
-
- let rec napp f = function
- | [] -> f
- | Cons (x, xs) -> napp (App (f, x)) xs
-
- let get_con_arities prog =
- let go_con m (Constr (name, tys)) = M.insert name (length tys) m
- let go m = function
- | Data (_, _, cons) -> foldl go_con m cons
- | _ -> m
- foldl go M.empty prog
-
- let gensym =
- let counter = ref 0
- fun () ->
- counter := !counter + 1
- "_s" ^ show !counter
-
- let rec add_n_args da exp =
- if da <= 0 then
- exp
- else
- let var = gensym ()
- Lam (var, App (add_n_args (da - 1) exp, Ref var))
-
- let rec eta_expand_cons arities =
- let rec go = function
- | Case (exp, alts) -> Case (go exp, map (second (second go)) alts)
- | Lam (var, alts) -> Lam (var, go alts)
- | If (a, b, c) -> If (go a, go b, go c)
- | Let (decs, body) -> Let (map (second go) decs, go body)
- | exp ->
- match spine exp with
- | Ref func, args ->
- let arg_len = length args
- match Map.lookup func arities with
- | Some arity when arity > arg_len -> add_n_args (arity - arg_len) (napp (Ref func) args)
- | _ -> exp
- | _, _ -> error @@ "What?"
- go
-
- let build_stg_app func = function
- | [] -> Stg.Atom func
- | args -> Stg.(App (func, args))
-
- let mk_lambda_form name exp =
- let free_vars = Stg.stg_fv exp
- { name, free_vars, update = Stg.Updatable, args = [], body = exp }
-
- let mk_function name args exp =
- let free_vars = foldl (flip Set.delete) (Stg.stg_fv exp) args
- { name, free_vars, update = Stg.Function, args, body = exp }
-
- let rec unlambda = function
- | Lam (var, body) ->
- let (args, body) = unlambda body
- (var :: args, body)
- | e -> ([], e)
-
- let rec lower_spine (func, args) kont =
- lower_atom func @@ fun func ->
- let rec go kont lowered = function
- | [] -> kont (build_stg_app func (reverse lowered))
- | Cons (Ref e, args) ->
- go kont (Stg.Var e :: lowered) args
- | Cons (Lit i, args) ->
- go kont (Stg.Int i :: lowered) args
- | Cons (arg, args) ->
- lower_atom arg @@ fun arg ->
- go kont (arg :: lowered) args
- go kont [] args
- and lower exp kont =
- match spine exp with
- | exp, [] ->
- match exp with
- | App _ -> error @@ "Impossible lower App with empty spine"
-
- (* STG atoms *)
- | Ref e -> kont Stg.(Atom (Var e))
- | Lit e -> kont Stg.(Con (0, 1, [Int e]))
-
- (* Lambdas need to be bound as lambda-forms *)
- | Lam _ as lam ->
- let name = gensym ()
- let (args, body) = unlambda lam
- let body = lower_body body
- Stg.Let ([mk_function name args body], kont Stg.(Atom (Var name)))
-
- | If (cond, th, el) ->
- lower cond @@ fun cond ->
- lower th @@ fun th ->
- lower el @@ fun el ->
- Stg.( Case (cond, "binder" ^ gensym(), [(Con_pat (0, []), th), (Default, el)]) )
- |> kont
-
- | Let (bindings, body) ->
- lower_binds bindings @@ fun lambda_forms ->
- Stg.Let (lambda_forms, lower body kont)
-
- | Case (scrut, arms) ->
- lower scrut @@ fun scrut ->
- lower_arms arms @@ fun arms ->
- Stg.Case (scrut, "binder" ^ gensym(), arms) |> kont
- | e -> lower_spine e kont
-
- and lower_atom exp kont =
- lower exp @@ function
- | Stg.Atom at -> kont at
- | e ->
- let name = gensym ()
- Stg.(Let ([mk_lambda_form name e], kont (Var name)))
-
- and lower_binds bindings kont =
- let rec go acc = function
- | [] -> kont (reverse acc)
- | Cons ((name, bind), rest) ->
- go (lower_rhs name bind :: acc) rest
- go [] bindings
-
- and lower_arms arms kont =
- let rec go i acc = function
- | [] -> kont (reverse acc)
- | Cons ((_, args, exp), rest) ->
- let body = lower_body exp
- go (i + 1) ((Stg.(Con_pat (i, args)), body) :: acc) rest
- go 0 [] arms
-
- and lower_rhs name exp =
- match exp with
- | Lam _ as lam ->
- let (args, body) = unlambda lam
- let body = lower_body body
- mk_function name args body
- | _ ->
- let body = lower_body exp
- mk_lambda_form name body
-
- and lower_body exp = lower exp (fun x -> x)
-
- let mk_stg_prim name prim =
- let binary_prim x =
- let open Stg
- let body =
- Case (Atom (Var "x"), "x",
- [( Default, Case (Atom (Var "y"), "y",
- [(Default, Prim (x, [Var "x", Var "y"]))]))])
- Fun { name, args = ["x", "y"], body, is_con = None }
- match prim with
- | "add" -> binary_prim Stg.Add
- | "sub" -> binary_prim Stg.Sub
- | "mul" -> binary_prim Stg.Mul
- | "div" -> binary_prim Stg.Div
- | "equ" -> binary_prim Stg.Equ
- | e -> error @@ "No such primitive " ^ e
-
- let lower_dec = function
- | Decl (name, manifest_args, expr) ->
- let (args, body) = unlambda expr
- let args = manifest_args ++ args
- let body = lower_body body
- [ Stg.Fun { name, args, body, is_con = None } ]
- | Data (_, _, cons) ->
- let mk_stg_con (Constr (name, args), i) =
- let args = [ gensym () | with _ <- args ]
- Stg.Fun { name, args, body = build_stg_app (Stg.Var name) (Stg.Var <$> args), is_con = Some i }
- [ mk_stg_con c | with c <- zip cons [0 .. length cons - 1] ]
- | Foreign (Fimport { cc = Prim, fent = prim, var = name }) ->
- [ mk_stg_prim name prim ]
- | Foreign (Fimport { cc = Lua, fent, var, ftype }) ->
- [ Stg.Ffi_def { name = var, fent, arity = arity ftype }]
|