desug.ml 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. open Printf
  2. open Ast
  3. open Util
  4. let rec var_init = function
  5. (* Move global initialisations to __init function *)
  6. | Program (decls, loc) ->
  7. let decls = flatten_blocks (List.map var_init decls) in
  8. let rec trav assigns = function
  9. | [] -> (assigns, [])
  10. | (Assign _ as h) :: t
  11. | (Allocate _ as h) :: t -> trav (assigns @ [h]) t
  12. | h :: t ->
  13. let (assigns, decls) = trav assigns t in
  14. (assigns, (h :: decls))
  15. in
  16. let (assigns, decls) = trav [] decls in (
  17. match assigns with
  18. | [] -> Program (decls, loc)
  19. | assigns ->
  20. let init_func = FunDef (true, Void, "__init", [], Block assigns, noloc) in
  21. Program (init_func :: decls, loc)
  22. )
  23. (* Global variable initialisation:
  24. * Add an assign statement and the Program node will remove it later on *)
  25. | GlobalDef (export, ctype, name, Some init, loc) ->
  26. Block [GlobalDef (export, ctype, name, None, loc);
  27. Assign (name, None, init, loc)]
  28. (* Global array definition:
  29. * - Create a new global variable for each dimension and initialise it to
  30. * the given expression
  31. * - create __allocate statement in __init *)
  32. | GlobalDef (export, Array (ctype, dims), name, None, loc) as dec ->
  33. let rec create_dimvars i = function
  34. | [] -> []
  35. | hd :: tl ->
  36. let dimname = name ^ "$" ^ string_of_int i in
  37. let var = Var (dimname, loc) in
  38. var :: (create_dimvars (i + 1) tl)
  39. in
  40. let dimvars = create_dimvars 1 dims in
  41. let create_globaldef dim = function
  42. | Var (dimname, loc) ->
  43. var_init (GlobalDef (export, Int, dimname, Some dim, loc))
  44. | _ -> raise InvalidNode
  45. in
  46. let vardecs = List.map2 create_globaldef dims dimvars in
  47. let alloc = [Allocate (name, dimvars, dec, loc)] in
  48. Block (vardecs @
  49. [GlobalDef (export, Array (ctype, dimvars), name, None, loc)] @
  50. alloc)
  51. (* Split local variable initialisations in declaration and assignment *)
  52. | FunDef (export, ret_type, name, params, Block body, loc) ->
  53. let move_inits body =
  54. let rec trav inits node = match node with
  55. (* Translate scalar array initialisation to ArrayScalar node,
  56. * for easy replacement later on *)
  57. | VarDec (Array _ as vtype, name, Some (BoolConst _ as v), loc) :: t
  58. | VarDec (Array _ as vtype, name, Some (FloatConst _ as v), loc) :: t
  59. | VarDec (Array _ as vtype, name, Some (IntConst _ as v), loc) :: t ->
  60. let init = Some (ArrayInit (ArrayScalar v, vtype)) in
  61. trav inits (VarDec (vtype, name, init, loc) :: t)
  62. (* Wrap ArrayConst in ArrayInit to pass dimensions *)
  63. | VarDec (Array _ as vtype, name, Some (ArrayConst _ as v), loc) :: t ->
  64. let init = Some (ArrayInit (v, vtype)) in
  65. trav inits (VarDec (vtype, name, init, loc) :: t)
  66. | VarDec (ctype, name, init, loc) as dec :: tl ->
  67. (* array definition: create __allocate statement *)
  68. let alloc = match ctype with
  69. | Array (_, dims) -> [Allocate (name, dims, dec, loc)]
  70. | _ -> []
  71. in
  72. (* initialisation: create assign statement *)
  73. let add = match init with
  74. | Some value -> alloc @ [Assign (name, None, value, loc)]
  75. | None -> alloc
  76. in
  77. VarDec (ctype, name, None, loc) :: (trav (inits @ add) tl)
  78. (* initialisations need to be placed after local functions *)
  79. | (FunDef (_, _, _, _, _, _) as h) :: t ->
  80. (var_init h) :: (trav inits t)
  81. (* rest of function body: recurse *)
  82. | rest -> inits @ (List.map var_init rest)
  83. in
  84. flatten_blocks (trav [] body)
  85. in
  86. let params = flatten_blocks (List.map var_init params) in
  87. FunDef (export, ret_type, name, params, Block (move_inits body), loc)
  88. | node -> transform_children var_init node
  89. let rec replace_var var replacement node =
  90. let trav = (replace_var var replacement) in
  91. match node with
  92. | Var (name, loc) when name = var ->
  93. Var (replacement, loc)
  94. | For (counter, start, stop, step, body, loc) when counter = var ->
  95. For (replacement, trav start, trav stop, trav step, trav body, loc)
  96. | node ->
  97. transform_children trav node
  98. let for_to_while node =
  99. let rec traverse new_vars = function
  100. | FunDef (export, ret_type, name, params, body, loc) ->
  101. let new_vars = ref [] in
  102. let body = traverse new_vars body in
  103. let create_vardec name = VarDec (Int, name, None, noloc) in
  104. let new_vardecs = List.map create_vardec !new_vars in
  105. let _body = new_vardecs @ (flatten_blocks (block_body body)) in
  106. FunDef (export, ret_type, name, params, Block _body, loc)
  107. (* Transform for-loops to while-loops *)
  108. | For (counter, start, stop, step, body, loc) ->
  109. let _i = fresh_var counter in
  110. let _stop = fresh_const "stop" in
  111. let _step = fresh_const "step" in
  112. new_vars := !new_vars @ [_i; _stop; _step];
  113. let vi = Var (_i, noloc) in
  114. let vstop = Var (_stop, locof stop) in
  115. let vstep = Var (_step, locof step) in
  116. let cond = Cond (
  117. Binop (Gt, vstep, IntConst (0, noloc), noloc),
  118. Binop (Lt, vi, vstop, noloc),
  119. Binop (Gt, vi, vstop, noloc),
  120. noloc
  121. ) in
  122. Block [
  123. Assign (_i, None, start, locof start);
  124. Assign (_stop, None, stop, locof stop);
  125. Assign (_step, None, step, locof step);
  126. traverse new_vars (While (cond, (Block (
  127. block_body (replace_var counter _i body) @
  128. [Assign (_i, None, Binop (Add, vi, vstep, noloc), noloc)]
  129. )), loc));
  130. ]
  131. (* Transform while-loops to do-while loops in if-statements *)
  132. | While (cond, body, loc) ->
  133. let cond = traverse new_vars cond in
  134. let body = traverse new_vars body in
  135. Block [If (cond, Block [DoWhile (cond, body, loc)], loc)]
  136. | node -> transform_children (traverse new_vars) node
  137. in
  138. traverse (ref []) node
  139. let rec array_init = function
  140. (* Transform scalar assignment into nested for-loops *)
  141. | Assign (name, None, ArrayInit (ArrayScalar value, Array (_, dims)), loc) ->
  142. let rec add_loop indices = function
  143. | [] ->
  144. Assign (name, Some indices, value, loc)
  145. | dim :: rest ->
  146. let counter = fresh_var "i" in
  147. let body = Block [add_loop (indices @ [Var (counter, noloc)]) rest] in
  148. For (counter, IntConst (0, noloc), dim, IntConst (1, noloc), body, noloc)
  149. in
  150. add_loop [] dims
  151. (* Transform array constant inisialisation into separate assign statements
  152. * for all entries in the constant array *)
  153. (* TODO: only allow when array dimensions are constant? *)
  154. | Assign (name, None, ArrayInit (ArrayConst _ as value, Array (_, dims)), loc) ->
  155. let ndims = list_size dims in
  156. let rec make_assigns depth i indices = function
  157. | [] -> []
  158. | hd :: tl ->
  159. let assigns = traverse depth (i :: indices) hd in
  160. make_assigns depth (i + 1) indices tl @ assigns
  161. and traverse depth indices = function
  162. | ArrayConst (values, _) ->
  163. make_assigns (depth + 1) 0 indices values
  164. | value when depth = ndims ->
  165. let indices = List.map (fun i -> IntConst (i, noloc)) indices in
  166. [Assign (name, Some (List.rev indices), value, loc)]
  167. | node ->
  168. let msg = sprintf
  169. "dimension mismatch: expected %d nesting levels, got %d"
  170. ndims depth
  171. in
  172. raise (NodeError (node, msg))
  173. in
  174. Block (List.rev (traverse 0 [] value))
  175. | node -> transform_children array_init node
  176. let rec phase input =
  177. prerr_endline "- Desugaring";
  178. match input with
  179. | Ast node -> Ast (for_to_while (array_init (var_init node)))
  180. | _ -> raise (InvalidInput "desugar")