(* Yoann Padioleau
 *
 * Copyright (C) 2010, University of Copenhagen DIKU and INRIA.
 * Copyright (C) 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

open Parser_c

(*****************************************************************************)
(* Is_xxx, categories *)
(*****************************************************************************)

(* could define a type  token_class = Comment | Ident | Operator | ...
 * update: now token_c can maybe do that.
 * but still, sometimes tokens belon to multiple classes. Could maybe
 * return then a set of classes.
 *)

let is_space = function
  | TCommentSpace _ -> true
  | TCommentNewline _ -> true
  | _ -> false

let is_whitespace = is_space

let is_just_comment_or_space = function
  | TComment _ -> true
  | TCommentSpace _ -> true
  | TCommentNewline _ -> true
  | _ -> false
let is_real_comment = is_just_comment_or_space

let is_just_comment = function
  | TComment _ -> true
  | _ -> false




let is_comment = function
  | TComment _
  | TCommentSpace _ | TCommentNewline _
  | TCommentCpp _
  | TCommentMisc _ -> true
  | _ -> false

(* coupling with comment_annotater_c.ml.
 * In fact more tokens than comments are not in the ast, but
 * they were usually temporally created by ocamllex and removed
 * in parsing_hacks.
*)
let is_not_in_ast = is_comment

let is_fake_comment = function
  | TCommentCpp _    | TCommentMisc _
      -> true
  | _ -> false

let is_not_comment x =
  not (is_comment x)


(* ---------------------------------------------------------------------- *)

let is_cpp_instruction = function
  | TInclude _
  | TDefine _
  | TIfdef _   | TIfdefelse _ | TIfdefelif _ | TEndif _
  | TIfdefBool _ | TIfdefMisc _ | TIfdefVersion _
  | TUndef _
  | TCppDirectiveOther _
      -> true
  | _ -> false


let is_gcc_token = function
  | Tasm _
  | Tinline _
  | Tattribute _
  | Ttypeof _
      -> true
  | _ -> false




(* ---------------------------------------------------------------------- *)
let is_opar = function
  | TOPar _ | TOParDefine _ -> true
  | _ -> false

let is_cpar = function
  | TCPar _ | TCParEOL _ -> true
  | _ -> false


let is_obrace = function
  | TOBrace _ | TOBraceDefineInit _ -> true
  | _ -> false

let is_cbrace = function
  | TCBrace _ -> true
  | _ -> false




(* ---------------------------------------------------------------------- *)

(* end of file *)
let is_eof = function
  | EOF x -> true
  | _ -> false


(* end of macro *)
let is_eom = function
  | TDefEOL _ -> true
  | _ -> false

let is_statement = function
  | Tfor _ | Tdo _ | Tif _ | Twhile _ | Treturn _
  | Tbreak _ | Telse _ | Tswitch _ | Tcase _ | Tcontinue _
  | Tgoto _
  | TPtVirg _
  | TMacroIterator _
      -> true
  | _ -> false

(* is_start_of_something is used in parse_c for error recovery, to find
 * a synchronisation token.
 *
 * Would like to put TIdent or TDefine, TIfdef but they can be in the
 * middle of a function, for instance with label:.
 *
 * Could put Typedefident but fired ? it would work in error recovery
 * on the already_passed tokens, which has been already gone in the
 * Parsing_hacks.lookahead machinery, but it will not work on the
 * "next" tokens. But because the namespace for labels is different
 * from namespace for ident/typedef, we can use the name for a typedef
 * for a label and so dangerous to put Typedefident at true here.
 *
 * Can look in parser_c.output to know what can be at toplevel
 * at the very beginning.
 *)

let is_start_of_something = function
  | Tchar _  | Tshort _ | Tint _ | Tdouble _ |  Tfloat _ | Tlong _
  | Tunsigned _ | Tsigned _ | Tvoid _ | Tsize_t _ | Tssize_t _ | Tptrdiff_t _
  | Tauto _ | Tregister _ | Textern _ | Tstatic _
  | Tconst _ | Tvolatile _
  | Ttypedef _
  | Tstruct _ | Tunion _ | Tenum _
    -> true
  | _ -> false



let is_binary_operator = function
  | TOrLog _ | TAndLog _ |  TOr _ |  TXor _ |  TAnd _
  | TEqEq _ |  TNotEq _  | TInf _ |  TSup _ |  TInfEq _ |  TSupEq _
  | TShl _ | TShr _
  | TPlus _ |  TMinus _ |  TMul _ |  TDiv _ |  TMod _ | TMin _ | TMax _
        -> true
  | _ -> false

let is_stuff_taking_parenthized = function
  | Tif _
  | Twhile _
  | Tswitch _
  | Ttypeof _
  | TMacroIterator _
    -> true
  | _ -> false


(* used in the algorithms for "10 most problematic errors" *)
let is_ident_like = function
  | TIdent _
  | TKRParam _
  | TypedefIdent _
  | TIdentDefine  _
  | TDefParamVariadic _

  | TUnknown _

  | TMacroAttr _
  | TMacroAttrStorage _
  | TMacroStmt _
  | TMacroString _
  | TMacroDecl _
  | TMacroDeclConst _
  | TMacroIterator _
      -> true

  | _ -> false


(*****************************************************************************)
(* Visitors *)
(*****************************************************************************)

(* Because ocamlyacc force us to do it that way. The ocamlyacc token
 * cant be a pair of a sum type, it must be directly a sum type.
 *)
let info_of_tok = function
  | TString ((string, isWchar), i) -> i
  | TChar  ((string, isWchar), i) -> i
  | TFloat ((string, floatType), i) -> i

  | TAssign  (assignOp, i) -> i

  | TIdent  (s, i) -> i
  | TKRParam  (s, i) -> i
  | Tconstructorname  (s, i) -> i
  | TypedefIdent  (s, i) -> i

  | TInt  (s, i) -> i

  | TDefine (ii) -> ii
  | TInclude (includes, filename, inifdef, i1) ->     i1

  | TUndef (ii) -> ii
  | TCppDirectiveOther (ii) -> ii

  | TIncludeStart (i1, inifdef) ->     i1
  | TIncludeFilename (s, i1) ->     i1

  | TDefEOL (i1) ->     i1
  | TOParDefine (i1) ->     i1
  | TIdentDefine  (s, i) -> i
  | TCppEscapedNewline (ii) -> ii
  | TDefParamVariadic (s, i1) ->     i1

  | TCppConcatOp (ii) -> ii

  | TOBraceDefineInit (i1) ->     i1

  | TUnknown             (i) -> i

  | TMacroIdentBuilder             (s, i) -> i
  | TMacroAttr             (s, i) -> i
  | TMacroAttrStorage             (s, i) -> i
  | TMacroStmt             (s, i) -> i
  | TMacroString             (s, i) -> i
  | TMacroDecl             (s, i) -> i
  | TMacroDeclConst             (i) -> i
  | TMacroIterator             (s,i) -> i
(*  | TMacroTop             (s,i) -> i *)
  | TCParEOL (i1) ->     i1

  | TAction             (i) -> i

  | TComment             (i) -> i
  | TCommentSpace        (i) -> i
  | TCommentNewline      (i) -> i
  | TCommentCpp          (cppkind, i) -> i
  | TCommentMisc         (i) -> i

  | TCommentSkipTagStart (i) -> i
  | TCommentSkipTagEnd (i) -> i

  | TIfdef               (_, i) -> i
  | TIfdefelse           (_, i) -> i
  | TIfdefelif           (_, i) -> i
  | TEndif               (_, i) -> i
  | TIfdefBool           (b, _, i) -> i
  | TIfdefMisc           (b, _, i) -> i
  | TIfdefVersion           (b, _, i) -> i

  | TOPar                (i) -> i
  | TCPar                (i) -> i
  | TOBrace              (i) -> i
  | TCBrace              (i) -> i
  | TOCro                (i) -> i
  | TCCro                (i) -> i
  | TDot                 (i) -> i
  | TComma               (i) -> i
  | TPtrOp               (i) -> i
  | TInc                 (i) -> i
  | TDec                 (i) -> i
  | TEq                  (i) -> i
  | TWhy                 (i) -> i
  | TTilde               (i) -> i
  | TBang                (i) -> i
  | TEllipsis            (i) -> i
  | TDotDot              (i) -> i
  | TPtVirg              (i) -> i
  | TOrLog               (i) -> i
  | TAndLog              (i) -> i
  | TOr                  (i) -> i
  | TXor                 (i) -> i
  | TAnd                 (i) -> i
  | TEqEq                (i) -> i
  | TNotEq               (i) -> i
  | TInf                 (i) -> i
  | TSup                 (i) -> i
  | TInfEq               (i) -> i
  | TSupEq               (i) -> i
  | TShl                 (i) -> i
  | TShr                 (i) -> i
  | TPlus                (i) -> i
  | TMinus               (i) -> i
  | TMul                 (i) -> i
  | TDiv                 (i) -> i
  | TMod                 (i) -> i
  | TMax                 (i) -> i
  | TMin                 (i) -> i

  | Tchar                (i) -> i
  | Tshort               (i) -> i
  | Tint                 (i) -> i
  | Tdouble              (i) -> i
  | Tfloat               (i) -> i
  | Tlong                (i) -> i
  | Tunsigned            (i) -> i
  | Tsigned              (i) -> i
  | Tvoid                (i) -> i
  | Tsize_t              (i) -> i
  | Tssize_t             (i) -> i
  | Tptrdiff_t           (i) -> i
  | Tauto                (i) -> i
  | Tregister            (i) -> i
  | Textern              (i) -> i
  | Tstatic              (i) -> i
  | Tconst               (i) -> i
  | Tvolatile            (i) -> i

  | Trestrict            (i) -> i

  | Tstruct              (i) -> i
  | Tenum                (i) -> i
  | Ttypedef             (i) -> i
  | Tunion               (i) -> i
  | Tbreak               (i) -> i
  | Telse                (i) -> i
  | Tswitch              (i) -> i
  | Tcase                (i) -> i
  | Tcontinue            (i) -> i
  | Tfor                 (i) -> i
  | Tdo                  (i) -> i
  | Tif                  (i) -> i
  | Twhile               (i) -> i
  | Treturn              (i) -> i
  | Tgoto                (i) -> i
  | Tdefault             (i) -> i
  | Tsizeof              (i) -> i
  | Tasm                 (i) -> i
  | Tattribute           (i) -> i
  | TattributeNoarg           (i) -> i
  | Tinline              (i) -> i
  | Ttypeof              (i) -> i
  | Tnew                 (i) -> i
  | Tdelete              (i) -> i
  | TOParCplusplusInit   (i) -> i

  | EOF                  (i) -> i
  | Tnamespace           (i) -> i




(* used by tokens to complete the parse_info with filename, line, col infos *)
let visitor_info_of_tok f = function
  | TString ((s, isWchar), i)  -> TString ((s, isWchar), f i)
  | TChar  ((s, isWchar), i)   -> TChar  ((s, isWchar), f i)
  | TFloat ((s, floatType), i) -> TFloat ((s, floatType), f i)
  | TAssign  (assignOp, i)     -> TAssign  (assignOp, f i)

  | TIdent  (s, i)         -> TIdent  (s, f i)
  | TKRParam(s, i)         -> TKRParam(s, f i)
  | Tconstructorname(s, i) -> Tconstructorname  (s, f i)
  | TypedefIdent  (s, i)   -> TypedefIdent  (s, f i)
  | TInt  (s, i)           -> TInt  (s, f i)

  | TDefine (i1) -> TDefine(f i1)

  | TUndef (i1) -> TUndef(f i1)
  | TCppDirectiveOther (i1) -> TCppDirectiveOther(f i1)

  | TInclude (includes, filename, inifdef, i1) ->
      TInclude (includes, filename, inifdef, f i1)

  | TIncludeStart (i1, inifdef) -> TIncludeStart (f i1, inifdef)
  | TIncludeFilename (s, i1) -> TIncludeFilename (s, f i1)

  | TCppEscapedNewline (i1) -> TCppEscapedNewline (f i1)
  | TDefEOL (i1) -> TDefEOL (f i1)

  | TCppConcatOp (ii) -> TCppConcatOp (f ii)

  | TOParDefine (i1) -> TOParDefine (f i1)
  | TIdentDefine  (s, i) -> TIdentDefine (s, f i)

  | TDefParamVariadic (s, i1) -> TDefParamVariadic (s, f i1)

  | TOBraceDefineInit (i1) -> TOBraceDefineInit (f i1)


  | TUnknown             (i) -> TUnknown                (f i)

  | TMacroIdentBuilder             (s, i) -> TMacroIdentBuilder (s, f i)
  | TMacroAttr           (s, i)   -> TMacroAttr            (s, f i)
  | TMacroAttrStorage           (s, i)   -> TMacroAttrStorage         (s, f i)
  | TMacroStmt           (s, i)   -> TMacroStmt            (s, f i)
  | TMacroString         (s, i)   -> TMacroString          (s, f i)
  | TMacroDecl           (s, i) -> TMacroDecl            (s, f i)
  | TMacroDeclConst      (i)   -> TMacroDeclConst       (f i)
  | TMacroIterator       (s, i) -> TMacroIterator        (s, f i)
(*  | TMacroTop          (s,i) -> TMacroTop             (s,f i) *)
  | TCParEOL (i) ->     TCParEOL (f i)


  | TAction               (i) -> TAction             (f i)

  | TComment             (i) -> TComment             (f i)
  | TCommentSpace        (i) -> TCommentSpace        (f i)
  | TCommentNewline      (i) -> TCommentNewline      (f i)
  | TCommentCpp          (cppkind, i) -> TCommentCpp (cppkind, f i)
  | TCommentMisc         (i) -> TCommentMisc         (f i)

  | TCommentSkipTagStart         (i) -> TCommentSkipTagStart         (f i)
  | TCommentSkipTagEnd         (i) -> TCommentSkipTagEnd         (f i)

  | TIfdef               (t, i) -> TIfdef               (t, f i)
  | TIfdefelse           (t, i) -> TIfdefelse           (t, f i)
  | TIfdefelif           (t, i) -> TIfdefelif           (t, f i)
  | TEndif               (t, i) -> TEndif               (t, f i)
  | TIfdefBool           (b, t, i) -> TIfdefBool        (b, t, f i)
  | TIfdefMisc           (b, t, i) -> TIfdefMisc        (b, t, f i)
  | TIfdefVersion        (b, t, i) -> TIfdefVersion     (b, t, f i)

  | TOPar                (i) -> TOPar                (f i)
  | TCPar                (i) -> TCPar                (f i)
  | TOBrace              (i) -> TOBrace              (f i)
  | TCBrace              (i) -> TCBrace              (f i)
  | TOCro                (i) -> TOCro                (f i)
  | TCCro                (i) -> TCCro                (f i)
  | TDot                 (i) -> TDot                 (f i)
  | TComma               (i) -> TComma               (f i)
  | TPtrOp               (i) -> TPtrOp               (f i)
  | TInc                 (i) -> TInc                 (f i)
  | TDec                 (i) -> TDec                 (f i)
  | TEq                  (i) -> TEq                  (f i)
  | TWhy                 (i) -> TWhy                 (f i)
  | TTilde               (i) -> TTilde               (f i)
  | TBang                (i) -> TBang                (f i)
  | TEllipsis            (i) -> TEllipsis            (f i)
  | TDotDot              (i) -> TDotDot              (f i)
  | TPtVirg              (i) -> TPtVirg              (f i)
  | TOrLog               (i) -> TOrLog               (f i)
  | TAndLog              (i) -> TAndLog              (f i)
  | TOr                  (i) -> TOr                  (f i)
  | TXor                 (i) -> TXor                 (f i)
  | TAnd                 (i) -> TAnd                 (f i)
  | TEqEq                (i) -> TEqEq                (f i)
  | TNotEq               (i) -> TNotEq               (f i)
  | TInf                 (i) -> TInf                 (f i)
  | TSup                 (i) -> TSup                 (f i)
  | TInfEq               (i) -> TInfEq               (f i)
  | TSupEq               (i) -> TSupEq               (f i)
  | TShl                 (i) -> TShl                 (f i)
  | TShr                 (i) -> TShr                 (f i)
  | TPlus                (i) -> TPlus                (f i)
  | TMinus               (i) -> TMinus               (f i)
  | TMul                 (i) -> TMul                 (f i)
  | TDiv                 (i) -> TDiv                 (f i)
  | TMax                 (i) -> TMax                 (f i)
  | TMin                 (i) -> TMin                 (f i)
  | TMod                 (i) -> TMod                 (f i)
  | Tchar                (i) -> Tchar                (f i)
  | Tshort               (i) -> Tshort               (f i)
  | Tint                 (i) -> Tint                 (f i)
  | Tdouble              (i) -> Tdouble              (f i)
  | Tfloat               (i) -> Tfloat               (f i)
  | Tlong                (i) -> Tlong                (f i)
  | Tunsigned            (i) -> Tunsigned            (f i)
  | Tsigned              (i) -> Tsigned              (f i)
  | Tvoid                (i) -> Tvoid                (f i)
  | Tsize_t              (i) -> Tsize_t              (f i)
  | Tssize_t             (i) -> Tssize_t             (f i)
  | Tptrdiff_t           (i) -> Tptrdiff_t           (f i)
  | Tauto                (i) -> Tauto                (f i)
  | Tregister            (i) -> Tregister            (f i)
  | Textern              (i) -> Textern              (f i)
  | Tstatic              (i) -> Tstatic              (f i)
  | Tconst               (i) -> Tconst               (f i)
  | Tvolatile            (i) -> Tvolatile            (f i)

  | Trestrict            (i) -> Trestrict            (f i)

  | Tstruct              (i) -> Tstruct              (f i)
  | Tenum                (i) -> Tenum                (f i)
  | Ttypedef             (i) -> Ttypedef             (f i)
  | Tunion               (i) -> Tunion               (f i)
  | Tbreak               (i) -> Tbreak               (f i)
  | Telse                (i) -> Telse                (f i)
  | Tswitch              (i) -> Tswitch              (f i)
  | Tcase                (i) -> Tcase                (f i)
  | Tcontinue            (i) -> Tcontinue            (f i)
  | Tfor                 (i) -> Tfor                 (f i)
  | Tdo                  (i) -> Tdo                  (f i)
  | Tif                  (i) -> Tif                  (f i)
  | Twhile               (i) -> Twhile               (f i)
  | Treturn              (i) -> Treturn              (f i)
  | Tgoto                (i) -> Tgoto                (f i)
  | Tdefault             (i) -> Tdefault             (f i)
  | Tsizeof              (i) -> Tsizeof              (f i)
  | Tasm                 (i) -> Tasm                 (f i)
  | Tattribute           (i) -> Tattribute           (f i)
  | TattributeNoarg      (i) -> TattributeNoarg      (f i)
  | Tinline              (i) -> Tinline              (f i)
  | Ttypeof              (i) -> Ttypeof              (f i)
  | Tnew                 (i) -> Tnew                 (f i)
  | Tdelete              (i) -> Tdelete              (f i)
  | TOParCplusplusInit   (i) -> TOParCplusplusInit   (f i)
  | EOF                  (i) -> EOF                  (f i)
  | Tnamespace           (i) -> Tnamespace           (f i)


(*****************************************************************************)
(* Accessors *)
(*****************************************************************************)

let linecol_of_tok tok =
  let info = info_of_tok tok in
  Ast_c.line_of_info info, Ast_c.col_of_info info

let col_of_tok x = snd (linecol_of_tok x)
let line_of_tok x = fst (linecol_of_tok x)
let pos_of_tok x =  Ast_c.opos_of_info (info_of_tok x)
let str_of_tok x =  Ast_c.str_of_info (info_of_tok x)
let file_of_tok x = Ast_c.file_of_info (info_of_tok x)
let pinfo_of_tok x = Ast_c.pinfo_of_info (info_of_tok x)

let is_origin x =
  match pinfo_of_tok x with Ast_c.OriginTok _ -> true | _ -> false
let is_expanded x =
  match pinfo_of_tok x with Ast_c.ExpandedTok _ -> true | _ -> false
let is_fake x =
  match pinfo_of_tok x with Ast_c.FakeTok _ -> true | _ -> false
let is_abstract x =
  match pinfo_of_tok x with Ast_c.AbstractLineTok _ -> true | _ -> false

(*****************************************************************************)
(* Helpers *)
(*****************************************************************************)
let is_same_line_or_close line tok =
  line_of_tok tok =|= line ||
  line_of_tok tok =|= line - 1 ||
  line_of_tok tok =|= line - 2

