Release coccinelle-0.2.4
[bpt/coccinelle.git] / parsing_cocci / merge.ml
CommitLineData
9bc82bae
C
1(*
2 * Copyright 2010, INRIA, University of Copenhagen
3 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
4 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
5 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
6 * This file is part of Coccinelle.
7 *
8 * Coccinelle is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, according to version 2 of the License.
11 *
12 * Coccinelle is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * The authors reserve the right to distribute this or future versions of
21 * Coccinelle under other licenses.
22 *)
23
24
c491d8ee
C
25(*
26 * Copyright 2010, INRIA, University of Copenhagen
27 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
28 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
29 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
30 * This file is part of Coccinelle.
31 *
32 * Coccinelle is free software: you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation, according to version 2 of the License.
35 *
36 * Coccinelle is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
43 *
44 * The authors reserve the right to distribute this or future versions of
45 * Coccinelle under other licenses.
46 *)
47
48
34e49164
C
49(* given parsed minus code and a stream of + code, figure out where to put
50the + code in the mcode of the minus code *)
51
52(* Need to be able to find the nearest inhabited line rather than just
53adding 1 or subtracting 1 to the actual line number. This is an issue for
54plus.ml as well. This problem is dealt with by the logical line field,
55which is not incremented for blank lines. *)
56
57module Ast = Ast_cocci
58module Ast0 = Ast0_cocci
59module V0 = Visitor_ast0
60
61(* --------------------------------------------------------------------- *)
62(* --------------------------------------------------------------------- *)
63(* Step 1: convert minus/context code to an ordered stream of tokens *)
64
65type position =
66 Minus of Ast.info * Ast.anything list list ref
67 | Context of Ast.info * Ast.anything Ast.befaft ref
68 | Bad of Ast.info
69
70let mcode = function
71 (_,_,Ast.MINUS(info,plus_stream)) -> [Minus (info,plus_stream)]
72 | (_,_,Ast.CONTEXT(info,plus_stream)) -> [Context (info,plus_stream)]
73 | _ -> failwith "not possible 1"
74
75let bad_mcode = function
76 (_,_,Ast.MINUS(info,plus_stream)) -> Bad(info)
77 | (_,_,Ast.CONTEXT(info,plus_stream)) -> Bad(info)
78 | _ -> failwith "not possible 2"
79
80let make_bad l =
81 List.map
82 (function
83 Minus(info,plus_stream) -> Bad(info)
84 | Context(info,plus_stream) -> Bad(info)
85 | x -> x)
86 l
87
88(* --------------------------------------------------------------------- *)
89(* combiner info *)
90
91let bind x y = x @ y
92let option_default = []
93
94(* --------------------------------------------------------------------- *)
95
96let get_option f = function
97 Some x -> f x
98 | None -> option_default
99
100let ident recursor k i = k i (* nothing special to do *)
101
102let expression recursor k e =
103 match Ast0.unwrap e with
104 Ast0.Edots(dots,whencode) | Ast0.Ecircles(dots,whencode)
105 | Ast0.Estars(dots,whencode) ->
106 (bad_mcode dots) ::
107 (get_option (function x -> make_bad(recursor.V0.combiner_expression x))
108 whencode)
109 | _ -> k e
110
111let donothing recursor k ft = k ft
112
113(* needs a case for things to which new code cannot be attached *)
114let parameterTypeDef recursor k p =
115 match Ast0.unwrap p with
116 Ast0.Pdots(dots) -> [bad_mcode dots]
117 | Ast0.Pcircles(dots) -> [bad_mcode dots]
118 | _ -> k p
119
120let statement recursor k s =
121 match Ast0.unwrap s with
122 Ast0.Dots(d,whencode) | Ast0.Circles(d,whencode)
123 | Ast0.Stars(d,whencode) ->
124 (bad_mcode d) ::
125 (get_option
126 (function x ->
127 make_bad(recursor.V0.combiner_statement_dots x))
128 whencode)
129 | _ -> k s
130
131let top_level recursor k t =
132 match Ast0.unwrap t with
133 Ast0.FILEINFO(old_file,new_file) ->
134 [bad_mcode old_file;bad_mcode new_file]
135 | Ast0.ERRORWORDS(exps) ->
136 make_bad (List.concat (List.map recursor.V0.combiner_expression exps))
137 | _ -> k t
138
139let recursor =
140 V0.combiner bind option_default
141 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
142 donothing donothing donothing
143 ident expression donothing donothing parameterTypeDef donothing
144 statement top_level
145
146let rule code = List.concat (List.map recursor.V0.combiner_top_level code)
147
148(* --------------------------------------------------------------------- *)
149(* --------------------------------------------------------------------- *)
150(* Step 2: merge the plus stream with the minus/context tokens *)
151
152(* Mcode *)
153
154let get_start l =
155 let (_,_,_,start,_) = List.hd (List.hd l) in
156 start
157
158let get_finish l =
159 let (_,_,_,_,finish) = List.hd (List.rev (List.hd (List.rev l))) in
160 finish
161
162let get_real_start l =
163 let (_,start,_,_,_) = List.hd (List.hd l) in
164 start
165
166let get_real_finish l =
167 let (_,_,finish,_,_) = List.hd (List.rev (List.hd (List.rev l))) in
168 finish
169
170let get_minus_next_line mline = function
171 [] -> mline + 1
172 | Bad(info)::xs -> info.Ast.logical_line
173 | Minus(info,_)::xs -> info.Ast.logical_line
174 | Context(info,_)::xs -> info.Ast.logical_line
175
176let drop_lines l = List.map (List.map (function (x,_,_,_,_) -> x)) l
177
178let rec merge minus_stream plus_stream =
179 match (minus_stream,plus_stream) with
180 (_,[]) -> ()
181 | ([],plus::plus_stream) ->
182 failwith
183 (Printf.sprintf
184 "minus stream ran out before plus stream\n(plus code begins on line %d)\n"
185 (get_real_start plus))
186 | (Bad(info)::minus_stream,plus::plus_stream) ->
187 let pfinish = get_finish plus in
188 if info.Ast.logical_line > pfinish
189 then
190 failwith
191 (Printf.sprintf
192 "plus code starting on line %d has no minus or context code to attach to\n"
193 (get_real_start plus))
194 else merge minus_stream (plus::plus_stream)
195 | (((Minus(info,cell)::minus_stream) as all_minus),plus::plus_stream) ->
196 let mline = info.Ast.logical_line in
197 let mnext_line = get_minus_next_line mline minus_stream in
198 let pstart = get_start plus in
199 let pfinish = get_finish plus in
200 if pstart < mline && pfinish > mline
201 then (cell := (drop_lines plus) @ !cell; merge minus_stream plus_stream)
202 else if pfinish + 1 = mline
203 then (cell := (drop_lines plus) @ !cell; merge all_minus plus_stream)
204 else if not(mline = mnext_line) && (pstart - 1 = mline)
205 then (cell := !cell @ (drop_lines plus); merge minus_stream plus_stream)
206 else if pfinish < mline
207 then
208 Printf.printf "failed to merge + code between lines %d and %d"
209 (get_real_start plus) (get_real_finish plus)
210 else merge minus_stream (plus::plus_stream)
211 | (((Context(info,cell)::minus_stream) as all_minus),plus::plus_stream) ->
212 let mline = info.Ast.logical_line in
213 let mnext_line = get_minus_next_line mline minus_stream in
214 let pstart = get_start plus in
215 let pfinish = get_finish plus in
216 if pfinish + 1 = mline
217 then (cell := Ast.BEFORE (drop_lines plus); merge all_minus plus_stream)
218 else if not(mline = mnext_line) && (pstart - 1 = mline)
219 then
220 begin
221 (match !cell with
222 Ast.BEFORE x -> cell := Ast.BEFOREAFTER (x,drop_lines plus)
223 | _ -> cell := Ast.AFTER (drop_lines plus));
224 merge minus_stream plus_stream
225 end
226 else if pfinish < mline
227 then
228 Printf.printf "failed to merge + code between lines %d and %d"
229 (get_real_start plus) (get_real_finish plus)
230 else merge minus_stream (plus::plus_stream)
231
232(* --------------------------------------------------------------------- *)
233(* --------------------------------------------------------------------- *)
234(* Entry point *)
235
236let do_merge minus plus_stream =
237 let minus_tokens = rule minus in
238 merge minus_tokens plus_stream