genxml.ml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. (*
  2. * Haxe Compiler
  3. * Copyright (c)2005 Nicolas Cannasse
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *)
  19. open Ast
  20. open Type
  21. open Common
  22. type xml =
  23. | Node of string * (string * string) list * xml list
  24. | PCData of string
  25. | CData of string
  26. let tag name = Node (name,[],[])
  27. let xml name att = Node (name,att,[])
  28. let node name att childs = Node (name,att,childs)
  29. let pcdata s = PCData s
  30. let cdata s = CData s
  31. let pmap f m =
  32. PMap.fold (fun x acc -> f x :: acc) m []
  33. let gen_path (p,n) priv =
  34. ("path",String.concat "." (p @ [n]))
  35. let gen_string s =
  36. if String.contains s '<' || String.contains s '>' || String.contains s '&' then cdata s else pcdata s
  37. let gen_doc s =
  38. (* remove trailing space and convert newlines *)
  39. let s = ExtString.String.strip s in
  40. let s = String.concat "\n" (ExtString.String.nsplit (String.concat "\n" (ExtString.String.nsplit s "\r\n")) "\r") in
  41. node "haxe_doc" [] [gen_string s]
  42. let gen_doc_opt d =
  43. match d with
  44. | None -> []
  45. | Some s -> [gen_doc s]
  46. let gen_arg_name (name,opt,_) =
  47. (if opt then "?" else "") ^ name
  48. let real_path path meta =
  49. let rec loop = function
  50. | [] -> path
  51. | (":realPath",[(Ast.EConst (Ast.String s),_)],_) :: _ -> parse_path s
  52. | _ :: l -> loop l
  53. in
  54. loop meta
  55. let cpath c =
  56. real_path c.cl_path c.cl_meta
  57. let epath e =
  58. real_path e.e_path e.e_meta
  59. let rec follow_param t =
  60. match t with
  61. | TMono r ->
  62. (match !r with
  63. | Some t -> follow_param t
  64. | _ -> t)
  65. | TType ({ t_path = [],"Null" } as t,tl) ->
  66. follow_param (apply_params t.t_types tl t.t_type)
  67. | _ ->
  68. t
  69. let rec sexpr (e,_) =
  70. match e with
  71. | EConst (String s) -> s
  72. | EConst c -> s_constant c
  73. | EParenthesis e -> "(" ^ (sexpr e) ^ ")"
  74. | EArrayDecl el -> "[" ^ (String.concat "," (List.map sexpr el)) ^ "]"
  75. | EObjectDecl fl -> "{" ^ (String.concat "," (List.map (fun (n,e) -> n ^ ":" ^ (sexpr e)) fl)) ^ "}"
  76. | _ -> "'???'"
  77. let gen_meta meta = match meta with
  78. | [] -> []
  79. | _ ->
  80. let meta = List.filter (fun (m,_,_) -> match m with ":?used" | ":realPath" -> false | _ -> true) meta in
  81. let nodes = List.map (fun (m,el,_) ->
  82. node "m" ["n",m] (List.map (fun e -> node "e" [] [gen_string (sexpr e)]) el)
  83. ) meta in
  84. [node "meta" [] nodes]
  85. let rec gen_type t =
  86. match t with
  87. | TMono m -> (match !m with None -> tag "unknown" | Some t -> gen_type t)
  88. | TEnum (e,params) -> node "e" [gen_path (epath e) e.e_private] (List.map gen_type params)
  89. | TInst (c,params) -> node "c" [gen_path (cpath c) c.cl_private] (List.map gen_type params)
  90. | TType (t,params) -> node "t" [gen_path t.t_path t.t_private] (List.map gen_type params)
  91. | TFun (args,r) -> node "f" ["a",String.concat ":" (List.map gen_arg_name args)] (List.map gen_type (List.map (fun (_,opt,t) -> if opt then follow_param t else t) args @ [r]))
  92. | TAnon a -> node "a" [] (pmap (fun f -> gen_field [] { f with cf_public = false }) a.a_fields)
  93. | TDynamic t2 -> node "d" [] (if t == t2 then [] else [gen_type t2])
  94. | TLazy f -> gen_type (!f())
  95. and gen_field att f =
  96. let add_get_set acc name att =
  97. match acc with
  98. | AccNormal | AccResolve | AccRequire _ -> att
  99. | AccNo | AccNever -> (name, "null") :: att
  100. | AccCall m -> (name,m) :: att
  101. | AccInline -> (name,"inline") :: att
  102. in
  103. let att = (match f.cf_expr with None -> att | Some e -> ("line",string_of_int (Lexer.get_error_line e.epos)) :: att) in
  104. let att = (match f.cf_kind with
  105. | Var v -> add_get_set v.v_read "get" (add_get_set v.v_write "set" att)
  106. | Method m ->
  107. (match m with
  108. | MethNormal | MethMacro -> ("set", "method") :: att
  109. | MethDynamic -> ("set", "dynamic") :: att
  110. | MethInline -> ("get", "inline") :: ("set","null") :: att)
  111. ) in
  112. let att = (match f.cf_params with [] -> att | l -> ("params", String.concat ":" (List.map (fun (n,_) -> n) l)) :: att) in
  113. node f.cf_name (if f.cf_public then ("public","1") :: att else att) (gen_type f.cf_type :: gen_meta f.cf_meta @ gen_doc_opt f.cf_doc)
  114. let gen_constr e =
  115. let doc = gen_doc_opt e.ef_doc in
  116. let args, t = (match follow e.ef_type with
  117. | TFun (args,_) ->
  118. ["a",String.concat ":" (List.map gen_arg_name args)] ,
  119. List.map (fun (_,opt,t) -> gen_type (if opt then follow_param t else t)) args @ doc
  120. | _ ->
  121. [] , doc
  122. ) in
  123. node e.ef_name args t
  124. let gen_type_params ipos priv path params pos m =
  125. let mpriv = (if priv then [("private","1")] else []) in
  126. let mpath = (if m.m_path <> path then [("module",snd (gen_path m.m_path false))] else []) in
  127. let file = (if ipos && pos <> null_pos then [("file",pos.pfile)] else []) in
  128. gen_path path priv :: ("params", String.concat ":" (List.map fst params)) :: (file @ mpriv @ mpath)
  129. let gen_class_path name (c,pl) =
  130. node name [("path",s_type_path (cpath c))] (List.map gen_type pl)
  131. let rec exists f c =
  132. PMap.exists f.cf_name c.cl_fields ||
  133. match c.cl_super with
  134. | None -> false
  135. | Some (csup,_) -> exists f csup
  136. let gen_type_decl com pos t =
  137. let m = (t_infos t).mt_module in
  138. match t with
  139. | TClassDecl c ->
  140. let stats = List.map (gen_field ["static","1"]) (List.filter (fun cf -> cf.cf_name <> "__meta__") c.cl_ordered_statics) in
  141. let fields = (match c.cl_super with
  142. | None -> List.map (fun f -> f,[]) c.cl_ordered_fields
  143. | Some (csup,_) -> List.map (fun f -> if exists f csup then (f,["override","1"]) else (f,[])) c.cl_ordered_fields
  144. ) in
  145. let fields = List.map (fun (f,att) -> gen_field att f) fields in
  146. let constr = (match c.cl_constructor with None -> [] | Some f -> [gen_field [] f]) in
  147. let impl = List.map (gen_class_path "implements") c.cl_implements in
  148. let tree = (match c.cl_super with
  149. | None -> impl
  150. | Some x -> gen_class_path "extends" x :: impl
  151. ) in
  152. let doc = gen_doc_opt c.cl_doc in
  153. let meta = gen_meta c.cl_meta in
  154. let ext = (if c.cl_extern then [("extern","1")] else []) in
  155. let interf = (if c.cl_interface then [("interface","1")] else []) in
  156. let dynamic = (match c.cl_dynamic with
  157. | None -> []
  158. | Some t -> [node "haxe_dynamic" [] [gen_type t]]
  159. ) in
  160. node "class" (gen_type_params pos c.cl_private (cpath c) c.cl_types c.cl_pos m @ ext @ interf) (tree @ stats @ fields @ constr @ doc @ meta @ dynamic)
  161. | TEnumDecl e ->
  162. let doc = gen_doc_opt e.e_doc in
  163. let meta = gen_meta e.e_meta in
  164. node "enum" (gen_type_params pos e.e_private (epath e) e.e_types e.e_pos m) (pmap gen_constr e.e_constrs @ doc @ meta)
  165. | TTypeDecl t ->
  166. let doc = gen_doc_opt t.t_doc in
  167. let meta = gen_meta t.t_meta in
  168. let tt = gen_type t.t_type in
  169. node "typedef" (gen_type_params pos t.t_private t.t_path t.t_types t.t_pos m) (tt :: doc @ meta)
  170. let att_str att =
  171. String.concat "" (List.map (fun (a,v) -> Printf.sprintf " %s=\"%s\"" a v) att)
  172. let rec write_xml ch tabs x =
  173. match x with
  174. | Node (name,att,[]) ->
  175. IO.printf ch "%s<%s%s/>" tabs name (att_str att)
  176. | Node (name,att,[x]) ->
  177. IO.printf ch "%s<%s%s>" tabs name (att_str att);
  178. write_xml ch "" x;
  179. IO.printf ch "</%s>" name;
  180. | Node (name,att,childs) ->
  181. IO.printf ch "%s<%s%s>\n" tabs name (att_str att);
  182. List.iter (fun x ->
  183. write_xml ch (tabs ^ "\t") x;
  184. IO.printf ch "\n";
  185. ) childs;
  186. IO.printf ch "%s</%s>" tabs name
  187. | PCData s ->
  188. IO.printf ch "%s" s
  189. | CData s ->
  190. IO.printf ch "<![CDATA[%s]]>" s
  191. let generate com file =
  192. let t = Common.timer "construct xml" in
  193. let x = node "haxe" [] (List.map (gen_type_decl com true) (List.filter (fun t -> not (has_meta ":noDoc" (t_infos t).mt_meta)) com.types)) in
  194. t();
  195. let t = Common.timer "write xml" in
  196. let ch = IO.output_channel (open_out_bin file) in
  197. write_xml ch "" x;
  198. IO.close_out ch;
  199. t()
  200. let gen_type_string ctx t =
  201. let x = gen_type_decl ctx false t in
  202. let ch = IO.output_string() in
  203. write_xml ch "" x;
  204. IO.close_out ch
  205. (* -------------------------------------------------------------------------- *)
  206. (* PRINT HX FROM TYPE *)
  207. let rec create_dir acc = function
  208. | [] -> ()
  209. | d :: l ->
  210. let path = acc ^ "/" ^ d in
  211. (try Unix.mkdir path 0o777 with _ -> ());
  212. create_dir path l
  213. let conv_path p =
  214. match List.rev (fst p) with
  215. | x :: l when x.[0] = '_' -> List.rev (("priv" ^ x) :: l), snd p
  216. | _ -> p
  217. let generate_type com t =
  218. let base_path = "hxclasses" in
  219. let pack , name = conv_path (t_path t) in
  220. create_dir "." (base_path :: pack);
  221. match pack, name with
  222. | ["flash";"net"], "NetStreamPlayTransitions"
  223. | ["flash";"filters"], "BitmapFilterQuality"
  224. | ["flash";"display"], ("BitmapDataChannel" | "GraphicsPathCommand") -> ()
  225. | _ ->
  226. let f = open_out_bin (base_path ^ "/" ^ (match pack with [] -> "" | l -> String.concat "/" l ^ "/") ^ name ^ ".hx") in
  227. let ch = IO.output_channel f in
  228. let p fmt = IO.printf ch fmt in
  229. if pack <> [] then IO.printf ch "package %s;\n\n" (String.concat "." pack);
  230. let rec notnull t =
  231. match t with
  232. | TMono r ->
  233. (match !r with
  234. | None -> t
  235. | Some t -> notnull t)
  236. | TLazy f ->
  237. notnull ((!f)())
  238. | TType ({ t_path = [],"Null" },[t]) ->
  239. t
  240. | _ ->
  241. t
  242. in
  243. let rec path p tl =
  244. let p = conv_path p in
  245. (if fst p = pack then snd p else s_type_path p) ^ (match tl with [] -> "" | _ -> "<" ^ String.concat "," (List.map stype tl) ^ ">")
  246. and stype t =
  247. match t with
  248. | TMono r ->
  249. (match !r with
  250. | None -> "Unknown"
  251. | Some t -> stype t)
  252. | TInst ({ cl_kind = KTypeParameter } as c,tl) ->
  253. path ([],snd c.cl_path) tl
  254. | TInst (c,tl) ->
  255. path c.cl_path tl
  256. | TEnum (e,tl) ->
  257. path e.e_path tl
  258. | TType (t,tl) ->
  259. path t.t_path tl
  260. | TAnon a ->
  261. let fields = PMap.fold (fun f acc -> (f.cf_name ^ " : " ^ stype f.cf_type) :: acc) a.a_fields [] in
  262. "{" ^ String.concat ", " fields ^ "}"
  263. | TLazy f ->
  264. stype ((!f)())
  265. | TDynamic t2 ->
  266. if t == t2 then "Dynamic" else "Dynamic<" ^ stype t2 ^ ">"
  267. | TFun ([],ret) ->
  268. "Void -> " ^ ftype ret
  269. | TFun (args,ret) ->
  270. String.concat " -> " (List.map (fun (_,_,t) -> ftype t) args) ^ " -> " ^ ftype ret
  271. and ftype t =
  272. match t with
  273. | TMono r ->
  274. (match !r with
  275. | None -> stype t
  276. | Some t -> ftype t)
  277. | TLazy f ->
  278. ftype ((!f)())
  279. | TFun _ ->
  280. "(" ^ stype t ^ ")"
  281. | _ ->
  282. stype t
  283. in
  284. let sparam (n,v,t) =
  285. match v with
  286. | None ->
  287. n ^ " : " ^ stype t
  288. | Some (Ident "null") ->
  289. "?" ^ n ^ " : " ^ stype (notnull t)
  290. | Some v ->
  291. n ^ " : " ^ stype t ^ " = " ^ (s_constant v)
  292. in
  293. let print_meta ml =
  294. List.iter (fun (m,pl,_) ->
  295. match m with
  296. | ":defparam" | ":core_api" -> ()
  297. | _ ->
  298. match pl with
  299. | [] -> p "@%s " m
  300. | l -> p "@%s(%s) " m (String.concat "," (List.map sexpr pl))
  301. ) ml
  302. in
  303. let access a =
  304. match a, pack with
  305. | AccNever, "flash" :: _ -> "null"
  306. | _ -> s_access a
  307. in
  308. let print_field stat f =
  309. p "\t";
  310. print_meta f.cf_meta;
  311. if stat then p "static ";
  312. (match f.cf_kind with
  313. | Var v ->
  314. p "var %s" f.cf_name;
  315. if v.v_read <> AccNormal || v.v_write <> AccNormal then p "(%s,%s)" (access v.v_read) (access v.v_write);
  316. p " : %s" (stype f.cf_type);
  317. | Method m ->
  318. let params, ret = (match follow f.cf_type with
  319. | TFun (args,ret) ->
  320. List.map (fun (a,o,t) ->
  321. let rec loop = function
  322. | [] -> Ident "null"
  323. | (":defparam",[(EConst (String p),_);(EConst v,_)],_) :: _ when p = a ->
  324. (match v with
  325. | Float "1.#QNAN" -> Float "0./*NaN*/"
  326. | Float "4294967295." -> Int "0xFFFFFFFF"
  327. | Int "16777215" -> Int "0xFFFFFF"
  328. | Float x ->
  329. (try
  330. let f = float_of_string x in
  331. let s = string_of_int (int_of_float f) in
  332. if s ^ "." = x then Int s else v
  333. with _ ->
  334. v)
  335. | _ -> v)
  336. | _ :: l -> loop l
  337. in
  338. a,(if o then Some (loop f.cf_meta) else None ),t
  339. ) args, ret
  340. | _ ->
  341. assert false
  342. ) in
  343. let tparams = (match f.cf_params with [] -> "" | l -> "<" ^ String.concat "," (List.map fst l) ^ ">") in
  344. p "function %s%s(%s) : %s" f.cf_name tparams (String.concat ", " (List.map sparam params)) (stype ret);
  345. );
  346. p ";\n"
  347. in
  348. (match t with
  349. | TClassDecl c ->
  350. print_meta c.cl_meta;
  351. p "extern %s %s" (if c.cl_interface then "interface" else "class") (stype (TInst (c,List.map snd c.cl_types)));
  352. let ext = (match c.cl_super with
  353. | None -> []
  354. | Some (c,pl) -> [" extends " ^ stype (TInst (c,pl))]
  355. ) in
  356. let ext = List.fold_left (fun acc (i,pl) -> (" implements " ^ stype (TInst (i,pl))) :: acc) ext c.cl_implements in
  357. let ext = (match c.cl_dynamic with
  358. | None -> ext
  359. | Some t ->
  360. (match c.cl_path with
  361. | ["flash";"errors"], _ -> ext
  362. | _ when t == t_dynamic -> " implements Dynamic" :: ext
  363. | _ -> (" implements Dynamic<" ^ stype t ^ ">") :: ext)
  364. ) in
  365. let ext = (match c.cl_path with
  366. | ["flash";"utils"], "ByteArray" -> " implements ArrayAccess<Int>" :: ext
  367. | ["flash";"utils"], "Dictionnary" -> [" implements ArrayAccess<Dynamic>"]
  368. | ["flash";"xml"], "XML" -> [" implements Dynamic<XMLList>"]
  369. | ["flash";"xml"], "XMLList" -> [" implements ArrayAccess<XML>"]
  370. | ["flash";"display"],"MovieClip" -> [" extends Sprite #if !flash_strict, implements Dynamic #end"]
  371. | ["flash";"errors"], "Error" -> [" #if !flash_strict implements Dynamic #end"]
  372. | _ -> ext
  373. ) in
  374. p "%s" (String.concat "," (List.rev ext));
  375. p " {\n";
  376. let sort l =
  377. let a = Array.of_list (List.filter (fun f -> f.cf_public && not (List.mem f.cf_name c.cl_overrides)) l) in
  378. let name = function "new" -> "" | n -> n in
  379. Array.sort (fun f1 f2 ->
  380. match f1.cf_kind, f2.cf_kind with
  381. | Var _, Var _ | Method _ , Method _ -> compare (name f1.cf_name) (name f2.cf_name)
  382. | Var _, _ -> -1
  383. | _ -> 1
  384. ) a;
  385. Array.to_list a
  386. in
  387. List.iter (print_field false) (sort (match c.cl_constructor with None -> c.cl_ordered_fields | Some f -> f :: c.cl_ordered_fields));
  388. List.iter (print_field true) (sort c.cl_ordered_statics);
  389. p "}\n";
  390. | TEnumDecl e ->
  391. print_meta e.e_meta;
  392. p "extern enum %s {\n" (stype (TEnum(e,List.map snd e.e_types)));
  393. let sort l =
  394. let a = Array.of_list l in
  395. Array.sort compare a;
  396. Array.to_list a
  397. in
  398. List.iter (fun n ->
  399. let c = PMap.find n e.e_constrs in
  400. p "\t%s" c.ef_name;
  401. (match follow c.ef_type with
  402. | TFun (args,_) -> p "(%s)" (String.concat ", " (List.map sparam (List.map (fun (a,o,t) -> a,(if o then Some (Ident "null") else None),t) args)))
  403. | _ -> ());
  404. p ";\n";
  405. ) (if has_meta ":fakeEnum" e.e_meta then sort e.e_names else e.e_names);
  406. p "}\n"
  407. | TTypeDecl t ->
  408. print_meta t.t_meta;
  409. p "extern typedef %s = " (stype (TType (t,List.map snd t.t_types)));
  410. p "%s" (stype t.t_type);
  411. p "\n";
  412. );
  413. IO.close_out ch
  414. let generate_hx com =
  415. List.iter (generate_type com) com.types