123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235(* 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.
*)openCommonmoduleFlag=Flag_parsingmoduleAst=Cst_cppmoduleTH=Token_helpers_cppmoduleParser=Parser_cppmoduleHack=Parsing_hacks_libopenParser_cppopenToken_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 *)(*****************************************************************************)letpr2,_pr2_once=Common2.mk_pr2_wrappersFlag.verbose_parsing(*****************************************************************************)(* Types *)(*****************************************************************************)(* the tokens in the body of the macro are all ExpandedTok *)typedefine_body=(unit,stringlist)either*Parser_cpp.tokenlist(* 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.tokenlist)assoc->Parser.tokenlist->Parser.tokenlist)=funenvxs->xs|>List.map(funtok->matchtokwith|TIdent(s,_i1)whenList.mem_assocsenv->Common2.assocsenv|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.
*)letapply_macro_defsdefsxs=letrecapply_macro_defsxs=matchxswith|[]->()(* recognized macro of standard.h (or other) *)|PToken({t=TIdent(s,_i1);_}asid)::Parenthised(xxs,info_parens)::xswhenHashtbl.memdefss->Hack.pr2_pp("MACRO: found known macro = "^s);(matchHashtbl.finddefsswith|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;|Rightparams,bodymacro->ifList.lengthparams=List.lengthxxsthenletxxs'=xxs|>List.map(funx->(tokens_of_paren_orderedx)|>List.map(funx->TH.visitor_info_of_tokAst.make_expandedx.t))inid.new_tokens_before<-cpp_engine(Common2.zipparamsxxs')bodymacroelsebeginpr2("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_commentToken_cpp.CppMacroExpanded);Hack.set_as_commentToken_cpp.CppMacroExpandedid;);apply_macro_defsxs|PToken({t=TIdent(s,_i1);_}asid)::xswhenHashtbl.memdefss->Hack.pr2_pp("MACRO: found known macro = "^s);(matchHashtbl.finddefsswith|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 *)(matchbodymacrowith|[newtok]->id.t<-(newtok|>TH.visitor_info_of_tok(fun_->TH.info_of_tokid.t))|_->Hack.set_as_commentToken_cpp.CppMacroExpandedid;id.new_tokens_before<-bodymacro;));apply_macro_defsxs(* recurse *)|(PToken_x)::xs->apply_macro_defsxs|(Parenthised(xxs,_info_parens))::xs->xxs|>List.iterapply_macro_defs;apply_macro_defsxsinapply_macro_defsxs(*****************************************************************************)(* Extracting macros (from a standard.h) *)(*****************************************************************************)(* assumes have called fix_tokens_define before, so have TOPar_Define *)letrecdefine_parsexs=matchxswith|[]->[]|TDefine_i1::TIdent_Define(s,_i2)::TOPar_Define_i3::xs->let(tokparams,_,xs)=xs|>Common2.split_when(functionTCPar_->true|_->false)inlet(body,_,xs)=xs|>Common2.split_when(functionTCommentNewline_DefineEndOfMacro_->true|_->false)inletparams=tokparams|>Common.map_filter(function|TComma_->None|TIdent(s,_)->Somes|x->Common2.error_cant_havex)inletbody=body|>List.map(TH.visitor_info_of_tokAst.make_expanded)inletdef=(s,(Rightparams,body))indef::define_parsexs|TDefine_i1::TIdent_Define(s,_i2)::xs->let(body,_,xs)=xs|>Common2.split_when(functionTCommentNewline_DefineEndOfMacro_->true|_->false)inletbody=body|>List.map(TH.visitor_info_of_tokAst.make_expanded)inletdef=(s,(Left(),body))indef::define_parsexs|TDefine_i1::_->raiseImpossible|_x::xs->define_parsexsletextract_macrosxs=letcleaner=xs|>List.filter(funx->not(TH.is_commentx))indefine_parsecleaner