Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
164 lines (146 sloc) 5.84 KB
(* Yoann Padioleau
*
* Copyright (C) 2002-2008 Yoann Padioleau
* Copyright (C) 2011 Facebook
*
* 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_cpp
module Ast = Ast_cpp
module Parser = Parser_cpp
module TH = Token_helpers_cpp
module Hack = Parsing_hacks_lib
(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(*
* To parse macro definitions I need to do some tricks
* as some information can be computed only at the lexing level. For instance
* the space after the name of the macro in '#define foo (x)' is meaningful
* but the grammar does not have this information. So define_ident() below
* look at such space and generate a special TOpar_Define token.
*
* In a similar way macro definitions can contain some antislash and newlines
* and the grammar need to know where the macro ends which is
* a line-level and so low token-level information. Hence the
* function define_line'()below and the TCommentNewline_DefineEndOfMacro.
*
* update: TCommentNewline_DefineEndOfMacro is handled in a special way
* at different places, a little bit like EOF, especially for error recovery,
* so this is an important token that should not be retagged!
*
* We also change the kind of TIdent to TIdent_Define to avoid bad interactions
* with other parsing_hack tricks. For instant if keep TIdent then
* the stringication heuristics can believe the TIdent is a string-macro.
* So simpler to change the kind of the TIdent in a macro too.
*
* ugly hack, a better solution perhaps would be to erase
* TCommentNewline_DefineEndOfMacro from the Ast and list of tokens in parse_c.
*
* note: I do a +1 somewhere, it's for the unparsing to correctly sync.
*
* note: can't replace mark_end_define by simply a fakeInfo(). The reason
* is where is the \n TCommentSpace. Normally there is always a last token
* to synchronize on, either EOF or the token of the next toplevel.
* In the case of the #define we got in list of token
* [TCommentSpace "\n"; TDefEOL] but if TDefEOL is a fakeinfo then we will
* not synchronize on it and so we will not print the "\n".
* A solution would be to put the TDefEOL before the "\n".
*
* todo?: could put a ExpandedTok for that ?
*)
(*****************************************************************************)
(* Wrappers *)
(*****************************************************************************)
let pr2, pr2_once = Common.mk_pr2_wrappers Flag_parsing_cpp.verbose_lexing
(*****************************************************************************)
(* Helpers *)
(*****************************************************************************)
let mark_end_define ii =
let ii' =
{ Parse_info.
token = Parse_info.OriginTok {
(Ast.parse_info_of_info ii) with
Parse_info.str = "";
Parse_info.charpos = Ast.pos_of_info ii + 1
};
transfo = Parse_info.NoTransfo;
comments = ();
}
in
(* fresh_tok *) TCommentNewline_DefineEndOfMacro (ii')
let pos ii = Ast.string_of_info ii
(*****************************************************************************)
(* Parsing hacks for #define *)
(*****************************************************************************)
(* put the TCommentNewline_DefineEndOfMacro at the good place *)
let rec define_line_1 xs =
match xs with
| [] -> []
| TDefine ii::xs ->
let line = Ast.line_of_info ii in
TDefine ii::define_line_2 line ii xs
| TCppEscapedNewline ii::xs ->
pr2 (spf "WEIRD: a \\ outside a #define at %s" (pos ii));
(* fresh_tok*) TCommentSpace ii::define_line_1 xs
| x::xs ->
x::define_line_1 xs
and define_line_2 line lastinfo xs =
match xs with
| [] ->
(* should not happened, should meet EOF before *)
pr2 "PB: WEIRD in Parsing_hack_define.define_line_2";
mark_end_define lastinfo::[]
| x::xs ->
let line' = TH.line_of_tok x in
let info = TH.info_of_tok x in
(match x with
| EOF ii ->
mark_end_define lastinfo::EOF ii::define_line_1 xs
| TCppEscapedNewline ii ->
if (line' <> line)
then pr2 "PB: WEIRD: not same line number";
(* fresh_tok*) TCommentSpace ii::define_line_2 (line+1) info xs
| x ->
if line' = line
then x::define_line_2 line info xs
else
mark_end_define lastinfo::define_line_1 (x::xs)
)
let rec define_ident xs =
match xs with
| [] -> []
| TDefine ii::xs ->
TDefine ii::
(match xs with
| TCommentSpace i1::TIdent (s,i2)::(* no space *)TOPar (i3)::xs ->
(* if TOPar_Define is just next to the ident (no space), then
* it's a macro-function. We change the token to avoid
* ambiguity between '#define foo(x)' and '#define foo (x)'
*)
TCommentSpace i1
::Hack.fresh_tok (TIdent_Define (s,i2))
::Hack.fresh_tok (TOPar_Define i3)
::define_ident xs
| TCommentSpace i1::TIdent (s,i2)::xs ->
TCommentSpace i1
::Hack.fresh_tok (TIdent_Define (s,i2))
::define_ident xs
| _ ->
pr2 (spf "WEIRD #define body, at %s" (pos ii));
define_ident xs
)
| x::xs ->
x::define_ident xs
let fix_tokens_define2 xs =
define_ident (define_line_1 xs)
let fix_tokens_define a =
Common.profile_code "Hack.fix_define" (fun () -> fix_tokens_define2 a)
Something went wrong with that request. Please try again.