123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477openBaseopenStdioopenPpxlibopenAst_builder.DefaultmoduleFilename=Caml.FilenamemoduleEnv=Interpreter.EnvmoduleValue=Interpreter.ValuemoduleOf_item=struct(* boilerplate code to pull extensions out of different ast nodes *)openTokenletdirective_or_block_of_ext~item({txt=ext_name;loc},payload)attrs=matchDirective.of_string_optext_namewith|None->(* not one of our extensions *)Block[item]|Somedir->assert_no_attributesattrs;Directive(dir,loc,payload)letstructureitem=matchitem.pstr_descwith|Pstr_extension(ext,attrs)->directive_or_block_of_ext~itemextattrs|_->Block[item]letsignatureitem=matchitem.psig_descwith|Psig_extension(ext,attrs)->directive_or_block_of_ext~itemextattrs|_->Block[item]letclass_structureitem=matchitem.pcf_descwith|Pcf_extensionext->directive_or_block_of_ext~itemext[]|_->Block[item]letclass_signatureitem=matchitem.pctf_descwith|Pctf_extensionext->directive_or_block_of_ext~itemext[]|_->Block[item]endmoduleAst_utils=structletget_expr~locpayload=matchpayloadwith|PStr[{pstr_desc=Pstr_eval(e,attrs);_}]->assert_no_attributesattrs;e|_->Location.raise_errorf~loc"optcomp: invalid directive syntax, expected single expression."letassert_no_arguments~locpayload=matchpayloadwith|PStr[]->()|_->Location.raise_errorf~loc"optcomp: invalid directive syntax, expected no arguments."letmake_apply_fun~locnameexpr=letiname={txt=Lidentname;loc}ineapply~loc(pexp_ident~lociname)[expr]letget_ident~locpayload=lete=get_expr~locpayloadinInterpreter.lid_of_expreletget_var~locpayload=lete=get_expr~locpayloadinInterpreter.var_of_expreletget_var_expr~locpayload=letapply_e=get_expr~locpayloadinmatchapply_e.pexp_descwith|Pexp_apply(var_e,[Nolabel,val_e])->Interpreter.var_of_exprvar_e,Someval_e|Pexp_construct(var_li,Someval_e)->Interpreter.var_of_lidvar_li,Someval_e|Pexp_apply(var_e,[])->Interpreter.var_of_exprvar_e,None|Pexp_construct(var_li,None)->Interpreter.var_of_lidvar_li,None|_->Location.raise_errorf~loc"optcomp: invalid directive syntax, expected var and expr"letget_string~locpayload=lete=get_expr~locpayloadinmatchewith|{pexp_desc=Pexp_constant(Pconst_string(x,_));_}->x|_->Location.raise_errorf~loc"optcomp: invalid directive syntax, expected string"endmoduleToken_stream:sigtype'at='aToken.tlistvalof_items:'alist->of_item:('a->'aToken.t)->'atend=structtype'at='aToken.tlisttypeftype=Ocaml|Cletresolve_import~loc~filename:string*ftype=letext=Filename.extension(Filename.basenamefilename)inletftype=matchextwith|".ml"|".mlh"->Ocaml|".h"->C|_->Location.raise_errorf~loc"optcomp: unknown file extension: %s\n\
Must be one of: .ml, .mlh or .h."extinletfbase=Filename.dirnameloc.loc_start.pos_fnameinletfpath=ifFilename.is_relativefilenamethenFilename.concatfbasefilenameelsefilenamein(fpath,ftype)letimport_open~locpayload=letfilename=Ast_utils.get_string~locpayloadinletfpath,ftype=resolve_import~loc~filenameinletin_ch=tryIn_channel.createfpathwithexn->letmsg=matchexnwith|Sys_errormsg->msg|_->Exn.to_stringexninLocation.raise_errorf~loc"optcomp: cannot open imported file: %s: %s"fpathmsgin(* disable old optcomp on imported files, or it consumes all variables :( *)Lexer.set_preprocessor(fun()->())(funx->x);letlexbuf=Lexing.from_channelin_chinlexbuf.lex_curr_p<-{pos_fname=fpath;pos_lnum=1;pos_bol=0;pos_cnum=0};in_ch,lexbuf,ftypeletunroll(stack:'aToken.tlist):('aToken.t*'aToken.tlist)=letbs,_,rest_rev=List.foldstack~init:([],false,[])~f:(fun(bs,found,rest)x->matchx,foundwith|Blockb,false->b@bs,false,rest|_->bs,true,x::rest)inBlockbs,List.revrest_revletrecof_items:'a.'alist->of_item:('a->'aToken.t)->'at=funitems~of_item->letof_items_stx=of_items~of_item:Of_item.structurexinlettokens_rev=List.folditems~init:[]~f:(funaccitem->matchof_itemitemwith|Directive(dir,loc,payload)astoken->letlast_block,rest=unrollaccinbeginmatchdirwith|Import->letin_ch,lexbuf,ftype=import_open~locpayloadinletnew_tokens=matchftypewith|C->Cparser.parse_looplexbuf|Ocaml->letst_items=Parse.implementationlexbufinToken.just_directives_exn~loc(of_items_stst_items)inIn_channel.closein_ch;List.revnew_tokens@(last_block::rest)|_->token::last_block::restend|_->beginmatchaccwith|Blockitems::acc->Block(items@[item])::acc|_->Block[item]::accend)inList.revtokens_revendmoduleMeta_ast:sigtype'atvalof_tokens:'aToken.tlist->'atvaleval:drop_item:('a->unit)->eval_item:(Env.t->'a->'a)->env:Env.t->'at->Env.t*'alistvalattr_mapper:to_loc:('a->location)->to_attrs:('a->attributes)->replace_attrs:('a->attributes->'a)->env:Env.t->'a->'aoptionend=structopenAst_utilstype'at=|Leafof'alist|Ifofexpression*'at*'at|Blockof'atlist|DefineofstringLocation.loc*expressionoption|UndefineofstringLocation.loc|ImportofstringLocation.loc|ErrorofstringLocation.loc|WarningofstringLocation.loctype'apartial_if=|EmptyIfof('at->'at->'at)(* [If] waiting for both blocks *)|PartialIfof('at->'at)(* [If] waiting for else block *)type'atemp_ast=|Fullof'at|Partialof'apartial_iflocletdeprecated_ifs~loc=Location.raise_errorf~loc"optcomp: elif(n)def is deprecated, use elif defined()."letunroll_exn~loc(acc:'atemp_astlist):('at*'apartial_if*'atemp_astlist)=(* split by first EmptyIf/PartialIf *)letpre,if_fun,post=List.foldacc~init:([],None,[])~f:(fun(pre,found,post)x->matchfoundwith|Some_->pre,found,x::post|None->matchxwith|Partial{txt=f;_}->pre,Somef,post|Fullast->ast::pre,None,post)inmatchif_funwith|None->Location.raise_errorf~loc"optcomp: else/endif/elif outside of if"|Somef->Blockpre,f,List.revpostletmake_if~loccond=letif_funast1ast2=If(cond,ast1,ast2)inPartial{txt=(EmptyIfif_fun);loc}letof_tokens(tokens:'aToken.tlist):('at)=letpre_parsed=List.foldtokens~init:([]:'atemp_astlist)~f:(funacctoken->matchtokenwith|Token.Block[]->acc|Token.Blockb->Full(Leafb)::acc|Token.Directive(dir,loc,payload)->matchdirwith|If->make_if~loc(get_expr~locpayload)::acc|Endif->assert_no_arguments~locpayload;let(last_block,if_fun,tail)=unroll_exn~locaccinbeginmatchif_funwith|PartialIff->Full(flast_block)::tail|EmptyIff->Full(flast_block(Block[]))::tailend|Elif->letcond=get_expr~locpayloadinlet(last_block,if_fun,tail)=unroll_exn~locaccinbeginmatchif_funwith|EmptyIff->letnew_if_funast1ast2=flast_block(If(cond,ast1,ast2))inPartial{txt=(EmptyIfnew_if_fun);loc}::tail|PartialIf_->Location.raise_errorf~loc"optcomp: elif after else clause."end|Else->assert_no_arguments~locpayload;let(last_block,if_fun,tail)=unroll_exn~locaccinbeginmatchif_funwith|EmptyIff->Partial{txt=PartialIf(flast_block);loc}::tail|PartialIf_->Location.raise_errorf~loc"optcomp: second else clause."end|Define->letident,expr=get_var_expr~locpayloadinFull(Define(ident,expr))::acc|Undef->Full(Undefine(get_var~locpayload))::acc|Error->Full(Error{txt=(get_string~locpayload);loc})::acc|Warning->Full(Warning{txt=(get_string~locpayload);loc})::acc|Import->Full(Import{txt=(get_string~locpayload);loc})::acc|Ifdef->letident=pexp_ident~loc(get_ident~locpayload)inletexpr=make_apply_fun~loc"defined"identinmake_if~locexpr::acc|Ifndef->letident=pexp_ident~loc(get_ident~locpayload)inletexpr=make_apply_fun~loc"not_defined"identinmake_if~locexpr::acc|Elifdef->deprecated_ifs~loc|Elifndef->deprecated_ifs~loc)inletextract_full=function|Fullx->x|Partial{loc;_}->Location.raise_errorf~loc"optcomp: unterminated if"inBlock(List.rev_mappre_parsed~f:extract_full)leteval~drop_item~eval_item~envast=letrecdropast=matchastwith|Leafl->List.iterl~f:drop_item|Block(ast::asts)->dropast;drop(Blockasts)|If(cond,ast1,ast2)->beginAttribute.explicitly_drop#expressioncond;dropast1;dropast2end|_->()inletrecaux_eval~env(ast:'at):(Env.t*'alistlist)=matchastwith|Leafl->letl'=List.mapl~f:(eval_itemenv)inenv,[l']|Block(ast::asts)->let(new_env,res)=aux_eval~envastinlet(newer_env,ress)=aux_eval~env:new_env(Blockasts)innewer_env,res@ress|Block[]->env,[]|Define(ident,Someexpr)->Env.addenv~var:ident~value:(Interpreter.evalenvexpr),[]|Define(ident,None)->Env.addenv~var:ident~value:(Value.Tuple[]),[]|Undefineident->Env.undefineenvident,[]|Import{loc;_}->Location.raise_errorf~loc"optcomp: import not supported in this context."|If(cond,ast1,ast2)->letcond=(* Explicitely allow the following pattern:
{[
[%%ifndef FOO]
[%%define FOO]
]}
*)matchcond.pexp_desc,ast1with|Pexp_apply({pexp_desc=Pexp_ident{txt=Lident"not_defined";_};_},[Nolabel,({pexp_desc=Pexp_ident{txt=Lidenti1;loc};_}asexpr)]),Block(Define({txt=i2;_},None)::_)whenString.(=)i1i2->make_apply_fun~loc"not_defined_permissive"expr|_->condinbeginmatch(Interpreter.evalenvcond)with|Boolb->drop(ifbthenast2elseast1);aux_eval~env(ifbthenast1elseast2)|v->Location.raise_errorf~loc:cond.pexp_loc"optcomp: if condition evaluated to non-bool: %s"(Value.to_stringv)end|Error{loc;txt}->Location.raise_errorf~loc"%s"txt|Warning{txt;loc}->letppf=Caml.Format.err_formatterinCaml.Format.fprintfppf"%a:@.Warning %s@."Location.printloctxt;env,[]inletnew_env,res=aux_eval~envastin(new_env,List.joinres)letattr_mapper~to_loc~to_attrs~replace_attrs~envitem=letloc=to_lociteminletis_our_attribute{attr_name={txt;_};_}=Token.Directive.matchestxt~expected:"if"inletour_as,other_as=List.partition_tf(to_attrsitem)~f:is_our_attributeinmatchour_aswith|[]->Someitem|[{attr_name={loc;_};attr_payload=payload;attr_loc=_;}asour_a]->Attribute.mark_as_handled_manuallyour_a;beginmatchInterpreter.evalenv(get_expr~locpayload)with|Boolb->ifbthenSome(replace_attrsitemother_as)elseNone|v->Location.raise_errorf~loc"optcomp: if condition evaulated to non-bool: %s"(Value.to_stringv)end|_->Location.raise_errorf~loc"optcomp: multiple [@if] attributes are not allowed"endletrewrite~drop_item~eval_item~of_item~env(x:'alist):Env.t*'alist=lettokens:('aToken.tlist)=Token_stream.of_itemsx~of_iteminletast=Meta_ast.of_tokenstokensinMeta_ast.eval~drop_item~eval_item~envast;;letmap=object(self)inherit[Env.t]Ast_traverse.map_with_contextassupermethodstructure_genenvx=rewritex~env~drop_item:Attribute.explicitly_drop#structure_item~eval_item:self#structure_item~of_item:Of_item.structuremethodsignature_genenvx=rewritex~env~drop_item:Attribute.explicitly_drop#signature_item~eval_item:self#signature_item~of_item:Of_item.signaturemethod!structureenvx=snd(self#structure_genenvx)method!signatureenvx=snd(self#signature_genenvx)method!class_structureenvx=let_,rewritten=rewritex.pcstr_fields~env~drop_item:Attribute.explicitly_drop#class_field~eval_item:self#class_field~of_item:Of_item.class_structurein{xwithpcstr_fields=rewritten}method!class_signatureenvx=let_,rewritten=rewritex.pcsig_fields~env~drop_item:Attribute.explicitly_drop#class_type_field~eval_item:self#class_type_field~of_item:Of_item.class_signaturein{xwithpcsig_fields=rewritten}method!type_kindenvx=letx=matchxwith|Ptype_variantcs->letf=Meta_ast.attr_mapper~env~to_loc:(func->c.pcd_loc)~to_attrs:(func->c.pcd_attributes)~replace_attrs:(funcattrs->{cwithpcd_attributes=attrs})inletfiltered_cs=List.filter_mapcs~finPtype_variantfiltered_cs|_->xinsuper#type_kindenvxmethod!expression_descenvx=letf=Meta_ast.attr_mapper~env~to_loc:(func->c.pc_lhs.ppat_loc)~to_attrs:(func->c.pc_lhs.ppat_attributes)~replace_attrs:(fun({pc_lhs;_}asc)attrs->{cwithpc_lhs={pc_lhswithppat_attributes=attrs}})inletx=matchxwith|Pexp_functioncs->Pexp_function(List.filter_mapcs~f)|Pexp_match(e,cs)->Pexp_match(super#expressionenve,List.filter_mapcs~f)|Pexp_try(e,cs)->Pexp_try(super#expressionenve,List.filter_mapcs~f)|_->xinsuper#expression_descenvxend;;(* Preserve the enrivonment between invocation using cookies *)letstate=refEnv.initlet()=Driver.Cookies.add_simple_handler"ppx_optcomp.env"Ast_pattern.__~f:(function|None->state:=Env.init|Somex->state:=Interpreter.EnvIO.of_expressionx);Driver.Cookies.add_post_handler(funcookies->Driver.Cookies.setcookies"ppx_optcomp.env"(Interpreter.EnvIO.to_expression!state));;letpreprocess~fx=letnew_env,x=f!statexinstate:=new_env;x;;let()=Driver.register_transformation"optcomp"~preprocess_impl:(preprocess~f:map#structure_gen)~preprocess_intf:(preprocess~f:map#signature_gen);;