New inference: flatten unions when solving for lower bounds
[hiphop-php.git] / hphp / hack / src / hh_parse.ml
blob13a131cc9f998487f33c4e92cfd7dac7fea129b7
1 (**
2 * Copyright (c) 2016, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 (**
12 * Usage: hh_parse [OPTIONS] [FILES]
14 * --full-fidelity-json
15 * --full-fidelity-errors
16 * --full-fidelity-errors-all
17 * --full-fidelity-s-expression
18 * --full-fidelity-ast-s-expression
19 * --program-text
20 * --pretty-print
21 * --show-file-name
23 * TODO: Parser for things other than scripts:
24 * types, expressions, statements, declarations, etc.
27 module Schema = Full_fidelity_schema
28 module SyntaxError = Full_fidelity_syntax_error
29 module SyntaxTree = Full_fidelity_syntax_tree
30 .WithSyntax(Full_fidelity_positioned_syntax)
31 module SourceText = Full_fidelity_source_text
32 module ParserErrors = Full_fidelity_parser_errors
33 .WithSyntax(Full_fidelity_positioned_syntax)
34 module DebugPos = Debug.WithSyntax(Full_fidelity_positioned_syntax)
36 module FullFidelityParseArgs = struct
38 type t =
40 (* Output options *)
41 full_fidelity_json : bool;
42 full_fidelity_text_json : bool;
43 full_fidelity_dot : bool;
44 full_fidelity_dot_edges : bool;
45 full_fidelity_errors : bool;
46 full_fidelity_errors_all : bool;
47 full_fidelity_s_expr : bool;
48 full_fidelity_ast_s_expr : bool;
49 program_text : bool;
50 pretty_print : bool;
51 schema: bool;
52 show_file_name : bool;
53 (* Configuring the parser *)
54 is_hh_file : bool;
55 codegen : bool;
56 php5_compat_mode : bool;
57 elaborate_namespaces : bool;
58 include_line_comments : bool;
59 keep_errors : bool;
60 quick_mode : bool;
61 lower_coroutines : bool;
62 enable_hh_syntax : bool;
63 enable_await_as_an_expression : bool;
64 fail_open : bool;
65 (* Defining the input *)
66 files : string list;
67 dump_nast : bool;
68 enable_stronger_await_binding : bool;
69 pocket_universes : bool;
72 let make
73 full_fidelity_json
74 full_fidelity_text_json
75 full_fidelity_dot
76 full_fidelity_dot_edges
77 full_fidelity_errors
78 full_fidelity_errors_all
79 full_fidelity_s_expr
80 full_fidelity_ast_s_expr
81 program_text
82 pretty_print
83 schema
84 is_hh_file
85 codegen
86 php5_compat_mode
87 elaborate_namespaces
88 include_line_comments
89 keep_errors
90 quick_mode
91 lower_coroutines
92 enable_hh_syntax
93 enable_await_as_an_expression
94 fail_open
95 show_file_name
96 files
97 dump_nast
98 enable_stronger_await_binding
99 pocket_universes = {
100 full_fidelity_json;
101 full_fidelity_dot;
102 full_fidelity_dot_edges;
103 full_fidelity_text_json;
104 full_fidelity_errors;
105 full_fidelity_errors_all;
106 full_fidelity_s_expr;
107 full_fidelity_ast_s_expr;
108 program_text;
109 pretty_print;
110 schema;
111 is_hh_file;
112 codegen;
113 php5_compat_mode;
114 elaborate_namespaces;
115 include_line_comments;
116 keep_errors;
117 quick_mode;
118 lower_coroutines;
119 enable_hh_syntax;
120 enable_await_as_an_expression;
121 fail_open;
122 show_file_name;
123 files;
124 dump_nast;
125 enable_stronger_await_binding;
126 pocket_universes
129 let parse_args () =
130 let usage = Printf.sprintf "Usage: %s [OPTIONS] filename\n" Sys.argv.(0) in
131 let full_fidelity_json = ref false in
132 let set_full_fidelity_json () = full_fidelity_json := true in
133 let full_fidelity_text_json = ref false in
134 let set_full_fidelity_text_json () = full_fidelity_text_json := true in
135 let full_fidelity_dot = ref false in
136 let set_full_fidelity_dot () = full_fidelity_dot := true in
137 let full_fidelity_dot_edges = ref false in
138 let set_full_fidelity_dot_edges () = full_fidelity_dot_edges := true in
139 let full_fidelity_errors = ref false in
140 let set_full_fidelity_errors () = full_fidelity_errors := true in
141 let full_fidelity_errors_all = ref false in
142 let set_full_fidelity_errors_all () =
143 full_fidelity_errors_all := true in
144 let full_fidelity_s_expr = ref false in
145 let full_fidelity_ast_s_expr = ref false in
146 let set_full_fidelity_s_expr () = full_fidelity_s_expr := true in
147 let set_full_fidelity_ast_s_expr () = full_fidelity_ast_s_expr := true in
148 let program_text = ref false in
149 let set_program_text () = program_text := true in
150 let pretty_print = ref false in
151 let set_pretty_print () = pretty_print := true in
152 let schema = ref false in
153 let set_schema () = schema := true in
154 let is_hh_file = ref false in
155 let codegen = ref false in
156 let php5_compat_mode = ref false in
157 let elaborate_namespaces = ref true in
158 let include_line_comments = ref false in
159 let keep_errors = ref true in
160 let quick_mode = ref false in
161 let lower_coroutines = ref true in
162 let enable_hh_syntax = ref false in
163 let enable_await_as_an_expression = ref false in
164 let fail_open = ref true in
165 let show_file_name = ref false in
166 let dump_nast = ref false in
167 let enable_stronger_await_binding = ref false in
168 let set_show_file_name () = show_file_name := true in
169 let pocket_universes = ref false in
170 let files = ref [] in
171 let push_file file = files := file :: !files in
172 let options = [
173 (* modes *)
174 "--full-fidelity-json",
175 Arg.Unit set_full_fidelity_json,
176 "Displays the full-fidelity parse tree in JSON format.";
177 "--full-fidelity-text-json",
178 Arg.Unit set_full_fidelity_text_json,
179 "Displays the full-fidelity parse tree in JSON format with token text.";
180 "--full-fidelity-dot",
181 Arg.Unit set_full_fidelity_dot,
182 "Displays the full-fidelity parse tree in GraphViz DOT format.";
183 "--full-fidelity-dot-edges",
184 Arg.Unit set_full_fidelity_dot_edges,
185 "Displays the full-fidelity parse tree in GraphViz DOT format with edge labels.";
186 "--full-fidelity-errors",
187 Arg.Unit set_full_fidelity_errors,
188 "Displays the full-fidelity parser errors, if any.
189 Some errors may be filtered out.";
190 "--full-fidelity-errors-all",
191 Arg.Unit set_full_fidelity_errors_all,
192 "Displays the full-fidelity parser errors, if any.
193 No errors are filtered out.";
194 "--full-fidelity-s-expression",
195 Arg.Unit set_full_fidelity_s_expr,
196 "Displays the full-fidelity parse tree in S-expression format.";
197 "--full-fidelity-ast-s-expression",
198 Arg.Unit set_full_fidelity_ast_s_expr,
199 "Displays the AST produced by the FFP in S-expression format.";
200 "--program-text",
201 Arg.Unit set_program_text,
202 "Displays the text of the given file.";
203 "--pretty-print",
204 Arg.Unit set_pretty_print,
205 "Displays the text of the given file after pretty-printing.";
206 "--schema",
207 Arg.Unit set_schema,
208 "Displays the parser version and schema of nodes.";
209 "--is-hh-file",
210 Arg.Set is_hh_file,
211 "Set the is_hh_file option for the parser.";
212 "--no-is-hh-file",
213 Arg.Clear is_hh_file,
214 "Unset the is_hh_file option for the parser.";
215 "--codegen",
216 Arg.Set codegen,
217 "Set the codegen option for the parser.";
218 "--no-codegen",
219 Arg.Clear codegen,
220 "Unset the codegen option for the parser.";
221 "--php5-compat-mode",
222 Arg.Set php5_compat_mode,
223 "Set the php5_compat_mode option for the parser.";
224 "--no-php5-compat-mode",
225 Arg.Clear php5_compat_mode,
226 "Unset the php5_compat_mode option for the parser.";
227 "--elaborate-namespaces",
228 Arg.Set elaborate_namespaces,
229 "Set the elaborate_namespaces option for the parser.";
230 "--no-elaborate-namespaces",
231 Arg.Clear elaborate_namespaces,
232 "Unset the elaborate_namespaces option for the parser.";
233 "--include-line-comments",
234 Arg.Set include_line_comments,
235 "Set the include_line_comments option for the parser.";
236 "--no-include-line-comments",
237 Arg.Clear include_line_comments,
238 "Unset the include_line_comments option for the parser.";
239 "--keep-errors",
240 Arg.Set keep_errors,
241 "Set the keep_errors option for the parser.";
242 "--no-keep-errors",
243 Arg.Clear keep_errors,
244 "Unset the keep_errors option for the parser.";
245 "--quick-mode",
246 Arg.Set quick_mode,
247 "Set the quick_mode option for the parser.";
248 "--no-quick-mode",
249 Arg.Clear quick_mode,
250 "Unset the quick_mode option for the parser.";
251 "--lower-coroutines",
252 Arg.Set lower_coroutines,
253 "Set the lower_coroutines option for the parser.";
254 "--no-lower-coroutines",
255 Arg.Clear lower_coroutines,
256 "Unset the lower_coroutines option for the parser.";
257 "--fail-open",
258 Arg.Set fail_open,
259 "Set the fail_open option for the parser.";
260 "--no-fail-open",
261 Arg.Clear fail_open,
262 "Unset the fail_open option for the parser.";
263 "--force-hh-syntax",
264 Arg.Set enable_hh_syntax,
265 "Force hh syntax for the parser.";
266 "--enable-await-as-an-expression",
267 Arg.Set enable_await_as_an_expression,
268 "Enable await-as-an-expression";
269 "--show-file-name",
270 Arg.Unit set_show_file_name,
271 "Displays the file name.";
272 "--dump-nast",
273 Arg.Set dump_nast,
274 "Converts the legacy AST to a NAST and prints it.";
275 "--stronger-await-binding",
276 Arg.Set enable_stronger_await_binding,
277 "Increases precedence of await during parsing.";
278 "--pocket-universes",
279 Arg.Set pocket_universes,
280 "Enables support for Pocket Universes";
281 ] in
282 Arg.parse options push_file usage;
283 make
284 !full_fidelity_json
285 !full_fidelity_text_json
286 !full_fidelity_dot
287 !full_fidelity_dot_edges
288 !full_fidelity_errors
289 !full_fidelity_errors_all
290 !full_fidelity_s_expr
291 !full_fidelity_ast_s_expr
292 !program_text
293 !pretty_print
294 !schema
295 !is_hh_file
296 !codegen
297 !php5_compat_mode
298 !elaborate_namespaces
299 !include_line_comments
300 !keep_errors
301 !quick_mode
302 !lower_coroutines
303 !enable_hh_syntax
304 !enable_await_as_an_expression
305 !fail_open
306 !show_file_name
307 (List.rev !files)
308 !dump_nast
309 !enable_stronger_await_binding
310 !pocket_universes
313 open FullFidelityParseArgs
315 let print_error error = error
316 |> Errors.to_absolute
317 |> Errors.to_string
318 |> output_string stdout
320 (* Prints a single FFP error. *)
321 let print_full_fidelity_error source_text error =
322 let text = SyntaxError.to_positioned_string
323 error (SourceText.offset_to_position source_text) in
324 Printf.printf "%s\n" text
326 (* Computes and prints list of all FFP errors from syntax pass and parser pass.
327 * Specifying all_errors=false will attempt to filter out duplicate errors. *)
328 let print_full_fidelity_errors ~source_text ~error_env =
329 let errors = ParserErrors.parse_errors error_env in
330 List.iter (print_full_fidelity_error source_text) errors
332 let handle_existing_file args filename =
333 let popt = ParserOptions.default in
334 let popt = ParserOptions.with_hh_syntax_for_hhvm popt
335 (args.codegen && args.enable_hh_syntax) in
336 let popt = ParserOptions.with_enable_await_as_an_expression popt
337 (args.enable_await_as_an_expression) in
339 (* Parse with the full fidelity parser *)
340 let file = Relative_path.create Relative_path.Dummy filename in
341 let suffix = Relative_path.suffix file in
342 let source_text = SourceText.from_file file in
343 let mode = Full_fidelity_parser.parse_mode source_text in
344 let env = Full_fidelity_parser_env.make
345 ~force_hh:args.enable_hh_syntax
346 ~enable_xhp:args.enable_hh_syntax
347 ~enable_stronger_await_binding:args.enable_stronger_await_binding
348 ~has_dot_hack_extension:(String_utils.string_ends_with suffix ".hack")
349 ?mode () in
350 let syntax_tree = SyntaxTree.make ~env source_text in
351 let editable = SyntaxTransforms.editable_from_positioned syntax_tree in
353 if args.show_file_name then begin
354 Printf.printf "%s\n" filename
355 end;
356 if args.program_text then begin
357 let text = Full_fidelity_editable_syntax.text editable in
358 Printf.printf "%s\n" text
359 end;
360 if args.pretty_print then begin
361 let pretty = Libhackfmt.format_tree syntax_tree in
362 Printf.printf "%s\n" pretty
363 end;
365 let print_errors =
366 args.codegen
367 || args.full_fidelity_errors
368 || args.full_fidelity_errors_all
370 if print_errors then begin
371 let level = if args.full_fidelity_errors_all
372 then ParserErrors.Maximum
373 else ParserErrors.Typical in
374 let hhvm_compat_mode = if args.codegen
375 then ParserErrors.HHVMCompat
376 else ParserErrors.NoCompat in
377 let error_env = ParserErrors.make_env syntax_tree
378 ~level
379 ~hhvm_compat_mode
380 ~codegen:args.codegen
381 ~parser_options:popt
383 print_full_fidelity_errors ~source_text ~error_env
384 end;
386 if args.full_fidelity_s_expr then begin
387 let root = SyntaxTree.root syntax_tree in
388 let str = DebugPos.dump_syntax root in
389 Printf.printf "%s\n" str
390 end;
391 if args.full_fidelity_ast_s_expr || args.dump_nast then begin
392 let module Lowerer = Full_fidelity_ast in
393 let popt =
394 if args.dump_nast
395 then { popt with GlobalOptions.po_enable_concurrent = true }
396 else popt in
397 let env =
398 Lowerer.make_env
399 ~codegen:args.codegen
400 ~php5_compat_mode:args.php5_compat_mode
401 ~elaborate_namespaces:args.elaborate_namespaces
402 ~include_line_comments:args.include_line_comments
403 ~keep_errors:args.keep_errors
404 ~quick_mode:args.quick_mode
405 ~lower_coroutines:args.lower_coroutines
406 ~enable_hh_syntax:args.enable_hh_syntax
407 ~enable_xhp:args.enable_hh_syntax
408 ~parser_options:popt
409 ~fail_open:args.fail_open
410 ~is_hh_file:args.is_hh_file
411 ~pocket_universes:args.pocket_universes
412 file
414 let res = Lowerer.from_file env in
415 let ast = res.Lowerer.ast in
416 let str =
417 if args.dump_nast then
418 Nast.show_program (Ast_to_nast.convert ast)
419 else
420 Debug.dump_ast (Ast.AProgram ast) in
421 Printf.printf "%s\n" str
422 end;
423 if args.full_fidelity_json then begin
424 let json = SyntaxTree.to_json syntax_tree in
425 let str = Hh_json.json_to_string json in
426 Printf.printf "%s\n" str
427 end;
428 if args.full_fidelity_text_json then begin
429 let json = Full_fidelity_editable_syntax.to_json editable in
430 let str = Hh_json.json_to_string json in
431 Printf.printf "%s\n" str
432 end;
433 if args.full_fidelity_dot then begin
434 let dot = Full_fidelity_editable_syntax.to_dot editable false in
435 Printf.printf "%s\n" dot
436 end;
437 if args.full_fidelity_dot_edges then begin
438 let dot = Full_fidelity_editable_syntax.to_dot editable true in
439 Printf.printf "%s\n" dot
442 let handle_file args filename =
443 if Path.file_exists (Path.make filename) then
444 handle_existing_file args filename
445 else
446 Printf.printf "File %s does not exist.\n" filename
448 let rec main args files =
449 if args.schema then begin
450 let schema = Schema.schema_as_json() in
451 Printf.printf "%s\n" schema
452 end;
453 match files with
454 | [] -> ()
455 | file :: tail ->
456 begin
457 Unix.handle_unix_error (handle_file args) file;
458 main args tail
461 let () =
462 let args = parse_args () in
463 EventLogger.init EventLogger.Event_logger_fake 0.0;
464 let handle = SharedMem.init ~num_workers:0 GlobalConfig.default_sharedmem_config in
465 ignore (handle: SharedMem.handle);
466 main args args.files