123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)openPpopenCErrorsopenUtilopenNamesopenLibobjectopenGenargopenExtendopenPcoqopenEgrammlopenVernacexpropenLibnamesopenNameopstype'agrammar_tactic_prod_item_expr='aPptactic.grammar_tactic_prod_item_expr=|TacTermofstring|TacNonTermof('a*Names.Id.toption)Loc.locatedtyperaw_argument=string*stringoptiontypeargument=Genarg.ArgT.anyExtend.user_symbol(**********************************************************************)(* Interpret entry names of the form "ne_constr_list" as entry keys *)letatacticn=Pcoq.Symbol.ntermlPltac.ltac_expr(string_of_intn)typeentry_name=EntryName:'araw_abstract_argument_type*(Tacexpr.raw_tactic_expr,_,'a)Pcoq.Symbol.t->entry_name(** Quite ad-hoc *)letget_tacentrynm=letcheck_lvln=Int.equalmn&¬(Int.equalm0)(* Because tactic0 is at simple_tactic *)inifcheck_lvlnthenEntryName(rawwitTacarg.wit_tactic,Pcoq.Symbol.self)elseifcheck_lvl(n+1)thenEntryName(rawwitTacarg.wit_tactic,Pcoq.Symbol.next)elseEntryName(rawwitTacarg.wit_tactic,atacticn)letget_separator=function|None->user_errPp.(str"Missing separator.")|Somesep->sepletcheck_separator?loc=function|None->()|Some_->user_err?loc(str"Separator is only for arguments with suffix _list_sep.")letrecparse_user_entry?locssep=letopenCStringinletmatchespresufs=String.lengths>(String.lengthpre+String.lengthsuf)&&is_prefixpres&&is_suffixsufsinletbasenamepresufs=letplen=String.lengthpreinString.subsplen(String.lengths-(plen+String.lengthsuf))inlettactic_len=String.length"tactic"inifmatches"ne_""_list"sthenletentry=parse_user_entry?loc(basename"ne_""_list"s)Noneincheck_separator?locsep;Ulist1entryelseifmatches"ne_""_list_sep"sthenletentry=parse_user_entry?loc(basename"ne_""_list_sep"s)NoneinUlist1sep(entry,get_separatorsep)elseifmatches"""_list"sthenletentry=parse_user_entry?loc(basename"""_list"s)Noneincheck_separator?locsep;Ulist0entryelseifmatches"""_list_sep"sthenletentry=parse_user_entry?loc(basename"""_list_sep"s)NoneinUlist0sep(entry,get_separatorsep)elseifmatches"""_opt"sthenletentry=parse_user_entry?loc(basename"""_opt"s)Noneincheck_separator?locsep;UoptentryelseifString.lengths=tactic_len+1&&is_prefix"tactic"s&&'5'>=s.[tactic_len]&&s.[tactic_len]>='0'thenletn=Char.codes.[tactic_len]-Char.code'0'incheck_separator?locsep;Uentryl("tactic",n)elselet_=check_separator?locsepinUentrysletinterp_entry_nameinterpsymb=letreceval=function|Ulist1e->Ulist1(evale)|Ulist1sep(e,sep)->Ulist1sep(evale,sep)|Ulist0e->Ulist0(evale)|Ulist0sep(e,sep)->Ulist0sep(evale,sep)|Uopte->Uopt(evale)|Uentrys->Uentry(interpsNone)|Uentryl(s,n)->Uentryl(interps(Somen),n)inevalsymb(**********************************************************************)(** Grammar declaration for Tactic Notation (Coq level) *)letget_tactic_entryn=ifInt.equaln0thenPltac.simple_tactic,Noneelseif1<=n&&n<=5thenPltac.ltac_expr,Some(string_of_intn)elseuser_errPp.(str("Invalid Tactic Notation level: "^(string_of_intn)^"."))(**********************************************************************)(** State of the grammar extensions *)typetactic_grammar={tacgram_level:int;tacgram_prods:Pptactic.grammar_terminals;}(* Declaration of the tactic grammar rule *)lethead_is_identtg=matchtg.tacgram_prodswith|TacTerm_::_->true|_->falseletrecprod_item_of_symbollev=function|Extend.Ulist1s->letEntryName(Rawwittyp,e)=prod_item_of_symbollevsinEntryName(Rawwit(ListArgtyp),Pcoq.Symbol.list1e)|Extend.Ulist0s->letEntryName(Rawwittyp,e)=prod_item_of_symbollevsinEntryName(Rawwit(ListArgtyp),Pcoq.Symbol.list0e)|Extend.Ulist1sep(s,sep)->letEntryName(Rawwittyp,e)=prod_item_of_symbollevsinEntryName(Rawwit(ListArgtyp),Pcoq.Symbol.list1sepe(Pcoq.Symbol.tokens[Pcoq.TPattern(Pcoq.terminalsep)])false)|Extend.Ulist0sep(s,sep)->letEntryName(Rawwittyp,e)=prod_item_of_symbollevsinEntryName(Rawwit(ListArgtyp),Pcoq.Symbol.list0sepe(Pcoq.Symbol.tokens[Pcoq.TPattern(Pcoq.terminalsep)])false)|Extend.Uopts->letEntryName(Rawwittyp,e)=prod_item_of_symbollevsinEntryName(Rawwit(OptArgtyp),Pcoq.Symbol.opte)|Extend.Uentryarg->letArgT.Anytag=arginletwit=ExtraArgtaginEntryName(Rawwitwit,Pcoq.Symbol.nterm(genarg_grammarwit))|Extend.Uentryl(s,n)->letArgT.Anytag=sinassert(CString.is_suffix"tactic"(ArgT.reprtag));get_tacentrynlev(** Tactic grammar extensions *)letadd_tactic_entry(kn,ml,tg)state=letopenTacexprinletentry,pos=get_tactic_entrytg.tacgram_levelinletmkactlocl=letmaparg=(* HACK to handle especially the tactic(...) entry *)letwit=Genarg.rawwitTacarg.wit_tacticinifGenarg.has_typeargwit&¬mlthenTacexp(Genarg.out_genwitarg)elseTacGeneric(None,arg)inletl=List.mapmaplin(CAst.make~loc(TacAlias(kn,l)):raw_tactic_expr)inlet()=ifInt.equaltg.tacgram_level0&¬(head_is_identtg)thenuser_errPp.(str"Notation for simple tactic must start with an identifier.")inletmap=function|TacTerms->GramTerminals|TacNonTerm(loc,(s,ido))->letEntryName(typ,e)=prod_item_of_symboltg.tacgram_levelsinGramNonTerminal(Loc.tag?loc@@(typ,e))inletprods=List.mapmaptg.tacgram_prodsinletrules=make_rulemkactprodsinletr=ExtendRule(entry,Pcoq.Reuse(pos,[rules]))in([r],state)lettactic_grammar=create_grammar_command"TacticGrammar"{gext_fun=add_tactic_entry;gext_eq=(==)(* FIXME *)}letextend_tactic_grammarknmlntn=extend_grammar_commandtactic_grammar(kn,ml,ntn)(**********************************************************************)(* Tactic Notation *)letentry_names=refString.Map.emptyletregister_tactic_notation_entrynameentry=letentry=matchentrywith|ExtraArgarg->ArgT.Anyarg|_->assertfalseinentry_names:=String.Map.addnameentry!entry_namesletinterp_prod_item=function|TacTerms->TacTerms|TacNonTerm(loc,((nt,sep),ido))->letsymbol=parse_user_entry?locntsepinletinterps=function|None->ifString.Map.mems!entry_namesthenString.Map.finds!entry_nameselsebeginmatchArgT.nameswith|None->ifs="var"thenuser_errPp.(str("var is deprecated, use hyp."))(* to remove in 8.14 *)elseuser_errPp.(str("Unknown entry "^s^"."))|Somearg->argend|Somen->(* FIXME: do better someday *)assert(String.equals"tactic");beginmatchTacarg.wit_tacticwith|ExtraArgtag->ArgT.Anytagendinletsymbol=interp_entry_nameinterpsymbolinTacNonTerm(loc,(symbol,ido))letmake_fresh_key=letid=Summary.ref~stage:Summary.Stage.Synterp~name:"TACTIC-NOTATION-COUNTER"0infunprods->letcur=incrid;!idinletmap=function|TacTerms->s|TacNonTerm_->"#"inletprods=String.concat"_"(List.mapmapprods)in(* We embed the hash of the kernel name in the label so that the identifier
should be mostly unique. This ensures that including two modules
together won't confuse the corresponding labels. *)lethash=(curlxor(ModPath.hash(Lib.current_mp())))land0x7FFFFFFFinletlbl=Id.of_string_soft(Printf.sprintf"%s_%08X"prodshash)inLib.make_knlbltypetactic_grammar_obj={tacobj_key:KerName.t;tacobj_local:locality_flag;tacobj_tacgram:tactic_grammar;tacobj_forml:bool;}letpprulepa={Pptactic.pptac_level=pa.tacgram_level;pptac_prods=pa.tacgram_prods;}letcheck_keykey=ifTacenv.check_aliaskeythenuser_errPp.(str"Conflicting tactic notations keys. This can happen when including \
twice the same module.")letcache_tactic_notation(tobj,body)=letkey=tobj.tacobj_keyinlet()=check_keykeyinTacenv.register_aliaskeybodyletopen_tactic_notationi_=()letload_tactic_notationi(tobj,body)=letkey=tobj.tacobj_keyinlet()=check_keykeyin(* Only add the printing and interpretation rules. *)Tacenv.register_aliaskeybodyletsubst_tactic_notation(subst,(tobj,body))=letopenTacenvin{tobjwithtacobj_key=Mod_subst.subst_knsubsttobj.tacobj_key},{bodywithalias_body=Tacsubst.subst_tacticsubstbody.alias_body}letclassify_tactic_notationtacobj=Substituteletltac_notation_cat=Libobject.create_category"ltac.notations"letinTacticGrammar:tactic_grammar_obj*Tacenv.alias_tactic->obj=declare_object{(default_object"TacticGrammar")withopen_function=simple_open~cat:ltac_notation_catopen_tactic_notation;load_function=load_tactic_notation;cache_function=cache_tactic_notation;subst_function=subst_tactic_notation;classify_function=classify_tactic_notation}letcache_tactic_syntaxtobj=letkey=tobj.tacobj_keyinextend_tactic_grammarkeytobj.tacobj_formltobj.tacobj_tacgram;Pptactic.declare_notation_tactic_pprulekey(ppruletobj.tacobj_tacgram)letopen_tactic_syntaxitobj=letkey=tobj.tacobj_keyinifInt.equali1&¬tobj.tacobj_localthenextend_tactic_grammarkeytobj.tacobj_formltobj.tacobj_tacgramletload_tactic_syntaxitobj=letkey=tobj.tacobj_keyin(* Only add the printing and interpretation rules. *)Pptactic.declare_notation_tactic_pprulekey(ppruletobj.tacobj_tacgram);ifInt.equali1&¬tobj.tacobj_localthenextend_tactic_grammarkeytobj.tacobj_formltobj.tacobj_tacgramletsubst_tactic_syntax(subst,tobj)={tobjwithtacobj_key=Mod_subst.subst_knsubsttobj.tacobj_key}letclassify_tactic_syntaxtacobj=SubstituteletinTacticSyntax:tactic_grammar_obj->obj=declare_object{(default_object"TacticSyntax")withobject_stage=Summary.Stage.Synterp;open_function=simple_open~cat:ltac_notation_catopen_tactic_syntax;load_function=load_tactic_syntax;cache_function=cache_tactic_syntax;subst_function=subst_tactic_syntax;classify_function=classify_tactic_syntax}letcons_production_parameter=function|TacTerm_->None|TacNonTerm(_,(_,ido))->idoletadd_glob_tactic_notation?deprecationtacobjidstac=letopenTacenvinletbody={alias_args=ids;alias_body=tac;alias_deprecation=deprecation}inLib.add_leaf(inTacticGrammar(tacobj,body))letadd_glob_tactic_notation_syntaxlocal~level?deprecationprodsforml=letparule={tacgram_level=level;tacgram_prods=prods;}inlettacobj={tacobj_key=make_fresh_keyprods;tacobj_local=local;tacobj_tacgram=parule;tacobj_forml=forml;}inLib.add_leaf(inTacticSyntaxtacobj);tacobjletadd_tactic_notation?deprecationtacobje=letids=List.map_filtercons_production_parametertacobj.tacobj_tacgram.tacgram_prodsinlettac=Tacintern.glob_tactic_envids(Global.env())einadd_glob_tactic_notation?deprecationtacobjidstacletadd_tactic_notation_syntaxlocaln?deprecationprods=letprods=List.mapinterp_prod_itemprodsinadd_glob_tactic_notation_syntaxlocal~level:n?deprecationprodsfalse(**********************************************************************)(* ML Tactic entries *)exceptionNonEmptyArgument(** ML tactic notations whose use can be restricted to an identifier are added
as true Ltac entries. *)letextend_atomic_tacticnameentries=letopenTacexprinletmap_prodprods=let(hd,rem)=matchprodswith|TacTerms::rem->(s,rem)|_->assertfalse(* Not handled by the ML extension syntax *)inletempty_value=function|TacTerms->raiseNonEmptyArgument|TacNonTerm(_,(symb,_))->letEntryName(typ,e)=prod_item_of_symbol0symbinletGenarg.Rawwitwit=typinletinjx=CAst.make@@TacArg(TacGeneric(None,Genarg.in_gentypx))inletdefault=epsilon_valueinjeinmatchdefaultwith|None->raiseNonEmptyArgument|Somedef->Tacintern.intern_tactic_or_tacarg(Genintern.empty_glob_sign~strict:trueEnviron.empty_env)defintrySome(hd,List.mapempty_valuerem)withNonEmptyArgument->Noneinletentries=List.mapmap_prodentriesinletadd_atomiciargs=matchargswith|None->()|Some(id,args)->letargs=List.map(funa->Tacexpa)argsinletentry={mltac_name=name;mltac_index=i}inletbody=CAst.make(TacML(entry,args))inTacenv.register_ltacfalsefalse(Names.Id.of_stringid)bodyinList.iteriadd_atomicentriesletadd_ml_tactic_notationname~level?deprecationprods=letlen=List.lengthprodsinletiteriprods=letopenTacexprinletget_id=function|TacTerms->None|TacNonTerm(_,(_,ido))->idoinletids=List.map_filterget_idprodsinletentry={mltac_name=name;mltac_index=len-i-1}inletmapid=Reference(Locus.ArgVar(CAst.makeid))inlettac=CAst.make(TacML(entry,List.mapmapids))inlettacobj=add_glob_tactic_notation_syntaxfalse~level?deprecationprodstrueinadd_glob_tactic_notation?deprecationtacobjidstacinList.iteriiter(List.revprods);(* We call [extend_atomic_tactic] only for "basic tactics" (the ones
at ltac_expr level 0) *)ifInt.equallevel0thenextend_atomic_tacticnameprods(**********************************************************************)(** Ltac quotations *)letltac_quotations=refString.Set.emptyletcreate_ltac_quotation~pluginnamecast(e,l)=let()=ifString.Set.memname!ltac_quotationsthenfailwith("Ltac quotation "^name^" already registered")inlet()=ltac_quotations:=String.Set.addname!ltac_quotationsinletentry=matchlwith|None->Pcoq.Symbol.nterme|Somel->Pcoq.Symbol.ntermle(string_of_intl)inletrule=Pcoq.(Rule.next(Rule.next(Rule.next(Rule.next(Rule.nextRule.stop(Symbol.token(Pcoq.terminalname)))(Symbol.token(Pcoq.terminal":")))(Symbol.token(Pcoq.terminal"(")))entry)(Symbol.token(Pcoq.terminal")")))inletaction_v___loc=cast(Someloc,v)inletgram=[Pcoq.Production.makeruleaction]inletplugin_uid=(plugin,"tacquot:"^name)inEgramml.grammar_extend~plugin_uidPltac.tactic_value(Pcoq.Reuse(None,gram))(** Command *)typetacdef_kind=|NewTacofId.t|UpdateTacofTacexpr.ltac_constantletis_defined_tackn=tryignore(Tacenv.interp_ltackn);truewithNot_found->falseletwarn_unusable_identifier=CWarnings.create~name:"unusable-identifier"~category:CWarnings.CoreCategories.parsing(funid->strbrk"The Ltac name"++spc()++Id.printid++spc()++strbrk"may be unusable because of a conflict with a notation.")letregister_ltaclocal?deprecationtacl=letmaptactic_body=matchtactic_bodywith|Tacexpr.TacticDefinition({CAst.loc;v=id},body)->letkn=Lib.make_knidinletid_pp=Id.printidinlet()=ifis_defined_tacknthenCErrors.user_err?loc(str"There is already an Ltac named "++id_pp++str".")inletis_shadowed=trymatchPcoq.parse_stringPltac.tactic(Id.to_stringid)with|{CAst.v=(Tacexpr.TacArg_)}->false|_->true(* most probably TacAtom, i.e. a primitive tactic ident *)withewhenCErrors.noncriticale->true(* prim tactics with args, e.g. "apply" *)inlet()=ifis_shadowedthenwarn_unusable_identifieridinNewTacid,body|Tacexpr.TacticRedefinition(qid,body)->letkn=tryTacenv.locate_tacticqidwithNot_found->CErrors.user_err?loc:qid.CAst.loc(str"There is no Ltac named "++pr_qualidqid++str".")inUpdateTackn,bodyinletrfun=List.mapmaptaclinletrecvars=letfoldaccu(op,_)=matchopwith|UpdateTac_->accu|NewTacid->(Lib.make_pathid,Lib.make_knid)::accuinList.fold_leftfold[]rfuninletist=Tacintern.make_empty_glob_sign~strict:trueinletmap(name,body)=letbody=Tacintern.intern_tactic_or_tacargistbodyin(name,body)inletdefs()=(* Register locally the tactic to handle recursivity. This
function affects the whole environment, so that we transactify
it afterwards. *)letiter_rec(sp,kn)=Tacenv.push_tactic(Nametab.Until1)spkninlet()=List.iteriter_recrecvarsinList.mapmaprfunin(* STATE XXX: Review what is going on here. Why does this needs
protection? Why is not the STM level protection enough? Fishy *)letdefs=Vernacstate.System.protectdefs()inletiter(def,tac)=matchdefwith|NewTacid->Tacenv.register_ltacfalselocalidtac?deprecation;Flags.if_verboseFeedback.msg_info(Id.printid++str" is defined")|UpdateTackn->Tacenv.redefine_ltaclocalkntac?deprecation;letname=Tacenv.shortest_qualid_of_tactickninFlags.if_verboseFeedback.msg_info(Libnames.pr_qualidname++str" is redefined")inList.iteriterdefs(** Queries *)letprint_ltacs()=letentries=KNmap.bindings(Tacenv.ltac_entries())inletsort(kn1,_)(kn2,_)=KerName.comparekn1kn2inletentries=List.sortsortentriesinletmap(kn,entry)=letqid=trySome(Tacenv.shortest_qualid_of_tactickn)withNot_found->Noneinmatchqidwith|None->None|Someqid->Some(qid,entry.Tacenv.tac_body)inletentries=List.map_filtermapentriesinletpr_entry(qid,body)=let(l,t)=matchbodywith|{CAst.v=(Tacexpr.TacFun(l,t))}->(l,t)|_->([],body)inletpr_ltac_fun_argn=spc()++Name.printninhov2(pr_qualidqid++prlistpr_ltac_fun_argl)inFeedback.msg_notice(prlist_with_sepfnlpr_entryentries)letlocatable_ltac="Ltac"letsplit_ltac_fun=function|{CAst.v=(Tacexpr.TacFun(l,t))}->(l,t)|t->([],t)letpr_ltac_fun_argn=spc()++Name.printnletprint_ltac_bodyqidtac=letfiltermp=trySome(Nametab.shortest_qualid_of_modulemp)withNot_found->Noneinletmods=List.map_filterfiltertac.Tacenv.tac_redefinletredefined=matchmodswith|[]->mt()|mods->letredef=prlist_with_sepfnlpr_qualidmodsinfnl()++str"Redefined by:"++fnl()++redefinletl,t=split_ltac_funtac.Tacenv.tac_bodyinhv2(hov2(str"Ltac"++spc()++pr_qualidqid++prlistpr_ltac_fun_argl++spc()++str":=")++spc()++Pptactic.pr_glob_tactic(Global.env())t)++redefinedlet()=letopenPrettypinletlocateqid=trySome(qid,Tacenv.locate_tacticqid)withNot_found->Noneinletlocate_allqid=List.map(funkn->(qid,kn))(Tacenv.locate_extended_all_tacticqid)inletshortest_qualid(qid,kn)=Tacenv.shortest_qualid_of_tactickninletname(qid,kn)=str"Ltac"++spc()++pr_path(Tacenv.path_of_tactickn)inletprint(qid,kn)=letentries=Tacenv.ltac_entries()inlettac=KNmap.findknentriesinprint_ltac_bodyqidtacinletabout=nameinregister_locatablelocatable_ltac{locate;locate_all;shortest_qualid;name;print;about;}letprint_located_tacticqid=Feedback.msg_notice(Prettyp.print_located_other(Global.env())locatable_ltacqid)letprint_ltacid=tryletkn=Tacenv.locate_tacticidinletentries=Tacenv.ltac_entries()inlettac=KNmap.findknentriesinprint_ltac_bodyidtacwithNot_found->user_err(pr_qualidid++spc()++str"is not a user defined tactic.")(** Grammar *)let()=letopenPcoq.Entryinletentries=[AnyPltac.ltac_expr;AnyPltac.simple_tactic;AnyPltac.tactic_value;]inregister_grammars_by_name"tactic"entriesletget_identifieri=(* Workaround for badly-designed generic arguments lacking a closure *)Names.Id.of_string_soft(Printf.sprintf"$%i"i)type_ty_sig=|TyNil:(Geninterp.interp_sign->unitProofview.tactic)ty_sig|TyIdent:string*'rty_sig->'rty_sig|TyArg:('a,'b,'c)Extend.ty_user_symbol*'rty_sig->('c->'r)ty_sigtypety_ml=TyML:'rty_sig*'r->ty_mlletrecuntype_user_symbol:typeabc.(a,b,c)ty_user_symbol->Genarg.ArgT.anyuser_symbol=funtu->matchtuwith|TUlist1l->Ulist1(untype_user_symboll)|TUlist1sep(l,s)->Ulist1sep(untype_user_symboll,s)|TUlist0l->Ulist0(untype_user_symboll)|TUlist0sep(l,s)->Ulist0sep(untype_user_symboll,s)|TUopt(o)->Uopt(untype_user_symbolo)|TUentrya->Uentry(Genarg.ArgT.Anya)|TUentryl(a,i)->Uentryl(Genarg.ArgT.Anya,i)letrecclause_of_sign:typea.int->aty_sig->Genarg.ArgT.anyExtend.user_symbolgrammar_tactic_prod_item_exprlist=funisign->matchsignwith|TyNil->[]|TyIdent(s,sig')->TacTerms::clause_of_signisig'|TyArg(a,sig')->letid=Some(get_identifieri)inTacNonTerm(None,(untype_user_symbola,id))::clause_of_sign(i+1)sig'letclause_of_ty_ml=function|TyML(t,_)->clause_of_sign1tletreceval_sign:typea.aty_sig->a->Geninterp.Val.tlist->Geninterp.interp_sign->unitProofview.tactic=funsigntac->matchsignwith|TyNil->beginfunvalsist->matchvalswith|[]->tacist|_::_->assertfalseend|TyIdent(s,sig')->eval_signsig'tac|TyArg(a,sig')->letf=eval_signsig'inbeginfuntacvalsist->matchvalswith|[]->assertfalse|v::vals->letv'=Taccoerce.Value.cast(topwit(Egramml.proj_symbola))vinf(tacv')valsistendtacleteval:ty_ml->Geninterp.Val.tlist->Geninterp.interp_sign->unitProofview.tactic=function|TyML(t,tac)->eval_signttacleteval_of_ty_ml=evalletis_constr_entry=function|TUentrya->Option.has_some@@genarg_type_eq(ExtraArga)Stdarg.wit_constr|_->falseletreconly_constr:typea.aty_sig->bool=function|TyNil->true|TyIdent(_,_)->false|TyArg(u,s)->ifis_constr_entryuthenonly_constrselsefalseletrecmk_sign_vars:typea.int->aty_sig->Name.tlist=funitu->matchtuwith|TyNil->[]|TyIdent(_,s)->mk_sign_varsis|TyArg(_,s)->Name(get_identifieri)::mk_sign_vars(i+1)sletdummy_id=Id.of_string"_"letlift_constr_tac_to_ml_tacvarstac=lettac_ist=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletmap=function|Anonymous->None|Nameid->letc=Id.Map.findidist.Geninterp.lfunintrySome(Taccoerce.Value.of_constr@@Taccoerce.coerce_to_closed_constrenvc)withTaccoerce.CannotCoerceToty->Taccoerce.error_ltac_variabledummy_id(Some(env,sigma))ctyinletargs=List.map_filtermapvarsintacargsistendintaclettactic_extendplugin_nametacname~level?deprecationsign=letopenTacexprinletml_tactic_name={mltac_tactic=tacname;mltac_plugin=plugin_name}inmatchsignwith|[TyML(TyIdent(name,s),tac)asml_tac]whenonly_constrs->(* The extension is only made of a name followed by constr
entries: we do not add any grammar nor printing rule and add it
as a true Ltac definition. *)letvars=mk_sign_vars1sinletml={Tacexpr.mltac_name=ml_tactic_name;Tacexpr.mltac_index=0}inlettac=matchswith|TyNil->evalml_tac(* Special handling of tactics without arguments: such tactics do
not do a Proofview.Goal.nf_enter to compute their arguments. It
matters for some whole-prof tactics like [shelve_unifiable]. *)|_->lift_constr_tac_to_ml_tacvars(evalml_tac)in(* Arguments are not passed directly to the ML tactic in the TacML
node, the ML tactic retrieves its arguments in the [ist]
environment instead. This is the rôle of the
[lift_constr_tac_to_ml_tac] function. *)letbody=CAst.make(Tacexpr.TacFun(vars,CAst.make(Tacexpr.TacML(ml,[]))))inletid=Names.Id.of_stringnameinletobj()=Tacenv.register_ltactruefalseidbody?deprecationinlet()=Tacenv.register_ml_tacticml_tactic_name[|tac|]inMltop.declare_cache_objobjplugin_name|_->letobj()=add_ml_tactic_notationml_tactic_name~level?deprecation(List.mapclause_of_ty_mlsign)inTacenv.register_ml_tacticml_tactic_name@@Array.of_list(List.mapevalsign);Mltop.declare_cache_objobjplugin_nametype(_,'a)ml_ty_sig=|MLTyNil:('a,'a)ml_ty_sig|MLTyArg:('r,'a)ml_ty_sig->(Geninterp.Val.t->'r,'a)ml_ty_sigletrecml_sig_len:typera.(r,a)ml_ty_sig->int=function|MLTyNil->0|MLTyArgsign->1+ml_sig_lensignletreccast_ml:typera.(r,a)ml_ty_sig->r->Geninterp.Val.tlist->a=funsignf->matchsignwith|MLTyNil->beginfunction|[]->f|_::_->CErrors.anomaly(str"Arity mismatch")end|MLTyArgsign->function|[]->CErrors.anomaly(str"Arity mismatch")|arg::args->cast_mlsign(farg)argsletml_tactic_extend~plugin~name~local?deprecationsigntac=letopenTacexprinlettacargs_=cast_mlsigntacargsinletml_tactic_name={mltac_tactic=name;mltac_plugin=plugin}inletml={mltac_name=ml_tactic_name;mltac_index=0}inletlen=ml_sig_lensigninletargs=List.initlen(funi->Id.of_string(Printf.sprintf"arg%i"i))inletvars=List.map(funid->Nameid)argsinletargs=List.map(funid->Reference(Locus.ArgVar(CAst.makeid)))argsinletbody=CAst.make(Tacexpr.TacFun(vars,CAst.make(Tacexpr.TacML(ml,args))))inletid=Names.Id.of_stringnameinletobj()=Tacenv.register_ltactruelocalidbody?deprecationinlet()=Tacenv.register_ml_tacticml_tactic_name[|tac|]inMltop.declare_cache_objobjpluginmoduleMLName=structopenTacexprtypet=ml_tactic_nameletcomparetac1tac2=letc=String.comparetac1.mltac_tactictac2.mltac_tacticinifc=0thenString.comparetac1.mltac_plugintac2.mltac_pluginelsecendmoduleMLTacMap=Map.Make(MLName)letml_table:(Geninterp.Val.tlist->Geninterp.Val.tFtactic.t)MLTacMap.tref=refMLTacMap.emptytypeml_ltac_val={tacval_tac:Tacexpr.ml_tactic_name;tacval_var:Id.tlist;}letin_tacval=(* This is a hack to emulate value-returning ML-implemented tactics in Ltac.
We use a dummy generic argument to work around the limitations of the Ltac
runtime. Indeed, the TacML node needs to return unit values, since it is
considered a "tactic" in the runtime. Changing it to allow arbitrary values
would require to toggle this status, and thus to make it a "value" node.
This would in turn create too much backwards incompatibility. Instead, we
piggy back on the TacGeneric node, which by construction is used to return
values.
The trick is to represent a n-ary application of a ML function as a generic
argument. We store in the node the name of the tactic and its arity, while
giving canonical names to the bound variables of the closure. This trick is
already performed in several external developments for specific calls, we
make it here generic. The argument should not be used for other purposes, so
we only export the registering functions.
*)letwit:(Empty.t,ml_ltac_val,Geninterp.Val.t)Genarg.genarg_type=Genarg.create_arg"ltac:val"in(* No need to internalize this ever *)letintern_fun_e=Empty.aborteinletsubst_funsv=vinlet()=Genintern.register_intern0witintern_funinlet()=Gensubst.register_subst0witsubst_funin(* No need to register a value tag for it via register_val0 since we will
never access this genarg directly. *)letinterp_funisttac=letargs=List.map(funid->Id.Map.getidist.Geninterp.lfun)tac.tacval_varinlettac=MLTacMap.gettac.tacval_tac!ml_tableintacargsinlet()=Geninterp.register_interp0witinterp_funin(funv->Genarg.in_gen(Genarg.Glbwitwit)v)letml_val_tactic_extend~plugin~name~local?deprecationsigntac=letopenTacexprinlettacargs=cast_mlsigntacargsinletml_tactic_name={mltac_tactic=name;mltac_plugin=plugin}inletlen=ml_sig_lensigninletvars=List.initlen(funi->Id.of_string(Printf.sprintf"arg%i"i))inletbody=TacGeneric(None,in_tacval{tacval_tac=ml_tactic_name;tacval_var=vars})inletvars=List.map(funid->Nameid)varsinletbody=CAst.make(Tacexpr.TacFun(vars,CAst.make(Tacexpr.TacArgbody)))inletid=Names.Id.of_stringnameinletobj()=Tacenv.register_ltactruelocalidbody?deprecationinlet()=assert(not@@MLTacMap.memml_tactic_name!ml_table)inlet()=ml_table:=MLTacMap.addml_tactic_nametac!ml_tableinMltop.declare_cache_objobjplugin(** ARGUMENT EXTEND *)openGeninterptype('a,'b,'c)argument_printer='aPptactic.raw_extra_genarg_printer*'bPptactic.glob_extra_genarg_printer*'cPptactic.extra_genarg_printertype('a,'b)argument_intern=|ArgInternFun:('a,'b)Genintern.intern_fun->('a,'b)argument_intern|ArgInternWit:('a,'b,'c)Genarg.genarg_type->('a,'b)argument_interntype'bargument_subst=|ArgSubstFun:'bGensubst.subst_fun->'bargument_subst|ArgSubstWit:('a,'b,'c)Genarg.genarg_type->'bargument_substtype('b,'c)argument_interp=|ArgInterpRet:('c,'c)argument_interp|ArgInterpFun:('b,Val.t)interp_fun->('b,'c)argument_interp|ArgInterpWit:('a,'b,'r)Genarg.genarg_type->('b,'c)argument_interp|ArgInterpSimple:(Geninterp.interp_sign->Environ.env->Evd.evar_map->'b->'c)->('b,'c)argument_interptype('a,'b,'c)tactic_argument={arg_parsing:'aVernacextend.argument_rule;arg_tag:'cVal.tagoption;arg_intern:('a,'b)argument_intern;arg_subst:'bargument_subst;arg_interp:('b,'c)argument_interp;arg_printer:('a,'b,'c)argument_printer;}letintern_fun(typeabc)name(arg:(a,b,c)tactic_argument):(a,b)Genintern.intern_fun=matcharg.arg_internwith|ArgInternFunf->f|ArgInternWitwit->funistv->letans=Genarg.out_gen(glbwitwit)(Tacintern.intern_genargist(Genarg.in_gen(rawwitwit)v))in(ist,ans)letsubst_fun(typeabc)(arg:(a,b,c)tactic_argument):bGensubst.subst_fun=matcharg.arg_substwith|ArgSubstFunf->f|ArgSubstWitwit->funsv->letans=Genarg.out_gen(glbwitwit)(Tacsubst.subst_genargs(Genarg.in_gen(glbwitwit)v))inansletinterp_fun(typeabc)name(arg:(a,b,c)tactic_argument)(tag:cVal.tag):(b,Val.t)interp_fun=matcharg.arg_interpwith|ArgInterpRet->(funistv->Ftactic.return(Geninterp.Val.injecttagv))|ArgInterpFunf->f|ArgInterpWitwit->(funistx->Tacinterp.interp_genargist(Genarg.in_gen(glbwitwit)x))|ArgInterpSimplef->(funistv->Ftactic.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletv=fistenvsigmavinFtactic.return(Geninterp.Val.injecttagv)end)letargument_extend(typeabc)~plugin~name(arg:(a,b,c)tactic_argument)=letwit=Genarg.create_argnameinlet()=Genintern.register_intern0wit(intern_funnamearg)inlet()=Gensubst.register_subst0wit(subst_funarg)inlettag=matcharg.arg_tagwith|None->let()=register_val0witNoneinval_tag(topwitwit)|Sometag->let()=register_val0wit(Sometag)intaginlet()=register_interp0wit(interp_funnameargtag)inletentry=matcharg.arg_parsingwith|Vernacextend.Arg_aliase->let()=Pcoq.register_grammarwiteine|Vernacextend.Arg_rulesrules->lete=Pcoq.create_generic_entry2name(Genarg.rawwitwit)inletplugin_uid=(plugin,"argextend:"^name)inlet()=Egramml.grammar_extend~plugin_uide(Pcoq.Fresh(Gramlib.Gramext.First,[None,None,rules]))ineinlet(rpr,gpr,tpr)=arg.arg_printerinlet()=Pptactic.declare_extra_genarg_pprulewitrprgprtprinlet()=create_ltac_quotation~pluginname(fun(loc,v)->Tacexpr.TacGeneric(Somename,Genarg.in_gen(Genarg.rawwitwit)v))(entry,None)in(wit,entry)