123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as published *)(* by the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* 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 *)(* GNU Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** Preprocessor for expanding macros *)openParseropenLexingopenMopsa_c_parser.C_ASTopenMopsa_c_parser.Clang_ASTopenMopsa_utilsletdebugfmt=Debug.debug~channel:"c_stubs_parser.passes.preprocessor"fmtletpp_tokenfmttoken=Format.pp_print_stringfmt(Lexer.token_to_stringtoken)letpp_token_listfmttokens=Format.pp_print_list~pp_sep:(funfmt()->Format.pp_print_stringfmt" ")pp_tokenfmttokens(* Stack containing called macros with their tokenized content *)letstack:(macro*tokenlist)Stack.t=Stack.create()letpp_stackfmtstack=letelements=Stack.fold(funacce->acc@[e])[]stackinFormat.fprintffmt"@[<v>%a@]"(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt"@,")(funfmt(macro,tokens)->Format.fprintffmt"%s: %a"macro.macro_namepp_token_listtokens))elements(* Check if we are already expanding a macro *)letinside_macromacrostack=letexceptionFoundintryStack.iter(fun(m,_)->ifm.macro_name=macro.macro_namethenraiseFound)stack;falsewithFound->true(* Get the next token *)letrecnext_token?(ret2caller=true)lexerlexbuf=ifStack.is_emptystackthenlexerlexbufelseletmacro,tokens=Stack.popstackinmatchtokenswith|[]->assertret2caller;next_tokenlexerlexbuf|[token]->ifnotret2callerthenStack.push(macro,[])stack;token|token::tl->Stack.push(macro,tl)stack;token(* Parse a string into a token using the lexer *)lettokenize_stringlexer(s:string):tokenlist=letreciterlexbuf=matchlexerlexbufwith|EOF->[]|tk->tk::iterlexbufiniter(Lexing.from_strings)(* Parse the arguments of a macro into a map of tokens *)lettokenize_argumentsmacrolexerlexbuf:tokenlistStringMap.t=matchmacro.macro_paramswith|[]->StringMap.empty|hd::tl->(* Read '(' *)ifnext_token~ret2caller:falselexerlexbuf<>LPARthenraise(Lexer.SyntaxError(Format.asprintf"macro %s is missing '('"macro.macro_name));(* Read arguments separated by ',' until reaching ')' *)letreciterparamparamsopenparpast_tokenstoken=matchtokenwith|EOF->raise(Lexer.SyntaxError(Format.asprintf"macro %s is missing ')'"macro.macro_name));|LPAR->iterparamparams(openpar+1)(token::past_tokens)(next_token~ret2caller:falselexerlexbuf)|RPAR->ifopenpar=0thenbeginifparams<>[]thenraise(Lexer.SyntaxError(Format.asprintf"macro %s is missing %d more argument%a"macro.macro_name(List.lengthparams)(Debug.plurial_int)(List.lengthparams)));StringMap.singletonparam(List.revpast_tokens)endelseiterparamparams(openpar-1)(token::past_tokens)(next_token~ret2caller:falselexerlexbuf)|COMMAwhenopenpar=0->beginmatchparamswith|[]->raise(Lexer.SyntaxError"macro %s is given too many arguments");|hd::tl->iterhdtl0[](next_token~ret2caller:falselexerlexbuf)|>StringMap.addparam(List.revpast_tokens)end|_->iterparamparamsopenpar(token::past_tokens)(next_token~ret2caller:falselexerlexbuf)initerhdtl0[](next_token~ret2caller:falselexerlexbuf)(* Add parenthesis around a list of tokens *)letadd_parenthesistokens=matchtokenswith|[]|[_]->tokens|_->LPAR::tokens@[RPAR](* Parse the content of a macro as a list of tokens and replace parameters with the corresponding arguments *)lettokeninze_macromacroargslexer:tokenlist=letcontent=List.fold_left(funaccs->acc@tokenize_stringlexers)[]macro.macro_contentsin(* Replace parameters with the corresponding arguments *)letreciter=function|[]->[]|(IDENTidashd)::tl->beginmatchStringMap.find_optidargswith|None->hd::itertl|Sometokens->add_parenthesistokens@itertlend|hd::tl->hd::itertlinadd_parenthesis(itercontent)exceptionAliasFoundofstring(* Parse a preprocessor directive *)letparse_directivelexerlexbuf=matchnext_tokenlexerlexbufwith|ALIAS->beginmatchnext_tokenlexerlexbufwith|IDENTalias->raise(AliasFoundalias)|token->raise(Lexer.SyntaxError(Format.asprintf"unexpected alias argument %s"(Lexer.token_to_stringtoken)))end|token->raise(Lexer.SyntaxError(Format.asprintf"unknown preprocessor directive %s"(Lexer.token_to_stringtoken)))(* Type of a predicate *)typepredicate={pred_name:string;pred_params:stringlist;pred_body:tokenlist;}(* Parse a predicate *)letparse_predicatelexerlexbuf=letget_predicate_name()=matchlexerlexbufwith|END_DELIM->None|PREDICATE->beginmatchlexerlexbufwith|IDENTname->Somename|token->raise(Lexer.SyntaxError("incorrect predicate name "^(Lexer.token_to_stringtoken)))end|token->raise(Lexer.SyntaxError("incorrect predicate declaration "^(Lexer.token_to_stringtoken)))inletget_predicate_params()=matchlexerlexbufwith|COLON->[]|LPAR->letreciter()=matchlexerlexbufwith|RPAR->beginmatchlexerlexbufwith|COLON->[]|token->raise(Lexer.SyntaxError("missing : after predicate header"))end|IDENTparam->beginmatchlexerlexbufwith|COMMA->param::iter()|RPAR->beginmatchlexerlexbufwith|COLON->[param]|token->raise(Lexer.SyntaxError("missing : after predicate header"))end|token->raise(Lexer.SyntaxError("incorrect predicate parameter separator "^(Lexer.token_to_stringtoken)))end|token->raise(Lexer.SyntaxError("incorrect predicate parameter "^(Lexer.token_to_stringtoken)))initer()|token->raise(Lexer.SyntaxError("incorrect predicate declaration "^(Lexer.token_to_stringtoken)))inletrecget_predicate_body()=matchlexerlexbufwith|SEMICOL->[]|EOF|END_DELIM->raise(Lexer.SyntaxError"missing ; at the end of the predicate")|token->token::get_predicate_body()inmatchget_predicate_name()with|None->None|Somename->letparams=get_predicate_params()inletbody=get_predicate_body()inSome{pred_name=name;pred_params=params;pred_body=body}(* Parse a sequence of predicates *)letparse_predicateslexerlexbuf=matchlexerlexbufwith|BEGIN_DELIM->letreciter()=matchparse_predicatelexerlexbufwith|None->[]|Somep->p::iter()initer()|token->raise(Lexer.SyntaxError("incorrect predicate comment symbol "^(Lexer.token_to_stringtoken)))(* Turn a predicate into a macro *)letpredicate_to_macropred={macro_name=pred.pred_name;macro_params=pred.pred_params;macro_contents=List.mapLexer.token_to_stringpred.pred_body;macro_loc={loc_line=-1;loc_column=-1;loc_file="";}}(* Entry point of the preprocessor *)letrecreadpredicatesmacrosenumslexerlexbuf=letstack0=Stack.copystackinlettoken=next_tokenlexerlexbufin(* Identifiers *may be* enums or macros, so check that *)matchtokenwith|IDENTid->beginmatchStringMap.find_optidenumswith(* Enum case *)|Somen->INT_CONST(n,NO_SUFFIX)|None->matchStringMap.find_optidpredicateswith(* Predicate case *)|Somepred->(* Save the lexer location *)letpredicate_start_pos=lexbuf.lex_start_pin(* Since predicates are similar to macros, we use the same processing *)letmacro=predicate_to_macropredin(* Parse the arguments *)letargs=tokenize_argumentsmacrolexerlexbufin(* Parse the body of the predicate *)lettokens=tokeninze_macromacroargslexerin(* Update the tokens stack and repeat the same process *)Stack.push(macro,tokens)stack;(* Restore the start position of the macro *)lexbuf.lex_start_p<-predicate_start_pos;readpredicatesmacrosenumslexerlexbuf|None->matchStringMap.find_optidmacroswith|None->token(* Macro, but inside itself, so treat it as an identifier *)|Somemacrowheninside_macromacrostack0->token(* Macro expansion case *)|Somemacro->(* Save the lexer location *)letmacro_start_pos=lexbuf.lex_start_pin(* Parse the arguments *)letargs=tokenize_argumentsmacrolexerlexbufin(* Parse the body of the macro *)lettokens=tokeninze_macromacroargslexerin(* Update the tokens stack and repeat the same process *)Stack.push(macro,tokens)stack;(* Restore the start position of the macro *)lexbuf.lex_start_p<-macro_start_pos;readpredicatesmacrosenumslexerlexbufend|SHARP->parse_directivelexerlexbuf|_->token