|
module M = import "data/map.ml"
|
|
module S = import "data/set.ml"
|
|
|
|
open import "prelude.ml"
|
|
open import "./lang.ml"
|
|
open import "./lib/monads.ml"
|
|
|
|
type addr =
|
|
| Combinator of string
|
|
| Arg of int
|
|
|
|
type gm_code =
|
|
| Push of addr
|
|
| Update of int
|
|
| Pop of int
|
|
| Unwind
|
|
| Mkap
|
|
|
|
instance show gm_code begin
|
|
let show = function
|
|
| Mkap -> "Mkap"
|
|
| Unwind -> "Unwind"
|
|
| Push (Combinator k) -> "Push " ^ k
|
|
| Push (Arg i) -> "Arg " ^ show i
|
|
| Update n -> "Update " ^ show n
|
|
| Pop n -> "Pop " ^ show n
|
|
end
|
|
|
|
let rec lambda_lift = function
|
|
| Ref v -> pure (Ref v)
|
|
| App (f, x) -> (| app (lambda_lift f) (lambda_lift x) |)
|
|
| Lam (v, x) ->
|
|
let! body = lambda_lift x
|
|
let! (i, defs, known_sc) = get
|
|
|
|
let vars =
|
|
x |> free_vars
|
|
|> S.delete v
|
|
|> flip S.difference known_sc
|
|
|> S.members
|
|
|
|
let def = ("Lam" ^ show i, vars ++ [v], body)
|
|
let app = foldl (fun f -> app f # Ref) (Ref ("Lam" ^ show i)) vars
|
|
|
|
put (i + 1, Decl def :: defs, known_sc)
|
|
|> map (const app)
|
|
| Case (sc, alts) ->
|
|
alts
|
|
|> map (fun (_, x) -> x)
|
|
|> foldl app sc
|
|
|> lambda_lift
|
|
|
|
let rec eta_contract = function
|
|
| Decl (n, a, e) as dec ->
|
|
match a, e with
|
|
| [], _ -> dec
|
|
| xs, App (f, Ref v) ->
|
|
if v == last xs && not (S.member v (free_vars f)) then
|
|
eta_contract (Decl (n, init a, f))
|
|
else
|
|
dec
|
|
| _, _ -> dec
|
|
| Data c -> Data c
|
|
|
|
let rec lambda_lift_sc = function
|
|
| Decl (n, a, e) ->
|
|
match e with
|
|
| Lam (v, e) -> lambda_lift_sc (Decl (n, a ++ [v], e))
|
|
| _ ->
|
|
let! e = lambda_lift e
|
|
let! _ = modify (fun (a, b, s) -> (a, b, S.insert n s))
|
|
pure (Decl (n, a, e))
|
|
| Data c -> Data c |> pure
|
|
|
|
type dlist 'a <- list 'a -> list 'a
|
|
|
|
let rec compile (env : M.t string int) = function
|
|
| Ref v ->
|
|
match M.lookup v env with
|
|
| Some i -> (Push (Arg i) ::)
|
|
| None -> (Push (Combinator v) ::)
|
|
|
|
| App (f, x) ->
|
|
let f = compile env f
|
|
let x = compile (map (1 +) env) x
|
|
f # x # (Mkap ::)
|
|
|
|
| Lam _ -> error "Can not compile lambda expression, did you forget to lift?"
|
|
| Case _ -> error "Can not compile case expression, did you forget to lift?"
|
|
|
|
let supercomb (_, args, exp) =
|
|
let env = M.from_list (zip args [0..length args])
|
|
let k = compile (M.from_list (zip args [0..length args])) exp
|
|
k [Update (length env), Pop (length env), Unwind]
|
|
|
|
let known_scs = S.from_list [ "getchar", "putchar" ]
|
|
|
|
let program decs =
|
|
let (decs, (_, lams, _)) =
|
|
run_state (traverse (lambda_lift_sc # eta_contract) decs) (0, [], known_scs)
|
|
flip map (lams ++ decs) @@ function
|
|
| Decl ((nm, args, _) as sc) ->
|
|
let code = supercomb sc
|
|
(nm, length args, code)
|
|
| Data _ -> error "data declaration in compiler"
|