You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

186 lines
5.4 KiB

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 }]