Release coccinelle-0.1.10
[bpt/coccinelle.git] / parsing_c / unparse_hrule.ml
CommitLineData
978fd7e5
C
1(* Copyright (C) 2006, 2007 Julia Lawall
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License (GPL)
5 * version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * file license.txt for more details.
11 *
12 * This file was part of Coccinelle.
13 *)
14
faf9a90c
C
15module Ast = Ast_cocci
16module V = Visitor_ast
34e49164 17
faf9a90c
C
18let error x s =
19 failwith
20 (Printf.sprintf "unparse_hrule: line: %d, %s" (Ast.get_line x) s)
34e49164 21
faf9a90c 22let names = ref ([] : (string * int ref) list)
34e49164 23
0708f913
C
24let started_files = ref ([] : (string * bool) list)
25let typedefs = ref ([] : (string * string list ref) list)
26let current_outfile = ref ""
27
28let prefix = "_cocci_"
29
34e49164
C
30(* ----------------------------------------------------------------------- *)
31(* Create rule to check for header include *)
32
33let print_header_rule pr srcfile =
34 match Str.split (Str.regexp "/") srcfile with
35 [x] ->
0708f913
C
36 pr "@header@\n@@\n\n#include \"";
37 pr x; pr "\"\n\n"; true
34e49164
C
38 | l ->
39 let rec loop = function
40 [] -> false
41 | [x] ->
0708f913
C
42 pr "@header@\n@@\n\n#include \"";
43 pr x; pr "\"\n\n"; true
34e49164
C
44 | "include"::(x::xs) ->
45 pr "@header@\n@@\n\n#include <";
46 let x =
47 if Str.string_match (Str.regexp "asm-") x 0 then "asm" else x in
48 pr (String.concat "/" (x::xs));
49 pr ">\n\n"; true
50 | x::xs -> loop xs in
51 loop l
52
faf9a90c
C
53(* ----------------------------------------------------------------------- *)
54(* Print check that we are not in the defining function *)
55
0708f913 56let print_check_rule pr function_name function_name_count header_req =
faf9a90c 57 (if header_req
0708f913
C
58 then pr (Printf.sprintf "@same_%s depends on header@\n" function_name_count)
59 else pr (Printf.sprintf "@same_%s@\n" function_name_count));
faf9a90c
C
60 pr "position p;\n";
61 pr "@@\n\n";
62 pr function_name; pr "@p(...) { ... }\n\n"
63
64(* ----------------------------------------------------------------------- *)
0708f913 65(* get parameters of the matched function *)
faf9a90c
C
66
67let rec env_lookup fn = function
68 [] -> failwith "no binding"
69 | (nm,vl)::rest when fn nm -> vl
70 | _::rest -> env_lookup fn rest
71
72let get_paramst env =
73 let argname = ref ("","") in
74 let fn ((_,nm) as name) =
75 if nm = "ARGS"
76 then (argname := name; true)
77 else false in
78 match env_lookup fn env with
79 Ast_c.MetaParamListVal(paramst) -> (paramst,!argname)
80 | _ -> failwith "not possible"
81
82let get_function_name rule env =
83 let donothing r k e = k e in
84 let option_default = [] in
85 let bind = Common.union_set in
86 let do_any_list_list r any_list_list =
87 List.fold_left
88 (List.fold_left
89 (function prev -> function cur ->
90 bind (r.V.combiner_anything cur) prev))
91 [] any_list_list in
92 let mcode r mc =
93 match Ast.get_mcodekind mc with
708f4980 94 Ast.MINUS(_,_,_,any_list_list) -> do_any_list_list r any_list_list
faf9a90c
C
95 | Ast.CONTEXT(_,any_befaft) ->
96 (match any_befaft with
97 Ast.BEFORE(any_list_list) | Ast.AFTER(any_list_list) ->
98 do_any_list_list r any_list_list
99 | Ast.BEFOREAFTER(ba,aa) ->
100 bind (do_any_list_list r ba) (do_any_list_list r aa)
101 | Ast.NOTHING -> [])
102 | Ast.PLUS -> [] in
103 let expression r k e =
104 bind (k e)
105 (match Ast.unwrap e with
106 Ast.FunCall(fn,lp,args,rp) ->
107 (match Ast.undots args with
108 [e] ->
109 (match Ast.unwrap e with
110 Ast.MetaExprList(nm,_,_,_) ->
111 (match Ast.unwrap_mcode nm with
112 (_,"ARGS") when Ast.get_mcodekind nm = Ast.PLUS ->
113 (match Ast.unwrap fn with
114 Ast.Ident(id) ->
115 (match Ast.unwrap id with
116 Ast.MetaId(nm,_,_,_)
117 | Ast.MetaFunc(nm,_,_,_)
118 | Ast.MetaLocalFunc(nm,_,_,_) ->
119 [Ast.unwrap_mcode nm]
120 | _ -> [])
121 | _ -> [])
122 | _ -> [])
123 | _ -> [])
124 | _ -> [])
125 | _ -> []) in
126 let names =
127 (V.combiner bind option_default
128 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
129 donothing donothing donothing donothing
130 donothing expression donothing donothing donothing donothing donothing
131 donothing donothing donothing donothing donothing).V.combiner_top_level
132 rule in
133 match names with
134 [name] ->
135 (match env_lookup (function nm -> nm = name) env with
136 Ast_c.MetaIdVal(s) | Ast_c.MetaFuncVal(s)
137 | Ast_c.MetaLocalFuncVal(s) -> s
138 | _ -> error rule "not possible")
139 | _ -> error rule "inconsistent rule generation"
140
34e49164
C
141(* ----------------------------------------------------------------------- *)
142(* Print metavariable declarations *)
143
0708f913 144let rec print_typedef pr = function
b1b2de81
C
145 (Ast_c.TypeName(name,_),_) ->
146 let s = Ast_c.str_of_name name in
0708f913
C
147 let typedefs =
148 try List.assoc !current_outfile !typedefs
149 with Not_found ->
150 let td = ref [] in
151 typedefs := (!current_outfile,td)::!typedefs;
152 td in
34e49164
C
153 if not (List.mem s !typedefs)
154 then (typedefs := s::!typedefs; pr "typedef "; pr s; pr ";\n")
0708f913 155 | (Ast_c.Pointer(_,ty),_) -> print_typedef pr ty
34e49164
C
156 | _ -> ()
157
faf9a90c
C
158let rewrap_str s ii =
159 {ii with Ast_c.pinfo =
160 (match ii.Ast_c.pinfo with
161 Ast_c.OriginTok pi ->
162 Ast_c.OriginTok { pi with Common.str = s;}
163 | Ast_c.ExpandedTok (pi,vpi) ->
164 Ast_c.ExpandedTok ({ pi with Common.str = s;},vpi)
165 | Ast_c.FakeTok (_,vpi) -> Ast_c.FakeTok (s,vpi)
166 | Ast_c.AbstractLineTok pi ->
167 Ast_c.AbstractLineTok { pi with Common.str = s;})}
168
b1b2de81
C
169let rewrap_prefix_name prefix name =
170 match name with
171 | Ast_c.RegularName (s, iiname) ->
172 let iis = Common.tuple_of_list1 iiname in
173 let iis' = rewrap_str (prefix^s) iis in
174 Ast_c.RegularName (prefix ^ s, [iis'])
175 | Ast_c.CppConcatenatedName _ | Ast_c.CppVariadicName _
176 | Ast_c.CppIdentBuilder _
177 -> raise Common.Todo
178
179
0708f913 180let print_metavar pr = function
b1b2de81
C
181 | {Ast_c.p_namei = Some name;
182 p_type = (_,(Ast_c.Pointer(_,(Ast_c.BaseType(Ast_c.Void),_)),_));
183 }
34e49164 184 ->
b1b2de81 185 let param = Ast_c.str_of_name name in
0708f913 186 pr ("expression "^prefix); pr param
b1b2de81
C
187 | ({Ast_c.p_namei = Some name; p_type = (_,ty)} : Ast_c.parameterType) ->
188
189 let name' = rewrap_prefix_name prefix name in
190
0708f913 191 print_typedef pr ty;
b1b2de81 192
34e49164
C
193 Pretty_print_c.pp_param_gen
194 (function x ->
195 let str = Ast_c.str_of_info x in
196 if not (List.mem str ["const";"volatile"])
faf9a90c 197 then pr str)
34e49164 198 (function _ -> pr " ")
b1b2de81
C
199 {Ast_c.p_register = (false,[]);
200 p_namei = Some name';
201 p_type = (({Ast_c.const = false; Ast_c.volatile = false},[]),ty)
202 }
34e49164
C
203 | _ -> failwith "function must have named parameters"
204
faf9a90c 205let make_exp = function
b1b2de81 206 ({Ast_c.p_namei = Some name; p_type = ty}, comma_ii) ->
faf9a90c 207 let no_info = (None,Ast_c.NotTest) in
b1b2de81
C
208
209 let name' = rewrap_prefix_name prefix name in
210
faf9a90c 211 let exp =
b1b2de81 212 ((Ast_c.Ident (name'),ref no_info),Ast_c.noii) in
faf9a90c
C
213 (name,(Common.Left exp,comma_ii))
214 | _ -> failwith "bad parameter"
215
0708f913 216let print_extra_typedefs pr env =
faf9a90c
C
217 let bigf =
218 { Visitor_c.default_visitor_c with
219 Visitor_c.ktype = (fun (k, bigf) ty ->
220 match ty with
0708f913 221 (_,((Ast_c.TypeName(_,_),_) as ty)) -> print_typedef pr ty
faf9a90c
C
222 | _ -> k ty) } in
223 List.iter
224 (function (_,vl) ->
225 match vl with
226 Ast_c.MetaIdVal(_) | Ast_c.MetaFuncVal(_)
227 | Ast_c.MetaLocalFuncVal(_) -> ()
228 | Ast_c.MetaExprVal(exp) -> Visitor_c.vk_expr bigf exp
229 | Ast_c.MetaExprListVal(args) -> Visitor_c.vk_argument_list bigf args
230 | Ast_c.MetaParamVal(param) -> Visitor_c.vk_param bigf param
231 | Ast_c.MetaParamListVal(params) -> Visitor_c.vk_param_list bigf params
232
233 | Ast_c.MetaTypeVal(ty) -> Visitor_c.vk_type bigf ty
113803cf 234 | Ast_c.MetaInitVal(ty) -> Visitor_c.vk_ini bigf ty
faf9a90c
C
235 | Ast_c.MetaStmtVal(stm) -> Visitor_c.vk_statement bigf stm
236 | Ast_c.MetaPosVal _ | Ast_c.MetaPosValList _
237 | Ast_c.MetaListlenVal _ -> ())
238 env
239
240let rename argids env =
b1b2de81
C
241 let argenv = List.map (function name ->
242 let arg = Ast_c.str_of_name name in
243 (arg,prefix^arg)
244 ) argids in
faf9a90c
C
245 let lookup x = try List.assoc x argenv with Not_found -> x in
246 let bigf =
247 { Visitor_c.default_visitor_c_s with
248 Visitor_c.kexpr_s = (fun (k,bigf) e ->
249 match e with
b1b2de81
C
250 ((Ast_c.Ident (name), info), []) ->
251
252 (* pad: assert is_regular_ident ? *)
253 let s = Ast_c.str_of_name name in
254 let ii = Ast_c.info_of_name name in
faf9a90c 255 let new_name = lookup s in
b1b2de81
C
256 let new_id = Ast_c.RegularName (new_name, [rewrap_str new_name ii]) in
257 ((Ast_c.Ident (new_id), info), Ast_c.noii)
faf9a90c
C
258 | _ -> k e) } in
259 List.map
260 (function (x,vl) ->
261 (x,
262 match vl with
263 Ast_c.MetaIdVal(_) | Ast_c.MetaFuncVal(_)
264 | Ast_c.MetaLocalFuncVal(_) -> vl
265 | Ast_c.MetaExprVal(exp) ->
266 Ast_c.MetaExprVal(Visitor_c.vk_expr_s bigf exp)
267 | Ast_c.MetaExprListVal(args) ->
268 Ast_c.MetaExprListVal(Visitor_c.vk_arguments_s bigf args)
269 | Ast_c.MetaParamVal(param) ->
270 Ast_c.MetaParamVal(Visitor_c.vk_param_s bigf param)
271 | Ast_c.MetaParamListVal(params) ->
272 Ast_c.MetaParamListVal(Visitor_c.vk_params_s bigf params)
273
274 | Ast_c.MetaTypeVal(ty) ->
275 Ast_c.MetaTypeVal(Visitor_c.vk_type_s bigf ty)
113803cf
C
276 | Ast_c.MetaInitVal(ini) ->
277 Ast_c.MetaInitVal(Visitor_c.vk_ini_s bigf ini)
faf9a90c
C
278 | Ast_c.MetaStmtVal(stm) ->
279 Ast_c.MetaStmtVal(Visitor_c.vk_statement_s bigf stm)
280 | Ast_c.MetaPosVal _ | Ast_c.MetaPosValList _
281 | Ast_c.MetaListlenVal _ -> vl))
282 env
283
0708f913
C
284let print_one_type pr env = function
285 (Type_cocci.MetaType(name,keep,inherited)) as ty ->
286 (try
287 match List.assoc name env with
288 Ast_c.MetaTypeVal ty ->
289 Pretty_print_c.pp_type_gen
290 (function x -> pr (Ast_c.str_of_info x))
291 (function _ -> pr " ")
292 ty
293 | _ -> failwith "impossible"
294 with Not_found -> pr (Type_cocci.type2c ty))
295 | ty -> pr (Type_cocci.type2c ty)
296
297let print_types pr env = function
faf9a90c 298 None -> ()
0708f913 299 | Some [ty] -> print_one_type pr env ty
faf9a90c
C
300 | Some types ->
301 pr "{";
0708f913
C
302 Common.print_between (function _ -> pr ", ") (print_one_type pr env)
303 types;
faf9a90c
C
304 pr "}"
305
0708f913 306let pp_meta_decl pr env decl =
faf9a90c
C
307 let no_arity = function Ast.NONE -> () | _ -> failwith "no arity allowed" in
308 let pp_name (_,n) = pr n in
309 match decl with
310 Ast.MetaIdDecl(ar, name) ->
311 no_arity ar; pr "identifier "; pp_name name; pr ";\n"
978fd7e5 312 | Ast.MetaFreshIdDecl(name, Ast.NoVal) ->
b1b2de81 313 pr "fresh identifier "; pp_name name; pr ";\n"
978fd7e5 314 | Ast.MetaFreshIdDecl(name, Ast.StringSeed x) ->
b1b2de81 315 pr "fresh identifier "; pp_name name; pr " = \""; pr x; pr "\";\n"
978fd7e5
C
316 | Ast.MetaFreshIdDecl(name, Ast.ListSeed x) ->
317 failwith "unparse_hrule: not supported"
faf9a90c
C
318 | Ast.MetaTypeDecl(ar, name) ->
319 no_arity ar; pr "type "; pp_name name; pr ";\n"
113803cf
C
320 | Ast.MetaInitDecl(ar, name) ->
321 no_arity ar; pr "initialiser "; pp_name name; pr ";\n"
faf9a90c
C
322 | Ast.MetaListlenDecl(name) -> ()
323 | Ast.MetaParamDecl(ar, name) ->
324 no_arity ar; pr "parameter "; pp_name name; pr ";\n"
325 | Ast.MetaParamListDecl(ar, name, None) ->
326 no_arity ar; pr "parameter list "; pp_name name; pr ";\n"
327 | Ast.MetaParamListDecl(ar, name, Some len) ->
328 no_arity ar; pr "parameter list "; pp_name name;
329 pr "["; pp_name len; pr "]"; pr ";\n"
330 | Ast.MetaConstDecl(ar, name, types) ->
0708f913 331 no_arity ar; pr "constant "; print_types pr env types;
faf9a90c
C
332 pp_name name; pr ";\n"
333 | Ast.MetaErrDecl(ar, name) ->
334 no_arity ar; pr "error "; pp_name name; pr ";\n"
335 | Ast.MetaExpDecl(ar, name, None) ->
336 no_arity ar; pr "expression "; pp_name name; pr ";\n"
337 | Ast.MetaExpDecl(ar, name, types) ->
0708f913 338 no_arity ar; print_types pr env types; pp_name name; pr ";\n"
faf9a90c
C
339 | Ast.MetaIdExpDecl(ar, name, types) ->
340 no_arity ar; pr "idexpression ";
0708f913 341 print_types pr env types; pp_name name; pr ";\n"
faf9a90c
C
342 | Ast.MetaLocalIdExpDecl(ar, name, types) ->
343 no_arity ar; pr "local idexpression ";
0708f913 344 print_types pr env types; pp_name name; pr ";\n"
faf9a90c
C
345 | Ast.MetaExpListDecl(ar, name, None) ->
346 no_arity ar; pr "parameter list "; pp_name name; pr ";\n"
347 | Ast.MetaExpListDecl(ar, name, Some len) ->
348 no_arity ar; pr "parameter list ";
349 pp_name name; pr "["; pp_name len; pr "]"; pr ";\n"
350 | Ast.MetaStmDecl(ar, name) ->
351 no_arity ar; pr "statement "; pp_name name; pr ";\n"
352 | Ast.MetaStmListDecl(ar, name) ->
353 no_arity ar; pr "statement list "; pp_name name; pr ";\n"
354 | Ast.MetaFuncDecl(ar, name) ->
355 no_arity ar; pr "function "; pp_name name; pr ";\n"
356 | Ast.MetaLocalFuncDecl(ar, name) ->
357 no_arity ar; pr "local function "; pp_name name; pr ";\n"
358 | Ast.MetaPosDecl(ar, name) ->
359 no_arity ar; pr "position "; pp_name name; pr ";\n"
360 | Ast.MetaDeclarerDecl(ar, name) ->
361 no_arity ar; pr "declarer "; pp_name name; pr ";\n"
362 | Ast.MetaIteratorDecl(ar, name) ->
363 no_arity ar; pr "iterator "; pp_name name; pr ";\n"
364
0708f913 365let print_metavariables pr local_metas paramst env header_req function_name =
34e49164
C
366 (if header_req
367 then pr "@depends on header@\n"
368 else pr "@@\n");
0708f913 369 pr (Printf.sprintf "position _p!=same_%s.p;\n" function_name);
faf9a90c 370 pr "identifier _f;\n";
faf9a90c 371 let rec loop = function
b1b2de81 372 [] | [{Ast_c.p_type =(_,(Ast_c.BaseType(Ast_c.Void),_))},_] -> []
faf9a90c 373 | ((first,_) as f)::rest ->
0708f913 374 print_metavar pr first; pr ";\n";
faf9a90c
C
375 (make_exp f) :: loop rest in
376 let args = loop paramst in
0708f913
C
377 print_extra_typedefs pr env;
378 List.iter (pp_meta_decl pr env) local_metas;
faf9a90c
C
379 pr "@@\n\n";
380 args
34e49164
C
381
382(* ----------------------------------------------------------------------- *)
faf9a90c
C
383(* print_start/end *)
384
385let print_start pr =
386 pr "_f@_p(...) { <+...\n"
34e49164 387
faf9a90c
C
388let print_end pr =
389 pr "\n...+> }\n"
34e49164
C
390
391(* ----------------------------------------------------------------------- *)
392(* Print call to the defined function *)
393
394let print_param_name pr = function
b1b2de81 395 {Ast_c.p_namei = Some name} -> pr (Ast_c.str_of_name name)
34e49164
C
396 | _ -> failwith "function must have named parameters"
397
485bce71 398let pp_def_gen pr defn isexp =
b1b2de81
C
399 let {Ast_c.f_name = name; f_type = (_, (paramst, (b, iib))); } = defn in
400 pr (Ast_c.str_of_name name); pr "(";
34e49164
C
401 (if b then failwith "not handling variable argument functions");
402 (match paramst with
b1b2de81 403 [] | [{Ast_c.p_type = (_,(Ast_c.BaseType(Ast_c.Void),_))},_] -> ()
34e49164
C
404 | (first,_)::rest ->
405 print_param_name pr first;
406 List.iter (function (x,_) -> pr ", "; print_param_name pr x) rest);
407 pr ")"; if not isexp then pr ";"
408
409(* ----------------------------------------------------------------------- *)
410(* Entry point *)
411
faf9a90c
C
412let pp_rule local_metas ast env srcfile =
413 let (paramst,args_name) = get_paramst env in
414 (* get rule information *)
415 let (rule,printable) =
416 match ast with
417 Ast.CocciRule(_,_,[body],_,_) -> (* could extend to use attributes *)
418 (body,
419 match Ast.unwrap body with
420 Ast.DECL(s) -> [[Ast.StatementTag s]]
421 | Ast.CODE(ss) -> [[Ast.StmtDotsTag ss]]
422 | _ -> error body "bad rule body")
423 | _ -> failwith "bad rule" in
424 (* create the output file *)
425 let outdir =
426 match !Flag.make_hrule with
427 Some outdir -> outdir
428 | None -> error rule "not possible" in
429 let function_name = get_function_name rule env in
0708f913 430 let function_name_count =
faf9a90c 431 try
0708f913 432 let cell = List.assoc function_name !names in
faf9a90c
C
433 let ct = !cell in
434 cell := ct + 1;
0708f913 435 function_name ^ (string_of_int ct)
faf9a90c 436 with Not_found ->
0708f913
C
437 let cell = ref 1 in
438 names := (function_name,cell) :: !names;
439 function_name in
440 let outfile = outdir ^ "/" ^
441 (if !Flag.hrule_per_file
442 then Filename.chop_extension (Filename.basename srcfile)
443 else function_name_count) in
444 let escape_re = Str.regexp_string "/" in
445 let dir = if !Flag.dir = "" then Filename.dirname srcfile else !Flag.dir in
446 let outdirfile = Str.global_replace escape_re "_"dir in
447 let outfile = outfile ^ outdirfile ^ ".cocci" in
448 let saved_header_req =
449 try let res = List.assoc outfile !started_files in Some res
450 with Not_found -> None in
451 current_outfile := outfile;
452 Common.with_open_outfile_append outfile (fun (pr,chan) ->
453 let header_req =
454 match saved_header_req with
455 Some x -> x
456 | None ->
457 let res = print_header_rule pr srcfile in
458 started_files := (outfile,res)::!started_files;
459 res in
460 print_check_rule pr function_name function_name_count header_req;
461 let args =
462 print_metavariables pr local_metas paramst env header_req
463 function_name_count in
faf9a90c
C
464 let (argids,args) = List.split args in
465 let env = rename argids env in
466 let env = (args_name,Ast_c.MetaExprListVal args)::env in
467 print_start pr;
468 (* for printing C tokens *)
469 let pr_c info =
470 match Ast_c.pinfo_of_info info with
471 Ast_c.AbstractLineTok _ -> pr (Ast_c.str_of_info info)
472 | Ast_c.FakeTok (s,_) -> pr s
473 | _ ->
474 Printf.printf "line: %s\n" (Common.dump info);
475 error rule "not an abstract line" in
708f4980 476 let pr_space _ = pr " " in
faf9a90c 477 Unparse_cocci.pp_list_list_any
708f4980
C
478 (env, (fun s _ _ _ -> pr s), pr_c, pr_space, pr_space, pr,
479 (fun _ _ -> ()), (function _ -> ()), (function _ -> ()))
faf9a90c
C
480 true printable Unparse_cocci.InPlace;
481 print_end pr;
482 pr "\n")