36e5b58bb1fc5813e0fe2f2d410ba94bed0f857a
[bpt/coccinelle.git] / parsing_cocci / merge.ml
1 (*
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.
9 *
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.
13 *
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.
18 *
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/>.
21 *
22 * The authors reserve the right to distribute this or future versions of
23 * Coccinelle under other licenses.
24 *)
25
26
27 # 0 "./merge.ml"
28 (* given parsed minus code and a stream of + code, figure out where to put
29 the + code in the mcode of the minus code *)
30
31 (* Need to be able to find the nearest inhabited line rather than just
32 adding 1 or subtracting 1 to the actual line number. This is an issue for
33 plus.ml as well. This problem is dealt with by the logical line field,
34 which is not incremented for blank lines. *)
35
36 module Ast = Ast_cocci
37 module Ast0 = Ast0_cocci
38 module V0 = Visitor_ast0
39
40 (* --------------------------------------------------------------------- *)
41 (* --------------------------------------------------------------------- *)
42 (* Step 1: convert minus/context code to an ordered stream of tokens *)
43
44 type position =
45 Minus of Ast.info * Ast.anything list list ref
46 | Context of Ast.info * Ast.anything Ast.befaft ref
47 | Bad of Ast.info
48
49 let mcode = function
50 (_,_,Ast.MINUS(info,plus_stream)) -> [Minus (info,plus_stream)]
51 | (_,_,Ast.CONTEXT(info,plus_stream)) -> [Context (info,plus_stream)]
52 | _ -> failwith "not possible 1"
53
54 let bad_mcode = function
55 (_,_,Ast.MINUS(info,plus_stream)) -> Bad(info)
56 | (_,_,Ast.CONTEXT(info,plus_stream)) -> Bad(info)
57 | _ -> failwith "not possible 2"
58
59 let make_bad l =
60 List.map
61 (function
62 Minus(info,plus_stream) -> Bad(info)
63 | Context(info,plus_stream) -> Bad(info)
64 | x -> x)
65 l
66
67 (* --------------------------------------------------------------------- *)
68 (* combiner info *)
69
70 let bind x y = x @ y
71 let option_default = []
72
73 (* --------------------------------------------------------------------- *)
74
75 let get_option f = function
76 Some x -> f x
77 | None -> option_default
78
79 let ident recursor k i = k i (* nothing special to do *)
80
81 let expression recursor k e =
82 match Ast0.unwrap e with
83 Ast0.Edots(dots,whencode) | Ast0.Ecircles(dots,whencode)
84 | Ast0.Estars(dots,whencode) ->
85 (bad_mcode dots) ::
86 (get_option (function x -> make_bad(recursor.V0.combiner_expression x))
87 whencode)
88 | _ -> k e
89
90 let donothing recursor k ft = k ft
91
92 (* needs a case for things to which new code cannot be attached *)
93 let parameterTypeDef recursor k p =
94 match Ast0.unwrap p with
95 Ast0.Pdots(dots) -> [bad_mcode dots]
96 | Ast0.Pcircles(dots) -> [bad_mcode dots]
97 | _ -> k p
98
99 let statement recursor k s =
100 match Ast0.unwrap s with
101 Ast0.Dots(d,whencode) | Ast0.Circles(d,whencode)
102 | Ast0.Stars(d,whencode) ->
103 (bad_mcode d) ::
104 (get_option
105 (function x ->
106 make_bad(recursor.V0.combiner_statement_dots x))
107 whencode)
108 | _ -> k s
109
110 let top_level recursor k t =
111 match Ast0.unwrap t with
112 Ast0.FILEINFO(old_file,new_file) ->
113 [bad_mcode old_file;bad_mcode new_file]
114 | Ast0.ERRORWORDS(exps) ->
115 make_bad (List.concat (List.map recursor.V0.combiner_expression exps))
116 | _ -> k t
117
118 let recursor =
119 V0.combiner bind option_default
120 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
121 donothing donothing donothing
122 ident expression donothing donothing parameterTypeDef donothing
123 statement top_level
124
125 let rule code = List.concat (List.map recursor.V0.combiner_top_level code)
126
127 (* --------------------------------------------------------------------- *)
128 (* --------------------------------------------------------------------- *)
129 (* Step 2: merge the plus stream with the minus/context tokens *)
130
131 (* Mcode *)
132
133 let get_start l =
134 let (_,_,_,start,_) = List.hd (List.hd l) in
135 start
136
137 let get_finish l =
138 let (_,_,_,_,finish) = List.hd (List.rev (List.hd (List.rev l))) in
139 finish
140
141 let get_real_start l =
142 let (_,start,_,_,_) = List.hd (List.hd l) in
143 start
144
145 let get_real_finish l =
146 let (_,_,finish,_,_) = List.hd (List.rev (List.hd (List.rev l))) in
147 finish
148
149 let get_minus_next_line mline = function
150 [] -> mline + 1
151 | Bad(info)::xs -> info.Ast.logical_line
152 | Minus(info,_)::xs -> info.Ast.logical_line
153 | Context(info,_)::xs -> info.Ast.logical_line
154
155 let drop_lines l = List.map (List.map (function (x,_,_,_,_) -> x)) l
156
157 let rec merge minus_stream plus_stream =
158 match (minus_stream,plus_stream) with
159 (_,[]) -> ()
160 | ([],plus::plus_stream) ->
161 failwith
162 (Printf.sprintf
163 "minus stream ran out before plus stream\n(plus code begins on line %d)\n"
164 (get_real_start plus))
165 | (Bad(info)::minus_stream,plus::plus_stream) ->
166 let pfinish = get_finish plus in
167 if info.Ast.logical_line > pfinish
168 then
169 failwith
170 (Printf.sprintf
171 "plus code starting on line %d has no minus or context code to attach to\n"
172 (get_real_start plus))
173 else merge minus_stream (plus::plus_stream)
174 | (((Minus(info,cell)::minus_stream) as all_minus),plus::plus_stream) ->
175 let mline = info.Ast.logical_line in
176 let mnext_line = get_minus_next_line mline minus_stream in
177 let pstart = get_start plus in
178 let pfinish = get_finish plus in
179 if pstart < mline && pfinish > mline
180 then (cell := (drop_lines plus) @ !cell; merge minus_stream plus_stream)
181 else if pfinish + 1 = mline
182 then (cell := (drop_lines plus) @ !cell; merge all_minus plus_stream)
183 else if not(mline = mnext_line) && (pstart - 1 = mline)
184 then (cell := !cell @ (drop_lines plus); merge minus_stream plus_stream)
185 else if pfinish < mline
186 then
187 Printf.printf "failed to merge + code between lines %d and %d"
188 (get_real_start plus) (get_real_finish plus)
189 else merge minus_stream (plus::plus_stream)
190 | (((Context(info,cell)::minus_stream) as all_minus),plus::plus_stream) ->
191 let mline = info.Ast.logical_line in
192 let mnext_line = get_minus_next_line mline minus_stream in
193 let pstart = get_start plus in
194 let pfinish = get_finish plus in
195 if pfinish + 1 = mline
196 then (cell := Ast.BEFORE (drop_lines plus); merge all_minus plus_stream)
197 else if not(mline = mnext_line) && (pstart - 1 = mline)
198 then
199 begin
200 (match !cell with
201 Ast.BEFORE x -> cell := Ast.BEFOREAFTER (x,drop_lines plus)
202 | _ -> cell := Ast.AFTER (drop_lines plus));
203 merge minus_stream plus_stream
204 end
205 else if pfinish < mline
206 then
207 Printf.printf "failed to merge + code between lines %d and %d"
208 (get_real_start plus) (get_real_finish plus)
209 else merge minus_stream (plus::plus_stream)
210
211 (* --------------------------------------------------------------------- *)
212 (* --------------------------------------------------------------------- *)
213 (* Entry point *)
214
215 let do_merge minus plus_stream =
216 let minus_tokens = rule minus in
217 merge minus_tokens plus_stream