2 * Copyright 2012, INRIA
3 * Julia Lawall, Gilles Muller
4 * Copyright 2010-2011, INRIA, University of Copenhagen
5 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
6 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
7 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
8 * This file is part of Coccinelle.
10 * Coccinelle is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, according to version 2 of the License.
14 * Coccinelle is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
22 * The authors reserve the right to distribute this or future versions of
23 * Coccinelle under other licenses.
27 #
0 "./function_prototypes.ml"
28 module Ast0
= Ast0_cocci
29 module Ast
= Ast_cocci
30 module V0
= Visitor_ast0
31 module VT0
= Visitor_ast0_types
33 type id
= Id
of string | Meta
of Ast.meta_name
35 let rec get_name name
=
36 match Ast0.unwrap name
with
37 Ast0.Id
(nm
) -> [Id
(Ast0.unwrap_mcode nm
)]
38 | Ast0.MetaId
(nm
,_
,_
,_
) | Ast0.MetaFunc
(nm
,_
,_
)
39 | Ast0.MetaLocalFunc
(nm
,_
,_
) -> [Meta
(Ast0.unwrap_mcode nm
)]
40 | Ast0.AsIdent
(id1
,id2
) -> failwith
"not supported"
41 | Ast0.DisjId
(_
,id_list
,_
,_
) -> List.concat
(List.map
get_name id_list
)
42 | Ast0.OptIdent
(id
) | Ast0.UniqueIdent
(id
) ->
45 (* --------------------------------------------------------------------- *)
46 (* collect all of the functions *)
48 let brace_to_semi (_
,arity
,info
,mcodekind
,pos
,adj
) =
50 (* drop column information, so that with -smpl_spacing the semicolon
51 will come out right after the close parenthesis *)
52 {info with Ast0.pos_info
= {info.Ast0.pos_info
with Ast0.column
= -1}} in
53 (";",Ast0.NONE
,info,mcodekind
,pos
,adj
)
55 let collect_function (stm
: Ast0.statement
) =
56 match Ast0.unwrap stm
with
57 Ast0.FunDecl
(_
,fninfo
,name
,lp
,params
,rp
,lbrace
,body
,rbrace
) ->
60 List.filter
(function Ast0.FStorage
(_
) -> true | _
-> false)
61 fninfo
with [Ast0.FStorage
(s
)] -> Some s
| _
-> None
in
64 List.filter
(function Ast0.FType
(_
) -> true | _
-> false)
65 fninfo
with [Ast0.FType
(t
)] -> Some t
| _
-> None
in
70 (Ast0.Decl
((Ast0.default_info
(),Ast0.context_befaft
()),
75 (Ast0.FunctionType
(ty,lp
,params
,rp
)),
76 name
,brace_to_semi lbrace
))))))
80 let collect_functions stmt_dots
=
81 List.concat
(List.map
collect_function (Ast0.undots stmt_dots
))
84 let mcode (term
,arity
,info,mc
,_
,adj
) =
85 (term
,arity
,info,mc
,ref [],adj
) in
86 let donothing r k e
= k e
in
89 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
90 donothing donothing donothing donothing donothing donothing
91 donothing donothing donothing donothing donothing donothing donothing
92 donothing donothing in
93 res.VT0.rebuilder_rec_statement
95 let get_all_functions rule
=
97 match Ast0.unwrap rule
with
98 Ast0.NONDECL
(stmt
) -> collect_function stmt
99 | Ast0.CODE
(rule_elem_dots
) -> collect_functions rule_elem_dots
102 (function (nm
,def
,vl
) ->
106 ((Iso_pattern.rebuild_mcode None
).VT0.rebuilder_rec_statement vl
))))
109 (* --------------------------------------------------------------------- *)
110 (* try to match up the functions *)
112 (* pass through the - and + functions in lockstep, until one runs out.
113 Then process the remaining minuses, if any. If we can find another
114 function of the same name for either the current - or + function, take that
115 one. Otherwise, align the two current ones. *)
117 let rec align all_minus all_plus
=
118 let rec loop = function
120 | ((mname
,(mdef
,mproto
))::minus
,[]) ->
122 let (_
,pproto
) = List.assoc mname all_plus
in
123 (mname
,mdef
,mproto
,Some pproto
)::(loop (minus
,[]))
124 with Not_found
-> (mname
,mdef
,mproto
,None
)::(loop (minus
, [])))
125 | ((mname
,(mdef
,mproto
))::minus
,(pname
,(pdef
,pproto
))::plus
) ->
127 then (mname
,mdef
,mproto
,Some pproto
)::(loop (minus
, []))
130 let (_
,pproto_for_minus
) = List.assoc mname all_plus
in
132 let _ = List.assoc mname all_plus
in
133 (* protos that match both *)
134 (mname
,mdef
,mproto
,Some pproto_for_minus
)::(loop (minus
, plus
))
136 (* proto that matches only minus *)
137 (mname
,mdef
,mproto
,Some pproto_for_minus
)::
138 (loop (minus
, ((pname
,(pdef
,pproto
))::plus
))))
141 let _ = List.assoc mname all_plus
in
142 (* proto only for plus *)
143 (mname
,mdef
,mproto
,None
)::(loop (minus
, plus
))
145 (* protos for no one *)
146 (mname
,mdef
,mproto
,Some pproto
)::(loop (minus
, plus
)))) in
147 List.filter changed_proto
(loop (all_minus
, all_plus
))
149 (* --------------------------------------------------------------------- *)
152 let donothing r k e
=
153 {(Ast0.wrap
(Ast0.unwrap
(k e
))) with
154 Ast0.mcodekind
= ref (Ast0.PLUS
Ast.ONE
)} in
155 let mcode (mc
,_,_,_,_,_) =
156 (mc
,Ast0.NONE
,Ast0.default_info
(),Ast0.PLUS
Ast.ONE
,
159 (* need a case for everything that has an unvisited component and can be in
160 a function prototype. Also get rid of constraints because pcre
161 constraints cannot be compared. *)
166 (match Ast0.unwrap e
with
167 Ast0.MetaId
(nm
,constraints
,seed
,pure
) ->
168 Ast0.MetaId
(nm
,Ast.IdNoConstraint
,seed
,Ast0.Pure
)
169 | Ast0.MetaFunc
(nm
,constraints
,pure
) ->
170 Ast0.MetaFunc
(nm
,Ast.IdNoConstraint
,Ast0.Pure
)
171 | Ast0.MetaLocalFunc
(nm
,constraints
,pure
) ->
172 Ast0.MetaLocalFunc
(nm
,Ast.IdNoConstraint
,Ast0.Pure
)
178 (match Ast0.unwrap e
with
179 Ast0.MetaType
(nm
,pure
) -> Ast0.MetaType
(nm
,Ast0.Pure
)
185 (match Ast0.unwrap e
with
186 Ast0.MetaParam
(nm
,pure
) ->
187 Ast0.MetaParam
(nm
,Ast0.Pure
)
188 | Ast0.MetaParamList
(nm
,lenname
,pure
) ->
189 Ast0.MetaParamList
(nm
,lenname
,Ast0.Pure
)
193 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
194 donothing donothing donothing donothing donothing donothing
195 ident donothing typeC donothing param donothing donothing
198 and changed_proto
= function
199 (mname
,mdef
,mproto
,None
) -> true
200 | (mname
,mdef
,mproto
,Some pproto
) ->
201 not
((strip
.VT0.rebuilder_rec_statement mproto
) =
202 (strip
.VT0.rebuilder_rec_statement pproto
))
204 (* --------------------------------------------------------------------- *)
207 let rec drop_param_name p
=
209 (match Ast0.unwrap p
with
210 Ast0.Param
(p
,_) -> Ast0.Param
(p
,None
)
211 | Ast0.OptParam
(p
) -> Ast0.OptParam
(drop_param_name p
)
212 | Ast0.UniqueParam
(p
) -> Ast0.UniqueParam
(p
)
216 let dec = (Iso_pattern.rebuild_mcode None
).VT0.rebuilder_rec_statement
dec in
217 match Ast0.unwrap
dec with
218 Ast0.Decl
(info,uninit
) ->
219 (match Ast0.unwrap uninit
with
220 Ast0.UnInit
(stg,typ
,name
,sem
) ->
221 (match Ast0.unwrap typ
with
222 Ast0.FunctionType
(ty,lp
,params
,rp
) ->
224 match Ast0.unwrap
params with
226 Ast0.rewrap
params (Ast0.DOTS
(List.map
drop_param_name l
))
229 (Ast0.CIRCLES
(List.map
drop_param_name l
))
230 | Ast0.STARS
(l
) -> failwith
"unexpected stars" in
238 (Ast0.FunctionType
(ty,lp
,params,rp
)),
240 | _ -> failwith
"function prototypes: unexpected type")
241 | _ -> failwith
"unexpected declaration")
242 | _ -> failwith
"unexpected term"
249 name^
"__"^
(string_of_int
n)
251 let rec rename_param old_name all
param =
252 match Ast0.unwrap
param with
253 Ast0.Param
(ty,Some id
) when all
->
254 (match Ast0.unwrap id
with
256 (((_,name
),arity
,info,mcodekind
,pos
,adj
),constraints
,seed
,pure
) ->
257 let nm = ("__no_name__",new_name name
) in
261 ((nm,arity
,info,mcodekind
,pos
,adj
),constraints
,seed
,
263 ([Ast.MetaIdDecl
(Ast.NONE
,nm)],
264 Ast0.rewrap
param (Ast0.Param
(ty,Some
new_id)))
267 let nm = (old_name
,new_name "__P") in
268 let nml = (old_name
,new_name "__n") in
271 (Ast0.MetaParamList
(Ast0.rewrap_mcode d
nm,
272 Ast0.MetaListLen
(Ast0.rewrap_mcode d
nml),
274 ([Ast.MetaParamListDecl
(Ast.NONE
,nm,Ast.MetaLen
nml);
275 Ast.MetaListlenDecl
(nml)],
277 | Ast0.OptParam
(p
) ->
278 let (metavars
,p
) = rename_param old_name all p
in
279 (metavars
,Ast0.rewrap
param (Ast0.OptParam
(p
)))
280 | Ast0.UniqueParam
(p
) ->
281 let (metavars
,p
) = rename_param old_name all p
in
282 (metavars
,Ast0.rewrap
param (Ast0.UniqueParam
(p
)))
285 (* try to convert names in the - parameter list to new metavariables, to
286 account for spelling mistakes on the part of the programmer *)
287 let fresh_names old_name mdef
dec =
288 let res = ([],[],dec,mdef
) in
289 match Ast0.unwrap
dec with
290 Ast0.Decl
(info,uninit
) ->
291 (match Ast0.unwrap uninit
with
292 Ast0.UnInit
(stg,typ
,name
,sem
) ->
293 (match Ast0.unwrap typ
with
294 Ast0.FunctionType
(ty,lp
,params,rp
) ->
295 let (metavars
,newdec
) =
298 (List.map
(rename_param old_name
true)
299 (Ast0.undots
params)) in
300 (List.concat metavars
,
309 (ty,lp
,Ast0.rewrap
params (Ast0.DOTS
(l
)),
312 let (def_metavars
,newdef
) =
313 match Ast0.unwrap mdef
with
314 Ast0.FunDecl
(x
,fninfo
,name
,lp
,params,rp
,lb
,body
,rb
) ->
315 let (def_metavars
,def_l
) =
317 (List.map
(rename_param old_name
false)
318 (Ast0.undots
params)) in
319 (List.concat def_metavars
,
321 (Ast0.FunDecl
(x
,fninfo
,name
,lp
,
322 Ast0.rewrap
params (Ast0.DOTS
(def_l
)),
324 | _ -> failwith
"unexpected function definition" in
325 (metavars
,def_metavars
,newdec
,newdef
)
330 (* since there is no + counterpart, the function must be completely deleted *)
332 match Ast0.unwrap
dec with
333 Ast0.Decl
(info,uninit
) ->
334 (match Ast0.unwrap uninit
with
335 Ast0.UnInit
(stg,typ
,name
,sem
) ->
336 (match Ast0.unwrap typ
with
337 Ast0.FunctionType
(ty,lp
,params,rp
) ->
348 (let info = Ast0.get_info
params in
350 (* use the mcodekind of an atomic minused
352 Ast0.get_mcode_mcodekind lp
in
354 ("...",Ast0.NONE
,info,mcodekind,
358 (Ast0.Pdots
(pdots))])),
366 Ast0.copywrap proto
(Ast0.CODE
(Ast0.copywrap proto
(Ast0.DOTS
[proto
])))
368 let merge mproto pproto
=
369 let mproto = Compute_lines.compute_lines
true [mkcode mproto] in
370 let pproto = Compute_lines.compute_lines
true [mkcode pproto] in
371 let (m
,p
) = List.split
(Context_neg.context_neg
mproto pproto) in
372 Insert_plus.insert_plus m p
true (* no isos for protos *);
373 (* convert to ast so that the + code will fall down to the tokens
374 and off the artificially added Ast0.CODE *)
375 let mproto = Ast0toast.ast0toast_toplevel
(List.hd
mproto) in
376 (* clean up the wrapping added above *)
377 match Ast.unwrap
mproto with
378 Ast.CODE
mproto -> List.hd
(Ast.undots
mproto)
379 | _ -> failwith
"not possible"
381 let make_rule rule_name
= function
382 (mname
,mdef
,mproto,Some
pproto) ->
383 let (metavars
,mdef_metavars
,mproto,mdef
) =
384 fresh_names rule_name mdef
mproto in
385 let no_name_mproto = drop_names mproto in
386 let no_name_pproto = drop_names pproto in
387 (metavars
,mdef_metavars
,
388 [merge mproto pproto; merge no_name_mproto no_name_pproto],mdef
)
389 | (mname
,mdef
,mproto,None
) ->
390 ([],[],[Ast0toast.statement
(no_names mproto)],mdef
)
392 (* --------------------------------------------------------------------- *)
394 let reinsert mdefs minus
=
398 match Ast0.unwrap x
with
399 Ast0.FunDecl
(_,fninfo
,name
,lp
,params,rp
,lbrace
,body
,rbrace
) ->
401 | _ -> failwith
"bad mdef")
405 match Ast0.unwrap x
with
406 Ast0.NONDECL
(stmt
) ->
407 (match Ast0.unwrap stmt
with
408 Ast0.FunDecl
(_,fninfo
,name
,lp
,params,rp
,lbrace
,body
,rbrace
) ->
409 (try Ast0.rewrap x
(Ast0.NONDECL
(List.assoc name
table))
412 | Ast0.CODE
(rule_elem_dots
) ->
413 (* let's hope there are no functions in here... *)
418 (* --------------------------------------------------------------------- *)
421 let rec split4 = function
424 let (ax
,bx
,cx
,dx
) = split4 rest
in (a
::ax
,b
::bx
,c
::cx
,d
::dx
)
426 let mk_ast_code proto
=
427 Ast.rewrap proto
(Ast.CODE
(Ast.rewrap proto
(Ast.DOTS
[proto
])))
429 let process rule_name rule_metavars dropped_isos minus plus ruletype
=
430 let minus_functions = List.concat
(List.map
get_all_functions minus
) in
431 match minus_functions with
432 [] -> ((rule_metavars
,minus
),None
)
435 List.concat
(List.map
get_all_functions plus
) in
436 let protos = align minus_functions plus_functions in
437 let (metavars
,mdef_metavars
,rules
,mdefs
) =
438 split4(List.map
(make_rule rule_name
) protos) in
439 let metavars = List.concat
metavars in
440 let mdef_metavars = (List.concat
mdef_metavars) @ rule_metavars
in
441 let rules = List.concat
rules in
442 let minus = reinsert mdefs
minus in
444 [] -> ((rule_metavars
,minus),None
)
446 (* probably not possible, since there is always the version with
447 variables and the version without *)
448 ((mdef_metavars,minus),
452 ("proto for "^rule_name
,
453 (Ast.Dep rule_name
,dropped_isos
,Ast.Forall
),
458 List.map
(function x
-> Ast.rewrap x
(Ast.DOTS
[x
])) rules in
461 ("proto for "^rule_name
,
462 (Ast.Dep rule_name
,dropped_isos
,Ast.Forall
),
463 [mk_ast_code (Ast.rewrap x
(Ast.Disj
drules))],
465 ((mdef_metavars,minus),Some
(metavars,res))