2 (****************************************************************************)
6 (* INRIA Rocquencourt *)
8 (* Copyright 2006 Institut National de Recherche en Informatique et *)
9 (* en Automatique. All rights reserved. This file is distributed under *)
10 (* the terms of the GNU Library General Public License, with the special *)
11 (* exception on linking described in LICENSE at the top of the Objective *)
12 (* Caml source tree. *)
14 (****************************************************************************)
17 * - Daniel de Rauglaudre: initial version
18 * - Nicolas Pouillard: refactoring
27 module CleanAst = Camlp4.Struct.CleanAst.Make Ast;
28 module SSet = Set.Make String;
30 value pa_r = "Camlp4OCamlRevisedParser";
31 value pa_rr = "Camlp4OCamlReloadedParser";
32 value pa_o = "Camlp4OCamlParser";
33 value pa_rp = "Camlp4OCamlRevisedParserParser";
34 value pa_op = "Camlp4OCamlParserParser";
35 value pa_g = "Camlp4GrammarParser";
36 value pa_m = "Camlp4MacroParser";
37 value pa_qb = "Camlp4QuotationCommon";
38 value pa_q = "Camlp4QuotationExpander";
39 value pa_rq = "Camlp4OCamlRevisedQuotationExpander";
40 value pa_oq = "Camlp4OCamlOriginalQuotationExpander";
41 value pa_l = "Camlp4ListComprehension";
45 value dyn_loader = ref (fun []);
46 value rcall_callback = ref (fun () -> ());
47 value loaded_modules = ref SSet.empty;
48 value add_to_loaded_modules name =
49 loaded_modules.val := SSet.add name loaded_modules.val;
51 value (objext,libext) =
52 if DynLoader.is_native then (".cmxs",".cmxs")
55 value rewrite_and_load n x =
56 let dyn_loader = dyn_loader.val () in
57 let find_in_path = DynLoader.find_in_path dyn_loader in
58 let real_load name = do {
59 add_to_loaded_modules name;
60 DynLoader.load dyn_loader name
62 let load = List.iter begin fun n ->
63 if SSet.mem n loaded_modules.val || List.mem n Register.loaded_modules.val then ()
65 add_to_loaded_modules n;
66 DynLoader.load dyn_loader (n ^ objext);
70 match (n, String.lowercase x) with
71 [ ("Parsers"|"", "pa_r.cmo" | "r" | "ocamlr" | "ocamlrevised" | "camlp4ocamlrevisedparser.cmo") -> load [pa_r]
72 | ("Parsers"|"", "rr" | "reloaded" | "ocamlreloaded" | "camlp4ocamlreloadedparser.cmo") -> load [pa_rr]
73 | ("Parsers"|"", "pa_o.cmo" | "o" | "ocaml" | "camlp4ocamlparser.cmo") -> load [pa_r; pa_o]
74 | ("Parsers"|"", "pa_rp.cmo" | "rp" | "rparser" | "camlp4ocamlrevisedparserparser.cmo") -> load [pa_r; pa_o; pa_rp]
75 | ("Parsers"|"", "pa_op.cmo" | "op" | "parser" | "camlp4ocamlparserparser.cmo") -> load [pa_r; pa_o; pa_rp; pa_op]
76 | ("Parsers"|"", "pa_extend.cmo" | "pa_extend_m.cmo" | "g" | "grammar" | "camlp4grammarparser.cmo") -> load [pa_g]
77 | ("Parsers"|"", "pa_macro.cmo" | "m" | "macro" | "camlp4macroparser.cmo") -> load [pa_m]
78 | ("Parsers"|"", "q" | "camlp4quotationexpander.cmo") -> load [pa_qb; pa_q]
79 | ("Parsers"|"", "q_mlast.cmo" | "rq" | "camlp4ocamlrevisedquotationexpander.cmo") -> load [pa_qb; pa_rq]
80 | ("Parsers"|"", "oq" | "camlp4ocamloriginalquotationexpander.cmo") -> load [pa_r; pa_o; pa_qb; pa_oq]
81 | ("Parsers"|"", "rf") -> load [pa_r; pa_rp; pa_qb; pa_q; pa_g; pa_l; pa_m]
82 | ("Parsers"|"", "of") -> load [pa_r; pa_o; pa_rp; pa_op; pa_qb; pa_rq; pa_g; pa_l; pa_m]
83 | ("Parsers"|"", "comp" | "camlp4listcomprehension.cmo") -> load [pa_l]
84 | ("Filters"|"", "lift" | "camlp4astlifter.cmo") -> load ["Camlp4AstLifter"]
85 | ("Filters"|"", "exn" | "camlp4exceptiontracer.cmo") -> load ["Camlp4ExceptionTracer"]
86 | ("Filters"|"", "prof" | "camlp4profiler.cmo") -> load ["Camlp4Profiler"]
87 (* map is now an alias of fold since fold handles map too *)
88 | ("Filters"|"", "map" | "camlp4mapgenerator.cmo") -> load ["Camlp4FoldGenerator"]
89 | ("Filters"|"", "fold" | "camlp4foldgenerator.cmo") -> load ["Camlp4FoldGenerator"]
90 | ("Filters"|"", "meta" | "camlp4metagenerator.cmo") -> load ["Camlp4MetaGenerator"]
91 | ("Filters"|"", "trash" | "camlp4trashremover.cmo") -> load ["Camlp4TrashRemover"]
92 | ("Filters"|"", "striploc" | "camlp4locationstripper.cmo") -> load ["Camlp4LocationStripper"]
93 | ("Printers"|"", "pr_r.cmo" | "r" | "ocamlr" | "camlp4ocamlrevisedprinter.cmo") ->
94 Register.enable_ocamlr_printer ()
95 | ("Printers"|"", "pr_o.cmo" | "o" | "ocaml" | "camlp4ocamlprinter.cmo") ->
96 Register.enable_ocaml_printer ()
97 | ("Printers"|"", "pr_dump.cmo" | "p" | "dumpocaml" | "camlp4ocamlastdumper.cmo") ->
98 Register.enable_dump_ocaml_ast_printer ()
99 | ("Printers"|"", "d" | "dumpcamlp4" | "camlp4astdumper.cmo") ->
100 Register.enable_dump_camlp4_ast_printer ()
101 | ("Printers"|"", "a" | "auto" | "camlp4autoprinter.cmo") ->
102 load ["Camlp4AutoPrinter"]
104 let y = "Camlp4"^n^"/"^x^objext in
105 real_load (try find_in_path y with [ Not_found -> x ]) ];
106 rcall_callback.val ();
109 value print_warning = eprintf "%a:\n%s@." Loc.print;
111 value rec parse_file dyn_loader name pa getdir =
112 let directive_handler = Some (fun ast ->
113 match getdir ast with
116 [ (_, "load", s) -> do { rewrite_and_load "" s; None }
117 | (_, "directory", s) -> do { DynLoader.include_dir dyn_loader s; None }
118 | (_, "use", s) -> Some (parse_file dyn_loader s pa getdir)
119 | (_, "default_quotation", s) -> do { Quotation.default.val := s; None }
120 | (loc, _, _) -> Loc.raise loc (Stream.Error "bad directive") ]
122 let loc = Loc.mk name
124 current_warning.val := print_warning;
125 let ic = if name = "-" then stdin else open_in_bin name;
126 let cs = Stream.of_channel ic;
127 let clear () = if name = "-" then () else close_in ic;
129 try pa ?directive_handler loc cs
130 with x -> do { clear (); raise x };
135 value output_file = ref None;
137 value process dyn_loader name pa pr clean fold_filters getdir =
138 let ast = parse_file dyn_loader name pa getdir in
139 let ast = fold_filters (fun t filter -> filter t) ast in
140 let ast = clean ast in
141 pr ?input_file:(Some name) ?output_file:output_file.val ast;
145 [ <:sig_item@loc< # $n$ $str:s$ >> -> Some (loc, n, s)
150 [ <:str_item@loc< # $n$ $str:s$ >> -> Some (loc, n, s)
153 value process_intf dyn_loader name =
154 process dyn_loader name CurrentParser.parse_interf CurrentPrinter.print_interf
155 (new CleanAst.clean_ast)#sig_item
156 AstFilters.fold_interf_filters gind;
157 value process_impl dyn_loader name =
158 process dyn_loader name CurrentParser.parse_implem CurrentPrinter.print_implem
159 (new CleanAst.clean_ast)#str_item
160 AstFilters.fold_implem_filters gimd;
162 value just_print_the_version () =
163 do { printf "%s@." Camlp4_config.version; exit 0 };
165 value print_version () =
166 do { eprintf "Camlp4 version %s@." Camlp4_config.version; exit 0 };
168 value print_stdlib () =
169 do { printf "%s@." Camlp4_config.camlp4_standard_library; exit 0 };
171 value usage ini_sl ext_sl =
174 Usage: camlp4 [load-options] [--] [other-options]
176 <file>.ml Parse this implementation file
177 <file>.mli Parse this interface file
178 <file>.%s Load this module inside the Camlp4 core@."
179 (if DynLoader.is_native then "cmxs " else "(cmo|cma)")
181 Options.print_usage_list ini_sl;
182 (* loop (ini_sl @ ext_sl) where rec loop =
184 [ [(y, _, _) :: _] when y = "-help" -> ()
185 | [_ :: sl] -> loop sl
186 | [] -> eprintf " -help Display this list of options.@." ]; *)
187 if ext_sl <> [] then do {
188 eprintf "Options added by loaded object files:@.";
189 Options.print_usage_list ext_sl;
194 value warn_noassert () =
197 camlp4 warning: option -noassert is obsolete
198 You should give the -noassert option to the ocaml compiler instead.@.";
205 | ModuleImpl of string
206 | IncludeDir of string ];
208 value search_stdlib = ref True;
209 value print_loaded_modules = ref False;
210 value (task, do_task) =
213 let () = Camlp4_config.current_input_file.val := x in
214 t.val := Some (if t.val = None then (fun _ -> f x)
215 else (fun usage -> usage ())) in
216 let do_task usage = match t.val with [ Some f -> f usage | None -> () ] in
219 let dyn_loader = dyn_loader.val () in
221 rcall_callback.val ();
223 [ Intf file_name -> task (process_intf dyn_loader) file_name
224 | Impl file_name -> task (process_impl dyn_loader) file_name
227 let (f, o) = Filename.open_temp_file "from_string" ".ml";
230 task (process_impl dyn_loader) f;
231 at_exit (fun () -> Sys.remove f);
233 | ModuleImpl file_name -> rewrite_and_load "" file_name
234 | IncludeDir dir -> DynLoader.include_dir dyn_loader dir ];
235 rcall_callback.val ();
238 value initial_spec_list =
239 [("-I", Arg.String (fun x -> input_file (IncludeDir x)),
240 "<directory> Add directory in search patch for object files.");
241 ("-where", Arg.Unit print_stdlib,
242 "Print camlp4 library directory and exit.");
243 ("-nolib", Arg.Clear search_stdlib,
244 "No automatic search for object files in library directory.");
245 ("-intf", Arg.String (fun x -> input_file (Intf x)),
246 "<file> Parse <file> as an interface, whatever its extension.");
247 ("-impl", Arg.String (fun x -> input_file (Impl x)),
248 "<file> Parse <file> as an implementation, whatever its extension.");
249 ("-str", Arg.String (fun x -> input_file (Str x)),
250 "<string> Parse <string> as an implementation.");
251 ("-unsafe", Arg.Set Camlp4_config.unsafe,
252 "Generate unsafe accesses to array and strings.");
253 ("-noassert", Arg.Unit warn_noassert,
254 "Obsolete, do not use this option.");
255 ("-verbose", Arg.Set Camlp4_config.verbose,
256 "More verbose in parsing errors.");
257 ("-loc", Arg.Set_string Loc.name,
258 "<name> Name of the location variable (default: " ^ Loc.name.val ^ ").");
259 ("-QD", Arg.String (fun x -> Quotation.dump_file.val := Some x),
260 "<file> Dump quotation expander result in case of syntax error.");
261 ("-o", Arg.String (fun x -> output_file.val := Some x),
262 "<file> Output on <file> instead of standard output.");
263 ("-v", Arg.Unit print_version,
264 "Print Camlp4 version and exit.");
265 ("-version", Arg.Unit just_print_the_version,
266 "Print Camlp4 version number and exit.");
267 ("-no_quot", Arg.Clear Camlp4_config.quotations,
268 "Don't parse quotations, allowing to use, e.g. \"<:>\" as token.");
269 ("-loaded-modules", Arg.Set print_loaded_modules, "Print the list of loaded modules.");
270 ("-parser", Arg.String (rewrite_and_load "Parsers"),
271 "<name> Load the parser Camlp4Parsers/<name>.cm(o|a|xs)");
272 ("-printer", Arg.String (rewrite_and_load "Printers"),
273 "<name> Load the printer Camlp4Printers/<name>.cm(o|a|xs)");
274 ("-filter", Arg.String (rewrite_and_load "Filters"),
275 "<name> Load the filter Camlp4Filters/<name>.cm(o|a|xs)");
276 ("-ignore", Arg.String ignore, "ignore the next argument");
277 ("--", Arg.Unit ignore, "Deprecated, does nothing")
280 Options.init initial_spec_list;
282 value anon_fun name =
284 (if Filename.check_suffix name ".mli" then Intf name
285 else if Filename.check_suffix name ".ml" then Impl name
286 else if Filename.check_suffix name objext then ModuleImpl name
287 else if Filename.check_suffix name libext then ModuleImpl name
288 else raise (Arg.Bad ("don't know what to do with " ^ name)));
291 let usage () = do { usage initial_spec_list (Options.ext_spec_list ()); exit 0 } in
293 let dynloader = DynLoader.mk ~ocaml_stdlib:search_stdlib.val
294 ~camlp4_stdlib:search_stdlib.val ();
295 dyn_loader.val := fun () -> dynloader;
296 let call_callback () =
297 Register.iter_and_take_callbacks
298 (fun (name, module_callback) ->
299 let () = add_to_loaded_modules name in
302 rcall_callback.val := call_callback;
303 match Options.parse anon_fun argv with
305 | ["-help"|"--help"|"-h"|"-?" :: _] -> usage ()
307 do { eprintf "%s: unknown or misused option\n" s;
308 eprintf "Use option -help for usage@.";
312 if print_loaded_modules.val then do {
313 SSet.iter (eprintf "%s@.") loaded_modules.val;
317 [ Arg.Bad s -> do { eprintf "Error: %s\n" s;
318 eprintf "Use option -help for usage@.";
320 | Arg.Help _ -> usage ()
321 | exc -> do { eprintf "@[<v0>%a@]@." ErrorHandler.print exc; exit 2 } ];