1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476(************************************************************************)(* * 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) *)(************************************************************************)(*i*)openCErrorsopenUtilopenPpopenNamesopenConstropenLibnamesopenGlobnamesopenConstrexpropenNotation_termopenGlob_termopenGlob_opsopenContext.Named.DeclarationopenNumTok(*i*)(*s A scope is a set of notations; it includes
- a set of ML interpreters/parsers for positive (e.g. 0, 1, 15, ...) and
negative numbers (e.g. -0, -2, -13, ...). These interpreters may
fail if a number has no interpretation in the scope (e.g. there is
no interpretation for negative numbers in [nat]); interpreters both for
terms and patterns can be set; these interpreters are in permanent table
[number_interpreter_tab]
- a set of ML printers for expressions denoting numbers parsable in
this scope
- a set of interpretations for infix (more generally distfix) notations
- an optional pair of delimiters which, when occurring in a syntactic
expression, set this scope to be the current scope
*)letnotation_entry_eqs1s2=match(s1,s2)with|InConstrEntry,InConstrEntry->true|InCustomEntrys1,InCustomEntrys2->String.equals1s2|(InConstrEntry|InCustomEntry_),_->falseletnotation_entry_level_eqs1s2=match(s1,s2)with|InConstrEntrySomeLevel,InConstrEntrySomeLevel->true|InCustomEntryLevel(s1,n1),InCustomEntryLevel(s2,n2)->String.equals1s2&&n1=n2|(InConstrEntrySomeLevel|InCustomEntryLevel_),_->falseletnotation_with_optional_scope_eqinscope1inscope2=match(inscope1,inscope2)with|LastLonelyNotation,LastLonelyNotation->true|NotationInScopes1,NotationInScopes2->String.equals1s2|(LastLonelyNotation|NotationInScope_),_->falseletnotation_eq(from1,ntn1)(from2,ntn2)=notation_entry_eqfrom1from2&&String.equalntn1ntn2letpair_eqfg(x1,y1)(x2,y2)=fx1x2&&gy1y2letnotation_binder_source_eqs1s2=matchs1,s2with|NtnParsedAsIdent,NtnParsedAsIdent->true|NtnParsedAsName,NtnParsedAsName->true|NtnParsedAsPatternb1,NtnParsedAsPatternb2->b1=b2|NtnBinderParsedAsConstrbk1,NtnBinderParsedAsConstrbk2->bk1=bk2|NtnParsedAsBinder,NtnParsedAsBinder->true|(NtnParsedAsIdent|NtnParsedAsName|NtnParsedAsPattern_|NtnBinderParsedAsConstr_|NtnParsedAsBinder),_->falseletntpe_eqt1t2=matcht1,t2with|NtnTypeConstr,NtnTypeConstr->true|NtnTypeBinders1,NtnTypeBinders2->notation_binder_source_eqs1s2|NtnTypeConstrList,NtnTypeConstrList->true|NtnTypeBinderList,NtnTypeBinderList->true|(NtnTypeConstr|NtnTypeBinder_|NtnTypeConstrList|NtnTypeBinderList),_->falseletvar_attributes_eq(_,((entry1,sc1),tp1))(_,((entry2,sc2),tp2))=notation_entry_level_eqentry1entry2&&pair_eq(Option.equalString.equal)(List.equalString.equal)sc1sc2&&ntpe_eqtp1tp2letinterpretation_eq(vars1,t1asx1)(vars2,t2asx2)=x1==x2||List.equalvar_attributes_eqvars1vars2&&Notation_ops.eq_notation_constr(List.mapfstvars1,List.mapfstvars2)t1t2letpr_notation(from,ntn)=qstringntn++matchfromwithInConstrEntry->mt()|InCustomEntrys->str" in custom "++strsmoduleNotationOrd=structtypet=notationletcompare=pervasives_compareendmoduleNotationSet=Set.Make(NotationOrd)moduleNotationMap=CMap.Make(NotationOrd)moduleSpecificNotationOrd=structtypet=specific_notationletcompare=pervasives_compareendmoduleSpecificNotationSet=Set.Make(SpecificNotationOrd)moduleSpecificNotationMap=CMap.Make(SpecificNotationOrd)(**********************************************************************)(* Scope of symbols *)typedelimiters=stringtypenotation_location=(DirPath.t*DirPath.t)*stringtypenotation_data={not_interp:interpretation;not_location:notation_location;not_deprecation:Deprecation.toption;}typeactivation=booltypeextra_printing_notation_data=(activation*notation_data)listtypeparsing_notation_data=|NoParsingData|OnlyParsingDataofactivation*notation_data|ParsingAndPrintingDataofactivation(* for parsing*)*activation(* for printing *)*notation_data(* common data for both *)typescope={notations:(parsing_notation_data*extra_printing_notation_data)NotationMap.t;delimiters:delimitersoption}(* Scopes table: scope_name -> symbol_interpretation *)letscope_map=refString.Map.empty(* Delimiter table : delimiter -> scope_name *)letdelimiters_map=refString.Map.emptyletempty_scope={notations=NotationMap.empty;delimiters=None}letdefault_scope=""(* empty name, not available from outside *)letinit_scope_map()=scope_map:=String.Map.adddefault_scopeempty_scope!scope_map(**********************************************************************)(* Operations on scopes *)letwarn_undeclared_scope=CWarnings.create~name:"undeclared-scope"~category:"deprecated"(fun(scope)->strbrk"Declaring a scope implicitly is deprecated; use in advance an explicit "++str"\"Declare Scope "++strscope++str".\".")letdeclare_scopescope=trylet_=String.Map.findscope!scope_mapin()withNot_found->scope_map:=String.Map.addscopeempty_scope!scope_mapleterror_unknown_scope~infosc=user_err~hdr:"Notation"~info(str"Scope "++strsc++str" is not declared.")letfind_scope?(tolerant=false)scope=tryString.Map.findscope!scope_mapwithNot_foundasexn->let_,info=Exninfo.captureexniniftolerantthen(* tolerant mode to be turn off after deprecation phase *)beginwarn_undeclared_scopescope;scope_map:=String.Map.addscopeempty_scope!scope_map;empty_scopeendelseerror_unknown_scope~infoscopeletcheck_scope?(tolerant=false)scope=let_=find_scope~tolerantscopein()letensure_scopescope=check_scope~tolerant:truescopeletfind_scopescope=find_scopescope(* [sc] might be here a [scope_name] or a [delimiter]
(now allowed after Open Scope) *)letnormalize_scopesc=trylet_=String.Map.findsc!scope_mapinscwithNot_found->tryletsc=String.Map.findsc!delimiters_mapinlet_=String.Map.findsc!scope_mapinscwithNot_foundasexn->let_,info=Exninfo.captureexninerror_unknown_scope~infosc(**********************************************************************)(* The global stack of scopes *)typescope_item=OpenScopeItemofscope_name|LonelyNotationItemofnotationtypescopes=scope_itemlistletscope_eqs1s2=matchs1,s2with|OpenScopeItems1,OpenScopeItems2->String.equals1s2|LonelyNotationItems1,LonelyNotationItems2->notation_eqs1s2|OpenScopeItem_,LonelyNotationItem_|LonelyNotationItem_,OpenScopeItem_->falseletscope_stack=ref[]letcurrent_scopes()=!scope_stackletscope_is_open_in_scopesscl=List.exists(functionOpenScopeItemsc'->String.equalscsc'|_->false)lletscope_is_opensc=scope_is_open_in_scopessc(!scope_stack)(* TODO: push nat_scope, z_scope, ... in scopes summary *)(* Exportation of scopes *)letopen_scopei(_,(local,op,sc))=ifInt.equali1thenscope_stack:=ifopthensc::!scope_stackelseList.exceptscope_eqsc!scope_stackletcache_scopeo=open_scope1oletsubst_scope(subst,sc)=scopenLibobjectletdischarge_scope(_,(local,_,_aso))=iflocalthenNoneelseSomeoletclassify_scope(local,_,_aso)=iflocalthenDisposeelseSubstituteoletinScope:bool*bool*scope_item->obj=declare_object{(default_object"SCOPE")withcache_function=cache_scope;open_function=simple_openopen_scope;subst_function=subst_scope;discharge_function=discharge_scope;classify_function=classify_scope}letopen_close_scope(local,opening,sc)=Lib.add_anonymous_leaf(inScope(local,opening,OpenScopeItem(normalize_scopesc)))letempty_scope_stack=[]letpush_scopescscopes=OpenScopeItemsc::scopesletpush_scopes=List.fold_rightpush_scopeletmake_current_scopes(tmp_scope,scopes)=Option.fold_rightpush_scopetmp_scope(push_scopesscopes!scope_stack)(**********************************************************************)(* Delimiters *)letdeclare_delimitersscopekey=letsc=find_scopescopeinletnewsc={scwithdelimiters=Somekey}inbeginmatchsc.delimiterswith|None->scope_map:=String.Map.addscopenewsc!scope_map|SomeoldkeywhenString.equaloldkeykey->()|Someoldkey->(* FIXME: implement multikey scopes? *)Flags.if_verboseFeedback.msg_info(str"Overwriting previous delimiting key "++stroldkey++str" in scope "++strscope);scope_map:=String.Map.addscopenewsc!scope_mapend;tryletoldscope=String.Map.findkey!delimiters_mapinifString.equaloldscopescopethen()elsebeginFlags.if_verboseFeedback.msg_info(str"Hiding binding of key "++strkey++str" to "++stroldscope);delimiters_map:=String.Map.addkeyscope!delimiters_mapendwithNot_found->delimiters_map:=String.Map.addkeyscope!delimiters_mapletremove_delimitersscope=letsc=find_scopescopeinletnewsc={scwithdelimiters=None}inmatchsc.delimiterswith|None->CErrors.user_err(str"No bound key for scope "++strscope++str".")|Somekey->scope_map:=String.Map.addscopenewsc!scope_map;trylet_=ignore(String.Map.findkey!delimiters_map)indelimiters_map:=String.Map.removekey!delimiters_mapwithNot_foundasexn->let_,info=Exninfo.captureexnin(* XXX info *)CErrors.anomaly~info(str"A delimiter for scope [scope] should exist")letfind_delimiters_scope?lockey=tryString.Map.findkey!delimiters_mapwithNot_found->user_err?loc~hdr:"find_delimiters"(str"Unknown scope delimiting key "++strkey++str".")(* Uninterpretation tables *)typeinterp_rule=|NotationRuleofspecific_notation|SynDefRuleofKerName.t(* We define keys for glob_constr and aconstr to split the syntax entries
according to the key of the pattern (adapted from Chet Murthy by HH) *)typekey=|RefKeyofGlobRef.t|Othletkey_comparek1k2=matchk1,k2with|RefKeygr1,RefKeygr2->GlobRef.CanOrd.comparegr1gr2|RefKey_,Oth->-1|Oth,RefKey_->1|Oth,Oth->0moduleKeyOrd=structtypet=keyletcompare=key_compareendmoduleKeyMap=Map.Make(KeyOrd)typenotation_applicative_status=|AppBoundedNotationofint|AppUnboundedNotation|NotAppNotationtypenotation_rule=interp_rule*interpretation*notation_applicative_statusletnotation_rule_eq(rule1,pat1,s1asx1)(rule2,pat2,s2asx2)=x1==x2||(rule1=rule2&&interpretation_eqpat1pat2&&s1=s2)letadjust_applicationc1c2=matchc1,c2with|NApp(t1,a1),(NList(_,_,NApp(_,a2),_,_)|NApp(_,a2))whenList.lengtha1>=List.lengtha2->NApp(t1,List.firstn(List.lengtha2)a1)|NApp(t1,a1),_->t1|_->c1letstrictly_finer_interpretation_than(_,(_,(vars1,c1),_))(_,(_,(vars2,c2),_))=letc1=adjust_applicationc1c2inNotation_ops.strictly_finer_notation_constr(List.mapfstvars1,List.mapfstvars2)c1c2letkeymap_addkeyinterpmap=letold=tryKeyMap.findkeymapwithNot_found->[]in(* strictly finer interpretation are kept in front *)letstrictly_finer,rest=List.partition(func->strictly_finer_interpretation_thancinterp)oldinKeyMap.addkey(strictly_finer@interp::rest)mapletkeymap_removekeyinterpmap=letold=tryKeyMap.findkeymapwithNot_found->[]inKeyMap.addkey(List.remove_first(fun(_,rule)->notation_rule_eqinterprule)old)mapletkeymap_findkeymap=tryKeyMap.findkeymapwithNot_found->[](* Scopes table : interpretation -> scope_name *)(* Boolean = for cases pattern also *)letnotations_key_table=ref(KeyMap.empty:(bool*notation_rule)listKeyMap.t)letglob_prim_constr_keyc=matchDAst.getcwith|GRef(ref,_)->Some(canonical_grref)|GApp(c,_)->beginmatchDAst.getcwith|GRef(ref,_)->Some(canonical_grref)|_->Noneend|_->Noneletglob_constr_keysc=matchDAst.getcwith|GApp(c,_)->beginmatchDAst.getcwith|GRef(ref,_)->[RefKey(canonical_grref);Oth]|_->[Oth]end|GRef(ref,_)->[RefKey(canonical_grref)]|_->[Oth]letcases_pattern_keyc=matchDAst.getcwith|PatCstr(ref,_,_)->RefKey(canonical_gr(GlobRef.ConstructRefref))|_->Othletnotation_constr_key=function(* Rem: NApp(NRef ref,[]) stands for @ref *)|NApp(NRef(ref,_),args)->RefKey(canonical_grref),AppBoundedNotation(List.lengthargs)|NList(_,_,NApp(NRef(ref,_),args),_,_)|NBinderList(_,_,NApp(NRef(ref,_),args),_,_)->RefKey(canonical_grref),AppBoundedNotation(List.lengthargs)|NRef(ref,_)->RefKey(canonical_grref),NotAppNotation|NApp(NList(_,_,NApp(NRef(ref,_),args),_,_),args')->RefKey(canonical_grref),AppBoundedNotation(List.lengthargs+List.lengthargs')|NApp(NList(_,_,NApp(_,args),_,_),args')->Oth,AppBoundedNotation(List.lengthargs+List.lengthargs')|NApp(_,args)->Oth,AppBoundedNotation(List.lengthargs)|NList(_,_,NApp(NVarx,_),_,_)whenx=Notation_ops.ldots_var->Oth,AppUnboundedNotation|_->Oth,NotAppNotation(** Dealing with precedences *)typelevel=notation_entry*entry_level*entry_relative_levellist(* first argument is InCustomEntry s for custom entries *)letentry_relative_level_eqt1t2=matcht1,t2with|LevelLtn1,LevelLtn2->Int.equaln1n2|LevelLen1,LevelLen2->Int.equaln1n2|LevelSome,LevelSome->true|(LevelLt_|LevelLe_|LevelSome),_->falseletlevel_eq(s1,l1,t1)(s2,l2,t2)=notation_entry_eqs1s2&&Int.equall1l2&&List.equalentry_relative_level_eqt1t2letnotation_level_map=Summary.ref~name:"notation_level_map"NotationMap.emptyletdeclare_notation_levelntnlevel=trylet_=NotationMap.findntn!notation_level_mapinanomaly(str"Notation "++pr_notationntn++str" is already assigned a level.")withNot_found->notation_level_map:=NotationMap.addntnlevel!notation_level_mapletlevel_of_notationntn=NotationMap.findntn!notation_level_map(**********************************************************************)(* Interpreting numbers (not in summary because functional objects) *)typerequired_module=full_path*stringlisttyperawnum=NumTok.Signed.ttypeprim_token_uid=stringtype'aprim_token_interpreter=?loc:Loc.t->'a->glob_constrtype'aprim_token_uninterpreter=any_glob_constr->'aoptiontype'aprim_token_interpretation='aprim_token_interpreter*'aprim_token_uninterpretermoduleInnerPrimToken=structtypeinterpreter=|RawNumInterpof(?loc:Loc.t->rawnum->glob_constr)|BigNumInterpof(?loc:Loc.t->Z.t->glob_constr)|StringInterpof(?loc:Loc.t->string->glob_constr)letinterp_eqff'=matchf,f'with|RawNumInterpf,RawNumInterpf'->f==f'|BigNumInterpf,BigNumInterpf'->f==f'|StringInterpf,StringInterpf'->f==f'|_->falseletdo_interp?locinterpprimtok=matchprimtok,interpwith|Numbern,RawNumInterpinterp->interp?locn|Numbern,BigNumInterpinterp->(matchNumTok.Signed.to_bigintnwith|Somen->interp?locn|None->raiseNot_found)|Strings,StringInterpinterp->interp?locs|(Number_|String_),(RawNumInterp_|BigNumInterp_|StringInterp_)->raiseNot_foundtypeuninterpreter=|RawNumUninterpof(any_glob_constr->rawnumoption)|BigNumUninterpof(any_glob_constr->Z.toption)|StringUninterpof(any_glob_constr->stringoption)letuninterp_eqff'=matchf,f'with|RawNumUninterpf,RawNumUninterpf'->f==f'|BigNumUninterpf,BigNumUninterpf'->f==f'|StringUninterpf,StringUninterpf'->f==f'|_->falseletmkNumbern=Number(NumTok.Signed.of_bigintCDecn)letmkString=function|None->None|Somes->ifUnicode.is_utf8sthenSome(Strings)elseNoneletdo_uninterpuninterpg=matchuninterpwith|RawNumUninterpu->Option.map(fun(s,n)->Number(s,n))(ug)|BigNumUninterpu->Option.mapmkNumber(ug)|StringUninterpu->mkString(ug)end(* The following two tables of (un)interpreters will *not* be
synchronized. But their indexes will be checked to be unique.
These tables contain primitive token interpreters which are
registered in plugins, such as string and ascii syntax. It is
essential that only plugins add to these tables, and that
vernacular commands do not. See
https://github.com/coq/coq/issues/8401 for details of what goes
wrong when vernacular commands add to these tables. *)letprim_token_interpreters=(Hashtbl.create7:(prim_token_uid,InnerPrimToken.interpreter)Hashtbl.t)letprim_token_uninterpreters=(Hashtbl.create7:(prim_token_uid,InnerPrimToken.uninterpreter)Hashtbl.t)(*******************************************************)(* Number notation interpretation *)typeprim_token_notation_error=|UnexpectedTermofConstr.t|UnexpectedNonOptionTermofConstr.texceptionPrimTokenNotationErrorofstring*Environ.env*Evd.evar_map*prim_token_notation_errortypenumnot_option=|Nop|WarningofNumTok.UnsignedNat.t|AbstractofNumTok.UnsignedNat.ttypeint_ty={dec_uint:Names.inductive;dec_int:Names.inductive;hex_uint:Names.inductive;hex_int:Names.inductive;uint:Names.inductive;int:Names.inductive}typez_pos_ty={z_ty:Names.inductive;pos_ty:Names.inductive}typenumber_ty={int:int_ty;decimal:Names.inductive;hexadecimal:Names.inductive;number:Names.inductive}typepos_neg_int63_ty={pos_neg_int63_ty:Names.inductive}typetarget_kind=|Intofint_ty(* Coq.Init.Number.int + uint *)|UIntofint_ty(* Coq.Init.Number.uint *)|Zofz_pos_ty(* Coq.Numbers.BinNums.Z and positive *)|Int63ofpos_neg_int63_ty(* Coq.Numbers.Cyclic.Int63.PrimInt63.pos_neg_int63 *)|Numberofnumber_ty(* Coq.Init.Number.number + uint + int *)typestring_target_kind=|ListByte|Bytetypeoption_kind=Option|Directtype'targetconversion_kind='target*option_kind(** A postprocessing translation [to_post] can be done after execution
of the [to_ty] interpreter. The reverse translation is performed
before the [of_ty] uninterpreter.
[to_post] is an array of [n] lists [l_i] of tuples [(f, t,
args)]. When the head symbol of the translated term matches one of
the [f] in the list [l_0] it is replaced by [t] and its arguments
are translated acording to [args] where [ToPostCopy] means that the
argument is kept unchanged and [ToPostAs k] means that the
argument is recursively translated according to [l_k].
[ToPostHole] introduces an additional implicit argument hole
(in the reverse translation, the corresponding argument is removed).
[ToPostCheck r] behaves as [ToPostCopy] except in the reverse
translation which fails if the copied term is not [r].
When [n] is null, no translation is performed. *)typeto_post_arg=ToPostCopy|ToPostAsofint|ToPostHole|ToPostCheckofGlobRef.ttype('target,'warning)prim_token_notation_obj={to_kind:'targetconversion_kind;to_ty:GlobRef.t;to_post:((GlobRef.t*GlobRef.t*to_post_arglist)list)array;of_kind:'targetconversion_kind;of_ty:GlobRef.t;ty_name:Libnames.qualid;(* for warnings / error messages *)warning:'warning}typenumber_notation_obj=(target_kind,numnot_option)prim_token_notation_objtypestring_notation_obj=(string_target_kind,unit)prim_token_notation_objmodulePrimTokenNotation=struct(** * Code shared between Number notation and String notation *)(** Reduction
The constr [c] below isn't necessarily well-typed, since we
built it via an [mkApp] of a conversion function on a term
that starts with the right constructor but might be partially
applied.
At least [c] is known to be evar-free, since it comes from
our own ad-hoc [constr_of_glob] or from conversions such
as [coqint_of_rawnum].
It is important to fully normalize the term, *including inductive
parameters of constructors*; see
https://github.com/coq/coq/issues/9840 for details on what goes
wrong if this does not happen, e.g., from using the vm rather than
cbv.
*)leteval_constrenvsigma(c:Constr.t)=letc=EConstr.of_constrcinletc'=Tacred.computeenvsigmacinEConstr.Unsafe.to_constrc'leteval_constr_appenvsigmac1c2=eval_constrenvsigma(mkApp(c1,[|c2|]))exceptionNotAValidPrimToken(** The uninterp function below work at the level of [glob_constr]
which is too low for us here. So here's a crude conversion back
to [constr] for the subset that concerns us.
Note that if you update [constr_of_glob], you should update the
corresponding number notation *and* string notation doc in
doc/sphinx/user-extensions/syntax-extensions.rst that describes
what it means for a term to be ground / to be able to be
considered for parsing. *)letconstr_of_globrefallow_constantenvsigma=function|GlobRef.ConstructRefc->letsigma,c=Evd.fresh_constructor_instanceenvsigmacinsigma,mkConstructUc|GlobRef.IndRefc->letsigma,c=Evd.fresh_inductive_instanceenvsigmacinsigma,mkIndUc|GlobRef.ConstRefcwhenallow_constant||Environ.is_primitive_typeenvc->letsigma,c=Evd.fresh_constant_instanceenvsigmacinsigma,mkConstUc|_->raiseNotAValidPrimTokenletrecconstr_of_globallow_constantto_postpostenvsigmag=matchDAst.getgwith|Glob_term.GRef(r,_)->leto=List.find_opt(fun(_,r',_)->GlobRef.equalrr')postinbeginmatchowith|None->constr_of_globrefallow_constantenvsigmar|Some(r,_,a)->(* [g] is not a GApp so check that [post]
does not expect any actual argument
(i.e., [a] contains only ToPostHole since they mean "ignore arg") *)ifList.exists((<>)ToPostHole)athenraiseNotAValidPrimToken;constr_of_globreftrueenvsigmarend|Glob_term.GApp(gc,gcl)->leto=matchDAst.getgcwith|Glob_term.GRef(r,_)->List.find_opt(fun(_,r',_)->GlobRef.equalrr')post|_->Noneinbeginmatchowith|None->letsigma,c=constr_of_globallow_constantto_postpostenvsigmagcinletsigma,cl=List.fold_left_map(constr_of_globallow_constantto_postpostenv)sigmagclinsigma,mkApp(c,Array.of_listcl)|Some(r,_,a)->letsigma,c=constr_of_globreftrueenvsigmarinletrecauxsigmaagcl=matcha,gclwith|[],[]->sigma,[]|ToPostCopy::a,gc::gcl->letsigma,c=constr_of_globallow_constant[||][]envsigmagcinletsigma,cl=auxsigmaagclinsigma,c::cl|ToPostCheckr::a,gc::gcl->let()=matchDAst.getgcwith|Glob_term.GRef(r',_)whenGlobRef.equalrr'->()|_->raiseNotAValidPrimTokeninletsigma,c=constr_of_globtrue[||][]envsigmagcinletsigma,cl=auxsigmaagclinsigma,c::cl|ToPostAsi::a,gc::gcl->letsigma,c=constr_of_globallow_constantto_postto_post.(i)envsigmagcinletsigma,cl=auxsigmaagclinsigma,c::cl|ToPostHole::post,_::gcl->auxsigmapostgcl|[],_::_|_::_,[]->raiseNotAValidPrimTokeninletsigma,cl=auxsigmaagclinsigma,mkApp(c,Array.of_listcl)end|Glob_term.GInti->sigma,mkInti|Glob_term.GFloatf->sigma,mkFloatf|Glob_term.GArray(_,t,def,ty)->letsigma,u'=Evd.fresh_array_instanceenvsigmainletsigma,def'=constr_of_globallow_constantto_postpostenvsigmadefinletsigma,t'=Array.fold_left_map(constr_of_globallow_constantto_postpostenv)sigmatinletsigma,ty'=constr_of_globallow_constantto_postpostenvsigmatyinsigma,mkArray(u',t',def',ty')|Glob_term.GSortgs->letsigma,c=Evd.fresh_sort_in_familysigma(Glob_ops.glob_sort_familygs)insigma,mkSortc|_->raiseNotAValidPrimTokenletconstr_of_globto_postenvsigma(Glob_term.AnyGlobConstrg)=letpost=matchto_postwith[||]->[]|_->to_post.(0)inconstr_of_globfalseto_postpostenvsigmagletrecglob_of_constrtoken_kind?locenvsigmac=matchConstr.kindcwith|App(c,ca)->letc=glob_of_constrtoken_kind?locenvsigmacinletcel=List.map(glob_of_constrtoken_kind?locenvsigma)(Array.to_listca)inDAst.make?loc(Glob_term.GApp(c,cel))|Construct(c,_)->DAst.make?loc(Glob_term.GRef(GlobRef.ConstructRefc,None))|Const(c,_)->DAst.make?loc(Glob_term.GRef(GlobRef.ConstRefc,None))|Ind(ind,_)->DAst.make?loc(Glob_term.GRef(GlobRef.IndRefind,None))|Varid->DAst.make?loc(Glob_term.GRef(GlobRef.VarRefid,None))|Inti->DAst.make?loc(Glob_term.GInti)|Floatf->DAst.make?loc(Glob_term.GFloatf)|Array(u,t,def,ty)->letdef'=glob_of_constrtoken_kind?locenvsigmadefandt'=Array.map(glob_of_constrtoken_kind?locenvsigma)tandty'=glob_of_constrtoken_kind?locenvsigmatyinDAst.make?loc(Glob_term.GArray(None,t',def',ty'))|SortSorts.SProp->DAst.make?loc(Glob_term.GSort(Glob_term.UNamed[Glob_term.GSProp,0]))|SortSorts.Prop->DAst.make?loc(Glob_term.GSort(Glob_term.UNamed[Glob_term.GProp,0]))|SortSorts.Set->DAst.make?loc(Glob_term.GSort(Glob_term.UNamed[Glob_term.GSet,0]))|Sort(Sorts.Type_)->DAst.make?loc(Glob_term.GSort(Glob_term.UAnonymous{rigid=true}))|_->Loc.raise?loc(PrimTokenNotationError(token_kind,env,sigma,UnexpectedTermc))letno_such_prim_tokenuninterpreted_token_kind?locty=CErrors.user_err?loc(str("Cannot interpret this "^uninterpreted_token_kind^" as a value of type ")++pr_qualidty)letrecpostprocesstoken_kind?loctyto_postpostg=letg',gl=matchDAst.getgwithGlob_term.GApp(g,gl)->g,gl|_->g,[]inleto=matchDAst.getg'with|Glob_term.GRef(r,None)->List.find_opt(fun(r',_,_)->GlobRef.equalrr')post|_->NoneinmatchowithNone->g|Some(_,r,a)->letrecfnagl=matcha,glwith|[],[]->[]|ToPostHole::a,gl->lete=Evar_kinds.ImplicitArg(r,(n,None),true)inleth=DAst.make?loc(Glob_term.GHole(e,Namegen.IntroAnonymous,None))inh::f(n+1)agl|(ToPostCopy|ToPostCheck_)::a,g::gl->g::f(n+1)agl|ToPostAsc::a,g::gl->postprocesstoken_kind?loctyto_postto_post.(c)g::f(n+1)agl|[],_::_|_::_,[]->no_such_prim_tokentoken_kind?loctyinletgl=f1aglinletg=DAst.make?loc(Glob_term.GRef(r,None))inDAst.make?loc(Glob_term.GApp(g,gl))letglob_of_constrtoken_kindty?locenvsigmato_postc=letg=glob_of_constrtoken_kind?locenvsigmacinmatchto_postwith[||]->g|_->postprocesstoken_kind?loctyto_postto_post.(0)gletinterp_optionuninterpreted_token_kindtoken_kindty?locenvsigmato_postc=matchConstr.kindcwith|App(_Some,[|_;c|])->glob_of_constrtoken_kindty?locenvsigmato_postc|App(_None,[|_|])->no_such_prim_tokenuninterpreted_token_kind?locty|x->Loc.raise?loc(PrimTokenNotationError(token_kind,env,sigma,UnexpectedNonOptionTermc))letuninterp_optionc=matchConstr.kindcwith|App(_Some,[|_;x|])->x|_->raiseNotAValidPrimTokenletuninterpto_rawon=letenv=Global.env()inletsigma=Evd.from_envenvinletsigma,of_ty=Evd.fresh_globalenvsigmao.of_tyinletof_ty=EConstr.Unsafe.to_constrof_tyintryletsigma,n=constr_of_globo.to_postenvsigmaninletc=eval_constr_appenvsigmaof_tyninletc=ifsndo.of_kind==Directthencelseuninterp_optioncinSome(to_raw(fsto.of_kind,c))with|Type_errors.TypeError_|Pretype_errors.PretypeError_->None(* cf. eval_constr_app *)|NotAValidPrimToken->None(* all other functions except NumTok.Signed.of_bigint *)endletz_two=Z.of_int2(** Conversion from bigint to int63 *)letint63_of_pos_biginti=Uint63.of_int64(Z.to_int64i)moduleNumbers=struct(** * Number notation *)openPrimTokenNotationletwarn_large_num=CWarnings.create~name:"large-number"~category:"numbers"(funty->strbrk"Stack overflow or segmentation fault happens when "++strbrk"working with large numbers in "++pr_qualidty++strbrk" (threshold may vary depending"++strbrk" on your system limits and on the command executed).")letwarn_abstract_large_num=CWarnings.create~name:"abstract-large-number"~category:"numbers"(fun(ty,f)->strbrk"To avoid stack overflow, large numbers in "++pr_qualidty++strbrk" are interpreted as applications of "++Nametab.pr_global_env(Termops.vars_of_env(Global.env()))f++strbrk".")(***********************************************************************)(** ** Conversion between Coq [Decimal.int] and internal raw string *)(** Decimal.Nil has index 1, then Decimal.D0 has index 2 .. Decimal.D9 is 11 *)letdigit_of_charc=assert('0'<=c&&c<='9'||'a'<=c&&c<='f');ifc<='9'thenChar.codec-Char.code'0'+2elseChar.codec-Char.code'a'+12letchar_of_digitn=assert(2<=n&&n<=17);ifn<=11thenChar.chr(n-2+Char.code'0')elseChar.chr(n-12+Char.code'a')letcoquint_of_rawnumindscn=letuint=matchcwithCDec->inds.dec_uint|CHex->inds.hex_uintinletnil=mkConstruct(uint,1)inmatchnwithNone->nil|Somen->letstr=NumTok.UnsignedNat.to_stringninletstr=matchcwith|CDec->str|CHex->String.substr2(String.lengthstr-2)in(* cut "0x" *)letrecdo_charssiacc=ifi<0thenaccelseletdg=mkConstruct(uint,digit_of_chars.[i])indo_charss(i-1)(mkApp(dg,[|acc|]))indo_charsstr(String.lengthstr-1)nilletcoqint_of_rawnumindsc(sign,n)=letind=matchcwithCDec->inds.dec_int|CHex->inds.hex_intinletuint=coquint_of_rawnumindsc(Somen)inletpos_neg=matchsignwithSPlus->1|SMinus->2inmkApp(mkConstruct(ind,pos_neg),[|uint|])letcoqnumber_of_rawnumindscn=letind=matchcwithCDec->inds.decimal|CHex->inds.hexadecimalinleti,f,e=NumTok.Signed.to_int_frac_and_exponentninleti=coqint_of_rawnuminds.intciinletf=coquint_of_rawnuminds.intcfinmatchewith|None->mkApp(mkConstruct(ind,1),[|i;f|])(* (D|Hexad)ecimal *)|Somee->lete=coqint_of_rawnuminds.intCDeceinmkApp(mkConstruct(ind,2),[|i;f;e|])(* (D|Hexad)ecimalExp *)letmkDecHexindcn=matchcwith|CDec->mkApp(mkConstruct(ind,1),[|n|])(* (UInt|Int|)Decimal *)|CHex->mkApp(mkConstruct(ind,2),[|n|])(* (UInt|Int|)Hexadecimal *)letcoqnumber_of_rawnumindsn=letc=NumTok.Signed.classifyninletn=coqnumber_of_rawnumindscninmkDecHexinds.numbercnletcoquint_of_rawnumindsn=letc=NumTok.UnsignedNat.classifyninletn=coquint_of_rawnumindsc(Somen)inmkDecHexinds.uintcnletcoqint_of_rawnumindsn=letc=NumTok.SignedNat.classifyninletn=coqint_of_rawnumindscninmkDecHexinds.intcnletrawnum_of_coquintclc=letrecof_uint_loopcbuf=matchConstr.kindcwith|Construct((_,1),_)(* Nil *)->()|App(c,[|a|])->(matchConstr.kindcwith|Construct((_,n),_)(* D0 to Df *)->let()=Buffer.add_charbuf(char_of_digitn)inof_uint_loopabuf|_->raiseNotAValidPrimToken)|_->raiseNotAValidPrimTokeninletbuf=Buffer.create64inifcl=CHexthen(Buffer.add_charbuf'0';Buffer.add_charbuf'x');let()=of_uint_loopcbufinifInt.equal(Buffer.lengthbuf)(matchclwithCDec->0|CHex->2)then(* To avoid ambiguities between Nil and (D0 Nil), we choose
to not display Nil alone as "0" *)raiseNotAValidPrimTokenelseNumTok.UnsignedNat.of_string(Buffer.contentsbuf)letrawnum_of_coqintclc=matchConstr.kindcwith|App(c,[|c'|])->(matchConstr.kindcwith|Construct((_,1),_)(* Pos *)->(SPlus,rawnum_of_coquintclc')|Construct((_,2),_)(* Neg *)->(SMinus,rawnum_of_coquintclc')|_->raiseNotAValidPrimToken)|_->raiseNotAValidPrimTokenletrawnum_of_coqnumberclc=letof_ifeife=letn=rawnum_of_coqintcliinletf=trySome(rawnum_of_coquintclf)withNotAValidPrimToken->Noneinlete=matchewithNone->None|Somee->Some(rawnum_of_coqintCDece)inNumTok.Signed.of_int_frac_and_exponentnfeinmatchConstr.kindcwith|App(_,[|i;f|])->of_ifeifNone|App(_,[|i;f;e|])->of_ifeif(Somee)|_->raiseNotAValidPrimTokenletdestDecHexc=matchConstr.kindcwith|App(c,[|c'|])->(matchConstr.kindcwith|Construct((_,1),_)(* (UInt|Int|)Decimal *)->CDec,c'|Construct((_,2),_)(* (UInt|Int|)Hexadecimal *)->CHex,c'|_->raiseNotAValidPrimToken)|_->raiseNotAValidPrimTokenletrawnum_of_coqnumberc=letcl,c=destDecHexcinrawnum_of_coqnumberclcletrawnum_of_coquintc=letcl,c=destDecHexcinrawnum_of_coquintclcletrawnum_of_coqintc=letcl,c=destDecHexcinrawnum_of_coqintclc(***********************************************************************)(** ** Conversion between Coq [Z] and internal bigint *)(** First, [positive] from/to bigint *)letrecpos_of_bigintpostyn=matchZ.div_remnz_twowith|(q,rem)whenrem=Z.zero->letc=mkConstruct(posty,2)in(* xO *)mkApp(c,[|pos_of_bigintpostyq|])|(q,_)whennot(Z.equalqZ.zero)->letc=mkConstruct(posty,1)in(* xI *)mkApp(c,[|pos_of_bigintpostyq|])|(q,_)->mkConstruct(posty,3)(* xH *)letrecbigint_of_posc=matchConstr.kindcwith|Construct((_,3),_)->(* xH *)Z.one|App(c,[|d|])->beginmatchConstr.kindcwith|Construct((_,n),_)->beginmatchnwith|1->(* xI *)Z.addZ.one(Z.mulz_two(bigint_of_posd))|2->(* xO *)Z.mulz_two(bigint_of_posd)|n->assertfalse(* no other constructor of type positive *)end|x->raiseNotAValidPrimTokenend|x->raiseNotAValidPrimToken(** Now, [Z] from/to bigint *)letz_of_bigint{z_ty;pos_ty}n=ifZ.(equalnzero)thenmkConstruct(z_ty,1)(* Z0 *)elselet(s,n)=ifZ.(leqzeron)then(2,n)(* Zpos *)else(3,Z.negn)(* Zneg *)inletc=mkConstruct(z_ty,s)inmkApp(c,[|pos_of_bigintpos_tyn|])letbigint_of_zz=matchConstr.kindzwith|Construct((_,1),_)->(* Z0 *)Z.zero|App(c,[|d|])->beginmatchConstr.kindcwith|Construct((_,n),_)->beginmatchnwith|2->(* Zpos *)bigint_of_posd|3->(* Zneg *)Z.neg(bigint_of_posd)|n->assertfalse(* no other constructor of type Z *)end|_->raiseNotAValidPrimTokenend|_->raiseNotAValidPrimToken(** Now, [Int63] from/to bigint *)letint63_of_pos_bigint?locn=leti=int63_of_pos_bigintninmkIntileterror_overflow?locn=CErrors.user_err?loc~hdr:"interp_int63"Pp.(str"overflow in int63 literal: "++str(Z.to_stringn))letcoqpos_neg_int63_of_bigint?locind(sign,n)=letuint=int63_of_pos_bigint?locninletpos_neg=matchsignwithSPlus->1|SMinus->2inmkApp(mkConstruct(ind,pos_neg),[|uint|])letinterp_int63?locindn=letsign=ifZ.(comparenzero>=0)thenSPluselseSMinusinletan=Z.absninifZ.(ltan(powz_two63))thencoqpos_neg_int63_of_bigint?locind(sign,an)elseerror_overflow?locnletbigint_of_int63c=matchConstr.kindcwith|Inti->Z.of_int64(Uint63.to_int64i)|_->raiseNotAValidPrimTokenletbigint_of_coqpos_neg_int63c=matchConstr.kindcwith|App(c,[|c'|])->(matchConstr.kindcwith|Construct((_,1),_)(* Pos *)->bigint_of_int63c'|Construct((_,2),_)(* Neg *)->Z.neg(bigint_of_int63c')|_->raiseNotAValidPrimToken)|_->raiseNotAValidPrimTokenletinterpo?locn=beginmatcho.warning,nwith|Warningthreshold,nwhenNumTok.Signed.is_bigger_int_thannthreshold->warn_large_numo.ty_name|_->()end;letc=matchfsto.to_kind,NumTok.Signed.to_intnwith|Intint_ty,Somen->coqint_of_rawnumint_tyn|UIntint_ty,Some(SPlus,n)->coquint_of_rawnumint_tyn|Zz_pos_ty,Somen->z_of_bigintz_pos_ty(NumTok.SignedNat.to_bigintn)|Int63pos_neg_int63_ty,Somen->interp_int63?locpos_neg_int63_ty.pos_neg_int63_ty(NumTok.SignedNat.to_bigintn)|(Int_|UInt_|Z_|Int63_),_->no_such_prim_token"number"?loco.ty_name|Numbernumber_ty,_->coqnumber_of_rawnumnumber_tyninletenv=Global.env()inletsigma=Evd.from_envenvinletsigma,to_ty=Evd.fresh_globalenvsigmao.to_tyinletto_ty=EConstr.Unsafe.to_constrto_tyinmatcho.warning,sndo.to_kindwith|Abstractthreshold,DirectwhenNumTok.Signed.is_bigger_int_thannthreshold->warn_abstract_large_num(o.ty_name,o.to_ty);assert(Array.lengtho.to_post=0);glob_of_constr"number"o.ty_name?locenvsigmao.to_post(mkApp(to_ty,[|c|]))|_->letres=eval_constr_appenvsigmato_tycinmatchsndo.to_kindwith|Direct->glob_of_constr"number"o.ty_name?locenvsigmao.to_postres|Option->interp_option"number""number"o.ty_name?locenvsigmao.to_postresletuninterpon=PrimTokenNotation.uninterpbeginfunction|(Int_,c)->NumTok.Signed.of_int(rawnum_of_coqintc)|(UInt_,c)->NumTok.Signed.of_nat(rawnum_of_coquintc)|(Z_,c)->NumTok.Signed.of_bigintCDec(bigint_of_zc)|(Int63_,c)->NumTok.Signed.of_bigintCDec(bigint_of_coqpos_neg_int63c)|(Number_,c)->rawnum_of_coqnumbercendonendmoduleStrings=struct(** * String notation *)openPrimTokenNotationletqualid_of_refn=n|>Coqlib.lib_ref|>Nametab.shortest_qualid_of_globalId.Set.emptyletq_list()=qualid_of_ref"core.list.type"letq_byte()=qualid_of_ref"core.byte.type"letunsafe_locate_indq=matchNametab.locateqwith|GlobRef.IndRefi->i|_->raiseNot_foundletlocate_list()=unsafe_locate_ind(q_list())letlocate_byte()=unsafe_locate_ind(q_byte())(***********************************************************************)(** ** Conversion between Coq [list Byte.byte] and internal raw string *)letcoqbyte_of_char_codebytec=mkConstruct(byte,1+c)letcoqbyte_of_string?locbytes=letp=ifInt.equal(String.lengths)1thenint_of_chars.[0]elseletn=ifInt.equal(String.lengths)3&&is_digits.[0]&&is_digits.[1]&&is_digits.[2]thenint_of_stringselse256inifn<256thennelseuser_err?loc~hdr:"coqbyte_of_string"(str"Expects a single character or a three-digit ASCII code.")incoqbyte_of_char_codebytepletcoqbyte_of_charbytec=coqbyte_of_char_codebyte(Char.codec)letmake_ascii_stringn=ifn>=32&&n<=126thenString.make1(char_of_intn)elsePrintf.sprintf"%03d"nletchar_code_of_coqbytec=matchConstr.kindcwith|Construct((_,c),_)->c-1|_->raiseNotAValidPrimTokenletstring_of_coqbytec=make_ascii_string(char_code_of_coqbytec)letcoqlist_byte_of_stringbyte_tylist_tystr=letcbyte=mkIndbyte_tyinletnil=mkApp(mkConstruct(list_ty,1),[|cbyte|])inletconsxxs=mkApp(mkConstruct(list_ty,2),[|cbyte;x;xs|])inletrecdo_charssiacc=ifi<0thenaccelseletb=coqbyte_of_charbyte_tys.[i]indo_charss(i-1)(consbacc)indo_charsstr(String.lengthstr-1)nil(* N.B. We rely on the fact that [nil] is the first constructor and [cons] is the second constructor, for [list] *)letstring_of_coqlist_bytec=letrecof_coqlist_byte_loopcbuf=matchConstr.kindcwith|App(_nil,[|_ty|])->()|App(_cons,[|_ty;b;c|])->let()=Buffer.add_charbuf(Char.chr(char_code_of_coqbyteb))inof_coqlist_byte_loopcbuf|_->raiseNotAValidPrimTokeninletbuf=Buffer.create64inlet()=of_coqlist_byte_loopcbufinBuffer.contentsbufletinterpo?locn=letbyte_ty=locate_byte()inletlist_ty=locate_list()inletc=matchfsto.to_kindwith|ListByte->coqlist_byte_of_stringbyte_tylist_tyn|Byte->coqbyte_of_string?locbyte_tyninletenv=Global.env()inletsigma=Evd.from_envenvinletsigma,to_ty=Evd.fresh_globalenvsigmao.to_tyinletto_ty=EConstr.Unsafe.to_constrto_tyinletres=eval_constr_appenvsigmato_tycinmatchsndo.to_kindwith|Direct->glob_of_constr"string"o.ty_name?locenvsigmao.to_postres|Option->interp_option"string""string"o.ty_name?locenvsigmao.to_postresletuninterpon=PrimTokenNotation.uninterpbeginfunction|(ListByte,c)->string_of_coqlist_bytec|(Byte,c)->string_of_coqbytecendonend(* A [prim_token_infos], which is synchronized with the document
state, either contains a unique id pointing to an unsynchronized
prim token function, or a number notation object describing how to
interpret and uninterpret. We provide [prim_token_infos] because
we expect plugins to provide their own interpretation functions,
rather than going through number notations, which are available as
a vernacular. *)typeprim_token_interp_info=Uidofprim_token_uid|NumberNotationofnumber_notation_obj|StringNotationofstring_notation_objtypeprim_token_infos={pt_local:bool;(** Is this interpretation local? *)pt_scope:scope_name;(** Concerned scope *)pt_interp_info:prim_token_interp_info;(** Unique id "pointing" to (un)interp functions, OR a number notation object describing (un)interp functions *)pt_required:required_module;(** Module that should be loaded first *)pt_refs:GlobRef.tlist;(** Entry points during uninterpretation *)pt_in_match:bool(** Is this prim token legal in match patterns ? *)}(* Table from scope_name to backtrack-able informations about interpreters
(in particular interpreter unique id). *)letprim_token_interp_infos=ref(String.Map.empty:(required_module*prim_token_interp_info)String.Map.t)(* Table from global_reference to backtrack-able informations about
prim_token uninterpretation (in particular uninterpreter unique id). *)letprim_token_uninterp_infos=ref(GlobRef.Map.empty:((scope_name*(prim_token_interp_info*bool))list)GlobRef.Map.t)lethashtbl_check_and_setallow_overwriteuidfheq=matchHashtbl.findhuidwith|exceptionNot_found->Hashtbl.addhuidf|_whenallow_overwrite->Hashtbl.addhuidf|gwheneqfg->()|_->user_err~hdr:"prim_token_interpreter"(str"Unique identifier "++struid++str" already used to register a number or string (un)interpreter.")letregister_gen_interpretationallow_overwriteuid(interp,uninterp)=hashtbl_check_and_setallow_overwriteuidinterpprim_token_interpretersInnerPrimToken.interp_eq;hashtbl_check_and_setallow_overwriteuiduninterpprim_token_uninterpretersInnerPrimToken.uninterp_eqletregister_rawnumeral_interpretation?(allow_overwrite=false)uid(interp,uninterp)=register_gen_interpretationallow_overwriteuid(InnerPrimToken.RawNumInterpinterp,InnerPrimToken.RawNumUninterpuninterp)letregister_bignumeral_interpretation?(allow_overwrite=false)uid(interp,uninterp)=register_gen_interpretationallow_overwriteuid(InnerPrimToken.BigNumInterpinterp,InnerPrimToken.BigNumUninterpuninterp)letregister_string_interpretation?(allow_overwrite=false)uid(interp,uninterp)=register_gen_interpretationallow_overwriteuid(InnerPrimToken.StringInterpinterp,InnerPrimToken.StringUninterpuninterp)letcache_prim_token_interpretation(_,infos)=letptii=infos.pt_interp_infoinletsc=infos.pt_scopeincheck_scope~tolerant:truesc;prim_token_interp_infos:=String.Map.addsc(infos.pt_required,ptii)!prim_token_interp_infos;letadd_uninterpr=letl=tryGlobRef.Map.findr!prim_token_uninterp_infoswithNot_found->[]inprim_token_uninterp_infos:=GlobRef.Map.addr((sc,(ptii,infos.pt_in_match))::l)!prim_token_uninterp_infosinList.iteradd_uninterpinfos.pt_refsletsubst_prim_token_interpretation(subs,infos)={infoswithpt_refs=List.map(subst_global_referencesubs)infos.pt_refs}letclassify_prim_token_interpretationinfos=ifinfos.pt_localthenDisposeelseSubstituteinfosletopen_prim_token_interpretationio=ifInt.equali1thencache_prim_token_interpretationoletinPrimTokenInterp:prim_token_infos->obj=declare_object{(default_object"PRIM-TOKEN-INTERP")withopen_function=simple_openopen_prim_token_interpretation;cache_function=cache_prim_token_interpretation;subst_function=subst_prim_token_interpretation;classify_function=classify_prim_token_interpretation}letenable_prim_token_interpretationinfos=Lib.add_anonymous_leaf(inPrimTokenInterpinfos)(** Compatibility.
Avoid the next two functions, they will now store unnecessary
objects in the library segment. Instead, combine
[register_*_interpretation] and [enable_prim_token_interpretation]
(the latter inside a [Mltop.declare_cache_obj]).
*)letfresh_string_of=letcount=ref0infunroot->count:=!count+1;(string_of_int!count)^"_"^rootletdeclare_numeral_interpreter?(local=false)scdirinterp(patl,uninterp,b)=letuid=fresh_string_ofscinregister_bignumeral_interpretationuid(interp,uninterp);enable_prim_token_interpretation{pt_local=local;pt_scope=sc;pt_interp_info=Uiduid;pt_required=dir;pt_refs=List.map_filterglob_prim_constr_keypatl;pt_in_match=b}letdeclare_string_interpreter?(local=false)scdirinterp(patl,uninterp,b)=letuid=fresh_string_ofscinregister_string_interpretationuid(interp,uninterp);enable_prim_token_interpretation{pt_local=local;pt_scope=sc;pt_interp_info=Uiduid;pt_required=dir;pt_refs=List.map_filterglob_prim_constr_keypatl;pt_in_match=b}letcheck_required_module?locsc(sp,d)=trylet_=Nametab.global_of_pathspin()withNot_foundasexn->let_,info=Exninfo.captureexninmatchdwith|[]->user_err?loc~info~hdr:"prim_token_interpreter"(str"Cannot interpret in "++strsc++str" because "++pr_pathsp++str" could not be found in the current environment.")|_->user_err?loc~info~hdr:"prim_token_interpreter"(str"Cannot interpret in "++strsc++str" without requiring first module "++str(List.lastd)++str".")(* Look if some notation or number printer in [scope] can be used in
the scope stack [scopes], and if yes, using delimiters or not *)letfind_with_delimiters=function|LastLonelyNotation->None|NotationInScopescope->match(String.Map.findscope!scope_map).delimiterswith|Somekey->Some(Somescope,Somekey)|None->None|exceptionNot_found->Noneletrecfind_without_delimitersfind(ntn_scope,ntn)=function|OpenScopeItemscope::scopes->(* Is the expected ntn/numpr attached to the most recently open scope? *)beginmatchntn_scopewith|NotationInScopescope'whenString.equalscopescope'->Some(None,None)|_->(* If the most recently open scope has a notation/number printer
but not the expected one then we need delimiters *)iffindscopethenfind_with_delimitersntn_scopeelsefind_without_delimitersfind(ntn_scope,ntn)scopesend|LonelyNotationItemntn'::scopes->beginmatchntnwith|Somentn''whennotation_eqntn'ntn''->beginmatchntn_scopewith|LastLonelyNotation->(* If the first notation with same string in the visibility stack
is the one we want to print, then it can be used without
risking a collision *)Some(None,None)|NotationInScope_->(* A lonely notation is liable to hide the scoped notation
to print, we check if the lonely notation is active to
know if the delimiter of the scoped notationis needed *)iffinddefault_scopethenfind_with_delimitersntn_scopeelsefind_without_delimitersfind(ntn_scope,ntn)scopesend|_->(* A lonely notation which does not interfere with the notation to use *)find_without_delimitersfind(ntn_scope,ntn)scopesend|[]->(* Can we switch to [scope]? Yes if it has defined delimiters *)find_with_delimitersntn_scope(* The mapping between notations and their interpretation *)letpr_optional_scope=function|LastLonelyNotation->mt()|NotationInScopescope->spc()++strbrk"in scope"++spc()++strscopeletwarn_notation_overridden=CWarnings.create~name:"notation-overridden"~category:"parsing"(fun(scope,ntn)->str"Notation"++spc()++pr_notationntn++spc()++strbrk"was already used"++pr_optional_scopescope++str".")letwarn_deprecation_overridden=CWarnings.create~name:"notation-overridden"~category:"parsing"(fun((scope,ntn),old,now)->matchold,nowwith|None,None->assertfalse|None,Some_->(str"Notation"++spc()++pr_notationntn++pr_optional_scopescope++spc()++strbrk"is now marked as deprecated"++str".")|Some_,None->(str"Cancelling previous deprecation of notation"++spc()++pr_notationntn++pr_optional_scopescope++str".")|Some_,Some_->(str"Amending deprecation of notation"++spc()++pr_notationntn++pr_optional_scopescope++str"."))typenotation_use=|OnlyPrinting|OnlyParsing|ParsingAndPrintingletwarn_override_if_needed(scopt,ntn)overriddendataold_data=ifoverriddenthenwarn_notation_overridden(scopt,ntn)elseifdata.not_deprecation<>old_data.not_deprecationthenwarn_deprecation_overridden((scopt,ntn),old_data.not_deprecation,data.not_deprecation)letcheck_parsing_override(scopt,ntn)data=function|OnlyParsingData(_,old_data)->letoverridden=not(interpretation_eqdata.not_interpold_data.not_interp)inwarn_override_if_needed(scopt,ntn)overriddendataold_data;None|ParsingAndPrintingData(_,on_printing,old_data)->letoverridden=not(interpretation_eqdata.not_interpold_data.not_interp)inwarn_override_if_needed(scopt,ntn)overriddendataold_data;ifon_printingthenSomeold_data.not_interpelseNone|NoParsingData->Noneletcheck_printing_override(scopt,ntn)dataparsingdataprintingdata=letparsing_update=matchparsingdatawith|OnlyParsingData_|NoParsingData->parsingdata|ParsingAndPrintingData(_,on_printing,old_data)->letoverridden=not(interpretation_eqdata.not_interpold_data.not_interp)inwarn_override_if_needed(scopt,ntn)overriddendataold_data;ifoverriddenthenNoParsingDataelseparsingdatainletexists=List.exists(fun(on_printing,old_data)->letexists=interpretation_eqdata.not_interpold_data.not_interpinifexists&&data.not_deprecation<>old_data.not_deprecationthenwarn_deprecation_overridden((scopt,ntn),old_data.not_deprecation,data.not_deprecation);exists)printingdatainparsing_update,existsletremove_uninterpretationrule(metas,caspat)=let(key,n)=notation_constr_keycinnotations_key_table:=keymap_removekey((rule,pat,n))!notations_key_tableletdeclare_uninterpretation?(also_in_cases_pattern=true)rule(metas,caspat)=let(key,n)=notation_constr_keycinnotations_key_table:=keymap_addkey(also_in_cases_pattern,(rule,pat,n))!notations_key_tableletupdate_notation_data(scopt,ntn)usedatatable=let(parsingdata,printingdata)=tryNotationMap.findntntablewithNot_found->(NoParsingData,[])inmatchusewith|OnlyParsing->letprinting_update=check_parsing_override(scopt,ntn)dataparsingdatainNotationMap.addntn(OnlyParsingData(true,data),printingdata)table,printing_update|ParsingAndPrinting->letprinting_update=check_parsing_override(scopt,ntn)dataparsingdatainNotationMap.addntn(ParsingAndPrintingData(true,true,data),printingdata)table,printing_update|OnlyPrinting->letparsingdata,exists=check_printing_override(scopt,ntn)dataparsingdataprintingdatainletprintingdata=ifexiststhenprintingdataelse(true,data)::printingdatainNotationMap.addntn(parsingdata,printingdata)table,Noneletrecfind_interpretationntnfind=function|[]->raiseNot_found|OpenScopeItemscope::scopes->(tryletn=findscopein(n,Somescope)withNot_found->find_interpretationntnfindscopes)|LonelyNotationItemntn'::scopeswhennotation_eqntn'ntn->(tryletn=finddefault_scopein(n,None)withNot_found->(* e.g. because single notation only for constr, not cases_pattern *)find_interpretationntnfindscopes)|LonelyNotationItem_::scopes->find_interpretationntnfindscopesletfind_notationntnsc=matchfst(NotationMap.findntn(find_scopesc).notations)with|OnlyParsingData(true,data)|ParsingAndPrintingData(true,_,data)->data|_->raiseNot_foundletnotation_of_prim_token=function|Constrexpr.Number(SPlus,n)->InConstrEntry,NumTok.Unsigned.sprintn|Constrexpr.Number(SMinus,n)->InConstrEntry,"- "^NumTok.Unsigned.sprintn|String_->raiseNot_foundletfind_prim_tokencheck_allowed?locpsc=(* Try for a user-defined numerical notation *)tryletn=find_notation(notation_of_prim_tokenp)scinlet(_,c)=n.not_interpinletdf=n.not_locationinletpat=Notation_ops.glob_constr_of_notation_constr?loccincheck_allowedpat;pat,dfwithNot_found->(* Try for a primitive numerical notation *)let(spdir,info)=String.Map.findsc!prim_token_interp_infosincheck_required_module?locscspdir;letinterp=matchinfowith|Uiduid->Hashtbl.findprim_token_interpretersuid|NumberNotationo->InnerPrimToken.RawNumInterp(Numbers.interpo)|StringNotationo->InnerPrimToken.StringInterp(Strings.interpo)inletpat=InnerPrimToken.do_interp?locinterppincheck_allowedpat;pat,((dirpath(fstspdir),DirPath.empty),"")letinterp_prim_token_gen?locgplocal_scopes=letscopes=make_current_scopeslocal_scopesinletp_as_ntn=trynotation_of_prim_tokenpwithNot_found->InConstrEntry,""intrylet(pat,loc),sc=find_interpretationp_as_ntn(find_prim_token?locgp)scopesinpat,(loc,sc)withNot_foundasexn->let_,info=Exninfo.captureexninuser_err?loc~info~hdr:"interp_prim_token"((matchpwith|Number_->str"No interpretation for number "++pr_notation(notation_of_prim_tokenp)|Strings->str"No interpretation for string "++qss)++str".")letinterp_prim_token?loc=interp_prim_token_gen?loc(fun_->())letreccheck_allowed_ref_in_patlooked_for=DAst.(with_val(function|GVar_|GHole_->()|GRef(g,_)->looked_forg|GApp(f,l)->beginmatchDAst.getfwith|GRef(g,_)->looked_forg;List.iter(check_allowed_ref_in_patlooked_for)l|_->raiseNot_foundend|_->raiseNot_found))letinterp_prim_token_cases_pattern_expr?loclooked_forp=interp_prim_token_gen?loc(check_allowed_ref_in_patlooked_for)pletwarn_deprecated_notation=Deprecation.create_warning~object_name:"Notation"~warning_name:"deprecated-notation"pr_notationletinterp_notation?locntnlocal_scopes=letscopes=make_current_scopeslocal_scopesintrylet(n,sc)=find_interpretationntn(find_notationntn)scopesinOption.iter(fund->warn_deprecated_notation?loc(ntn,d))n.not_deprecation;n.not_interp,(n.not_location,sc)withNot_foundasexn->let_,info=Exninfo.captureexninuser_err?loc~info(str"Unknown interpretation for notation "++pr_notationntn++str".")letuninterp_notationsc=List.map_append(funkey->List.mapsnd(keymap_findkey!notations_key_table))(glob_constr_keysc)letfilter_also_for_pattern=List.map_filter(function(true,x)->Somex|_->None)letuninterp_cases_pattern_notationsc=filter_also_for_pattern(keymap_find(cases_pattern_keyc)!notations_key_table)letuninterp_ind_pattern_notationsind=filter_also_for_pattern(keymap_find(RefKey(canonical_gr(GlobRef.IndRefind)))!notations_key_table)lethas_active_parsing_rule_in_scopentnsc=trymatchNotationMap.findntn(String.Map.findsc!scope_map).notationswith|OnlyParsingData(active,_),_|ParsingAndPrintingData(active,_,_),_->active|_->falsewithNot_found->falseletis_printing_active_in_scope(scope,ntn)pat=letsc=matchscopewithNotationInScopesc->sc|LastLonelyNotation->default_scopeinletis_activeextra=trylet(_,(active,_))=List.extract_first(fun(active,d)->interpretation_eqd.not_interppat)extrainactivewithNot_found->falseintrymatchNotationMap.findntn(String.Map.findsc!scope_map).notationswith|ParsingAndPrintingData(_,active,d),extra->ifinterpretation_eqd.not_interppatthenactiveelseis_activeextra|_,extra->is_activeextrawithNot_found->falseletis_printing_inactive_rulerulepat=matchrulewith|NotationRule(scope,ntn)->not(is_printing_active_in_scope(scope,ntn)pat)|SynDefRulekn->trylet_=Nametab.path_of_syndefkninfalsewithNot_found->trueletavailability_of_notation(ntn_scope,ntn)scopes=find_without_delimiters(has_active_parsing_rule_in_scopentn)(ntn_scope,Somentn)(make_current_scopesscopes)(* We support coercions from a custom entry at some level to an entry
at some level (possibly the same), and from and to the constr entry. E.g.:
Notation "[ expr ]" := expr (expr custom group at level 1).
Notation "( x )" := x (in custom group at level 0, x at level 1).
Notation "{ x }" := x (in custom group at level 0, x constr).
Supporting any level is maybe overkill in that coercions are
commonly from the lowest level of the source entry to the highest
level of the target entry. *)typeentry_coercion=(notation_with_optional_scope*notation)listmoduleEntryCoercionOrd=structtypet=notation_entry*notation_entryletcompare=pervasives_compareendmoduleEntryCoercionMap=Map.Make(EntryCoercionOrd)letentry_coercion_map:(((entry_leveloption*entry_leveloption)*entry_coercion)listEntryCoercionMap.t)ref=refEntryCoercionMap.emptyletlevel_ordlevlev'=matchlev,lev'with|None,_->true|_,None->true|Somen,Somen'->n<=n'letrecsearchnfromnto=function|[]->raiseNot_found|((pfrom,pto),coe)::l->iflevel_ordpfromnfrom&&level_ordntoptothencoeelsesearchnfromntolletmake_notation_entry_levelentrylevel=matchentrywith|InConstrEntry->InConstrEntrySomeLevel|InCustomEntrys->InCustomEntryLevel(s,level)letdecompose_notation_entry_level=function|InConstrEntrySomeLevel->InConstrEntry,None|InCustomEntryLevel(s,n)->InCustomEntrys,Somenletavailability_of_entry_coercionentryentry'=letentry,lev=decompose_notation_entry_levelentryinletentry',lev'=decompose_notation_entry_levelentry'inifnotation_entry_eqentryentry'&&level_ordlev'levthenSome[]elsetrySome(searchlevlev'(EntryCoercionMap.find(entry,entry')!entry_coercion_map))withNot_found->Noneletbetter_path((lev1,lev2),path)((lev1',lev2'),path')=(* better = shorter and lower source and higher target *)level_ordlev1lev1'&&level_ordlev2'lev2&&List.lengthpath<=List.lengthpath'letshorter_path(_,path)(_,path')=List.lengthpath<=List.lengthpath'letrecinsert_coercion_pathpath=function|[]->[path]|path'::pathsasallpaths->(* If better or equal we keep the more recent one *)ifbetter_pathpathpath'thenpath::pathselseifbetter_pathpath'paththenallpathselseifshorter_pathpathpath'thenpath::allpathselsepath'::insert_coercion_pathpathpathsletdeclare_entry_coercion(scope,(entry,key))leventry'=letentry',lev'=decompose_notation_entry_levelentry'in(* Transitive closure *)lettoaddleft=EntryCoercionMap.fold(fun(entry'',entry''')pathsl->List.fold_right(fun((lev'',lev'''),path)l->ifnotation_entry_eqentryentry'''&&level_ordlevlev'''&¬(notation_entry_eqentry'entry'')then((entry'',entry'),((lev'',lev'),path@[(scope,(entry,key))]))::lelsel)pathsl)!entry_coercion_map[]inlettoaddright=EntryCoercionMap.fold(fun(entry'',entry''')pathsl->List.fold_right(fun((lev'',lev'''),path)l->ifentry'=entry''&&level_ordlev''lev'&&entry<>entry'''then((entry,entry'''),((lev,lev'''),path@[(scope,(entry,key))]))::lelsel)pathsl)!entry_coercion_map[]inentry_coercion_map:=List.fold_right(fun(pair,path)->letolds=tryEntryCoercionMap.findpair!entry_coercion_mapwithNot_found->[]inEntryCoercionMap.addpair(insert_coercion_pathpatholds))(((entry,entry'),((lev,lev'),[(scope,(entry,key))]))::toaddright@toaddleft)!entry_coercion_mapletentry_has_global_map=refString.Map.emptyletdeclare_custom_entry_has_globalsn=tryletp=String.Map.finds!entry_has_global_mapinuser_err(str"Custom entry "++strs++str" has already a rule for global references at level "++intp++str".")withNot_found->entry_has_global_map:=String.Map.addsn!entry_has_global_mapletentry_has_global=function|InConstrEntrySomeLevel->true|InCustomEntryLevel(s,n)->tryString.Map.finds!entry_has_global_map<=nwithNot_found->falseletentry_has_ident_map=refString.Map.emptyletdeclare_custom_entry_has_identsn=tryletp=String.Map.finds!entry_has_ident_mapinuser_err(str"Custom entry "++strs++str" has already a rule for global references at level "++intp++str".")withNot_found->entry_has_ident_map:=String.Map.addsn!entry_has_ident_mapletentry_has_ident=function|InConstrEntrySomeLevel->true|InCustomEntryLevel(s,n)->tryString.Map.finds!entry_has_ident_map<=nwithNot_found->falsetypeentry_coercion_kind=|IsEntryCoercionofnotation_entry_level|IsEntryGlobalofstring*int|IsEntryIdentofstring*intletdeclare_notation(scopt,ntn)patdf~use~also_in_cases_patterncoedeprecation=(* Register the interpretation *)letscope=matchscoptwithNotationInScopes->s|LastLonelyNotation->default_scopeinletsc=find_scopescopeinletnotdata={not_interp=pat;not_location=df;not_deprecation=deprecation;}inletnotation_update,printing_update=update_notation_data(scopt,ntn)usenotdatasc.notationsinletsc={scwithnotations=notation_update}inscope_map:=String.Map.addscopesc!scope_map;(* Update the uninterpretation cache *)beginmatchprinting_updatewith|Somepat->remove_uninterpretation(NotationRule(scopt,ntn))pat|None->()end;ifuse<>OnlyParsingthendeclare_uninterpretation~also_in_cases_pattern(NotationRule(scopt,ntn))pat;(* Register visibility of lonely notations *)beginmatchscoptwith|LastLonelyNotation->scope_stack:=LonelyNotationItemntn::!scope_stack|NotationInScope_->()end;(* Declare a possible coercion *)beginmatchcoewith|Some(IsEntryCoercionentry)->let(_,level,_)=level_of_notationntninletlevel=matchfstntnwith|InConstrEntry->None|InCustomEntry_->Somelevelindeclare_entry_coercion(scopt,ntn)levelentry|Some(IsEntryGlobal(entry,n))->declare_custom_entry_has_globalentryn|Some(IsEntryIdent(entry,n))->declare_custom_entry_has_idententryn|None->()endletavailability_of_prim_tokennprinter_scopelocal_scopes=letfscope=tryletuid=snd(String.Map.findscope!prim_token_interp_infos)inletopenInnerPrimTokeninmatchn,uidwith|Constrexpr.Number_,NumberNotation_->true|_,NumberNotation_->false|String_,StringNotation_->true|_,StringNotation_->false|_,Uiduid->letinterp=Hashtbl.findprim_token_interpretersuidinmatchn,interpwith|Constrexpr.Number_,(RawNumInterp_|BigNumInterp_)->true|String_,StringInterp_->true|_->falsewithNot_found->falseinletscopes=make_current_scopeslocal_scopesinOption.mapsnd(find_without_delimitersf(NotationInScopeprinter_scope,None)scopes)letrecfind_uninterpretationneed_delimdeffind=function|[]->List.find_map(fun(sc,_,_)->trySome(findneed_delimsc)withNot_found->None)def|OpenScopeItemscope::scopes->(tryfindneed_delimscopewithNot_found->find_uninterpretationneed_delimdeffindscopes)(* TODO: here we should also update the need_delim list with all regular notations in scope [scope] that could shadow a number notation *)|LonelyNotationItemntn::scopes->find_uninterpretation(ntn::need_delim)deffindscopesletuninterp_prim_tokenclocal_scopes=matchglob_prim_constr_keycwith|None->raiseNotation_ops.No_match|Somer->letuninterp(sc,(info,_))=tryletuninterp=matchinfowith|Uiduid->Hashtbl.findprim_token_uninterpretersuid|NumberNotationo->InnerPrimToken.RawNumUninterp(Numbers.uninterpo)|StringNotationo->InnerPrimToken.StringUninterp(Strings.uninterpo)inmatchInnerPrimToken.do_uninterpuninterp(AnyGlobConstrc)with|None->None|Somen->Some(sc,n)withNot_found->Noneinletadd_key(sc,n)=Option.map(funk->sc,n,k)(availability_of_prim_tokennsclocal_scopes)inletl=tryGlobRef.Map.findr!prim_token_uninterp_infoswithNot_found->raiseNotation_ops.No_matchinletl=List.map_filteruninterplinletl=List.map_filteradd_keylinletfindneed_delimsc=let_,n,k=List.find(fun(sc',_,_)->String.equalsc'sc)linifk<>Nonethenn,kelselethidden=List.exists(funn'->notation_eqn'(notation_of_prim_tokenn))need_deliminifnothiddenthenn,kelsematch(String.Map.findsc!scope_map).delimiterswith|Somek->n,Somek|None->raiseNot_foundinletscopes=make_current_scopeslocal_scopesintryfind_uninterpretation[]lfindscopeswithNot_found->matchlwith(_,n,k)::_->n,k|[]->raiseNotation_ops.No_matchletuninterp_prim_token_cases_patternclocal_scopes=matchglob_constr_of_closed_cases_pattern(Global.env())cwith|exceptionNot_found->raiseNotation_ops.No_match|na,c->let(sc,n)=uninterp_prim_tokenclocal_scopesin(na,sc,n)(* Miscellaneous *)letisNVar_or_NHole=functionNVar_|NHole_->true|_->false(**********************************************************************)(* Mapping classes to scopes *)openCoercionopstypescope_class=cl_typletscope_class_compare:scope_class->scope_class->int=cl_typ_ordletcompute_scope_classenvsigmat=let(cl,_,_)=find_class_typeenvsigmatinclmoduleScopeClassOrd=structtypet=scope_classletcompare=scope_class_compareendmoduleScopeClassMap=Map.Make(ScopeClassOrd)letinitial_scope_class_map:scope_nameScopeClassMap.t=ScopeClassMap.emptyletscope_class_map=refinitial_scope_class_mapletdeclare_scope_classsccl=scope_class_map:=ScopeClassMap.addclsc!scope_class_mapletfind_scope_classcl=ScopeClassMap.findcl!scope_class_mapletfind_scope_class_opt=function|None->None|Somecl->trySome(find_scope_classcl)withNot_found->None(**********************************************************************)(* Special scopes associated to arguments of a global reference *)letreccompute_arguments_classesenvsigmat=matchEConstr.kindsigma(Reductionops.whd_betaiotazetaenvsigmat)with|Prod(na,t,u)->letenv=EConstr.push_rel(Context.Rel.Declaration.LocalAssum(na,t))envinletcl=trySome(compute_scope_classenvsigmat)withNot_found->Noneincl::compute_arguments_classesenvsigmau|_->[]letcompute_arguments_scope_fullenvsigmat=letcls=compute_arguments_classesenvsigmatinletscs=List.mapfind_scope_class_optclsinscs,clsletcompute_arguments_scopeenvsigmat=fst(compute_arguments_scope_fullenvsigmat)letcompute_type_scopeenvsigmat=find_scope_class_opt(trySome(compute_scope_classenvsigmat)withNot_found->None)letcurrent_type_scope_name()=find_scope_class_opt(SomeCL_SORT)letscope_class_of_class(x:cl_typ):scope_class=x(** Updating a scope list, thanks to a list of argument classes
and the current Bind Scope base. When some current scope
have been manually given, the corresponding argument class
is emptied below, so this manual scope will be preserved. *)letupdate_scopeclsco=matchfind_scope_class_optclwith|None->sco|sco'->sco'letrecupdate_scopesclsscl=matchcls,sclwith|[],_->scl|_,[]->List.mapfind_scope_class_optcls|cl::cls,sco::scl->update_scopeclsco::update_scopesclssclletarguments_scope=refGlobRef.Map.emptytypearguments_scope_discharge_request=|ArgsScopeAuto|ArgsScopeManual|ArgsScopeNoDischargeletload_arguments_scope_(_,(_,r,n,scl,cls))=List.iter(Option.itercheck_scope)scl;letinitial_stamp=ScopeClassMap.emptyinarguments_scope:=GlobRef.Map.addr(scl,cls,initial_stamp)!arguments_scopeletcache_arguments_scopeo=load_arguments_scope1oletsubst_scope_classenvsubstcs=trySome(subst_cl_typenvsubstcs)withNot_found->Noneletsubst_arguments_scope(subst,(req,r,n,scl,cls))=letr'=fst(subst_globalsubstr)inletsubst_clocl=matchoclwith|None->ocl|Somecl->letenv=Global.env()inmatchsubst_scope_classenvsubstclwith|Somecl'asocl'whencl'!=cl->ocl'|_->oclinletcls'=List.Smart.mapsubst_clclsin(ArgsScopeNoDischarge,r',n,scl,cls')letdischarge_arguments_scope(_,(req,r,n,l,_))=ifreq==ArgsScopeNoDischarge||(isVarRefr&&Lib.is_in_sectionr)thenNoneelseletn=tryletvars=Lib.variable_section_segment_of_referencerinvars|>List.filteris_local_assum|>List.lengthwithNot_found(* Not a ref defined in this section *)->0inSome(req,r,n,l,[])letclassify_arguments_scope(req,_,_,_,_asobj)=ifreq==ArgsScopeNoDischargethenDisposeelseSubstituteobjletrebuild_arguments_scopesigma(req,r,n,l,_)=matchreqwith|ArgsScopeNoDischarge->assertfalse|ArgsScopeAuto->letenv=Global.env()in(*FIXME?*)lettyp=EConstr.of_constr@@fst(Typeops.type_of_global_in_contextenvr)inletscs,cls=compute_arguments_scope_fullenvsigmatypin(req,r,List.lengthscs,scs,cls)|ArgsScopeManual->(* Add to the manually given scopes the one found automatically
for the extra parameters of the section. Discard the classes
of the manually given scopes to avoid further re-computations. *)letenv=Global.env()in(*FIXME?*)lettyp=EConstr.of_constr@@fst(Typeops.type_of_global_in_contextenvr)inletl',cls=compute_arguments_scope_fullenvsigmatypinletl1=List.firstnnl'inletcls1=List.firstnnclsin(req,r,0,l1@l,cls1)typearguments_scope_obj=arguments_scope_discharge_request*GlobRef.t*(* Used to communicate information from discharge to rebuild *)(* set to 0 otherwise *)int*scope_nameoptionlist*scope_classoptionlistletinArgumentsScope:arguments_scope_obj->obj=declare_object{(default_object"ARGUMENTS-SCOPE")withcache_function=cache_arguments_scope;load_function=load_arguments_scope;subst_function=subst_arguments_scope;classify_function=classify_arguments_scope;discharge_function=discharge_arguments_scope;(* XXX: Should we pass the sigma here or not, see @herbelin's comment in 6511 *)rebuild_function=rebuild_arguments_scopeEvd.empty}letis_locallocalref=local||isVarRefref&&Lib.is_in_sectionrefletdeclare_arguments_scope_genreqrn(scl,cls)=Lib.add_anonymous_leaf(inArgumentsScope(req,r,n,scl,cls))letdeclare_arguments_scopelocalrscl=letreq=ifis_locallocalrthenArgsScopeNoDischargeelseArgsScopeManualin(* We empty the list of argument classes to disable further scope
re-computations and keep these manually given scopes. *)declare_arguments_scope_genreqr0(scl,[])letfind_arguments_scoper=trylet(scl,cls,stamp)=GlobRef.Map.findr!arguments_scopeinletcur_stamp=!scope_class_mapinifstamp==cur_stampthensclelse(* Recent changes in the Bind Scope base, we re-compute the scopes *)letscl'=update_scopesclssclinarguments_scope:=GlobRef.Map.addr(scl',cls,cur_stamp)!arguments_scope;scl'withNot_found->[]letdeclare_ref_arguments_scopesigmaref=letenv=Global.env()in(* FIXME? *)lettyp=EConstr.of_constr@@fst@@Typeops.type_of_global_in_contextenvrefinlet(scs,clsaso)=compute_arguments_scope_fullenvsigmatypindeclare_arguments_scope_genArgsScopeAutoref(List.lengthscs)o(********************************)(* Encoding notations as string *)typesymbol=|Terminalofstring|NonTerminalofId.t|SProdListofId.t*symbollist|Breakofintletrecsymbol_eqs1s2=matchs1,s2with|Terminals1,Terminals2->String.equals1s2|NonTerminalid1,NonTerminalid2->Id.equalid1id2|SProdList(id1,l1),SProdList(id2,l2)->Id.equalid1id2&&List.equalsymbol_eql1l2|Breaki1,Breaki2->Int.equali1i2|_->falseletrecstring_of_symbol=function|NonTerminal_->["_"]|Terminal"_"->["'_'"]|Terminals->[s]|SProdList(_,l)->letl=List.flatten(List.mapstring_of_symboll)in"_"::l@".."::l@["_"]|Break_->[]letmake_notation_keyfromsymbols=(from,String.concat" "(List.flatten(List.mapstring_of_symbolsymbols)))letdecompose_notation_key(from,s)=letlen=String.lengthsinletrecdecomp_ntndirsn=ifn>=lenthenList.revdirselseletpos=tryString.index_fromsn' 'withNot_found->leninlettok=matchString.subsn(pos-n)with|"_"->NonTerminal(Id.of_string"_")|s->Terminal(String.drop_simple_quotess)indecomp_ntn(tok::dirs)(pos+1)infrom,decomp_ntn[]0(************)(* Printing *)letpr_delimiters_info=function|None->str"No delimiting key"|Somekey->str"Delimiting key is "++strkeyletclasses_of_scopesc=ScopeClassMap.fold(funclsc'l->ifString.equalscsc'thencl::lelsel)!scope_class_map[]letpr_scope_class=pr_classletpr_scope_classessc=letl=classes_of_scopescinmatchlwith|[]->mt()|_::ll->letopt_s=matchllwith[]->mt()|_->str"es"inhov0(str"Bound to class"++opt_s++spc()++prlist_with_sepspcpr_scope_classl)letpr_notation_infoprglobntnc=str"\""++strntn++str"\" :="++brk(1,2)++prglob(Notation_ops.glob_constr_of_notation_constrc)letpr_notation_statuson_parsingon_printing=letdeactivatedb=ifbthen[]else["deactivated"]inletl=matchon_parsing,on_printingwith|Someon,None->"only parsing"::deactivatedon|None,Someon->"only printing"::deactivatedon|Somefalse,Somefalse->["deactivated"]|Sometrue,Somefalse->["deactivated for printing"]|Somefalse,Sometrue->["deactivated for parsing"]|Sometrue,Sometrue->[]|None,None->assertfalseinmatchlwith|[]->mt()|l->str"("++prlist_with_seppr_commastrl++str")"letpr_non_emptyspcpp=ifpp=mt()thenmt()elsespc++ppletpr_notation_dataprglob(on_parsing,on_printing,{not_interp=(_,r);not_location=(_,df)})=hov0(pr_notation_infoprglobdfr++pr_non_empty(brk(1,2))(pr_notation_statuson_parsingon_printing))letextract_notation_data(main,extra)=letmain=matchmainwith|NoParsingData->[]|ParsingAndPrintingData(on_parsing,on_printing,d)->[Someon_parsing,Someon_printing,d]|OnlyParsingData(on_parsing,d)->[Someon_parsing,None,d]inletextra=List.map(fun(on_printing,d)->(None,Someon_printing,d))extrainmain@extraletpr_named_scopeprglob(scope,sc)=(ifString.equalscopedefault_scopethenmatchNotationMap.cardinalsc.notationswith|0->str"No lonely notation"|n->str"Lonely notation"++(ifInt.equaln1thenmt()elsestr"s")elsestr"Scope "++strscope++fnl()++pr_delimiters_infosc.delimiters)++pr_non_empty(fnl())(pr_scope_classesscope)++prlist(funa->fnl()++pr_notation_dataprgloba)(NotationMap.fold(funntndatal->extract_notation_datadata@l)sc.notations[])letpr_scopeprglobscope=pr_named_scopeprglob(scope,find_scopescope)letpr_scopesprglob=letl=String.Map.bindings!scope_mapinprlist_with_sep(fun()->fnl()++fnl())(pr_named_scopeprglob)lletrecfind_defaultntn=function|[]->None|OpenScopeItemscope::scopes->ifhas_active_parsing_rule_in_scopentnscopethenSomescopeelsefind_defaultntnscopes|LonelyNotationItemntn'::scopes->ifnotation_eqntnntn'thenSomedefault_scopeelsefind_defaultntnscopesletfactorize_entries=function|[]->[]|(ntn,sc',c)::l->let(ntn,l_of_ntn,rest)=List.fold_left(fun(a',l,rest)(a,sc,c)->ifnotation_eqaa'then(a',(sc,c)::l,rest)else(a,[sc,c],(a',l)::rest))(ntn,[sc',c],[])lin(ntn,l_of_ntn)::resttypesymbol_token=WhiteSpaceofint|Stringofstringletsplit_notation_stringstr=letpush_tokenbegil=ifInt.equalbegithenlelselets=String.substrbeg(i-beg)inStrings::linletpush_whitespacebegil=ifInt.equalbegithenlelseWhiteSpace(i-beg)::linletrecloopbegi=ifi<String.lengthstrthenifstr.[i]==' 'thenpush_tokenbegi(loop_on_whitespace(i+1)(i+1))elseloopbeg(i+1)elsepush_tokenbegi[]andloop_on_whitespacebegi=ifi<String.lengthstrthenifstr.[i]!=' 'thenpush_whitespacebegi(loopi(i+1))elseloop_on_whitespacebeg(i+1)elsepush_whitespacebegi[]inloop00letrecraw_analyze_notation_tokens=function|[]->[]|String".."::sl->NonTerminalNotation_ops.ldots_var::raw_analyze_notation_tokenssl|String"_"::_->user_errPp.(str"_ must be quoted.")|Stringx::slwhenId.is_validx->NonTerminal(Names.Id.of_stringx)::raw_analyze_notation_tokenssl|Strings::sl->Terminal(String.drop_simple_quotess)::raw_analyze_notation_tokenssl|WhiteSpacen::sl->Breakn::raw_analyze_notation_tokensslletrecraw_analyze_anonymous_notation_tokens=function|[]->[]|String".."::sl->NonTerminalNotation_ops.ldots_var::raw_analyze_anonymous_notation_tokenssl|String"_"::sl->NonTerminal(Id.of_string"dummy")::raw_analyze_anonymous_notation_tokenssl|Strings::sl->Terminal(String.drop_simple_quotess)::raw_analyze_anonymous_notation_tokenssl|WhiteSpacen::sl->raw_analyze_anonymous_notation_tokenssl(* Interpret notations with a recursive component *)letout_nt=functionNonTerminalx->x|_->assertfalseletmsg_expected_form_of_recursive_notation="In the notation, the special symbol \"..\" must occur in\na configuration of the form \"x symbs .. symbs y\"."letrecfind_patternntxl=function|Breaknasx::l,Breakn'::l'whenInt.equalnn'->find_patternnt(x::xl)(l,l')|Terminalsasx::l,Terminals'::l'whenString.equalss'->find_patternnt(x::xl)(l,l')|[],NonTerminalx'::l'->(out_ntnt,x',List.revxl),l'|_,Breaks::_|Breaks::_,_->user_errPp.(str("A break occurs on one side of \"..\" but not on the other side."))|_,Terminals::_|Terminals::_,_->user_err~hdr:"Metasyntax.find_pattern"(str"The token \""++strs++str"\" occurs on one side of \"..\" but not on the other side.")|_,[]->user_errPp.(strmsg_expected_form_of_recursive_notation)|((SProdList_|NonTerminal_)::_),_|_,(SProdList_::_)->anomaly(Pp.str"Only Terminal or Break expected on left, non-SProdList on right.")letrecinterp_list_parserhd=function|[]->[],List.revhd|NonTerminalid::tlwhenId.equalidNotation_ops.ldots_var->ifList.is_emptyhdthenuser_errPp.(strmsg_expected_form_of_recursive_notation);lethd=List.revhdinlet((x,y,sl),tl')=find_pattern(List.hdhd)[](List.tlhd,tl)inletxyl,tl''=interp_list_parser[]tl'in(* We remember each pair of variable denoting a recursive part to *)(* remove the second copy of it afterwards *)(x,y)::xyl,SProdList(x,sl)::tl''|(Terminal_|Break_)ass::tl->ifList.is_emptyhdthenletyl,tl'=interp_list_parser[]tlinyl,s::tl'elseinterp_list_parser(s::hd)tl|NonTerminal_asx::tl->letxyl,tl'=interp_list_parser[x]tlinxyl,List.rev_appendhdtl'|SProdList_::_->anomaly(Pp.str"Unexpected SProdList in interp_list_parser.")letget_notation_varsl=List.map_filter(functionNonTerminalid|SProdList(id,_)->Someid|_->None)lletdecompose_raw_notationntn=letl=split_notation_stringntninletl=raw_analyze_notation_tokenslinletrecvars,l=interp_list_parser[]linletvars=get_notation_varslinrecvars,vars,lletinterpret_notation_stringntn=(* We collect the possible interpretations of a notation string depending on whether it is
in "x 'U' y" or "_ U _" format *)lettoks=split_notation_stringntninlettoks=ifList.exists(functionString"_"->true|_->false)toks||List.for_all(functionStringid->Id.is_validid|_->false)toksthen(* Only "_ U _" format *)raw_analyze_anonymous_notation_tokenstokselse(* Includes the case of only a subset of tokens or an "x 'U' y"-style format *)raw_analyze_notation_tokenstoksinlet_,toks=interp_list_parser[]toksinlet_,ntn'=make_notation_keyNonetoksinntn'(* Tell if a non-recursive notation is an instance of a recursive one *)letis_approximationntnntn'=letrecauxtoks1toks2=match(toks1,toks2)with|Terminals1::toks1,Terminals2::toks2->String.equals1s2&&auxtoks1toks2|NonTerminal_::toks1,NonTerminal_::toks2->auxtoks1toks2|SProdList(_,l1)::toks1,SProdList(_,l2)::toks2->auxl1l2&&auxtoks1toks2|NonTerminal_::toks1,SProdList(_,l2)::toks2->aux'toks1l2l2toks2||auxtoks1toks2|[],[]->true|(Break_::_,_)|(_,Break_::_)->assertfalse|(Terminal_|NonTerminal_|SProdList_)::_,_->false|[],_->falseandaux'toks1l2l2fulltoks2=match(toks1,l2)with|Terminals1::toks1,Terminals2::l2whenString.equals1s2->aux'toks1l2l2fulltoks2|NonTerminal_::toks1,[]->aux'toks1l2fulll2fulltoks2||auxtoks1toks2|_->falseinlet_,toks=interp_list_parser[](raw_analyze_anonymous_notation_tokens(split_notation_stringntn))inlet_,toks'=interp_list_parser[](raw_analyze_anonymous_notation_tokens(split_notation_stringntn'))inauxtokstoks'letbrowse_notationstrictntnmap=letntn=interpret_notation_stringntninletfind(from,ntn'asfullntn')=ifString.containsntn' 'thenifString.string_contains~where:ntn'~what:".."thenis_approximationntnntn'elseString.equalntnntn'elselet_,toks=decompose_notation_keyfullntn'inletget_terminals=functionTerminalntn->Somentn|_->Noneinlettrms=List.map_filterget_terminalstoksinifstrictthenString.List.equal[ntn]trmselseString.List.memntntrmsinletl=String.Map.fold(funscope_namesc->NotationMap.fold(funntndatal->iffindntnthenList.map(fund->(ntn,scope_name,d))(extract_notation_datadata)@lelsel)sc.notations)map[]inList.sort(funxy->String.compare(snd(pi1x))(snd(pi1y)))lletglobal_reference_of_notation~headtest(ntn,sc,(on_parsing,on_printing,{not_interp=(_,c)}))=matchcwith|NRef(ref,_)whentestref->Some(on_parsing,on_printing,ntn,sc,ref)|NApp(NRef(ref,_),l)whenhead||List.for_allisNVar_or_NHolel&&testref->Some(on_parsing,on_printing,ntn,sc,ref)|_->Noneleterror_ambiguous_notation?loc_ntn=user_err?loc(str"Ambiguous notation.")leterror_notation_not_reference?locntn=user_err?loc(str"Unable to interpret "++quote(strntn)++str" as a reference.")letinterp_notation_as_global_reference?loc~headtestntnsc=letscopes=matchscwith|Somesc->letscope=find_scope(find_delimiters_scopesc)inString.Map.addscscopeString.Map.empty|None->!scope_mapinletntns=browse_notationtruentnscopesinletrefs=List.map(global_reference_of_notation~headtest)ntnsinmatchOption.List.flattenrefswith|[Sometrue,_(* why not if the only one? *),_,_,ref]->ref|[]->error_notation_not_reference?locntn|refs->letf(on_parsing,_,ntn,sc,ref)=letdef=find_defaultntn!scope_stackinmatchdefwith|None->false|Somesc'->on_parsing=Sometrue&&String.equalscsc'inmatchList.filterfrefswith|[_,_,_,_,ref]->ref|[]->error_notation_not_reference?locntn|_->error_ambiguous_notation?locntnletlocate_notationprglobntnscope=letntns=factorize_entries(browse_notationfalsentn!scope_map)inletscopes=Option.fold_rightpush_scopescope!scope_stackinmatchntnswith|[]->str"Unknown notation"|_->prlist_with_sepfnl(fun(ntn,l)->letscope=find_defaultntnscopesinprlist_with_sepfnl(fun(sc,(on_parsing,on_printing,{not_interp=(_,r);not_location=(_,df)}))->hov0(str"Notation"++brk(1,2)++pr_notation_infoprglobdfr++(ifString.equalscdefault_scopethenmt()else(brk(1,2)++str": "++strsc))++(ifOption.equalString.equal(Somesc)scopethenbrk(1,2)++str"(default interpretation)"elsemt())++pr_non_empty(brk(1,2))(pr_notation_statuson_parsingon_printing)))l)ntnsletcollect_notation_in_scopescopescknown=assert(not(String.equalscopedefault_scope));NotationMap.fold(funntnd(l,knownasacc)->ifList.mem_fnotation_eqntnknownthenaccelse(extract_notation_datad@l,ntn::known))sc.notations([],known)letcollect_notationsstack=fst(List.fold_left(fun(all,knownntnasacc)->function|OpenScopeItemscope->ifString.List.mem_assocscopeallthenaccelselet(l,knownntn)=collect_notation_in_scopescope(find_scopescope)knownntnin((scope,l)::all,knownntn)|LonelyNotationItemntn->ifList.mem_fnotation_eqntnknownntnthen(all,knownntn)elsetryletdatas=extract_notation_data(NotationMap.findntn(find_scopedefault_scope).notations)inletall'=matchallwith|(s,lonelyntn)::restwhenString.equalsdefault_scope->(s,datas@lonelyntn)::rest|_->(default_scope,datas)::allin(all',ntn::knownntn)withNot_found->(* e.g. if only printing *)(all,knownntn))([],[])stack)letpr_visible_in_scopeprglob(scope,ntns)=letstrm=List.fold_right(fundstrm->pr_notation_dataprglobd++fnl()++strm)ntns(mt())in(ifString.equalscopedefault_scopethenstr"Lonely notation"++(matchntnswith[_]->mt()|_->str"s")elsestr"Visible in scope "++strscope)++fnl()++strmletpr_scope_stackprglobstack=prlist_with_sepfnl(pr_visible_in_scopeprglob)(collect_notationsstack)letpr_visibilityprglob=function|Somescope->pr_scope_stackprglob(push_scopescope!scope_stack)|None->pr_scope_stackprglob!scope_stack(**********************************************************************)(* Synchronisation with reset *)letfreeze~marshallable=(!scope_map,!scope_stack,!arguments_scope,!delimiters_map,!notations_key_table,!scope_class_map,!prim_token_interp_infos,!prim_token_uninterp_infos,!entry_coercion_map,!entry_has_global_map,!entry_has_ident_map)letunfreeze(scm,scs,asc,dlm,fkm,clsc,ptii,ptui,coe,globs,ids)=scope_map:=scm;scope_stack:=scs;delimiters_map:=dlm;arguments_scope:=asc;notations_key_table:=fkm;scope_class_map:=clsc;prim_token_interp_infos:=ptii;prim_token_uninterp_infos:=ptui;entry_coercion_map:=coe;entry_has_global_map:=globs;entry_has_ident_map:=idsletinit()=init_scope_map();delimiters_map:=String.Map.empty;notations_key_table:=KeyMap.empty;scope_class_map:=initial_scope_class_map;prim_token_interp_infos:=String.Map.empty;prim_token_uninterp_infos:=GlobRef.Map.emptylet_=Summary.declare_summary"symbols"{Summary.freeze_function=freeze;Summary.unfreeze_function=unfreeze;Summary.init_function=init}letwith_notation_protectionfx=letfs=freeze~marshallable:falseintryleta=fxinunfreezefs;awithreraise->letreraise=Exninfo.capturereraiseinlet()=unfreezefsinExninfo.iraisereraise