123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512(************************************************************************)(* * The Rocq Prover / The Rocq 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) *)(************************************************************************)openUtilopenGenargopenGramlib(** The parser of Rocq *)includeGrammar.GMake(CLexer.Lexer)(** Marshallable representation of grammar extensions *)moduleEntryCommand=Dyn.Make()moduleGrammarCommand=Dyn.Make()moduleGramState=Store.Make()typegrammar_entry=|GramExtofGrammarCommand.t|EntryExt:'aEntryCommand.tag*string->grammar_entry(** State handling (non marshallable!) *)moduleEntryData=structtype_t=Ex:'aEntry.tString.Map.t->'atendmoduleEntryDataMap=EntryCommand.Map(EntryData)typefull_state={(* the state used for parsing *)current_state:GState.t;(* grammar state containing only non-marshallable extensions
(NB: this includes entries from Entry.make) *)base_state:GState.t;(* current_state = List.fold_right add_entry current_sync_extensions base_state
this means the list is in reverse order of addition *)current_sync_extensions:grammar_entrylist;(* some user data tied to the grammar state, typically contains info on declared levels *)user_state:GramState.t;(* map to find custom entries *)custom_entries:EntryDataMap.t;}letempty_full_state=letempty_gstate={GState.estate=EState.empty;kwstate=CLexer.empty_keyword_state}in{current_state=empty_gstate;base_state=empty_gstate;current_sync_extensions=[];user_state=GramState.empty;custom_entries=EntryDataMap.empty;}(** Not marshallable! *)letstate=refempty_full_stateletgstate()=(!state).current_stateletget_keyword_state()=(gstate()).kwstateletterminals=CLexer.terminal(get_keyword_state())sletreset_to_basestate={base_state=state.base_state;current_state=state.base_state;current_sync_extensions=[];user_state=GramState.empty;custom_entries=EntryDataMap.empty;}letmodify_state_unsyncfstate=letis_base=state.base_state==state.current_stateinletbase_state=fstate.base_stateinletcurrent_state=ifis_basethenbase_stateelsefstate.current_statein{statewithbase_state;current_state}letmodify_state_unsyncf()=state:=modify_state_unsyncf!stateletmodify_keyword_statef=modify_state_unsync(fun{estate;kwstate}->{estate;kwstate=fkwstate})()letmake_entry_unsyncmakeremakestate=letis_base=state.base_state==state.current_stateinletbase_estate,e=makestate.base_state.estateinletbase_state={state.base_statewithestate=base_estate}inletcurrent_state=ifis_basethenbase_stateelseletcurrent_estate=remakestate.current_state.estateein{state.current_statewithestate=current_estate}in{statewithbase_state;current_state},eletmake_entry_unsyncmakeremake()=letstatev,e=make_entry_unsyncmakeremake!stateinstate:=statev;eletadd_kw={add_kw=CLexer.add_keyword_tok}letepsilon_value(typestra)f(e:(s,tr,a)Symbol.t)=letr=Production.make(Rule.nextRule.stope)(funx_->fx)inlet{GState.estate;kwstate}=gstate()inletestate,entry=Entry.make"epsilon"estateinletext=Fresh(Gramlib.Gramext.First,[None,None,[r]])inletestate,kwstate=safe_extendadd_kwestatekwstateentryextinletstrm=Stream.empty()inletstrm=Parsable.makestrmintrySome(Entry.parseentrystrm{estate;kwstate})withewhenCErrors.noncriticale->Noneletextend_gstate{GState.kwstate;estate}eext=letestate,kwstate=safe_extendadd_kwestatekwstateeextin{GState.kwstate;estate}(* XXX rename to grammar_extend_unsync? *)letgrammar_extendeext=letextend_oneg=extend_gstategeextinmodify_state_unsyncextend_one()typeextend_rule=|ExtendRule:'aEntry.t*'aextend_statement->extend_ruleletgrammar_extend_syncuser_stateentryrulesstate=letextend_one_syncstate=function|ExtendRule(e,ext)->extend_gstatestateeextinletcurrent_state=List.fold_leftextend_one_syncstate.current_staterulesin{statewithcurrent_state;user_state;current_sync_extensions=GramExtentry::state.current_sync_extensions;}letgrammar_extend_syncster()=state:=grammar_extend_syncster!stateletextend_entry_sync(typea)(tag:aEntryCommand.tag)(name:string)state:_*aEntry.t=letcurrent_estate,e=Entry.makenamestate.current_state.estateinletcurrent_state={state.current_statewithestate=current_estate}inletcustom_entries=letEntryData.Exold=tryEntryDataMap.findtagstate.custom_entrieswithNot_found->EntryData.ExString.Map.emptyinlet()=assert(not@@String.Map.memnameold)inletentries=String.Map.addnameeoldinEntryDataMap.addtag(EntryData.Exentries)state.custom_entriesinletstate={statewithcurrent_state;current_sync_extensions=EntryExt(tag,name)::state.current_sync_extensions;custom_entries;}instate,eletextend_entry_commandtagname=letstatev,e=extend_entry_synctagname!stateinstate:=statev;emoduleParsable=structincludeParsableletconsumexlen=consumexlen(get_keyword_state())endmoduleEntry=structincludeEntryletmakename=make_entry_unsync(funestate->Entry.makenameestate)Unsafe.existing_entry()letparseep=parseep(gstate())letof_parsernap=make_entry_unsync(funestate->of_parsernapestate)(funestatee->Unsafe.existing_of_parserestateep)()letparse_token_streamestrm=parse_token_streamestrm(gstate())letprintfmte=printfmte(gstate()).estateletis_emptye=is_emptye(gstate()).estateletaccumulate_ine=accumulate_ine(gstate()).estateletall_in()=all_in()(gstate()).estateendmoduleLookahead=structleterr()=raiseStream.Failuretypet=int->CLexer.keyword_state->(CLexer.keyword_state,Tok.t)LStream.t->intoptionletreccontiguousnmstrm=n==m||let(_,ep)=Loc.unloc(LStream.get_locnstrm)inlet(bp,_)=Loc.unloc(LStream.get_loc(n+1)strm)inInt.equalepbp&&contiguous(succn)mstrmletcheck_no_spacem_kwstatestrm=letn=LStream.countstrminifcontiguousn(n+m-1)strmthenSomemelseNoneletto_entrys(lk:t)=letrunkwstatestrm=matchlk0kwstatestrmwithNone->err()|Some_->()inEntry.(of_parsers{parser_fun=run})let(>>)(lk1:t)lk2nkwstatestrm=matchlk1nkwstatestrmwith|None->None|Somen->lk2nkwstatestrmlet(<+>)(lk1:t)lk2nkwstatestrm=matchlk1nkwstatestrmwith|None->lk2nkwstatestrm|Somen->Somenletlk_emptynkwstatestrm=Somenletlk_kwkwnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Tok.KEYWORDkw'|Tok.IDENTkw'->ifString.equalkwkw'thenSome(n+1)elseNone|_->Noneletlk_kwskwsnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Tok.KEYWORDkw|Tok.IDENTkw->ifList.mem_fString.equalkwkwsthenSome(n+1)elseNone|_->Noneletlk_identnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Tok.IDENT_->Some(n+1)|_->Noneletlk_name=lk_ident<+>lk_kw"_"letlk_ident_exceptidentsnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Tok.IDENTidentwhennot(List.mem_fString.equalidentidents)->Some(n+1)|_->Noneletlk_natnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Tok.NUMBERpwhenNumTok.Unsigned.is_natp->Some(n+1)|_->Noneletreclk_listlk_elemnkwstatestrm=((lk_elem>>lk_listlk_elem)<+>lk_empty)nkwstatestrmletlk_ident_list=lk_listlk_identletlk_fieldnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Tok.FIELD_->Some(n+1)|_->Noneletlk_qualid=lk_ident>>lk_listlk_fieldend(** An entry that checks we reached the end of the input. *)(* used by the Tactician plugin *)leteoi_entryen=lete=Entry.make((Entry.nameen)^"_eoi")inletsymbs=Rule.next(Rule.nextRule.stop(Symbol.ntermen))(Symbol.tokenTok.PEOI)inletact=fun_xloc->xinletext=Fresh(Gramlib.Gramext.First,[None,None,[Production.makesymbsact]])ingrammar_extendeext;e(* Parse a string, does NOT check if the entire string was read
(use eoi_entry) *)letparse_stringf?locx=letstrm=Stream.of_stringxinEntry.parsef(Parsable.make?locstrm)moduleGrammarObj=structtype('r,_,_)obj='rEntry.tletname="grammar"letdefault_=NoneendmoduleGrammar=Register(GrammarObj)letwarn_deprecated_intropattern=CWarnings.create~name:"deprecated-intropattern-entry"~category:Deprecation.Version.v8_11(fun()->Pp.strbrk"Entry name intropattern has been renamed in order \
to be consistent with the documented grammar of tactics. Use \
\"simple_intropattern\" instead.")letcheck_compatibility=function|Genarg.ExtraArgswhenArgT.reprs="intropattern"->warn_deprecated_intropattern()|_->()letregister_grammar=Grammar.register0letgenarg_grammarx=check_compatibilityx;Grammar.objxletcreate_generic_entry2(typea)s(etyp:araw_abstract_argument_type):aEntry.t=lete=Entry.makesinletRawwitt=etypinlet()=Grammar.register0teine(* Initial grammar entries *)modulePrim=struct(* Entries that can be referred via the string -> Entry.t table *)(* Typically for tactic or vernac extensions *)letpreident=Entry.make"preident"letident=Entry.make"ident"letnatural=Entry.make"natural"letinteger=Entry.make"integer"letbignat=Entry.make"bignat"letbigint=Entry.make"bigint"letstring=Entry.make"string"letlstring=Entry.make"lstring"letreference=Entry.make"reference"letfields=Entry.make"fields"letby_notation=Entry.make"by_notation"letsmart_global=Entry.make"smart_global"letstrategy_level=Entry.make"strategy_level"(* parsed like ident but interpreted as a term *)lethyp=Entry.make"hyp"letvar=hypletname=Entry.make"name"letidentref=Entry.make"identref"letuniv_decl=Entry.make"univ_decl"letident_decl=Entry.make"ident_decl"letpattern_ident=Entry.make"pattern_ident"letqualid=Entry.make"qualid"letfullyqualid=Entry.make"fullyqualid"letdirpath=Entry.make"dirpath"letne_string=Entry.make"ne_string"letne_lstring=Entry.make"ne_lstring"letbar_cbrace=Entry.make"'|}'"endmoduleConstr=struct(* Entries that can be referred via the string -> Entry.t table *)letconstr=Entry.make"constr"letterm=Entry.make"term"letconstr_eoi=eoi_entryconstrletlconstr=Entry.make"lconstr"letbinder_constr=Entry.make"binder_constr"letident=Entry.make"ident"letglobal=Entry.make"global"letuniverse_name=Entry.make"universe_name"letsort=Entry.make"sort"letsort_quality_or_set=Entry.make"sort_quality_or_set"letpattern=Entry.make"pattern"letconstr_pattern=Entry.make"constr_pattern"letcpattern=Entry.make"cpattern"letclosed_binder=Entry.make"closed_binder"letbinder=Entry.make"binder"letbinders=Entry.make"binders"letopen_binders=Entry.make"open_binders"letone_open_binder=Entry.make"one_open_binder"letone_closed_binder=Entry.make"one_closed_binder"letbinders_fixannot=Entry.make"binders_fixannot"lettypeclass_constraint=Entry.make"typeclass_constraint"letrecord_declaration=Entry.make"record_declaration"letarg=Entry.make"arg"lettype_cstr=Entry.make"type_cstr"endmoduleModule=structletmodule_expr=Entry.make"module_expr"letmodule_type=Entry.make"module_type"end(** Synchronized grammar extensions *)type'agrammar_extension={gext_fun:'a->GramState.t->extend_rulelist*GramState.t;gext_eq:'a->'a->bool;}moduleGrammarInterp=structtype'at='agrammar_extensionendmoduleGrammarInterpMap=GrammarCommand.Map(GrammarInterp)letgrammar_interp=refGrammarInterpMap.emptytype'agrammar_command='aGrammarCommand.tagtype'aentry_command='aEntryCommand.tagletcreate_grammar_commandnameinterp:_grammar_command=letobj=GrammarCommand.createnameinlet()=grammar_interp:=GrammarInterpMap.addobjinterp!grammar_interpinobjletcreate_entry_commandname:'aentry_command=EntryCommand.createnameletextend_grammar_commandtagg=letmodify=GrammarInterpMap.findtag!grammar_interpinletgrammar_state=(!state).user_stateinlet(rules,st)=modify.gext_funggrammar_stateingrammar_extend_syncst(Dyn(tag,g))rules()letfind_custom_entrytagname=letEntryData.Exmap=EntryDataMap.findtag(!state).custom_entriesinString.Map.findnamemap(** Registering extra grammar *)letgrammar_names:Entry.any_tlistString.Map.tref=refString.Map.emptyletregister_grammars_by_namenamegrams=grammar_names:=String.Map.addnamegrams!grammar_namesletfind_grammars_by_namename=tryString.Map.findname!grammar_nameswithNot_found->letfold(EntryDataMap.Any(tag,EntryData.Exmap))accu=tryEntry.Any(String.Map.findnamemap)::accuwithNot_found->accuinEntryDataMap.foldfold(!state).custom_entries[](** Summary functions: the state of the lexer is included in that of the parser.
Because the grammar affects the set of keywords when adding or removing
grammar rules. *)typefrozen_t={frozen_sync:grammar_entrylist;frozen_base_kw:CLexer.keyword_state;frozen_kw:CLexer.keyword_state;}letunfreeze_only_keywords=function|{frozen_base_kw;frozen_kw}->letis_base=!(state).base_state==(!state).current_state&&frozen_base_kw==frozen_kwinletbase_state={(!state).base_statewithkwstate=frozen_base_kw}inletcurrent_state=ifis_basethenbase_stateelse{(!state).current_statewithkwstate=frozen_kw}instate:={!statewithbase_state;current_state;}leteq_gramsg1g2=matchg1,g2with|GramExt(GrammarCommand.Dyn(t1,v1)),GramExt(GrammarCommand.Dyn(t2,v2))->beginmatchGrammarCommand.eqt1t2with|None->false|SomeRefl->letdata=GrammarInterpMap.findt1!grammar_interpindata.gext_eqv1v2end|EntryExt(t1,v1),EntryExt(t2,v2)->beginmatchEntryCommand.eqt1t2with|None->false|SomeRefl->String.equalv1v2end|(GramExt_,EntryExt_)|(EntryExt_,GramExt_)->false(* We compare the current state of the grammar and the state to unfreeze,
by computing the longest common suffixes *)letfactorize_gramsl1l2=ifl1==l2then([],[],l1)elseList.share_tailseq_gramsl1l2letreplay_sync_extension=function|GramExt(Dyn(tag,g))->extend_grammar_commandtagg|EntryExt(tag,name)->ignore(extend_entry_commandtagname:_Entry.t)letunfreeze=function|{frozen_sync;}asfrozen->letto_remove,to_add,_common=factorize_grams(!state).current_sync_extensionsfrozen_syncinifCList.is_emptyto_removethenbeginList.iterreplay_sync_extension(List.revto_add);unfreeze_only_keywordsfrozenendelsebeginstate:=reset_to_base!state;List.iterreplay_sync_extension(List.revfrozen_sync);(* put back the keyword state, needed to support ssr hacks *)unfreeze_only_keywordsfrozenendletfreeze_statestate={frozen_sync=state.current_sync_extensions;frozen_base_kw=state.base_state.kwstate;frozen_kw=state.current_state.kwstate;}letfreeze():frozen_t=freeze_state!state(** No need to provide an init function : the grammar state is
statically available, and already empty initially, while
the lexer state should not be reset, since it contains
keywords declared in g_*.mlg
XXX is this still true? if not we can do (fun () -> unfreeze (FreezeFull empty_full_state)) *)letparser_summary_tag=Summary.declare_summary_tag"GRAMMAR_LEXER"{stage=Summary.Stage.Synterp;Summary.freeze_function=freeze;Summary.unfreeze_function=unfreeze;Summary.init_function=Summary.nop}letwith_grammar_rule_protectionfx=letfs=freeze()intryleta=fxinunfreezefs;awithreraise->letreraise=Exninfo.capturereraiseinlet()=unfreezefsinExninfo.iraisereraise