223e785afb89bc2dd98bf8297d07a06007e5c436
[bpt/coccinelle.git] / parsing_c / parsing_recovery_c.ml
1 (* Yoann Padioleau
2 *
3 * Copyright (C) 2010, University of Copenhagen DIKU and INRIA.
4 * Copyright (C) 2006, 2007, 2008 Ecole des Mines de Nantes
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License (GPL)
8 * version 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * file license.txt for more details.
14 *)
15
16 open Common
17
18 module TH = Token_helpers
19
20 (*****************************************************************************)
21 (* Wrappers *)
22 (*****************************************************************************)
23 let pr2_err, pr2_once = Common.mk_pr2_wrappers Flag_parsing_c.verbose_parsing
24
25 (*****************************************************************************)
26 (* Helpers *)
27 (*****************************************************************************)
28
29 let is_defined_passed_bis last_round =
30 let xs = last_round +> List.filter TH.is_not_comment in
31 match xs with
32 | Parser_c.TDefine _::_ -> true
33 | _ -> false
34
35
36 (*****************************************************************************)
37 (* Skipping stuff, find next "synchronisation" point *)
38 (*****************************************************************************)
39
40 (* todo: do something if find Parser_c.Eof ? *)
41 let rec find_next_synchro ~next ~already_passed =
42
43 (* Maybe because not enough }, because for example an ifdef contains
44 * in both branch some opening {, we later eat too much, "on deborde
45 * sur la fonction d'apres". So already_passed may be too big and
46 * looking for next synchro point starting from next may not be the
47 * best. So maybe we can find synchro point inside already_passed
48 * instead of looking in next.
49 *
50 * But take care! must progress. We must not stay in infinite loop!
51 * For instance now I have as a error recovery to look for
52 * a "start of something", corresponding to start of function,
53 * but must go beyond this start otherwise will loop.
54 * So look at premier(external_declaration2) in parser.output and
55 * pass at least those first tokens.
56 *
57 * I have chosen to start search for next synchro point after the
58 * first { I found, so quite sure we will not loop. *)
59
60 let last_round = List.rev already_passed in
61 if is_defined_passed_bis last_round
62 then find_next_synchro_define (last_round ++ next) []
63 else
64
65 let (before, after) =
66 last_round +> Common.span (fun tok ->
67 match tok with
68 (* by looking at TOBrace we are sure that the "start of something"
69 * will not arrive too early
70 *)
71 | Parser_c.TOBrace _ -> false
72 | Parser_c.TDefine _ -> false
73 | _ -> true
74 )
75 in
76 find_next_synchro_orig (after ++ next) (List.rev before)
77
78
79
80 and find_next_synchro_define next already_passed =
81 match next with
82 | [] ->
83 pr2_err "ERROR-RECOV: end of file while in recovery mode";
84 already_passed, []
85 | (Parser_c.TDefEOL i as v)::xs ->
86 pr2_err ("ERROR-RECOV: found sync end of #define, line "^i_to_s(TH.line_of_tok v));
87 v::already_passed, xs
88 | v::xs ->
89 find_next_synchro_define xs (v::already_passed)
90
91
92
93
94 and find_next_synchro_orig next already_passed =
95 match next with
96 | [] ->
97 pr2_err "ERROR-RECOV: end of file while in recovery mode";
98 already_passed, []
99
100 | (Parser_c.TCBrace i as v)::xs when TH.col_of_tok v =|= 0 ->
101 pr2_err ("ERROR-RECOV: found sync '}' at line "^i_to_s (TH.line_of_tok v));
102
103 (match xs with
104 | [] -> raise Impossible (* there is a EOF token normally *)
105
106 (* still useful: now parser.mly allow empty ';' so normally no pb *)
107 | Parser_c.TPtVirg iptvirg::xs ->
108 pr2_err "ERROR-RECOV: found sync bis, eating } and ;";
109 (Parser_c.TPtVirg iptvirg)::v::already_passed, xs
110
111 | Parser_c.TIdent x::Parser_c.TPtVirg iptvirg::xs ->
112 pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
113 (Parser_c.TPtVirg iptvirg)::(Parser_c.TIdent x)::v::already_passed,
114 xs
115
116 | Parser_c.TCommentSpace sp::Parser_c.TIdent x::Parser_c.TPtVirg iptvirg
117 ::xs ->
118 pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
119 (Parser_c.TCommentSpace sp)::
120 (Parser_c.TPtVirg iptvirg)::
121 (Parser_c.TIdent x)::
122 v::
123 already_passed,
124 xs
125
126 | Parser_c.TCommentNewline sp::Parser_c.TIdent x::Parser_c.TPtVirg iptvirg
127 ::xs ->
128 pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
129 (Parser_c.TCommentNewline sp)::
130 (Parser_c.TPtVirg iptvirg)::
131 (Parser_c.TIdent x)::
132 v::
133 already_passed,
134 xs
135
136 | _ ->
137 v::already_passed, xs
138 )
139 | v::xs when TH.col_of_tok v =|= 0 && TH.is_start_of_something v ->
140 pr2_err ("ERROR-RECOV: found sync col 0 at line "^ i_to_s(TH.line_of_tok v));
141 already_passed, v::xs
142
143 | v::xs ->
144 find_next_synchro_orig xs (v::already_passed)