
  * Copyright 2014 , INRIA 
  *   
  * This file is part of Cgen .   Much of it comes from Coccinelle , which is 
  * also available under the GPLv2 license 
  * 
  * Cgen is free software : you can redistribute it and/or modify 
  * it under the terms of the GNU General Public License as published by 
  * the Free Software Foundation , according to version 2 of the License . 
  * 
  * Cgen is distributed in the hope that it will be useful , 
  * but WITHOUT ANY WARRANTY ; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .   See the 
  * GNU General Public License for more details . 
  * 
  * You should have received a copy of the GNU General Public License 
  * along with Cgen .   If not , see < / > . 
  * 
  * The authors reserve the right to distribute this or future versions of 
  * Cgen under other licenses . 
 
 * Copyright 2014, INRIA
 * Julia Lawall
 * This file is part of Cgen.  Much of it comes from Coccinelle, which is
 * also available under the GPLv2 license
 *
 * Cgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, according to version 2 of the License.
 *
 * Cgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cgen.  If not, see </>.
 *
 * The authors reserve the right to distribute this or future versions of
 * Cgen under other licenses.
 *)


# 0 "./parsing_recovery_c.ml"
    
  * 
  * Copyright ( C ) 2010 , University of Copenhagen DIKU and INRIA . 
  * Copyright ( C ) 2006 , 2007 , 2008 Ecole des Mines de Nantes 
  * 
  * This program is free software ; you can redistribute it and/or 
  * modify it under the terms of the GNU General Public License ( GPL ) 
  * version 2 as published by the Free Software Foundation . 
  * 
  * This program is distributed in the hope that it will be useful , 
  * but WITHOUT ANY WARRANTY ; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .   See the 
  * file license.txt for more details . 
 
 *
 * Copyright (C) 2010, University of Copenhagen DIKU and INRIA.
 * Copyright (C) 2006, 2007, 2008 Ecole des Mines de Nantes
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License (GPL)
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * file license.txt for more details.
 *)

open Common

module TH = Token_helpers

(*****************************************************************************)
(* Wrappers *)
(*****************************************************************************)
let pr2_err, pr2_once = Common.mk_pr2_wrappers Flag_parsing_c.verbose_parsing

(*****************************************************************************)
(* Helpers *)
(*****************************************************************************)

let is_defined_passed_bis last_round =
  let xs = last_round +> List.filter TH.is_not_comment in
  match xs with
  | Parser_c.TDefine _::_ -> true
  | _ -> false


(*****************************************************************************)
(* Skipping stuff, find next "synchronisation" point *)
(*****************************************************************************)

  todo : do something if find  . Eof ?
let rec find_next_synchro ~next ~already_passed =

  Maybe because not enough } , because for example an ifdef contains 
    * in both branch some opening { , we later eat too much , " on deborde 
    * sur la fonction d'apres " . So already_passed may be too big and 
    * looking for next synchro point starting from next may not be the 
    * best . So maybe we can find synchro point inside already_passed 
    * instead of looking in next . 
    * 
    * But take care ! must progress . We must not stay in infinite loop ! 
    * For instance now I have as a error recovery to look for 
    * a " start of something " , corresponding to start of function , 
    * but must go beyond this start otherwise will loop . 
    * So look at premier(external_declaration2 ) in parser.output and 
    * pass at least those first tokens . 
    * 
    * I have chosen to start search for next synchro point after the 
    * first { I found , so quite sure we will not loop .
   * in both branch some opening {, we later eat too much, "on deborde
   * sur la fonction d'apres". So already_passed may be too big and
   * looking for next synchro point starting from next may not be the
   * best. So maybe we can find synchro point inside already_passed
   * instead of looking in next.
   *
   * But take care! must progress. We must not stay in infinite loop!
   * For instance now I have as a error recovery to look for
   * a "start of something", corresponding to start of function,
   * but must go beyond this start otherwise will loop.
   * So look at premier(external_declaration2) in parser.output and
   * pass at least those first tokens.
   *
   * I have chosen to start search for next synchro point after the
   * first { I found, so quite sure we will not loop. *)

  let last_round = List.rev already_passed in
  if is_defined_passed_bis last_round
  then find_next_synchro_define (last_round ++ next) []
  else

  let (before, after) =
    last_round +> Common.span (fun tok ->
      match tok with
      (* by looking at TOBrace we are sure that the "start of something"
       * will not arrive too early
       *)
      | Parser_c.TOBrace _ -> false
      | Parser_c.TDefine _ -> false
      | _ -> true
    )
  in
  find_next_synchro_orig (after ++ next)  (List.rev before)



and find_next_synchro_define next already_passed =
  match next with
  | [] ->
      pr2_err "ERROR-RECOV: end of file while in recovery mode";
      already_passed, []
  | (Parser_c.TDefEOL i as v)::xs  ->
      pr2_err ("ERROR-RECOV: found sync end of #define, line "^i_to_s(TH.line_of_tok v));
      v::already_passed, xs
  | v::xs ->
      find_next_synchro_define xs (v::already_passed)




and find_next_synchro_orig next already_passed =
  match next with
  | [] ->
      pr2_err "ERROR-RECOV: end of file while in recovery mode";
      already_passed, []

  | (Parser_c.TCBrace i as v)::xs when TH.col_of_tok v =|= 0 ->
      pr2_err ("ERROR-RECOV: found sync '}' at line "^i_to_s (TH.line_of_tok v));

      (match xs with
  there is a EOF token normally

      (* still useful: now parser.mly allow empty ';' so normally no pb *)
      | Parser_c.TPtVirg iptvirg::xs ->
          pr2_err "ERROR-RECOV: found sync bis, eating } and ;";
          (Parser_c.TPtVirg iptvirg)::v::already_passed, xs

      | Parser_c.TIdent x::Parser_c.TPtVirg iptvirg::xs ->
          pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
          (Parser_c.TPtVirg iptvirg)::(Parser_c.TIdent x)::v::already_passed,
          xs

      | Parser_c.TCommentSpace sp::Parser_c.TIdent x::Parser_c.TPtVirg iptvirg
        ::xs ->
          pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
          (Parser_c.TPtVirg iptvirg)::
          (Parser_c.TIdent x)::
          (Parser_c.TCommentSpace sp)::
            v::
            already_passed,
          xs

      | Parser_c.TCommentNewline sp::Parser_c.TIdent x::Parser_c.TPtVirg iptvirg
        ::xs ->
          pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
          (Parser_c.TPtVirg iptvirg)::
          (Parser_c.TIdent x)::
          (Parser_c.TCommentNewline sp)::
            v::
            already_passed,
          xs

      | _ ->
          v::already_passed, xs
      )
  | v::xs when TH.col_of_tok v =|= 0 && TH.is_start_of_something v  ->
      pr2_err ("ERROR-RECOV: found sync col 0 at line "^ i_to_s(TH.line_of_tok v));
      already_passed, v::xs

  | v::xs ->
      find_next_synchro_orig xs (v::already_passed)

***************************************************************************
 Wrappers 
***************************************************************************
***************************************************************************
 Helpers 
***************************************************************************
***************************************************************************
 Skipping stuff, find next "synchronisation" point 
***************************************************************************
 by looking at TOBrace we are sure that the "start of something"
       * will not arrive too early
       
 still useful: now parser.mly allow empty ';' so normally no pb 

  * Copyright 2014 , INRIA 
  *   
  * This file is part of Cgen .   Much of it comes from Coccinelle , which is 
  * also available under the GPLv2 license 
  * 
  * Cgen is free software : you can redistribute it and/or modify 
  * it under the terms of the GNU General Public License as published by 
  * the Free Software Foundation , according to version 2 of the License . 
  * 
  * Cgen is distributed in the hope that it will be useful , 
  * but WITHOUT ANY WARRANTY ; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .   See the 
  * GNU General Public License for more details . 
  * 
  * You should have received a copy of the GNU General Public License 
  * along with Cgen .   If not , see < / > . 
  * 
  * The authors reserve the right to distribute this or future versions of 
  * Cgen under other licenses . 
 
 * Copyright 2014, INRIA
 * Julia Lawall
 * This file is part of Cgen.  Much of it comes from Coccinelle, which is
 * also available under the GPLv2 license
 *
 * Cgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, according to version 2 of the License.
 *
 * Cgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cgen.  If not, see </>.
 *
 * The authors reserve the right to distribute this or future versions of
 * Cgen under other licenses.
 *)


# 0 "./parsing_recovery_c.ml"
    
  * 
  * Copyright ( C ) 2010 , University of Copenhagen DIKU and INRIA . 
  * Copyright ( C ) 2006 , 2007 , 2008 Ecole des Mines de Nantes 
  * 
  * This program is free software ; you can redistribute it and/or 
  * modify it under the terms of the GNU General Public License ( GPL ) 
  * version 2 as published by the Free Software Foundation . 
  * 
  * This program is distributed in the hope that it will be useful , 
  * but WITHOUT ANY WARRANTY ; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .   See the 
  * file license.txt for more details . 
 
 *
 * Copyright (C) 2010, University of Copenhagen DIKU and INRIA.
 * Copyright (C) 2006, 2007, 2008 Ecole des Mines de Nantes
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License (GPL)
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * file license.txt for more details.
 *)

open Common

module TH = Token_helpers

let pr2_err, pr2_once = Common.mk_pr2_wrappers Flag_parsing_c.verbose_parsing


let is_defined_passed_bis last_round =
  let xs = last_round +> List.filter TH.is_not_comment in
  match xs with
  | Parser_c.TDefine _::_ -> true
  | _ -> false



  todo : do something if find  . Eof ?
let rec find_next_synchro ~next ~already_passed =

  Maybe because not enough } , because for example an ifdef contains 
    * in both branch some opening { , we later eat too much , " on deborde 
    * sur la fonction d'apres " . So already_passed may be too big and 
    * looking for next synchro point starting from next may not be the 
    * best . So maybe we can find synchro point inside already_passed 
    * instead of looking in next . 
    * 
    * But take care ! must progress . We must not stay in infinite loop ! 
    * For instance now I have as a error recovery to look for 
    * a " start of something " , corresponding to start of function , 
    * but must go beyond this start otherwise will loop . 
    * So look at premier(external_declaration2 ) in parser.output and 
    * pass at least those first tokens . 
    * 
    * I have chosen to start search for next synchro point after the 
    * first { I found , so quite sure we will not loop .
   * in both branch some opening {, we later eat too much, "on deborde
   * sur la fonction d'apres". So already_passed may be too big and
   * looking for next synchro point starting from next may not be the
   * best. So maybe we can find synchro point inside already_passed
   * instead of looking in next.
   *
   * But take care! must progress. We must not stay in infinite loop!
   * For instance now I have as a error recovery to look for
   * a "start of something", corresponding to start of function,
   * but must go beyond this start otherwise will loop.
   * So look at premier(external_declaration2) in parser.output and
   * pass at least those first tokens.
   *
   * I have chosen to start search for next synchro point after the
   * first { I found, so quite sure we will not loop. *)

  let last_round = List.rev already_passed in
  if is_defined_passed_bis last_round
  then find_next_synchro_define (last_round ++ next) []
  else

  let (before, after) =
    last_round +> Common.span (fun tok ->
      match tok with
      | Parser_c.TOBrace _ -> false
      | Parser_c.TDefine _ -> false
      | _ -> true
    )
  in
  find_next_synchro_orig (after ++ next)  (List.rev before)



and find_next_synchro_define next already_passed =
  match next with
  | [] ->
      pr2_err "ERROR-RECOV: end of file while in recovery mode";
      already_passed, []
  | (Parser_c.TDefEOL i as v)::xs  ->
      pr2_err ("ERROR-RECOV: found sync end of #define, line "^i_to_s(TH.line_of_tok v));
      v::already_passed, xs
  | v::xs ->
      find_next_synchro_define xs (v::already_passed)




and find_next_synchro_orig next already_passed =
  match next with
  | [] ->
      pr2_err "ERROR-RECOV: end of file while in recovery mode";
      already_passed, []

  | (Parser_c.TCBrace i as v)::xs when TH.col_of_tok v =|= 0 ->
      pr2_err ("ERROR-RECOV: found sync '}' at line "^i_to_s (TH.line_of_tok v));

      (match xs with
  there is a EOF token normally

      | Parser_c.TPtVirg iptvirg::xs ->
          pr2_err "ERROR-RECOV: found sync bis, eating } and ;";
          (Parser_c.TPtVirg iptvirg)::v::already_passed, xs

      | Parser_c.TIdent x::Parser_c.TPtVirg iptvirg::xs ->
          pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
          (Parser_c.TPtVirg iptvirg)::(Parser_c.TIdent x)::v::already_passed,
          xs

      | Parser_c.TCommentSpace sp::Parser_c.TIdent x::Parser_c.TPtVirg iptvirg
        ::xs ->
          pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
          (Parser_c.TPtVirg iptvirg)::
          (Parser_c.TIdent x)::
          (Parser_c.TCommentSpace sp)::
            v::
            already_passed,
          xs

      | Parser_c.TCommentNewline sp::Parser_c.TIdent x::Parser_c.TPtVirg iptvirg
        ::xs ->
          pr2_err "ERROR-RECOV: found sync bis, eating ident, }, and ;";
          (Parser_c.TPtVirg iptvirg)::
          (Parser_c.TIdent x)::
          (Parser_c.TCommentNewline sp)::
            v::
            already_passed,
          xs

      | _ ->
          v::already_passed, xs
      )
  | v::xs when TH.col_of_tok v =|= 0 && TH.is_start_of_something v  ->
      pr2_err ("ERROR-RECOV: found sync col 0 at line "^ i_to_s(TH.line_of_tok v));
      already_passed, v::xs

  | v::xs ->
      find_next_synchro_orig xs (v::already_passed)
