123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525(************************************************************************)(* * 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()typegramext={ignore_kw:bool;entry:GrammarCommand.t}typegrammar_entry=|GramExtofgramext|EntryExt:('a*'b)EntryCommand.tag*'a->grammar_entry(** State handling (non marshallable!) *)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;}letempty_full_state=letempty_gstate={GState.estate=EState.empty;kwstate=CLexer.empty_keyword_state;recover=true;has_non_assoc=false}in{current_state=empty_gstate;base_state=empty_gstate;current_sync_extensions=[];user_state=GramState.empty;}letassert_synterp()=if!Flags.in_synterp_phase=SomefalsethenCErrors.anomalyPp.(str"The grammar cannot be modified during the interp phase.")(** Not marshallable! *)letstate=refempty_full_stateletgramstate()=(!state).user_stateletgstate()=(!state).current_stateletget_keyword_state()=(gstate()).kwstateletreset_to_basestate={base_state=state.base_state;current_state=state.base_state;current_sync_extensions=[];user_state=GramState.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()=assert_synterp();state:=modify_state_unsyncf!stateletmodify_keyword_statef=modify_state_unsync(fun{estate;kwstate;recover;has_non_assoc}->{estate;kwstate=fkwstate;recover;has_non_assoc})()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()=assert_synterp();letstatev,e=make_entry_unsyncmakeremake!stateinstate:=statev;eletadd_kw={add_kw=CLexer.add_keyword_tok}letno_add_kw={add_kw=fun()_->()}letepsilon_value(typestra)f(e:(s,tr,a)Symbol.t)=letr=Production.make(Rule.nextRule.stope)(funx_->fx)inlet{GState.estate;kwstate;recover;has_non_assoc}=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;recover;has_non_assoc})withewhenCErrors.noncriticale->Noneletextend_gstate~ignore_kw{GState.kwstate;estate;recover;has_non_assoc}eext=letestate,kwstate=ifignore_kwthenletestate,()=safe_extendno_add_kwestate()eextinestate,kwstateelsesafe_extendadd_kwestatekwstateeextin{GState.kwstate;estate;recover;has_non_assoc}(* XXX rename to grammar_extend_unsync? *)letgrammar_extend~ignore_kweext=letextend_oneg=extend_gstate~ignore_kwgeextinmodify_state_unsyncextend_one()typeextend_rule=|ExtendRule:'aEntry.t*'aextend_statement->extend_ruleletgrammar_extend_sync~ignore_kwuser_stateentryrulesstate=letextend_one_syncstate=function|ExtendRule(e,ext)->extend_gstatestateeextinletcurrent_state=List.fold_left(extend_one_sync~ignore_kw)state.current_staterulesin{statewithcurrent_state;user_state;current_sync_extensions=GramExt{ignore_kw;entry}::state.current_sync_extensions;}letgrammar_extend_sync~ignore_kwster()=assert_synterp();state:=grammar_extend_sync~ignore_kwster!statetype('a,'b)entry_extension={eext_fun:'a->'bEntry.t->GramState.t->GramState.t;eext_name:'a->string;eext_eq:'a->'a->bool;}letextend_entry_sync(typeab)(tag:(a*b)EntryCommand.tag)(interp:(a,b)entry_extension)(data:a)state=letname=interp.eext_namedatainletcurrent_estate,e=Entry.makenamestate.current_state.estateinletcurrent_state={state.current_statewithestate=current_estate}inletuser_state=interp.eext_fundataestate.user_stateinletstate={statewithcurrent_state;current_sync_extensions=EntryExt(tag,data)::state.current_sync_extensions;user_state;}instateletextend_entry_synctaginterpdata()=assert_synterp();letstatev=extend_entry_synctaginterpdata!stateinstate:=statevmoduleParsable=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=letgstate=gstate()inprintfmtegstate.estategstate.kwstateletis_emptye=is_emptye(gstate()).estateletaccumulate_ine=accumulate_ine(gstate()).estateletall_in()=all_in()(gstate()).estateendmoduleLookahead=structtypet=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->Error()|Some_->Ok()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|Some(Tok.KEYWORDkw'|Tok.IDENTkw')->ifString.equalkwkw'thenSome(n+1)elseNone|_->Noneletlk_kwskwsnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Some(Tok.KEYWORDkw|Tok.IDENTkw)->ifList.mem_fString.equalkwkwsthenSome(n+1)elseNone|_->Noneletlk_identnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Some(Tok.IDENT_)->Some(n+1)|_->Noneletlk_name=lk_ident<+>lk_kw"_"letlk_ident_exceptidentsnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Some(Tok.IDENTident)whennot(List.mem_fString.equalidentidents)->Some(n+1)|_->Noneletlk_natnkwstatestrm=matchLStream.peek_nthkwstatenstrmwith|Some(Tok.NUMBERp)whenNumTok.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|Some(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]])in(* ignore_kw: doesn't matter here, no potential keywords in the rule *)grammar_extend~ignore_kw:trueeext;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)letregister_grammar=Grammar.register0letgenarg_grammarx=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"letname=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"letsort_quality_var=Entry.make"sort_quality_var"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.tagletcreate_grammar_commandnameinterp:_grammar_command=letobj=GrammarCommand.createnameinlet()=grammar_interp:=GrammarInterpMap.addobjinterp!grammar_interpinobjletextend_grammar_command~ignore_kwtagg=letmodify=GrammarInterpMap.findtag!grammar_interpinletgrammar_state=(!state).user_stateinlet(rules,st)=modify.gext_funggrammar_stateingrammar_extend_sync~ignore_kwst(Dyn(tag,g))rules()moduleEntryInterp=structtype_t=EExt:('a,'b)entry_extension->('a*'b)tendmoduleEntryInterpMap=EntryCommand.Map(EntryInterp)letentry_interp=refEntryInterpMap.emptytype('a,'b)entry_command=('a*'b)EntryCommand.tagletcreate_entry_commandnameinterp:_entry_command=letobj=EntryCommand.createnameinlet()=entry_interp:=EntryInterpMap.addobj(EExtinterp)!entry_interpinobjletextend_entry_commandtagdata=letEExtinterp=EntryInterpMap.findtag!entry_interpinextend_entry_synctaginterpdata()(** 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=(* XXX look through custom entries somehow? *)Option.default[](String.Map.find_optname!grammar_names)(** 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{ignore_kw=kw1;entry=GrammarCommand.Dyn(t1,v1)},GramExt{ignore_kw=kw2;entry=GrammarCommand.Dyn(t2,v2)}->beginBool.equalkw1kw2&&matchGrammarCommand.eqt1t2with|None->false|SomeRefl->letdata=GrammarInterpMap.findt1!grammar_interpindata.gext_eqv1v2end|EntryExt(t1,d1),EntryExt(t2,d2)->beginmatchEntryCommand.eqt1t2with|None->false|SomeRefl->letEExtinterp=EntryInterpMap.findt1!entry_interpininterp.eext_eqd1d2end|(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{ignore_kw;entry=Dyn(tag,g)}->extend_grammar_command~ignore_kwtagg|EntryExt(tag,data)->extend_entry_commandtagdataletunfreeze({frozen_sync;}asfrozen)=(* allow unfreezing synterp state even during interp phase *)Flags.with_modified_refFlags.in_synterp_phase(fun_->None)(fun()->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_keywordsfrozenend)()letfreeze_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=letopenMemprof_coq.Resource_bindinlet&()=Util.protect_state~freeze~unfreezeinfx(* Usually we don't need to protect unfreeze, as for references doing
unfreeze again will restore them to a coherent state, however this
is not the case for Procq *)letunfreeze=Util.atomifyunfreeze