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.

269 lines
9.0 KiB

  1. module Stg = import "./stg.ml"
  2. module Map = import "data/map.ml"
  3. module Set = import "data/set.ml"
  4. module Strings = import "../lib/strings.ml"
  5. open Stg
  6. open import "lua/io.ml"
  7. open import "prelude.ml"
  8. type lua_ref 'expr =
  9. | Lvar of string
  10. | Lindex of lua_ref 'expr * 'expr
  11. type lua_expr 'stmt =
  12. | Lfunc of list string * list 'stmt
  13. | Lcall_e of lua_expr 'stmt * list (lua_expr 'stmt)
  14. | Lstr of string
  15. | Lint of int
  16. | Lref of lua_ref (lua_expr 'stmt)
  17. | Lbop of lua_expr 'stmt * string * lua_expr 'stmt
  18. | Ltable of list (lua_expr 'stmt * lua_expr 'stmt)
  19. | Ltrue
  20. | Ldots
  21. type lua_stmt =
  22. | Return of lua_expr lua_stmt
  23. | Local of list string * list (lua_expr lua_stmt)
  24. | Func of string * list string * list lua_stmt
  25. | Assign of list (lua_ref (lua_expr lua_stmt)) * list (lua_expr lua_stmt)
  26. | If of lua_expr lua_stmt * list lua_stmt * list lua_stmt
  27. let rec ppr_ref indl = function
  28. | Lvar v -> v
  29. | Lindex (e, Lstr x) -> ppr_ref indl e ^ "." ^ x
  30. | Lindex (e, e') -> ppr_ref indl e ^ "[" ^ ppr_expr indl e' ^ "]"
  31. and ppr_expr indl = function
  32. | Lfunc (args, body) ->
  33. "function(" ^ ppr_args args ^ ")\n" ^ ppr_body (indl ^ " ") body ^ indl ^ "end"
  34. | Lcall_e (Lref _ as func, args) ->
  35. ppr_expr indl func ^ "(" ^ ppr_args (ppr_expr indl <$> args) ^ ")"
  36. | Lcall_e (func, args) ->
  37. "(" ^ ppr_expr indl func ^ ")(" ^ ppr_args (ppr_expr indl <$> args) ^ ")"
  38. | Lstr s -> show s
  39. | Lint i -> show i
  40. | Ldots -> "..."
  41. | Lref r -> ppr_ref indl r
  42. | Ltrue -> "true"
  43. | Lbop (l, o, r) -> ppr_expr indl l ^ " " ^ o ^ " " ^ ppr_expr indl r
  44. | Ltable entries -> "{" ^ ppr_args (ppr_pair indl <$> entries) ^ "}"
  45. and ppr_stmt indl = function
  46. | Return r -> "return " ^ ppr_expr indl r
  47. | If (c, t, []) ->
  48. "if " ^ ppr_expr indl c ^ " then\n"
  49. ^ ppr_body (indl ^ " ") t
  50. ^ indl ^ "end"
  51. | If (c, [], e) ->
  52. "if not (" ^ ppr_expr indl c ^ ") then\n"
  53. ^ ppr_body (indl ^ " ") e
  54. ^ indl ^ "end"
  55. | If (c, t, e) ->
  56. "if " ^ ppr_expr indl c ^ " then\n"
  57. ^ ppr_body (indl ^ " ") t
  58. ^ indl ^ "else\n"
  59. ^ ppr_body (indl ^ " ") e
  60. ^ indl ^ "end"
  61. | Local ([], []) -> ""
  62. | Local (vs, []) -> "local " ^ ppr_args vs
  63. | Local (vs, es) ->
  64. "local " ^ ppr_args vs ^ " = " ^ ppr_args (ppr_expr indl <$> es)
  65. | Assign (vs, es) ->
  66. ppr_args (ppr_ref indl <$> vs) ^ " = " ^ ppr_args (ppr_expr indl <$> es)
  67. | Func (n, args, body) ->
  68. "function " ^ n ^ "(" ^ ppr_args args ^ ")\n" ^ ppr_body (indl ^ " ") body ^ indl ^ "end"
  69. and ppr_args = function
  70. | [] -> ""
  71. | Cons (a, args) -> foldl (fun a b -> a ^ ", " ^ b) a args
  72. and ppr_body indl = function
  73. | [] -> "\n"
  74. | Cons (a, args) ->
  75. foldl (fun a b -> a ^ "\n" ^ indl ^ b) (indl ^ ppr_stmt indl a) (ppr_stmt indl <$> args) ^ "\n"
  76. and ppr_pair indl (k, v) = "[" ^ ppr_expr indl k ^ "] = " ^ ppr_expr indl v
  77. let gensym =
  78. let counter = ref 0
  79. fun () ->
  80. counter := !counter + 1
  81. "_a" ^ show !counter
  82. let escape = function
  83. | "nil" -> "_Lnil"
  84. | x -> x
  85. let var x = Lref (Lvar (escape x))
  86. let mk_pap_def =
  87. "\
  88. local function mk_pap(fun, ...) \
  89. local pending = { ... }\
  90. return setmetatable({}, { __call = function(...) \
  91. local args = table.pack(...)\
  92. for i = 1, #pending do\
  93. table.insert(args, i, pending[i])\
  94. end\
  95. return fun(unpack(args, 1, args.n + #pending))\
  96. end}) \
  97. end"
  98. let make_lambda name args body =
  99. let name = escape name
  100. let args = map escape args
  101. let arity = length args
  102. [ Local ([name, name ^ "_entry" ], []),
  103. Func (name ^ "_entry", args, body),
  104. Func (name, ["..."], [
  105. If (Lbop (Lcall_e (var "select", [Lstr "#", Ldots]), "==", Lint arity), [
  106. Return (Lcall_e (var (name ^ "_entry"), [Ldots]))
  107. ], [
  108. If (Lbop (Lcall_e (var "select", [Lstr "#", Ldots]), ">", Lint arity), [
  109. Local (["_spill"], [Lcall_e (var "table.pack", [Ldots])]),
  110. Return (Lcall_e (Lcall_e (var (name ^ "_entry"), [Ldots]),
  111. [Lcall_e (var "table.unpack", [var "_spill", Lint arity, var "_spill.n"])]))
  112. ], [
  113. Return (Lcall_e (var "mk_pap", [var name, Ldots]))
  114. ])])])]
  115. let expr_of_atom = function
  116. | Var v -> var v
  117. | Int i -> Lfunc ([], [Return (Lint i)])
  118. let return x = [Return x]
  119. let rec stmts_of_expr arities = function
  120. | Atom _ as a -> expr_of_expr arities a |> return
  121. | App _ as a -> expr_of_expr arities a |> return
  122. | Prim (f, xs) -> stmts_of_prim (f, expr_of_atom <$> xs)
  123. | Con _ as a -> expr_of_expr arities a |> return
  124. | Case (expr, binder, alts) ->
  125. let rec make_cases = function
  126. | [] -> [Return (Lcall_e (var "error", [Lstr "Unmatched case"]))]
  127. | Cons ((Default, tail), _) -> stmts_of_expr arities tail
  128. | Cons ((Con_pat (tag, names), tail), rest) ->
  129. let accesses =
  130. [ Lref (Lindex (Lvar binder, Lint (i + 1)))
  131. | with i <- [1 .. length names]
  132. ]
  133. [If (Lbop (Lref (Lindex (Lvar binder, Lint 1)), "==", Lint tag),
  134. Local (names, accesses) :: stmts_of_expr arities tail,
  135. make_cases rest
  136. )]
  137. Local ([binder], [enter arities expr]) :: make_cases alts
  138. | Let (binders, rest) ->
  139. let names = map (.name) binders
  140. Local (names, []) :: gen_lambda_forms arities binders ++ stmts_of_expr arities rest
  141. and expr_of_expr arities = function
  142. | Atom (Var v) ->
  143. match Map.lookup v arities with
  144. | Some (Left (0, tag)) -> Lcall_e (var "setmetatable", [ Ltable [(Lint 1, Lint tag)], var "Constr_mt" ])
  145. | _ -> expr_of_atom (Var v)
  146. | Atom a -> expr_of_atom a
  147. | App (f, xs) ->
  148. match f with
  149. | Int _ -> error "Attempt to call int"
  150. | Var v ->
  151. match Map.lookup v arities with
  152. | Some (Right x) when x == length xs ->
  153. (Lcall_e (var (v ^ "_entry"), expr_of_atom <$> xs))
  154. | Some (Left (x, tag)) when x == length xs ->
  155. let go i a = (Lint (i + 1), expr_of_atom a)
  156. Lcall_e (var "setmetatable", [
  157. Ltable ((Lint 1, Lint tag) :: zip_with go [1..length xs] xs),
  158. var "Constr_mt"
  159. ])
  160. | _ -> Lcall_e (var v, expr_of_atom <$> xs)
  161. | Prim (f, xs) -> expr_of_prim (f, expr_of_atom <$> xs)
  162. | Con (tag, _, atoms) ->
  163. let go i a = (Lint (i + 1), expr_of_atom a)
  164. Lcall_e (var "setmetatable", [
  165. Ltable ((Lint 1, Lint tag) :: zip_with go [1..length atoms] atoms),
  166. var "Constr_mt"
  167. ])
  168. | e -> Lcall_e (Lfunc ([], stmts_of_expr arities e), [])
  169. and enter arities expr =
  170. Lcall_e (expr_of_expr arities expr, [])
  171. and expr_of_prim = function
  172. | Add, [a, b] -> Lfunc ([], [Return (Lbop (a, "+", b))])
  173. | Sub, [a, b] -> Lfunc ([], [Return (Lbop (a, "-", b))])
  174. | Mul, [a, b] -> Lfunc ([], [Return (Lbop (a, "*", b))])
  175. | Div, [a, b] -> Lfunc ([], [Return (Lbop (a, "/", b))])
  176. | e -> Lcall_e (Lfunc ([], stmts_of_prim e), [])
  177. and stmts_of_prim = function
  178. | Equ, [a, b] -> [
  179. If (Lbop (a, "==", b),
  180. stmts_of_expr Map.empty (Con (0, 0, [])),
  181. stmts_of_expr Map.empty (Con (1, 0, [])))
  182. ]
  183. | e -> expr_of_prim e |> return
  184. and gen_lambda_forms arities : list (lambda_form stg_expr) -> list lua_stmt = function
  185. | [] -> []
  186. | Cons ({update = Function, name, args, body}, rest) ->
  187. let arities = Map.insert name (Right (length args)) arities
  188. let bst = stmts_of_expr arities body
  189. tail (make_lambda name args bst) ++ gen_lambda_forms arities rest
  190. | Cons ({update = Updatable, name, args, body}, rest) ->
  191. let body = expr_of_expr arities body
  192. let s = Assign ([Lvar name], [
  193. Lcall_e (var "setmetatable", [
  194. Ltable [],
  195. Ltable [ (Lstr "__call", Lfunc (["_self"], [
  196. If (Lref (Lindex (Lvar "_self", Lint 1)), [
  197. Return (Lref (Lindex (Lvar "_self", Lint 1)))
  198. ], [
  199. Local (["val"], [Lcall_e (body, [])]),
  200. Assign ([Lindex (Lvar "_self", Lint 1)], [var "val"]),
  201. Return (var "val")
  202. ])
  203. ]))
  204. ]
  205. ])
  206. ])
  207. s :: gen_lambda_forms arities rest
  208. let private pasted_files : ref (Set.t string) = ref Set.empty
  209. let stmts_of_def (arities, code, locals) = function
  210. | Fun { name, args, body, is_con } ->
  211. let arities = Map.insert name (match is_con with | Some i -> Left (length args, i) | None -> Right (length args)) arities
  212. let body = stmts_of_expr arities body
  213. let Cons (Local (locals', _), def) = make_lambda name args body
  214. (arities, def ++ code, locals' ++ locals)
  215. | Ffi_def { name, fent, arity } ->
  216. let fspec =
  217. match Strings.split_on " " fent with
  218. | [file, func] ->
  219. pasted_files := Set.insert file !pasted_files
  220. func
  221. | [func] -> func
  222. | _ -> error @@ "Foreign spec too big: " ^ fent
  223. let args = [ gensym () | with _ <- [1 .. arity] ]
  224. let Cons (Local (locals', _), def) = make_lambda name args [Return (Lcall_e (var fspec, var <$> args))]
  225. (arities, def ++ code, locals' ++ locals)
  226. let get_file_contents () =
  227. let files = Set.members !pasted_files
  228. let go contents path =
  229. let f = open_for_reading path
  230. let x = read_all f
  231. close_file f
  232. match x with
  233. | Some s -> "--- foreign file: " ^ path ^ "\n" ^ s ^ "\n" ^ contents
  234. | None -> contents
  235. foldl go "" files