235 lines
8.2 KiB
OCaml
235 lines
8.2 KiB
OCaml
(* Yoann Padioleau
|
|
*
|
|
* Copyright (C) 2007, 2008 Ecole des Mines de Nantes
|
|
* 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
|
|
|
|
module Ast = Ast_cpp
|
|
module Flag = Flag_parsing_cpp
|
|
module TH = Token_helpers_cpp
|
|
module Parser = Parser_cpp
|
|
module Hack = Parsing_hacks_lib
|
|
|
|
open Parser_cpp
|
|
open Token_views_cpp
|
|
|
|
(*****************************************************************************)
|
|
(* Prelude *)
|
|
(*****************************************************************************)
|
|
|
|
(*
|
|
* CPP functions working at the token level. See pp_ast.ml for cpp functions
|
|
* working at the AST level (which is very unusual but makes sense in
|
|
* the coccinelle context for instance).
|
|
*
|
|
* Note that because I use a single lexer to work both at the C and cpp level
|
|
* there are some inconveniencies.
|
|
* For instance 'for' is a valid name for a macro parameter and macro
|
|
* body, but is interpreted in a special way by our single lexer, and
|
|
* so at some places where I expect a TIdent I need also to
|
|
* handle special cases and accept Tfor, Tif, etc at those places.
|
|
*
|
|
* There are multiple issues related to those keywords incorrect tokens.
|
|
* Those keywords can be:
|
|
*
|
|
* - (1) in the name of the macro as in #define inline
|
|
* - (2) in a parameter of the macro as in #define foo(char) char x;
|
|
* - (3) in an argument to a macro call as in IDENT(if);
|
|
*
|
|
* Case 1 is easy to fix in define_ident in ???
|
|
*
|
|
* Case 2 is easy to fix in define_parse below, where we detect such tokens
|
|
* in the parameters and then replace their occurence in the body with
|
|
* a TIdent.
|
|
*
|
|
* Case 3 is only an issue when the expanded token is not really used
|
|
* as usual but used for instance in concatenation as in a ## if
|
|
* when expanded. In the case the grammar this time will not be happy
|
|
* so this is also easy to fix in cpp_engine.
|
|
*)
|
|
|
|
(*****************************************************************************)
|
|
(* Wrappers *)
|
|
(*****************************************************************************)
|
|
let pr2, _pr2_once = Common2.mk_pr2_wrappers Flag_parsing_cpp.verbose_parsing
|
|
|
|
(*****************************************************************************)
|
|
(* Types *)
|
|
(*****************************************************************************)
|
|
|
|
(* the tokens in the body of the macro are all ExpandedTok *)
|
|
type define_body = (unit,string list) either * Parser_cpp.token list
|
|
|
|
(* TODO:
|
|
type define_def = string * define_param * define_body
|
|
and define_param =
|
|
| NoParam
|
|
| Params of string list
|
|
and define_body =
|
|
| DefineBody of Parser_c.token list
|
|
| DefineHint of parsinghack_hint
|
|
|
|
and parsinghack_hint =
|
|
| HintIterator
|
|
| HintDeclarator
|
|
| HintMacroString
|
|
| HintMacroStatement
|
|
| HintAttribute
|
|
| HintMacroIdentBuilder
|
|
*)
|
|
|
|
|
|
(*****************************************************************************)
|
|
(* Apply macro (using standard.h or other defs) *)
|
|
(*****************************************************************************)
|
|
(* cpp-builtin part1, macro, using standard.h or other defs *)
|
|
|
|
(* Thanks to this function many stuff are not anymore hardcoded in
|
|
* OCaml code (but are now hardcoded in standard.h ...)
|
|
*)
|
|
let (cpp_engine:
|
|
(string , Parser.token list) assoc -> Parser.token list -> Parser.token list)
|
|
= fun env xs ->
|
|
xs +> List.map (fun tok ->
|
|
match tok with
|
|
| TIdent (s,_i1) when List.mem_assoc s env -> Common2.assoc s env
|
|
| x -> [x]
|
|
)
|
|
+> List.flatten
|
|
|
|
(*
|
|
* We apply a macro by generating new ExpandedToken and by
|
|
* commenting the old macro call.
|
|
*
|
|
* no need to take care to substitute the macro name itself
|
|
* that occurs in the macro definition because the macro name is
|
|
* after fix_token_define a TDefineIdent, no more a TIdent.
|
|
*)
|
|
let apply_macro_defs defs xs =
|
|
|
|
let rec apply_macro_defs xs =
|
|
match xs with
|
|
| [] -> ()
|
|
|
|
(* recognized macro of standard.h (or other) *)
|
|
| PToken ({t=TIdent (s,_i1);_} as id)::Parenthised (xxs,info_parens)::xs
|
|
when Hashtbl.mem defs s ->
|
|
Hack.pr2_pp ("MACRO: found known macro = " ^ s);
|
|
(match Hashtbl.find defs s with
|
|
| Left (), bodymacro ->
|
|
pr2 ("macro without param used before parenthize, wierd: " ^ s);
|
|
(* ex: PRINTP("NCR53C400 card%s detected\n" ANDP(((struct ... *)
|
|
Hack.set_as_comment (Token_cpp.CppMacroExpanded) id;
|
|
id.new_tokens_before <- bodymacro;
|
|
| Right params, bodymacro ->
|
|
if List.length params = List.length xxs
|
|
then
|
|
let xxs' = xxs +> List.map (fun x ->
|
|
(tokens_of_paren_ordered x) +> List.map (fun x ->
|
|
TH.visitor_info_of_tok Ast.make_expanded x.t
|
|
)
|
|
) in
|
|
id.new_tokens_before <-
|
|
cpp_engine (Common2.zip params xxs') bodymacro
|
|
|
|
else begin
|
|
pr2 ("macro with wrong number of arguments, wierd: " ^ s);
|
|
id.new_tokens_before <- bodymacro;
|
|
end;
|
|
(* important to do that after have apply the macro, otherwise
|
|
* will pass as argument to the macro some tokens that
|
|
* are all TCommentCpp
|
|
*)
|
|
[Parenthised (xxs, info_parens)] +>
|
|
iter_token_paren (Hack.set_as_comment Token_cpp.CppMacroExpanded);
|
|
Hack.set_as_comment Token_cpp.CppMacroExpanded id;
|
|
|
|
|
|
|
|
);
|
|
apply_macro_defs xs
|
|
|
|
| PToken ({t=TIdent (s,_i1);_} as id)::xs
|
|
when Hashtbl.mem defs s ->
|
|
Hack.pr2_pp ("MACRO: found known macro = " ^ s);
|
|
(match Hashtbl.find defs s with
|
|
| Right _params, _bodymacro ->
|
|
pr2 ("macro with params but no parens found, wierd: " ^ s);
|
|
(* dont apply the macro, perhaps a redefinition *)
|
|
()
|
|
| Left (), bodymacro ->
|
|
(* special case when 1-1 substitution, we reuse the token *)
|
|
(match bodymacro with
|
|
| [newtok] ->
|
|
id.t <- (newtok +> TH.visitor_info_of_tok (fun _ ->
|
|
TH.info_of_tok id.t))
|
|
|
|
| _ ->
|
|
Hack.set_as_comment Token_cpp.CppMacroExpanded id;
|
|
id.new_tokens_before <- bodymacro;
|
|
)
|
|
);
|
|
apply_macro_defs xs
|
|
|
|
(* recurse *)
|
|
| (PToken _x)::xs -> apply_macro_defs xs
|
|
| (Parenthised (xxs, _info_parens))::xs ->
|
|
xxs +> List.iter apply_macro_defs;
|
|
apply_macro_defs xs
|
|
|
|
in
|
|
apply_macro_defs xs
|
|
|
|
(*****************************************************************************)
|
|
(* Extracting macros (from a standard.h) *)
|
|
(*****************************************************************************)
|
|
|
|
(* assumes have called fix_tokens_define before, so have TOPar_Define *)
|
|
let rec define_parse xs =
|
|
match xs with
|
|
| [] -> []
|
|
| TDefine _i1::TIdent_Define (s,_i2)::TOPar_Define _i3::xs ->
|
|
let (tokparams, _, xs) =
|
|
xs +> Common2.split_when (function TCPar _ -> true | _ -> false) in
|
|
let (body, _, xs) =
|
|
xs +> Common2.split_when
|
|
(function TCommentNewline_DefineEndOfMacro _ -> true | _ -> false) in
|
|
let params =
|
|
tokparams +> Common.map_filter (function
|
|
| TComma _ -> None
|
|
| TIdent (s, _) -> Some s
|
|
| x -> Common2.error_cant_have x
|
|
) in
|
|
let body = body +> List.map
|
|
(TH.visitor_info_of_tok Ast.make_expanded) in
|
|
let def = (s, (Right params, body)) in
|
|
def::define_parse xs
|
|
|
|
| TDefine _i1::TIdent_Define (s,_i2)::xs ->
|
|
let (body, _, xs) =
|
|
xs +> Common2.split_when
|
|
(function TCommentNewline_DefineEndOfMacro _ -> true | _ -> false) in
|
|
let body = body +> List.map
|
|
(TH.visitor_info_of_tok Ast.make_expanded) in
|
|
let def = (s, (Left (), body)) in
|
|
def::define_parse xs
|
|
|
|
| TDefine _i1::_ ->
|
|
raise Impossible
|
|
| _x::xs -> define_parse xs
|
|
|
|
|
|
let extract_macros xs =
|
|
let cleaner = xs +> List.filter (fun x -> not (TH.is_comment x)) in
|
|
define_parse cleaner
|