3 * Copyright (C) 2010, University of Copenhagen DIKU and INRIA.
4 * Copyright (C) 2008, 2009 University of Urbana Champaign
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License (GPL)
8 * version 2 as published by the Free Software Foundation.
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 * file license.txt for more details.
19 (*****************************************************************************)
21 (*****************************************************************************)
23 Common.mk_pr2_wrappers
Flag_parsing_c.verbose_cpp_ast
24 let pr2_debug,pr2_debug_once
=
25 Common.mk_pr2_wrappers
Flag_parsing_c.debug_cpp_ast
27 (*****************************************************************************)
28 (* Cpp Ast Manipulations *)
29 (*****************************************************************************)
32 * cpp-include-expander-builtin.
34 * alternative1: parse and call cpp tour a tour. So let cpp work at
35 * the token level. That's what most tools do.
36 * alternative2: apply cpp at the very end. Process that go through ast
37 * and do the stuff such as #include, macro expand,
38 * ifdef but on the ast!
40 * But need keep those info in ast at least, even bad
41 * macro for instance, and for parse error region ? maybe can
42 * get another chance ?
43 * I think it's better to do the cpp-include-expander in a different step
44 * rather than embedding it in the parser. The parser is already too complex.
45 * Also keep with the tradition to try to parse as-is.
47 * todo? but maybe could discover new info that could help reparse
48 * the ParseError in original file. Try again parsing it by
49 * putting it in a minifile ?
52 * todo? maybe can do some pass that work at the ifdef level and for instance
53 * try to paren them, so have in Ast some stuff that are not
54 * present at parsing time but that can then be constructed after
55 * some processing (a little bit like my type for expression filler,
56 * or position info filler, or include relative position filler).
58 * ??add such info about what was done somewhere ? could build new
59 * ??ast each time but too tedious (maybe need delta-programming!)
61 * todo? maybe change cpp_ast_c to go deeper on local "" ?
69 * cpp_ifdef_statementize: again better to separate concern and in parser
70 * just add the directives in a flat way (IfdefStmt) and later do more
71 * processing and transform them in a tree with some IfdefStmt2.
76 (*****************************************************************************)
78 (*****************************************************************************)
82 | D
of string * string option
86 let i_of_cpp_options xs
=
87 xs
+> Common.map_filter
(function
92 let cpp_option_of_cmdline (xs
, ys
) =
93 (xs
+> List.map
(fun s
-> I s
)) ++
94 (ys
+> List.map
(fun s
->
95 if s
=~
"\\([A-Z][A-Z0-9_]*\\)=\\(.*\\)"
97 let (def
, value) = matched2 s
in
103 (*****************************************************************************)
105 (*****************************************************************************)
106 let (show_cpp_i_opts
: string list
-> unit) = fun xs
->
107 if not
(null xs
) then begin
113 let (show_cpp_d_opts
: string list
-> unit) = fun xs
->
114 if not
(null xs
) then begin
119 (* ---------------------------------------------------------------------- *)
120 let trace_cpp_process depth mark inc_file
=
121 pr2_debug (spf
"%s>%s %s"
122 (Common.repeat
"-" depth
+> Common.join
"")
124 (s_of_inc_file_bis inc_file
));
129 (*****************************************************************************)
131 (*****************************************************************************)
134 let _hcandidates = Hashtbl.create
101
136 let init_adjust_candidate_header_files dir
=
138 let files = Common.files_of_dir_or_files
ext [dir
] in
140 files +> List.iter
(fun file
->
141 let base = Filename.basename file
in
143 Hashtbl.add
_hcandidates base file
;
149 (* may return a list of match ? *)
150 let find_header_file1 cppopts dirname inc_file
=
154 Filename.concat dirname
(Ast_c.s_of_inc_file inc_file
) in
155 if Sys.file_exists
finalfile
159 i_of_cpp_options cppopts
+> Common.map_filter
(fun dirname
->
161 Filename.concat dirname
(Ast_c.s_of_inc_file inc_file
) in
162 if Sys.file_exists
finalfile
167 pr2 ("CPPAST: weird include not handled:" ^ s
);
170 (* todo? can try find most precise ? first just use basename but
171 * then maybe look if have also some dir in common ?
173 let find_header_file2 inc_file
=
177 let s = (Ast_c.s_of_inc_file inc_file
) in
178 let base = Filename.basename
s in
180 let res = Hashtbl.find_all
_hcandidates base in
183 pr2_debug ("CPPAST: find header in other dir: " ^ file
);
193 let find_header_file cppopts dirname inc_file
=
194 let res1 = find_header_file1 cppopts dirname inc_file
in
197 | [] -> find_header_file2 inc_file
203 (* ---------------------------------------------------------------------- *)
204 let _headers_hash = Hashtbl.create
101
206 (* On freebsd ocaml is trashing, use up to 1.6Go of memory and then
207 * building the database_c takes ages.
209 * So just limit with following threshold to avoid this trashing, simple.
211 * On netbsd, got a Out_of_memory exn on this file;
212 * /home/pad/software-os-src2/netbsd/dev/microcode/cyclades-z/
213 * even if the cache is small. That's because huge single
214 * ast element and probably the ast marshalling fail.
216 let default_threshold_cache_nb_files = 200
218 let parse_c_and_cpp_cache
219 ?
(threshold_cache_nb_files
= default_threshold_cache_nb_files) file
=
221 if Hashtbl.length
_headers_hash > threshold_cache_nb_files
222 then Hashtbl.clear
_headers_hash;
224 Common.memoized
_headers_hash file
(fun () ->
225 Parse_c.parse_c_and_cpp file
230 (*****************************************************************************)
232 (*****************************************************************************)
235 let (cpp_expand_include2
:
236 ?depth_limit
:int option ->
237 ?threshold_cache_nb_files
:int ->
238 cpp_option list
-> Common.dirname
-> Ast_c.program
-> Ast_c.program
) =
239 fun ?
(depth_limit
=None
) ?threshold_cache_nb_files iops dirname ast
->
241 if !Flag_parsing_c.debug_cpp_ast
242 then pr2_xxxxxxxxxxxxxxxxx
();
244 let already_included = ref [] in
246 let rec aux stack dirname ast
=
247 let depth = List.length stack
in
249 ast
+> Visitor_c.vk_program_s
{ Visitor_c.default_visitor_c_s
with
250 Visitor_c.kcppdirective_s
= (fun (k
, bigf
) cpp ->
252 | Include
{i_include
= (inc_file
, ii
);
253 i_rel_pos
= h_rel_pos
;
258 (match depth_limit
with
259 | Some limit
when depth >= limit
-> cpp
262 (match find_header_file iops dirname inc_file
with
264 if List.mem file
!already_included
266 (* pr2 ("already included: " ^ file); *)
267 trace_cpp_process depth "*" inc_file
;
270 trace_cpp_process depth "" inc_file
;
271 Common.push2 file
already_included;
273 Flag_parsing_c.verbose_parsing
:= false;
274 Flag_parsing_c.verbose_lexing
:= false;
276 parse_c_and_cpp_cache ?threshold_cache_nb_files file
279 let ast = Parse_c.program_of_program2 ast2
in
280 let dirname'
= Filename.dirname file
in
283 let ast'
= aux (file
::stack
) dirname'
ast in
285 Include
{i_include
= (inc_file
, ii
);
286 i_rel_pos
= h_rel_pos
;
288 i_content
= Some
(file
, ast'
);
292 trace_cpp_process depth "!!" inc_file
;
293 pr2 "CPPAST: file not found";
296 trace_cpp_process depth "!!" inc_file
;
297 pr2 "CPPAST: too much candidates";
308 let cpp_expand_include ?depth_limit ?threshold_cache_nb_files a b c
=
309 Common.profile_code
"cpp_expand_include"
310 (fun () -> cpp_expand_include2 ?depth_limit ?threshold_cache_nb_files a b c
)
313 let unparse_showing_include_content ?
317 (*****************************************************************************)
318 (* Ifdef-statementize *)
319 (*****************************************************************************)
322 let is_ifdef_and_same_tag tag x
=
324 | IfdefStmt
(IfdefDirective
((_
, tag2
),_
)) ->
326 | StmtElem _
| CppDirectiveStmt _
-> false
327 | IfdefStmt2 _
-> raise
(Impossible
77)
331 (* What if I skipped in the parser only some of the ifdef elements
332 * of the same tag. Once I passed one, I should pass all of them and so
333 * at least should detect here that one tag is not "valid". Maybe in the parser
334 * can return or marked some tags as "partially_passed_ifdef_tag".
335 * Maybe could do in ast_c a MatchingTag of int * bool ref (* one_was_passed *)
336 * where the
ref will be shared by the ifdefs
with the same matching tag
337 * indice
. Or simply count the number
of directives
with the same tag
and
338 * put this information
in the tag
. Hence the total_with_this_tag below
.
340 let should_ifdefize (tag
,ii
) ifdefs_directives xxs
=
341 let IfdefTag (_tag
, total_with_this_tag
) = tag
in
343 if total_with_this_tag
<> List.length ifdefs_directives
345 let strloc = Ast_c.strloc_of_info
(List.hd ii
) in
346 pr2 (spf
"CPPASTC: can not ifdefize ifdef at %s" strloc);
347 pr2 "CPPASTC: some of its directives were passed";
350 (* todo? put more condition ? dont ifdefize declaration ? *)
357 (* return a triple, (ifdefs directive * grouped xs * remaining sequencable)
358 * XXX1 XXX2 elsif YYY1 else ZZZ1 endif WWW1 WWW2
359 * => [elsif, else, endif], [XXX1 XXX2; YYY1; ZZZ1], [WWW1 WWW2]
361 let group_ifdef tag xs
=
362 let (xxs
, xs
) = group_by_post
(is_ifdef_and_same_tag tag
) xs
in
364 xxs
+> List.map snd
+> List.map
(fun x
->
367 | StmtElem _
| CppDirectiveStmt _
| IfdefStmt2 _
-> raise
(Impossible
78)
373 let rec cpp_ifdef_statementize ast =
374 Visitor_c.vk_program_s
{ Visitor_c.default_visitor_c_s
with
375 Visitor_c.kstatementseq_list_s
= (fun (k
, bigf
) xs
->
382 Visitor_c.vk_statement_sequencable_s bigf stseq
::aux xs
383 | CppDirectiveStmt directive
->
384 Visitor_c.vk_statement_sequencable_s bigf stseq
::aux xs
387 | IfdefDirective
((Ifdef
,tag
),ii
) ->
389 let (restifdefs
, xxs
, xs'
) = group_ifdef tag xs
in
390 if should_ifdefize (tag
,ii
) (ifdef
::restifdefs
) xxs
392 let res = IfdefStmt2
(ifdef
::restifdefs
, xxs
) in
393 Visitor_c.vk_statement_sequencable_s bigf
res::aux xs'
395 Visitor_c.vk_statement_sequencable_s bigf stseq
::aux xs
397 | IfdefDirective
(((IfdefElseif
|IfdefElse
|IfdefEndif
),b
),ii
) ->
398 pr2 "weird: first directive is not a ifdef";
399 (* maybe not weird, just that should_ifdefize
401 Visitor_c.vk_statement_sequencable_s bigf stseq
::aux xs
404 | IfdefStmt2
(ifdef
, xxs
) ->
405 failwith
"already applied cpp_ifdef_statementize"
413 (*****************************************************************************)
415 (*****************************************************************************)
417 let (cpp_expand_macro_expr
:
418 Ast_c.define_kind
-> Ast_c.argument
Ast_c.wrap2 list
->
419 Ast_c.expression
option) =