2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
10 module Hh_debug
= Debug
12 module Lowerer
= Full_fidelity_ast
13 module Syntax
= Full_fidelity_positioned_syntax
14 module SyntaxKind
= Full_fidelity_syntax_kind
15 module SourceText
= Full_fidelity_source_text
16 module SyntaxTree
= Full_fidelity_syntax_tree
19 let purpose = "Read a single Hack file and produce the resulting S-Expression."
20 let extra = "(Options for development / parser selection and comparisson.)"
21 let usage = Printf.sprintf
22 "Usage: %s <options> filename\n%s\n%s"
27 type parser_return
= Parser_return.t
* float
33 let exit_code : result
-> int = function
41 | Benchmark_batch
of int
43 let exit_with : result
-> 'a
= fun r
-> exit
(exit_code r
)
45 let handle_errors : Errors.t
-> unit = fun errorl
->
47 let print_err err
= Out_channel.output_string stderr
(to_string
(to_absolute err
)) in
51 iter_error_list
print_err errorl
;
61 (file
: Relative_path.t
)
66 ~include_line_comments
:true
67 ~fail_open
:allow_malformed
68 ~keep_errors
:(not allow_malformed
)
72 if iters
< 1 then () else
74 ignore
(Lowerer.from_file
env : Lowerer.result
);
77 let run_validated_ffp : bool -> Relative_path.t
-> Lowerer.result
=
78 fun pocket_universes file
->
79 let open SyntaxTree
in
80 let source_text = SourceText.from_file file
in
81 let tree = make
source_text in
82 let script = root
tree in
85 Syntax.Validated.validate_script
script
87 | Syntax.Validated.Validation_failure
(k
,s
) as e
-> begin
88 Printf.eprintf
"FAILURE: expected: %s actual: %s\n"
89 (Option.value_map ~f
:SyntaxKind.to_string ~default
:"Some token" k
)
90 (SyntaxKind.to_string
(Syntax.kind s
));
94 let invalidated = Syntax.Validated.invalidate_script
validated in
95 let revalidated = Syntax.Validated.validate_script
invalidated in
96 assert (validated = revalidated); (* Idempotence *after* validation *)
97 assert (script = invalidated); (* Idempotence *of* validation *)
99 Full_fidelity_editable_positioned_syntax.from_positioned_syntax
101 let is_hh_file = is_hack
tree in
102 let env = Lowerer.make_env ~
is_hh_file ~pocket_universes file
in
103 let comments = Lowerer.scour_comments_and_add_fixmes
env source_text script in
104 let module Lowerer
= Lowerer.WithPositionedSyntax
(Full_fidelity_editable_positioned_syntax
) in
105 Lowerer.lower
env ~
source_text ~
script:invalidated comments
107 let measure : (unit -> 'a
) -> 'a
* float = fun f
->
108 let start = Unix.gettimeofday
() in
110 let stop = Unix.gettimeofday
() in
116 (file
: Relative_path.t
)
117 (conf
: parser_config
)
126 let res = run_ffp ~codegen ~allow_malformed ~pocket_universes file
in
127 let ast = res.Lowerer.ast in
130 let nast = Ast_to_nast.convert
ast in
131 Nast.show_program
nast
133 if not hash
then dumper
ast
135 let decl_hash = Ast_utils.generate_ast_decl_hash
ast in
136 OpaqueDigest.to_hex
decl_hash
138 Printf.printf
"%s" output
140 let res = run_validated_ffp pocket_universes file
in
141 Printf.printf
"%s" (dumper
res.Lowerer.ast)
142 | Benchmark_batch iters
->
143 let filename = Relative_path.S.to_string file
in
145 try (measure (fun () ->
146 run_ffp ~codegen ~iters ~allow_malformed
:false ~pocket_universes file
))
148 Printf.printf
"FAIL, %s\n" filename;
152 let res = Printf.sprintf
153 "PASS, %s, %12.10f\n"
158 Printexc.record_backtrace
true;
159 let use_parser = ref "ffp" in
160 let hash = ref false in
161 let dumper = ref Hh_debug.dump_ast
in
162 let filename = ref "" in
163 let num_runs = ref 100 in
164 let benchmark_files = ref [] in
165 let no_codegen = ref false in
166 let allow_malformed = ref false in
167 let dump_nast = ref false in
168 let pocket_universes = ref false in
170 [ ("--hash", Set
hash,
171 "Get the decl level parsing hash of a given file "
173 ; ("--sorted", Unit
(fun () -> dumper := Hh_debug.dump_sorted_ast
),
174 "When using the `compare` parser, the (lexicographically) sort the " ^
175 "S-Expressions before diffing"
177 ; ("--show-pos", Unit
(fun () -> Sof.show_pos
:= true),
178 "Show positional information on the AST"
180 ; ("--num-runs", Int
(fun x
-> num_runs := x
),
181 "How many times to benchmark if in benchmark mode [default: 100]"
183 ; ("--benchmark_batch", Rest
(fun fn
-> benchmark_files := fn
::!benchmark_files),
184 "Run benchmarking on a list of files"
186 ; ("--no-codegen", Set
no_codegen,
187 "Turn off codegen mode when parsing with FFP [default: false]"
189 ; ("--nast", Set
dump_nast,
190 "Convert to NAST and print [default: false]"
192 ; ("--allow-malformed", Set
allow_malformed,
193 "Allow malformed files (such as for testing IDE services) [default: false]"
195 ; ("--pocket-universes", Set
pocket_universes,
196 "Enables support for Pocket Universes [default: false]"
198 ]) (fun fn
-> filename := fn
) usage;
199 let parse_function = match !use_parser with
200 | _ when !benchmark_files <> [] -> Benchmark_batch
!num_runs
202 | "validated" -> ValidatedFFP
203 | s
-> raise
(Failure
(Printf.sprintf
"Unknown parser '%s'\n" s
))
205 if String.length
!filename = 0 && !benchmark_files = [] then failwith
"No filename given";
206 EventLogger.init
EventLogger.Event_logger_fake
0.0;
207 let handle = SharedMem.init ~num_workers
:0 GlobalConfig.default_sharedmem_config
in
208 ignore
(handle: SharedMem.handle);
209 let dumper ast = !dumper (Ast.AProgram
ast) in
211 let file = Relative_path.create
Relative_path.Dummy fn
in
217 ~codegen
:(not
!no_codegen)
218 ~
allow_malformed:!allow_malformed
219 ~
dump_nast:!dump_nast
220 ~
pocket_universes:!pocket_universes
222 if !benchmark_files <> [] then
223 List.iter ~f
:parse_file !benchmark_files
225 Unix.handle_unix_error
(parse_file) !filename