1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788(************************************************************************)(* * 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*)openCErrorsopenUtilopenPpopenNamesopenConstropenLibnamesopenGlobnamesopenLibobjectopenConstrexpropenNotation_termopenGlob_termopenGlob_opsopenNumTokopenNotationextern(*i*)letmkRef(env,sigmaref)r=letsigma,c=Evd.fresh_globalenv!sigmarefrinsigmaref:=sigma;EConstr.Unsafe.to_constrcletmkConstructesigc=mkRefesig(ConstructRefc)letmkIndesigi=mkRefesig(IndRefi)letnotation_cat=Libobject.create_category"notations"(*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
*)letpr_notation(from,ntn)=qstringntn++matchfromwithInConstrEntry->mt()|InCustomEntrys->str" in custom "++strsmoduleNotationOrd=structtypet=notationletcompare=Stdlib.compareendmoduleNotationSet=Set.Make(NotationOrd)moduleNotationMap=CMap.Make(NotationOrd)moduleSpecificNotationOrd=structtypet=specific_notationletcompare=Stdlib.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_user_warns:UserWarn.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_scope_start_=CWarnings.create~name:"scope-underscore-start"~category:CWarnings.CoreCategories.syntax~default:CWarnings.AsError(fun()->strbrk"Scope names should not start with an underscore.")letwarn_undeclared_scope=CWarnings.create~name:"undeclared-scope"~category:Deprecation.Version.v8_10(fun(scope)->strbrk"Declaring a scope implicitly is deprecated; use in advance an explicit "++str"\"Declare Scope "++strscope++str".\".")letdeclare_scopescope=ifscope<>""&&scope.[0]='_'thenwarn_scope_start_();trylet_=String.Map.findscope!scope_mapin()withNot_found->scope_map:=String.Map.addscopeempty_scope!scope_mapleterror_unknown_scope~infosc=user_err~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_scopescopeletscope_delimitersscope=scope.delimiters(* [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_item_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 *)letopen_scopesc=scope_stack:=OpenScopeItemsc::!scope_stackletclose_scopesc=scope_stack:=List.removescope_item_eq(OpenScopeItemsc)!scope_stackletempty_scope_stack=[]letpush_scopescscopes=OpenScopeItemsc::scopesletpush_scopes=List.fold_rightpush_scopeletmake_current_scopes(tmp_scopes,scopes)=push_scopestmp_scopes(push_scopesscopes!scope_stack)(**********************************************************************)(* Delimiters *)letwarn_scope_delimiter_start_=CWarnings.create~name:"scope-delimiter-underscore-start"~category:CWarnings.CoreCategories.syntax~default:CWarnings.AsError(fun()->strbrk"Scope delimiters should not start with an underscore.")letwarn_overwriting_key=CWarnings.create~name:"overwriting-delimiting-key"~category:CWarnings.CoreCategories.parsingPp.(fun(oldkey,scope)->str"Overwriting previous delimiting key "++stroldkey++str" in scope "++strscope)letwarn_hiding_key=CWarnings.create~name:"hiding-delimiting-key"~category:CWarnings.CoreCategories.parsingPp.(fun(key,oldscope)->str"Hiding binding of key "++strkey++str" to "++stroldscope)letdeclare_delimitersscopekey=ifkey<>""&&key.[0]='_'thenwarn_scope_delimiter_start_();letsc=find_scopescopeinletnewsc={scwithdelimiters=Somekey}inbeginmatchsc.delimiterswith|None->scope_map:=String.Map.addscopenewsc!scope_map|SomeoldkeywhenString.equaloldkeykey->()|Someoldkey->(* FIXME: implement multikey scopes? *)warn_overwriting_key(oldkey,scope);scope_map:=String.Map.addscopenewsc!scope_mapend;tryletoldscope=String.Map.findkey!delimiters_mapinifString.equaloldscopescopethen()elsebeginwarn_hiding_key(key,oldscope);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(str"Unknown scope delimiting key "++strkey++str".")(** Dealing with precedences *)letentry_relative_level_lechild=function|LevelLtparent->child<parent|LevelLeparent->child<=parent|LevelSome->trueletnotation_level_map=Summary.ref~stage:Summary.Stage.Synterp~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_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 *)|Float64(* Coq.Floats.PrimFloat.float *)|Numberofnumber_ty(* Coq.Init.Number.number + uint + int *)typestring_target_kind=|ListByte|Byte|PStringtypeoption_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|ToPostCheckofConstr.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_objtype'atoken_kind=|TVarofId.t*'alist|TSortofSorts.t|TConstofConstant.t*'alist|TIndofinductive*'alist|TConstructofconstructor*'alist|TIntofUint63.t|TFloatofFloat64.t|TStringofPstring.t|TArrayof'aarray*'a*'a|TOthermoduleTokenValue:sigtypetvalkind:t->ttoken_kindvalmake:Environ.env->Evd.evar_map->EConstr.unsafe_judgment->tvalrepr:t->Constr.tend=structtypet=Constr.t(* Guaranteed to be in strong normal form *)letkindc=lethd,args=decompose_app_listcinmatchConstr.kindhdwith|Varid->TVar(id,args)|Sorts->TSorts|Const(c,_)->TConst(c,args)|Ind(ind,_)->TInd(ind,args)|Construct(c,_)->TConstruct(c,args)|Inti->TInti|Floatf->TFloatf|Strings->TStrings|Array(_,t,u,v)->TArray(t,u,v)|Rel_|Meta_|Evar_|Cast_|Prod_|Lambda_|LetIn_|App_|Proj_|Case_|Fix_|CoFix_->TOtherletmakeenvsigmac=letc'=Tacred.computeenvsigmac.Environ.uj_valinEConstr.Unsafe.to_constr@@c'letreprc=cendmodulePrimTokenNotation=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_constrcinletsigma,t=Typing.type_ofenvsigmacinTokenValue.makeenvsigma{Environ.uj_val=c;Environ.uj_type=t}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_globref?(allow_constant=true)envsigma=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|_->raiseNotAValidPrimToken(** [check_glob g c] checks that glob [g] is equal to constr [c]
and returns [g] as a constr (with fresh universe instances)
or raises [NotAValidPrimToken]. *)letreccheck_globenvsigmagc=matchDAst.getg,Constr.kindcwith|Glob_term.GRef(GlobRef.ConstructRefcasg,_),Constr.Construct(c',_)whenEnviron.QConstruct.equalenvcc'->constr_of_globrefenvsigmag|Glob_term.GRef(GlobRef.IndRefcasg,_),Constr.Ind(c',_)whenEnviron.QInd.equalenvcc'->constr_of_globrefenvsigmag|Glob_term.GRef(GlobRef.ConstRefcasg,_),Constr.Const(c',_)whenEnviron.QConstant.equalenvcc'->constr_of_globrefenvsigmag|Glob_term.GApp(gc,gcl),Constr.App(gc',gc'a)->letsigma,c=check_globenvsigmagcgc'inletsigma,cl=tryList.fold_left2_map(check_globenv)sigmagcl(Array.to_listgc'a)withInvalid_argument_->raiseNotAValidPrimTokeninsigma,mkApp(c,Array.of_listcl)|Glob_term.GInti,Constr.Inti'whenUint63.equalii'->sigma,mkInti|Glob_term.GFloatf,Constr.Floatf'whenFloat64.equalff'->sigma,mkFloatf|Glob_term.GStrings,Constr.Strings'whenPstring.equalss'->sigma,mkStrings|Glob_term.GArray(_,t,def,ty),Constr.Array(_,t',def',ty')->letsigma,u=Evd.fresh_array_instanceenvsigmainletsigma,def=check_globenvsigmadefdef'inletsigma,t=tryArray.fold_left2_map(check_globenv)sigmatt'withInvalid_argument_->raiseNotAValidPrimTokeninletsigma,ty=check_globenvsigmatyty'insigma,mkArray(u,t,def,ty)|Glob_term.GSorts,Constr.Sorts'->letsigma,s=Evd.fresh_sort_in_familysigma(Glob_ops.glob_sort_familys)inlets=EConstr.ESorts.kindsigmasinifnot(Sorts.equalss')thenraiseNotAValidPrimToken;sigma,mkSorts|_->raiseNotAValidPrimTokenletrecconstr_of_globto_postpostenvsigmag=matchDAst.getgwith|Glob_term.GRef(r,_)->leto=List.find_opt(fun(_,r',_)->Environ.QGlobRef.equalenvrr')postinbeginmatchowith|None->constr_of_globref~allow_constant:falseenvsigmar|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_globrefenvsigmarend|Glob_term.GApp(gc,gcl)->leto=matchDAst.getgcwith|Glob_term.GRef(r,_)->List.find_opt(fun(_,r',_)->Environ.QGlobRef.equalenvrr')post|_->Noneinbeginmatchowith|None->letsigma,c=constr_of_globto_postpostenvsigmagcinletsigma,cl=List.fold_left_map(constr_of_globto_postpostenv)sigmagclinsigma,mkApp(c,Array.of_listcl)|Some(r,_,a)->letsigma,c=constr_of_globrefenvsigmarinletrecauxsigmaagcl=matcha,gclwith|[],[]->sigma,[]|ToPostCopy::a,gc::gcl->letsigma,c=constr_of_glob[||][]envsigmagcinletsigma,cl=auxsigmaagclinsigma,c::cl|ToPostCheckr::a,gc::gcl->letsigma,c=check_globenvsigmagcrinletsigma,cl=auxsigmaagclinsigma,c::cl|ToPostAsi::a,gc::gcl->letsigma,c=constr_of_globto_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.GStrings->sigma,mkStrings|Glob_term.GArray(_,t,def,ty)->letsigma,u'=Evd.fresh_array_instanceenvsigmainletsigma,def'=constr_of_globto_postpostenvsigmadefinletsigma,t'=Array.fold_left_map(constr_of_globto_postpostenv)sigmatinletsigma,ty'=constr_of_globto_postpostenvsigmatyinsigma,mkArray(u',t',def',ty')|Glob_term.GSortgs->letsigma,c=Evd.fresh_sort_in_familysigma(Glob_ops.glob_sort_familygs)inletc=EConstr.ESorts.kindsigmacinsigma,mkSortc|_->raiseNotAValidPrimTokenletconstr_of_globto_postenvsigma(Glob_term.AnyGlobConstrg)=letpost=matchto_postwith[||]->[]|_->to_post.(0)inconstr_of_globto_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)|Strings->DAst.make?loc(Glob_term.GStrings)|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.GSortGlob_ops.glob_SProp_sort)|SortSorts.Prop->DAst.make?loc(Glob_term.GSortGlob_ops.glob_Prop_sort)|SortSorts.Set->DAst.make?loc(Glob_term.GSortGlob_ops.glob_Set_sort)|Sort(Sorts.Type_)->DAst.make?loc(Glob_term.GSortGlob_ops.glob_Type_sort)|_->Loc.raise?loc(PrimTokenNotationError(token_kind,env,sigma,UnexpectedTermc))letmkGApp?lochdargs=matchargswith|[]->hd|_::_->mkGApp?lochdargsletrecglob_of_tokentoken_kind?locenvsigmac=matchTokenValue.kindcwith|TConstruct(c,l)->letce=DAst.make?loc(Glob_term.GRef(GlobRef.ConstructRefc,None))inletcel=List.map(glob_of_tokentoken_kind?locenvsigma)linmkGApp?loccecel|TConst(c,l)->letce=DAst.make?loc(Glob_term.GRef(GlobRef.ConstRefc,None))inletcel=List.map(glob_of_tokentoken_kind?locenvsigma)linmkGApp?loccecel|TInd(ind,l)->letce=DAst.make?loc(Glob_term.GRef(GlobRef.IndRefind,None))inletcel=List.map(glob_of_tokentoken_kind?locenvsigma)linmkGApp?loccecel|TVar(id,l)->letce=DAst.make?loc(Glob_term.GRef(GlobRef.VarRefid,None))inletcel=List.map(glob_of_tokentoken_kind?locenvsigma)linmkGApp?loccecel|TInti->DAst.make?loc(Glob_term.GInti)|TFloatf->DAst.make?loc(Glob_term.GFloatf)|TStrings->DAst.make?loc(Glob_term.GStrings)|TArray(t,def,ty)->letdef'=glob_of_tokentoken_kind?locenvsigmadefandt'=Array.map(glob_of_tokentoken_kind?locenvsigma)tandty'=glob_of_tokentoken_kind?locenvsigmatyinDAst.make?loc(Glob_term.GArray(None,t',def',ty'))|TSortSorts.SProp->DAst.make?loc(Glob_term.GSortGlob_ops.glob_SProp_sort)|TSortSorts.Prop->DAst.make?loc(Glob_term.GSortGlob_ops.glob_Prop_sort)|TSortSorts.Set->DAst.make?loc(Glob_term.GSortGlob_ops.glob_Set_sort)|TSort(Sorts.Type_|Sorts.QSort_)->DAst.make?loc(Glob_term.GSortGlob_ops.glob_Type_sort)|TOther->letc=TokenValue.reprcinLoc.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.CanOrd.equalrr')post|_->NoneinmatchowithNone->g|Some(_,r,a)->letrecfnagl=matcha,glwith|[],[]->[]|ToPostHole::a,gl->lete=GImplicitArg(r,(n,None),true)inleth=DAst.make?loc(Glob_term.GHolee)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)gletglob_of_tokentoken_kindty?locenvsigmato_postc=letg=glob_of_tokentoken_kind?locenvsigmacinmatchto_postwith[||]->g|_->postprocesstoken_kind?loctyto_postto_post.(0)gletinterp_optionuninterpreted_token_kindtoken_kindty?locenvsigmato_postc=matchTokenValue.kindcwith|TConstruct(_Some,[_;c])->glob_of_tokentoken_kindty?locenvsigmato_postc|TConstruct(_None,[_])->no_such_prim_tokenuninterpreted_token_kind?locty|x->letc=TokenValue.reprcinLoc.raise?loc(PrimTokenNotationError(token_kind,env,sigma,UnexpectedNonOptionTermc))letuninterp_optionc=matchTokenValue.kindcwith|TConstruct(_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:CWarnings.CoreCategories.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:CWarnings.CoreCategories.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_rawnumesigindscn=letuint=matchcwithCDec->inds.dec_uint|CHex->inds.hex_uintinletnil=mkConstructesig(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=mkConstructesig(uint,digit_of_chars.[i])indo_charss(i-1)(mkApp(dg,[|acc|]))indo_charsstr(String.lengthstr-1)nilletcoqint_of_rawnumesigindsc(sign,n)=letind=matchcwithCDec->inds.dec_int|CHex->inds.hex_intinletuint=coquint_of_rawnumesigindsc(Somen)inletpos_neg=matchsignwithSPlus->1|SMinus->2inmkApp(mkConstructesig(ind,pos_neg),[|uint|])letcoqnumber_of_rawnumesigindscn=letind=matchcwithCDec->inds.decimal|CHex->inds.hexadecimalinleti,f,e=NumTok.Signed.to_int_frac_and_exponentninleti=coqint_of_rawnumesiginds.intciinletf=coquint_of_rawnumesiginds.intcfinmatchewith|None->mkApp(mkConstructesig(ind,1),[|i;f|])(* (D|Hexad)ecimal *)|Somee->lete=coqint_of_rawnumesiginds.intCDeceinmkApp(mkConstructesig(ind,2),[|i;f;e|])(* (D|Hexad)ecimalExp *)letmkDecHexesigindcn=matchcwith|CDec->mkApp(mkConstructesig(ind,1),[|n|])(* (UInt|Int|)Decimal *)|CHex->mkApp(mkConstructesig(ind,2),[|n|])(* (UInt|Int|)Hexadecimal *)letcoqnumber_of_rawnumesigindsn=letc=NumTok.Signed.classifyninletn=coqnumber_of_rawnumesigindscninmkDecHexesiginds.numbercnletcoquint_of_rawnumesigindsn=letc=NumTok.UnsignedNat.classifyninletn=coquint_of_rawnumesigindsc(Somen)inmkDecHexesiginds.uintcnletcoqint_of_rawnumesigindsn=letc=NumTok.SignedNat.classifyninletn=coqint_of_rawnumesigindscninmkDecHexesiginds.intcnletrawnum_of_coquintclc=letrecof_uint_loopcbuf=matchTokenValue.kindcwith|TConstruct((_,1),_)(* Nil *)->()|TConstruct((_,n),[a])(* D0 to Df *)->let()=Buffer.add_charbuf(char_of_digitn)inof_uint_loopabuf|_->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=matchTokenValue.kindcwith|TConstruct((_,1),[c'])(* Pos *)->(SPlus,rawnum_of_coquintclc')|TConstruct((_,2),[c'])(* Neg *)->(SMinus,rawnum_of_coquintclc')|_->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_exponentnfeinmatchTokenValue.kindcwith|TConstruct(_,[i;f])->of_ifeifNone|TConstruct(_,[i;f;e])->of_ifeif(Somee)|_->raiseNotAValidPrimTokenletdestDecHexc=matchTokenValue.kindcwith|TConstruct((_,1),[c'])(* (UInt|Int|)Decimal *)->CDec,c'|TConstruct((_,2),[c'])(* (UInt|Int|)Hexadecimal *)->CHex,c'|_->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_bigintesigpostyn=matchZ.div_remnz_twowith|(q,rem)whenrem=Z.zero->letc=mkConstructesig(posty,2)in(* xO *)mkApp(c,[|pos_of_bigintesigpostyq|])|(q,_)whennot(Z.equalqZ.zero)->letc=mkConstructesig(posty,1)in(* xI *)mkApp(c,[|pos_of_bigintesigpostyq|])|(q,_)->mkConstructesig(posty,3)(* xH *)letrecbigint_of_posc=matchTokenValue.kindcwith|TConstruct((_,3),[])->(* xH *)Z.one|TConstruct((_,1),[d])->(* xI *)Z.addZ.one(Z.mulz_two(bigint_of_posd))|TConstruct((_,2),[d])->(* xO *)Z.mulz_two(bigint_of_posd)|x->raiseNotAValidPrimToken(** Now, [Z] from/to bigint *)letz_of_bigintesig{z_ty;pos_ty}n=ifZ.(equalnzero)thenmkConstructesig(z_ty,1)(* Z0 *)elselet(s,n)=ifZ.(leqzeron)then(2,n)(* Zpos *)else(3,Z.negn)(* Zneg *)inletc=mkConstructesig(z_ty,s)inmkApp(c,[|pos_of_bigintesigpos_tyn|])letbigint_of_zz=matchTokenValue.kindzwith|TConstruct((_,1),[])->(* Z0 *)Z.zero|TConstruct((_,2),[d])->(* Zpos *)bigint_of_posd|TConstruct((_,3),[d])->(* Zneg *)Z.neg(bigint_of_posd)|_->raiseNotAValidPrimToken(** Now, [Int63] from/to bigint *)letint63_of_pos_bigint?locn=leti=int63_of_pos_bigintninmkIntileterror_overflow?locn=CErrors.user_err?locPp.(str"Overflow in int63 literal: "++str(Z.to_stringn)++str".")letcoqpos_neg_int63_of_bigint?locesigind(sign,n)=letuint=int63_of_pos_bigint?locninletpos_neg=matchsignwithSPlus->1|SMinus->2inmkApp(mkConstructesig(ind,pos_neg),[|uint|])letinterp_int63?locesigindn=letsign=ifZ.(comparenzero>=0)thenSPluselseSMinusinletan=Z.absninifZ.(ltan(powz_two63))thencoqpos_neg_int63_of_bigint?locesigind(sign,an)elseerror_overflow?locnletwarn_inexact_float=CWarnings.create~name:"inexact-float"~category:CWarnings.CoreCategories.parsing(fun(sn,f)->Pp.strbrk(Printf.sprintf"The constant %s is not a binary64 floating-point value. \
A closest value %s will be used and unambiguously printed %s."sn(Float64.to_hex_stringf)(Float64.to_stringf)))letinterp_float64?locn=letsn=NumTok.Signed.to_stringninletf=Float64.of_stringsnin(* return true when f is not exactly equal to n,
this is only used to decide whether or not to display a warning
and does not play any actual role in the parsing *)letinexact()=matchFloat64.classifyfwith|Float64.(PInf|NInf|NaN)->true|Float64.(PZero|NZero)->not(NumTok.Signed.is_zeron)|Float64.(PNormal|NNormal|PSubn|NSubn)->letm,e=let(_,i),f,e=NumTok.Signed.to_int_frac_and_exponentninleti=NumTok.UnsignedNat.to_stringiinletf=matchfwith|None->""|Somef->NumTok.UnsignedNat.to_stringfinlete=matchewith|None->"0"|Somee->NumTok.SignedNat.to_stringeinZ.of_string(i^f),(tryint_of_stringewithFailure_->0)-String.lengthfinletm',e'=letm',e'=Float64.frshiftexpfinletm'=Float64.normfr_mantissam'inlete'=Uint63.to_int_mine'4096-Float64.eshift-53inZ.of_string(Uint63.to_stringm'),e'inletc2,c5=Z.(of_int2,of_int5)in(* check m*5^e <> m'*2^e' *)letcheckmem'e'=not(Z.(equal(mulm(powc5e))(mulm'(powc2e'))))in(* check m*5^e*2^e' <> m' *)letcheck'mee'm'=not(Z.(equal(mul(mulm(powc5e))(powc2e'))m'))in(* we now have to check m*10^e <> m'*2^e' *)ife>=0thenife<=e'thencheckmem'(e'-e)elsecheck'me(e-e')m'else(* e < 0 *)ife'<=ethencheckm'(-e)m(e-e')elsecheck'm'(-e)(e'-e)minifNumTok.(Signed.classifyn=CDec)&&inexact()thenwarn_inexact_float?loc(sn,f);mkFloatfletbigint_of_int63c=matchTokenValue.kindcwith|TInti->Z.of_int64(Uint63.to_int64i)|_->raiseNotAValidPrimTokenletbigint_of_coqpos_neg_int63c=matchTokenValue.kindcwith|TConstruct((_,1),[c'])(* Pos *)->bigint_of_int63c'|TConstruct((_,2),[c'])(* Neg *)->Z.neg(bigint_of_int63c')|_->raiseNotAValidPrimTokenlet{Goptions.get=get_printing_float}=Goptions.declare_bool_option_and_ref~key:["Printing";"Float"]~value:true()letuninterp_float64c=matchTokenValue.kindcwith|TFloatfwhennot(Float64.is_infinityf||Float64.is_neg_infinityf||Float64.is_nanf)&&get_printing_float()->NumTok.Signed.of_string(Float64.to_stringf)|_->raiseNotAValidPrimTokenletinterpo?locn=beginmatcho.warning,nwith|Warningthreshold,nwhenNumTok.Signed.is_bigger_int_thannthreshold->warn_large_numo.ty_name|_->()end;letenv=Global.env()inletsigma=ref(Evd.from_envenv)inletesig=env,sigmainletc=matchfsto.to_kind,NumTok.Signed.to_intnwith|Intint_ty,Somen->coqint_of_rawnumesigint_tyn|UIntint_ty,Some(SPlus,n)->coquint_of_rawnumesigint_tyn|Zz_pos_ty,Somen->z_of_bigintesigz_pos_ty(NumTok.SignedNat.to_bigintn)|Int63pos_neg_int63_ty,Somen->interp_int63?locesigpos_neg_int63_ty.pos_neg_int63_ty(NumTok.SignedNat.to_bigintn)|(Int_|UInt_|Z_|Int63_),_->no_such_prim_token"number"?loco.ty_name|Float64,_->interp_float64?locn|Numbernumber_ty,_->coqnumber_of_rawnumesignumber_tyninletsigma=!sigmainletsigma,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_token"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)|(Float64,c)->uninterp_float64c|(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_codeesigbytec=mkConstructesig(byte,1+c)letcoqbyte_of_string?locesigbytes=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(str"Expects a single character or a three-digit ASCII code.")incoqbyte_of_char_codeesigbytepletcoqbyte_of_charesigbytec=coqbyte_of_char_codeesigbyte(Char.codec)letpstring_of_string?locs=matchPstring.of_stringswith|Somes->Constr.mkStrings|None->user_err?loc(str"String literal would be too large on a 32-bits system.")letmake_ascii_stringn=ifn>=32&&n<=126thenString.make1(char_of_intn)elsePrintf.sprintf"%03d"nletchar_code_of_coqbytec=matchTokenValue.kindcwith|TConstruct((_,c),[])->c-1|_->raiseNotAValidPrimTokenletstring_of_coqbytec=make_ascii_string(char_code_of_coqbytec)letstring_of_pstringc=matchTokenValue.kindcwith|TStrings->Pstring.to_strings|_->raiseNotAValidPrimTokenletcoqlist_byte_of_stringesigbyte_tylist_tystr=letcbyte=mkIndesigbyte_tyinletnil=mkApp(mkConstructesig(list_ty,1),[|cbyte|])inletconsxxs=mkApp(mkConstructesig(list_ty,2),[|cbyte;x;xs|])inletrecdo_charssiacc=ifi<0thenaccelseletb=coqbyte_of_charesigbyte_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=matchTokenValue.kindcwith|TConstruct(_nil,[_ty])->()|TConstruct(_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()inletenv=Global.env()inletsigma=ref(Evd.from_envenv)inletesig=env,sigmainletc=matchfsto.to_kindwith|ListByte->coqlist_byte_of_stringesigbyte_tylist_tyn|Byte->coqbyte_of_string?locesigbyte_tyn|PString->pstring_of_string?locninletsigma=!sigmainletsigma,to_ty=Evd.fresh_globalenvsigmao.to_tyinletto_ty=EConstr.Unsafe.to_constrto_tyinletres=eval_constr_appenvsigmato_tycinmatchsndo.to_kindwith|Direct->glob_of_token"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_coqbytec|(PString,c)->string_of_pstringcendonend(* 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(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_interpretationinfos=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_localthenDisposeelseSubstituteletopen_prim_token_interpretationio=ifInt.equali1thencache_prim_token_interpretationoletinPrimTokenInterp:prim_token_infos->obj=declare_object{(default_object"PRIM-TOKEN-INTERP")withopen_function=simple_open~cat:notation_catopen_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_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]).
*)letglob_prim_constr_keyc=matchDAst.getcwith|GRef(ref,_)->Some(canonical_grref)|GApp(c,_)->beginmatchDAst.getcwith|GRef(ref,_)->Some(canonical_grref)|_->Noneend|GProj((cst,_),_,_)->Some(canonical_gr(GlobRef.ConstRefcst))|_->Noneletcheck_required_module?locsc(sp,d)=trylet_=Nametab.global_of_pathspin()withNot_foundasexn->let_,info=Exninfo.captureexninmatchdwith|[]->user_err?loc~info(str"Cannot interpret in "++strsc++str" because "++pr_pathsp++str" could not be found in the current environment.")|_->user_err?loc~info(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()++strscopeletwarning_overridden_name="notation-overridden"letw_nota_overridden=CWarnings.create_warning~from:[CWarnings.CoreCategories.parsing]~name:warning_overridden_name()letwarn_notation_overridden=CWarnings.create_inw_nota_overridden(fun(scope,ntn)->str"Notation"++spc()++pr_notationntn++spc()++strbrk"was already used"++pr_optional_scopescope++str".")letwarn_deprecation_overridden=CWarnings.create_inw_nota_overridden(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"."))letwarn_override_if_needed(scopt,ntn)overriddendataold_data=ifoverriddenthenwarn_notation_overridden(scopt,ntn)elseifdata.not_user_warns<>old_data.not_user_warnsthenwarn_deprecation_overridden((scopt,ntn),old_data.not_user_warns,data.not_user_warns)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_user_warns<>old_data.not_user_warnsthenwarn_deprecation_overridden((scopt,ntn),old_data.not_user_warns,data.not_user_warns);exists)printingdatainparsing_update,existsletupdate_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|Strings->InConstrEntry,String.quote_coq_stringsletfind_prim_tokencheck_allowed?locpsc=(* Try for a user-defined numerical notation *)tryletn=find_notation(notation_of_prim_tokenp)scinlet(_,c)=n.not_interpinletpat=Notation_ops.glob_constr_of_notation_constr?loccincheck_allowedpat;patwithNot_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;patletinterp_prim_token_gen?locgplocal_scopes=letscopes=make_current_scopeslocal_scopesinletp_as_ntn=trynotation_of_prim_tokenpwithNot_found->InConstrEntry,""intryletpat,sc=find_interpretationp_as_ntn(find_prim_token?locgp)scopesinpat,scwithNot_foundasexn->let_,info=Exninfo.captureexninuser_err?loc~info((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_->())letinterp_prim_token_cases_pattern_expr?loccheck_allowedp=interp_prim_token_gen?loccheck_allowedpletwarn_deprecated_notation=Deprecation.create_warning~object_name:"Notation"~warning_name_if_no_since:"deprecated-notation"pr_notationletinterp_notation?locntnlocal_scopes=letscopes=make_current_scopeslocal_scopesintrylet(n,sc)=find_interpretationntn(find_notationntn)scopesinOption.iter(fund->Option.iter(fund->warn_deprecated_notation?loc(ntn,d))d.UserWarn.depr)n.not_user_warns;n.not_interp,(n.not_location,sc)withNot_foundasexn->let_,info=Exninfo.captureexninuser_err?loc~info(str"Unknown interpretation for notation "++pr_notationntn++str".")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)|AbbrevRulekn->trylet_=Nametab.path_of_abbreviationkninfalsewithNot_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=Stdlib.compareendmoduleEntryCoercionMap=Map.Make(EntryCoercionOrd)(* We index coercions by pairs of entry names to avoid a full linear search *)letentry_coercion_map:(((entry_level*entry_relative_level)*entry_coercion)listEntryCoercionMap.t)ref=refEntryCoercionMap.emptyletsublevel_ordlevlev'=matchlev,lev'with|_,LevelSome->true|LevelSome,_->false|LevelLtn,LevelLtn'|LevelLen,LevelLen'->n<=n'|LevelLtn,LevelLen'->n<n'|LevelLen,LevelLtn'->n<=n'-1letis_coercion{notation_entry=e1;notation_level=n1}{notation_subentry=e2;notation_relative_level=n2}=not(notation_entry_eqe1e2)||matchn2with|LevelLtn2|LevelLen2->n1<n2|LevelSome->true(* unless n2 is the entry top level but we shall know it only dynamically *)letincluded{notation_entry=e1;notation_level=n1}{notation_subentry=e2;notation_relative_level=n2}=notation_entry_eqe1e2&&entry_relative_level_len1n2letrecsearchnfromnto=function|[]->raiseNot_found|((pfrom,pto),coe)::l->ifentry_relative_level_lepfromnfrom&&entry_relative_level_lentoptothencoeelsesearchnfromntolletavailability_of_entry_coercion?(non_included=false)({notation_subentry=entry;notation_relative_level=sublev}asentry_sublev)({notation_entry=entry';notation_level=lev'}asentry_lev)=ifincludedentry_leventry_sublev&¬non_includedthen(* [entry] is by default included in [relative_entry] *)Some[]elsetrySome(searchsublevlev'(EntryCoercionMap.find(entry,entry')!entry_coercion_map))withNot_found->Noneletbetter_path((lev1,sublev2),path)((lev1',sublev2'),path')=(* better = shorter and lower source and higher target *)lev1<=lev1'&&sublevel_ordsublev2'sublev2&&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'paththenallpathselsepath'::insert_coercion_pathpathpathsletdeclare_entry_coercionntnentry_levelentry_relative_level'=let{notation_entry=entry;notation_level=lev}=entry_levelinlet{notation_subentry=entry';notation_relative_level=sublev'}=entry_relative_level'in(* Transitive closure *)lettoaddleft=EntryCoercionMap.fold(fun(entry'',entry''')pathsl->List.fold_right(fun((lev'',sublev'''),path)l->ifincludedentry_level{notation_subentry=entry''';notation_relative_level=sublev''';notation_position=None}&¬(included{notation_entry=entry'';notation_level=lev''}entry_relative_level')then((entry'',entry'),((lev'',sublev'),path@[ntn]))::lelsel)pathsl)!entry_coercion_map[]inlettoaddright=EntryCoercionMap.fold(fun(entry'',entry''')pathsl->List.fold_right(fun((lev'',sublev'''),path)l->ifincluded{notation_entry=entry'';notation_level=lev''}entry_relative_level'&¬(includedentry_level{notation_subentry=entry''';notation_relative_level=sublev''';notation_position=None})then((entry,entry'''),((lev,sublev'''),ntn::path))::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,sublev'),[ntn]))::toaddright@toaddleft)!entry_coercion_map(* Hard-wired coercion in constr corresponding to "( x )" *)let_=entry_coercion_map:=(EntryCoercionMap.add(InConstrEntry,InConstrEntry)[(0,LevelSome),[]]!entry_coercion_map)letentry_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{notation_subentry=entry;notation_relative_level=n}=matchentrywith|InConstrEntry->true|InCustomEntrys->tryentry_relative_level_le(String.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{notation_subentry=entry;notation_relative_level=n}=matchentrywith|InConstrEntry->true|InCustomEntrys->tryentry_relative_level_le(String.Map.finds!entry_has_ident_map)nwithNot_found->falseletapp_level=10letprec_lesschild=function|LevelLtparent->child<parent|LevelLeparent->child<=parent|LevelSome->trueletmay_capture_cont_afterchildparent=matchchildwith|None->false|Somelev_after->prec_lesslev_afterparenttypeentry_coercion_kind=|IsEntryCoercionofnotation_entry_level*notation_entry_relative_level|IsEntryGlobalofstring*int|IsEntryIdentofstring*intletdeclare_notation(scopt,ntn)patdf~usecoeuser_warns=(* Register the interpretation *)letscope=matchscoptwithNotationInScopes->s|LastLonelyNotation->default_scopeinletsc=find_scopescopeinletnotdata={not_interp=pat;not_location=df;not_user_warns=user_warns;}inletnotation_update,printing_update=update_notation_data(scopt,ntn)usenotdatasc.notationsinletsc={scwithnotations=notation_update}inscope_map:=String.Map.addscopesc!scope_map;(* Register visibility of lonely notations *)beginmatchscoptwith|LastLonelyNotation->scope_stack:=LonelyNotationItemntn::!scope_stack|NotationInScope_->()end;(* Declare a possible coercion *)ifuse<>OnlyParsingthenbeginmatchcoewith|Some(IsEntryCoercion(entry,subentry))->declare_entry_coercion(scopt,ntn)entrysubentry|Some(IsEntryGlobal(entry,n))->declare_custom_entry_has_globalentryn|Some(IsEntryIdent(entry,n))->declare_custom_entry_has_idententryn|None->(* Update the uninterpretation cache *)beginmatchprinting_updatewith|Somepat->remove_uninterpretation(NotationRule(scopt,ntn))pat|None->()end;declare_uninterpretation(NotationRule(scopt,ntn))patendletavailability_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|[]->CList.find_map_exn(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)(* pairs of Top and Bottom additions (Boolean is for locality) *)typescope_class_map=((scope_name*bool)list*(scope_name*bool)list)ScopeClassMap.tletinitial_scope_class_map:scope_class_map=ScopeClassMap.emptyletscope_class_map=refinitial_scope_class_maptypeadd_scope_where=AddScopeTop|AddScopeBottomletdeclare_scope_classislocalsc?wherecl=letmap=matchwherewith|None->ScopeClassMap.addcl([sc,islocal],[])!scope_class_map|Somewhere->letadd(scl1,scl2)=matchwherewithAddScopeTop->((sc,islocal)::scl1,scl2)|AddScopeBottom->(scl1,scl2@[sc,islocal])inletscl=tryScopeClassMap.findcl!scope_class_mapwithNot_found->([],[])inScopeClassMap.addcl(addscl)!scope_class_mapinscope_class_map:=mapletfind_scope_class_blocks_optmap=function|None->[],[]|Somecl->tryletltop,lbot=ScopeClassMap.findclmapinList.mapfstltop,List.mapfstlbotwithNot_found->[],[]letfind_scope_class_optmapcl=letltop,lbot=find_scope_class_blocks_optmapclinltop@lbot(**********************************************************************)(* Special scopes associated to arguments of a global reference *)letcompute_telescopeenvsigmatyp=letopenCClosureinletinfos=Evarutil.create_clos_infosenvsigmaRedFlags.betaiotazetainlettab=create_tab()inletrecapply_rectypaccu=lettyp,stk=whd_stackinfostabtyp[]inmatchfterm_oftypwith|FProd(na,c1,c2,e)->letc1=EConstr.of_constr@@term_of_fconstrc1inletc2=mk_clos(CClosure.usubs_lifte)c2inapply_recc2((EConstr.of_binder_annotna,c1)::accu)|_->List.revaccuinapply_rec(CClosure.inject(EConstr.Unsafe.to_constrtyp))[]letcompute_arguments_classesenvsigmat=lettelescope=compute_telescopeenvsigmatinletrecauxenv=function|(na,t)::decls->letcl=trySome(compute_scope_classenvsigmat)withNot_found->Noneinletenv=EConstr.push_rel(Context.Rel.Declaration.LocalAssum(na,t))envincl::auxenvdecls|[]->[]inauxenvtelescopeletcompute_arguments_scope_fullenvsigmamapt=letcls=compute_arguments_classesenvsigmatinletscs=List.map(find_scope_class_optmap)clsinscs,clsletcompute_arguments_scopeenvsigmat=fst(compute_arguments_scope_fullenvsigma!scope_class_mapt)letcompute_type_scopeenvsigmat=find_scope_class_opt!scope_class_map(trySome(compute_scope_classenvsigmat)withNot_found->None)letcurrent_type_scope_names()=find_scope_class_opt!scope_class_map(SomeCL_SORT)letcompute_glob_type_scopet=find_scope_class_opt!scope_class_map(trySome(find_class_glob_typet)withNot_found->None)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. That is,
cls and scl have this form:
dynam. recomputed
when out of sync manual
/----------\ /-----------\
scl = sc1 ... scn sc1' ... scn'
cls = cl1 ... cln empty list
\----------/
static. computed
at cache/rebuild time
*)letupdate_scopescocl=let(sctop,scbot)=find_scope_class_blocks_opt!scope_class_mapclinletsco=List.filter(funsc->not(List.exists(String.equalsc)sctop||List.exists(String.equalsc)scbot))scoinsctop@sco@scbotletrecupdate_scopesclsscl=matchcls,sclwith|[],_->scl|_,[]->List.map(update_scope[])cls|cl::cls,sco::scl->update_scopescocl::update_scopesclssclletarguments_scope=refGlobRef.Map.emptytypearguments_scope_discharge_request=|ArgsScopeAuto|ArgsScopeManual|ArgsScopeNoDischargeletload_arguments_scope_(_,r,scl,cls,allscopes)=List.iter(List.itercheck_scope)scl;(* force recomputation to take into account the possible extra "Bind
Scope" of the current environment (e.g. so that after inlining of a
parameter in a functor, it takes the current environment into account *)letinitial_stamp=initial_scope_class_mapinarguments_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,scl,cls,allscopes))=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',scl,cls',allscopes)letdischarge_available_scopesmap=(* Remove local scopes *)ScopeClassMap.filter_map(funcl(ltop,lbot)->letltop=List.filter(funx->not(sndx))ltopinletlbot=List.filter(funx->not(sndx))lbotinifList.is_emptyltop&&List.is_emptylbotthenNoneelseSome(ltop,lbot))mapletdischarge_arguments_scope(req,r,scs,_cls,available_scopes)=ifreq==ArgsScopeNoDischarge||(isVarRefr&&Lib.is_in_sectionr)thenNoneelseletn=tryArray.length(Lib.section_instancer)withNot_found(* Not a ref defined in this section *)->0inletavailable_scopes=discharge_available_scopesavailable_scopesin(* Hack: use list cls to encode an integer to pass to rebuild for Manual case *)(* since cls is anyway recomputed in rebuild *)letn_as_cls=List.makenNoneinSome(req,r,scs,n_as_cls,available_scopes)letclassify_arguments_scope(req,_,_,_,_)=ifreq==ArgsScopeNoDischargethenDisposeelseSubstituteletrebuild_arguments_scopesigma(req,r,scs,n_as_cls,available_scopes)=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_fullenvsigmaavailable_scopestypin(* Note: cls is fixed, but scs can be recomputed in find_arguments_scope *)(req,r,scs,cls,available_scopes)|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?*)letn=List.lengthn_as_clsinlettyp=EConstr.of_constr@@fst(Typeops.type_of_global_in_contextenvr)inletscs',cls=compute_arguments_scope_fullenvsigmaavailable_scopestypinletscs1=List.firstnnscs'inletcls1=List.firstnnclsin(* Note: the extra cls1 is fixed, but its associated scs can be recomputed *)(* on the undefined part of cls, scs is however fixed *)(req,r,scs1@scs,cls1,available_scopes)typearguments_scope_obj=arguments_scope_discharge_request*GlobRef.t*scope_namelistlist*scope_classoptionlist*scope_class_mapletinArgumentsScope: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_genreqr(scl,cls)=Lib.add_leaf(inArgumentsScope(req,r,scl,cls,!scope_class_map))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_genreqr(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_scoperef=letenv=Global.env()in(* FIXME? *)letsigma=Evd.from_envenvinlettyp=EConstr.of_constr@@fst@@Typeops.type_of_global_in_contextenvrefin(* cls is fixed but scs is only an initial value that can be modified in find_arguments_scope *)let(scs,clsaso)=compute_arguments_scope_fullenvsigma!scope_class_maptypindeclare_arguments_scope_genArgsScopeAutorefo(********************************)(* 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"_"->["'_'"](* Symbols starting with a double quote without denoting a string are single quoted *)|Terminalswhens.[0]='"'&&(String.lengths=1||s.[String.lengths-1]<>'"')->["'"^s^"'"]|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_pure_keys=letlen=String.lengthsinletrecfind_string_endn=letnext=tryString.index_froms(n+1)'"'withNot_found->assertfalseinifnext=len-1thennext+1elseifs.[next+1]='"'then(* skip doubled double quotes: *)find_string_end(next+2)elsenext+1inletrecdecomp_ntndirsn=ifn>=lenthenList.revdirselseletpos=ifs.[n]='"'thenfind_string_endnelsetryString.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)indecomp_ntn[]0letdecompose_notation_key(from,s)=from,decompose_notation_pure_keysletis_prim_token_constant_in_constr(entry,symbs)=matchentry,List.filter(functionBreak_->false|_->true)symbswith(* Is this a numeral? *)|InConstrEntry,([Terminal"-";Terminalx]|[Terminalx])whenNumTok.Unsigned.parse_stringx<>None->true(* Is this a string constant? *)|InConstrEntry,[Terminalx]whenletn=String.lengthxinn>1&&x.[0]='"'&&x.[n-1]='"'->true|_->falseletlevel_of_notationntn=ifis_prim_token_constant_in_constr(decompose_notation_keyntn)then(* A primitive notation *)({notation_entry=fstntn;notation_level=0},[])(* TODO: string notations*)elseNotationMap.findntn!notation_level_map(************)(* Printing *)letpr_delimiters_info=function|None->str"No delimiting key"|Somekey->str"Delimiting key is "++strkeyletclasses_of_scopesc=letmap=!scope_class_mapinScopeClassMap.fold(funcl(scltop,sclbot)l->ifList.exists(fun(sc',_)->String.equalscsc')scltop||List.exists(fun(sc',_)->String.equalscsc')sclbotthencl::lelsel)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_statuson_parsingon_printing=letdisabledb=ifbthen[]else["disabled"]inletl=matchon_parsing,on_printingwith|Someon,None->"only parsing"::disabledon|None,Someon->"only printing"::disabledon|Somefalse,Somefalse->["disabled"]|Sometrue,Somefalse->["disabled for printing"]|Somefalse,Sometrue->["disabled 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(Notation_ops.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(String.pluraln"Lonely notation")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))elseifbeg=i&&str.[i]='"'thenloop_on_stringi(i+1)elseloopbeg(i+1)elsepush_tokenbegi[]andloop_on_whitespacebegi=ifi<String.lengthstrthenifstr.[i]!=' 'thenpush_whitespacebegi(loopii)elseloop_on_whitespacebeg(i+1)elsepush_whitespacebegi[]andloop_on_stringbegi=(* we accept any string, possibly with spaces, single quotes, and
doubled double quotes inside, but necessarily ended with a unique
double quote followed either by a space or the end of the
notation string *)ifi<String.lengthstrthenifstr.[i]='"'thenifi+1<String.lengthstrthenifstr.[i+1]='"'then(* double quote in the string: *)loop_on_stringbeg(i+2)elseifstr.[i+1]=' 'then(* end of the string: *)push_tokenbeg(i+1)(loop_on_whitespace(i+2)(i+2))elseuser_err(Pp.str"End of quoted string not followed by a space in notation.")elsepush_tokenbeg(i+1)[]elseloop_on_stringbeg(i+1)elseuser_err(Pp.str"Unterminated string in notation.")(* we accept any sequences of non-space symbols starting with a
single quote, up to the next space or end of notation string;
double quotes and single quotes not followed by a space or the
end of notation string are allowed;
note that if the resulting sequence ends with a single quote,
the two extreme single quotes will eventually be removed *)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 *)typenotation_symbols={recvars:(Id.t*Id.t)list;(* pairs (x,y) as in [ x ; .. ; y ] *)mainvars:Id.tlist;(* variables non involved in a recursive pattern *)symbols:symbollist;(* the decomposition of the notation into terminals and nonterminals *)}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(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_stringntninletsymbols=raw_analyze_notation_tokenslinletrecvars,symbols=interp_list_parser[]symbolsinletmainvars=get_notation_varssymbolsin{recvars;mainvars;symbols}letinterpret_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'letmatch_notation_keystrictntnntn'=ifString.containsntn' 'thenifString.string_contains~where:ntn'~what:".."thenis_approximationntnntn'elseString.equalntnntn'elselettoks=decompose_notation_pure_keyntn'inletget_terminals=functionTerminalntn->Somentn|_->Noneinlettrms=List.map_filterget_terminalstoksinifstrictthenString.List.equal[ntn]trmselseString.List.memntntrmsletbrowse_notationstrictntnmap=letntn=interpret_notation_stringntninletfind(from,ntn')=match_notation_keystrictntnntn'inletl=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=(_,casinterp);not_location=(_,df)}))=matchcwith|NRef(ref,_)whentestref->Some(on_parsing,on_printing,ntn,df,sc,interp,ref)|NApp(NRef(ref,_),l)whenhead||List.for_allisNVar_or_NHolel&&testref->Some(on_parsing,on_printing,ntn,df,sc,interp,ref)|_->Nonetypenotation_as_reference_error=|AmbiguousNotationAsReferenceofnotation_key|NotationNotReferenceofEnviron.env*Evd.evar_map*notation_key*(notation_key*notation_constr)listexceptionNotationAsReferenceErrorofnotation_as_reference_errorleterror_ambiguous_notation?locntn=Loc.raise?loc(NotationAsReferenceError(AmbiguousNotationAsReferencentn))leterror_notation_not_reference?locntnntns=letntns=List.map(fun(_,_,(_,_,{not_interp=(_,r);not_location=(_,df)}))->df,r)ntnsinletenv=Global.env()inletsigma=Evd.from_envenvinLoc.raise?loc(NotationAsReferenceError(NotationNotReference(env,sigma,ntn,ntns)))letinterp_notation_as_global_reference_expanded?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)ntnsinletmake_scopesc=ifString.equalscdefault_scopethenLastLonelyNotationelseNotationInScopescinmatchOption.List.flattenrefswith|[Sometrue,_(* why not if the only one? *),ntn,df,sc,interp,ref]->(ntn,df,make_scopesc,interp,ref)|[]->error_notation_not_reference?locntnntns|refs->letf(on_parsing,_,ntn,df,sc,_,ref)=letdef=find_defaultntn!scope_stackinmatchdefwith|None->false|Somesc'->on_parsing=Sometrue&&String.equalscsc'inmatchList.filterfrefswith|[_,_,ntn,df,sc,interp,ref]->(ntn,df,make_scopesc,interp,ref)|[]->error_notation_not_reference?locntnntns|_->error_ambiguous_notation?locntnletinterp_notation_as_global_reference?loc~headtestntnsc=let_,_,_,_,ref=interp_notation_as_global_reference_expanded?loc~headtestntnscinrefletlocate_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)++Notation_ops.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(String.plural(List.lengthntns)"Lonely notation")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(* Enabling/disabling notations *)lettoggle_main_notation~on~usefoundtestntn_datamain=letfoundd=found:=(Inl(d.not_location,ntn_data),d.not_interp)::!foundinmatchmain,usewith|OnlyParsingData(is_on,d),OnlyPrintingwhentestd.not_interp->user_err(strbrk"Unexpected only printing for an only parsing notation.")|OnlyParsingData(is_on,d)asx,(OnlyParsing|ParsingAndPrinting)whentestd.not_interp->ifis_on<>onthenbeginfoundd;OnlyParsingData(on,d)endelsex|ParsingAndPrintingData(is_parsing_on,is_printing_on,d)asx,_whentestd.not_interp->letparsing_changed=matchusewith|OnlyPrinting->false|OnlyParsing|ParsingAndPrinting->is_parsing_on<>oninletprinting_changed=matchusewith|OnlyParsing->false|OnlyPrinting|ParsingAndPrinting->is_printing_on<>oninifparsing_changed||printing_changedthenlet()=founddinParsingAndPrintingData(is_parsing_on<>parsing_changed,is_printing_on<>printing_changed,d)elsex|(NoParsingData|OnlyParsingData_|ParsingAndPrintingData_),_->mainlettoggle_extra_only_printing_notation~on~usefoundtestntn_data(is_on,dasx)=letfoundd=found:=(Inl(d.not_location,ntn_data),d.not_interp)::!foundinmatchusewith|OnlyParsing->user_err(strbrk"Unexpected only parsing for an only printing notation.")|OnlyPrinting|ParsingAndPrinting->iftestd.not_interpthenifis_on<>onthenlet()=founddin(on,d)elsexelsexlettoggle_notation_data~on~usefoundtestntn_data(main,extraasdata)=letmain'=toggle_main_notation~on~usefoundtestntn_datamaininletextra'=List.Smart.map(toggle_extra_only_printing_notation~on~usefoundtestntn_data)extrainifmain'==main&&extra'==extrathendataelse(main',extra')type'anotation_query_pattern_gen={notation_entry_pattern:notation_entrylist;interp_rule_key_pattern:(notation_key,'a)Util.unionoption;use_pattern:notation_use;scope_pattern:notation_with_optional_scopeoption;interpretation_pattern:interpretationoption;}typenotation_query_pattern=qualidnotation_query_pattern_genletmatch_notation_interpretationnotation_interpretationpat=matchnotation_interpretationwith|None->true|Somepat'->Notation_ops.finer_interpretation_thanpatpat'letmatch_notation_entrynotation_entry_patternnotation_entry=List.is_emptynotation_entry_pattern||List.exists(notation_entry_eqnotation_entry)notation_entry_patternletmatch_notation_ruleinterp_rule_key_patternnotation_key=matchinterp_rule_key_patternwith|None->true|Some(Inlntn)->match_notation_keyfalsentnnotation_key|Some(Inr_)->falselettoggle_notations_by_interpretation~onfoundntn_patternntn_data(main,extraasdata)=letuse=ntn_pattern.use_patterninlettest=match_notation_interpretationntn_pattern.interpretation_patternintoggle_notation_data~on~usefoundtestntn_datadatalettoggle_notations_in_scope~onfoundinscopentn_patternntns=matchntn_pattern.notation_entry_pattern,ntn_pattern.interp_rule_key_patternwith|_,Some(Inrkn)->ntns(* This is the table of notations, not of abbreviations *)|_::_asntn_entries,Some(Inlntn)->(* shortcut *)List.fold_right(funntn_entryntns->tryNotationMap.add(ntn_entry,ntn)(toggle_notations_by_interpretation~onfoundntn_pattern(inscope,(ntn_entry,ntn))(NotationMap.find(ntn_entry,ntn)ntns))ntnswithNot_found->ntns)ntn_entriesntns(* Deal with full notations *)|ntn_entries,ntn_rule->(* This is the table of notations, not of abbreviations *)NotationMap.mapi(fun(ntn_entry,ntn_key'asntn')data->ifmatch_notation_entryntn_entriesntn_entry&&match_notation_rulentn_rulentn_key'thentoggle_notations_by_interpretation~onfoundntn_pattern(inscope,ntn')dataelsedata)ntnsletwarn_abbreviation_not_bound_to_entry=CWarnings.create~name:"conflicting-abbreviation-entry"~category:CWarnings.CoreCategories.syntax(fun()->strbrk"Activation of abbreviations does not expect mentioning a grammar entry.")letwarn_abbreviation_not_bound_to_scope=CWarnings.create~name:"conflicting-abbreviation-scope"~category:CWarnings.CoreCategories.syntax(fun()->strbrk"Activation of abbreviations does not expect mentioning a scope.")lettoggle_abbreviations~onfoundntn_pattern=tryletqid=matchntn_pattern.interp_rule_key_pattern,ntn_pattern.notation_entry_pattern,ntn_pattern.scope_patternwith|Some(Inrqid),[],None->Someqid|Some(Inrqid),entries,inscope->ifnot(List.is_emptyentries)thenwarn_abbreviation_not_bound_to_entry();ifOption.has_someinscopethenwarn_abbreviation_not_bound_to_scope();raiseExit|Some(Inl_),_,_|None,_::_,_|None,_,Some_->raiseExit(* Not about abbreviation, shortcut *)|None,[],None->Noneinlettestspa=letres=match_notation_interpretationntn_pattern.interpretation_patternainletres'=matchqidwith|Someqid->Libnames.is_qualid_suffix_of_full_pathqidsp|None->trueinletres''=res&&res'inifres''thenfound:=(Inrsp,a)::!found;res''inAbbreviation.toggle_abbreviations~on~use:ntn_pattern.use_patterntestwithExit->()letwarn_nothing_to_enable_or_disable=CWarnings.create~name:"no-notation-to-enable-or-disable"~category:CWarnings.CoreCategories.syntax(fun()->strbrk"Found no matching notation to enable or disable.")lettoggle_notations~on~all?(verbose=true)prglobntn_pattern=letfound=ref[]in(* Deal with (parsing) notations *)beginmatchntn_pattern.scope_patternwith|None->scope_map:=String.Map.mapi(funsc{notations;delimiters}->letinscope=ifString.equalscdefault_scopethenLastLonelyNotationelseNotationInScopescin{notations=toggle_notations_in_scope~onfoundinscopentn_patternnotations;delimiters})!scope_map;|Someinscope->(* shortcut when a scope is given *)letsc=matchinscopewithNotationInScopesc->sc|LastLonelyNotation->default_scopeinscope_map:=String.Map.addsc(let{notations;delimiters}=find_scopescin{notations=toggle_notations_in_scope~onfoundinscopentn_patternnotations;delimiters})!scope_mapend;(* Deal with abbreviations *)toggle_abbreviations~onfoundntn_pattern;match!foundwith|[]->warn_nothing_to_enable_or_disable()|_::_::_whennotall->user_err(strbrk"More than one interpretation bound to this notation, confirm with the \"all\" modifier.")|_->ifverbosethenFeedback.msg_info(str"The following notations have been "++str(ifonthen"enabled"else"disabled")++(matchntn_pattern.use_patternwith|OnlyParsing->str" for parsing"|OnlyPrinting->str" for printing"|ParsingAndPrinting->mt())++str":"++fnl()++prlist_with_sepfnl(fun(kind,(vars,aasi))->matchkindwith|Inl(l,(sc,(entry,_)))->letsc=matchscwithNotationInScopesc->sc|LastLonelyNotation->default_scopeinletdata={not_interp=i;not_location=l;not_user_warns=None}inhov0(str"Notation "++pr_notation_dataprglob(Sometrue,Sometrue,data)++(matchentrywithInCustomEntrys->str" (in custom "++strs++str")"|_->mt())++(ifString.equalscdefault_scopethenmt()else(brk(1,2)++str": "++strsc)))|Inrsp->hov0(str"Notation "++Libnames.pr_pathsp++prlist(fun(a,_)->spc()++Id.printa)vars++spc()++str":="++spc()++prglob(Notation_ops.glob_constr_of_notation_constra)))!found)(**********************************************************************)(* Synchronisation with reset *)letfreeze()=(!scope_map,!scope_stack,!arguments_scope,!delimiters_map,!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,clsc,ptii,ptui,coe,globs,ids)=scope_map:=scm;scope_stack:=scs;delimiters_map:=dlm;arguments_scope:=asc;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;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"{stage=Summary.Stage.Interp;Summary.freeze_function=freeze;Summary.unfreeze_function=unfreeze;Summary.init_function=init}letwith_notation_protectionfx=letfs=freeze()intryleta=with_notation_uninterpretation_protectionfxinunfreezefs;awithreraise->letreraise=Exninfo.capturereraiseinlet()=unfreezefsinExninfo.iraisereraise