permit multiline comments and strings in macros
[bpt/coccinelle.git] / parsing_cocci / merge.ml
CommitLineData
f537ebc4 1(*
17ba0788
C
2 * Copyright 2012, INRIA
3 * Julia Lawall, Gilles Muller
4 * Copyright 2010-2011, INRIA, University of Copenhagen
f537ebc4
C
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
feec80c3 27# 0 "./merge.ml"
34e49164
C
28(* given parsed minus code and a stream of + code, figure out where to put
29the + code in the mcode of the minus code *)
30
31(* Need to be able to find the nearest inhabited line rather than just
32adding 1 or subtracting 1 to the actual line number. This is an issue for
33plus.ml as well. This problem is dealt with by the logical line field,
34which is not incremented for blank lines. *)
35
36module Ast = Ast_cocci
37module Ast0 = Ast0_cocci
38module V0 = Visitor_ast0
39
40(* --------------------------------------------------------------------- *)
41(* --------------------------------------------------------------------- *)
42(* Step 1: convert minus/context code to an ordered stream of tokens *)
43
44type 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
49let 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
54let 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
59let 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
70let bind x y = x @ y
71let option_default = []
72
73(* --------------------------------------------------------------------- *)
74
75let get_option f = function
76 Some x -> f x
77 | None -> option_default
78
79let ident recursor k i = k i (* nothing special to do *)
80
81let 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
90let donothing recursor k ft = k ft
91
92(* needs a case for things to which new code cannot be attached *)
93let 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
99let 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
110let 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
118let 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
125let 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
133let get_start l =
134 let (_,_,_,start,_) = List.hd (List.hd l) in
135 start
136
137let get_finish l =
138 let (_,_,_,_,finish) = List.hd (List.rev (List.hd (List.rev l))) in
139 finish
140
141let get_real_start l =
142 let (_,start,_,_,_) = List.hd (List.hd l) in
143 start
144
145let get_real_finish l =
146 let (_,_,finish,_,_) = List.hd (List.rev (List.hd (List.rev l))) in
147 finish
148
149let 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
155let drop_lines l = List.map (List.map (function (x,_,_,_,_) -> x)) l
156
157let 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
215let do_merge minus plus_stream =
216 let minus_tokens = rule minus in
217 merge minus_tokens plus_stream