1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999(************************************************************************)(* * 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) *)(************************************************************************)openPpopenCErrorsopenUtilopenNamesopenConstrexpropenConstrexpr_opsopenVernacexpropenNotation_termopenNotationexternopenNotation_gramopenNotation_opsopenPpextendopenExtendopenLibobjectopenConstrinternopenLibnamesopenNotationopenNameops(**********************************************************************)(* Printing grammar entries *)letentry_buf=Buffer.create64letpr_entrye=let()=Buffer.clearentry_bufinletft=Format.formatter_of_bufferentry_bufinlet()=Pcoq.Entry.printfteinstr(Buffer.contentsentry_buf)leterror_unknown_entry?locname=user_err?locPp.(str"Unknown or unprintable grammar entry "++strname++str".")letpr_registered_grammarname=letgram=Pcoq.find_grammars_by_namenameinmatchgramwith|[]->error_unknown_entryname|entries->letpr_one(Pcoq.Entry.Anye)=str"Entry "++str(Pcoq.Entry.namee)++str" is"++fnl()++pr_entryeinprlistpr_oneentriesletpr_grammar_subsetgrammar=letpp=String.Map.mapi(funnamel->matchlwith|[]->assertfalse|entries->str"Entry "++strname++str" is"++fnl()++prlist_with_sep(fun()->str"or"++fnl())(fun(Pcoq.Entry.Anye)->pr_entrye)entries)grammarinletpp=CString.Map.bindingsppinprlist_with_sepfnl(fun(_,pp)->pp)ppletis_known=letopenPcoq.Entryinfunction|"constr"|"term"|"binder_constr"->Some[AnyPcoq.Constr.constr;AnyPcoq.Constr.lconstr;AnyPcoq.Constr.binder_constr;AnyPcoq.Constr.term;]|"vernac"->SomePvernac.Vernac_.[Anyvernac_control;(* main_entry="vernac", included not because it's interesting but because otherwise it's shadowed by the "vernac" group defined here *)Anymain_entry;Anycommand;Anysyntax;Anygallina;Anygallina_ext;]|name->letgram=Pcoq.find_grammars_by_namenameinmatchgramwith|[]->None|entries->Someentriesletfull_grammar()=Pcoq.Entry.accumulate_inPvernac.Vernac_.vernac_controlletsame_entry(Pcoq.Entry.Anye)(Pcoq.Entry.Anye')=(Obj.magice)==(Obj.magice')letpr_grammar=function|[]->letgrammar=full_grammar()inpr_grammar_subsetgrammar|names->letknown,other=List.fold_left(fun(known,other)name->matchis_knownnamewith|Somev->v@known,other|None->known,name::other)([],[])namesinletgrammar=ifList.is_emptyotherthenString.Map.emptyelsefull_grammar()inlet()=List.iter(funname->ifnot(String.Map.memnamegrammar)thenerror_unknown_entryname)otherinletother=String.Set.of_listotherinletgrammar=String.Map.filter(funname_->String.Set.memnameother)grammarinletgrammar=List.fold_left(fungrammar(Pcoq.Entry.Anyeasany)->String.Map.update(Pcoq.Entry.namee)(function|None->Some[any]|Somevlasv->ifList.mem_fsame_entryanyvlthenvelseSome(any::vl))grammar)grammarknowninpr_grammar_subsetgrammarletpr_custom_grammarname=pr_registered_grammar("custom:"^name)letpr_keywords()=Pp.prlist_with_sepPp.fnlPp.str(CString.Set.elements(CLexer.keywords(Pcoq.get_keyword_state())))(**********************************************************************)(* Parse a format (every terminal starting with a letter or a single
quote (except a single quote alone) must be quoted) *)letparse_format({CAst.loc;v=str}:lstring)=letlen=String.lengthstrin(* TODO: update the line of the location when the string contains newlines *)letmake_locij=Option.map(Loc.shift_loc(i+1)(j-len))locinletpush_tokenloca=function|(i,cur)::l->(i,(loc,a)::cur)::l|[]->assertfalseinletpush_whiteinl=ifInt.equaln0thenlelsepush_token(make_loci(i+n))(UnpTerminal(String.maken' '))linletclose_boxstartstopb=function|(_,a)::(_::_asl)->push_token(make_locstartstop)(UnpBox(b,a))l|[a]->user_err?loc:(make_locstartstop)Pp.(str"Non terminated box in format.")|[]->assertfalseinletclose_quotationstarti=ifi<len&&str.[i]=='\''thenif(Int.equal(i+1)len||str.[i+1]==' ')theni+1elseuser_err?loc:(make_loc(i+1)(i+1))Pp.(str"Space expected after quoted expression.")elseuser_err?loc:(make_locstart(i-1))Pp.(str"Beginning of quoted expression expected to be ended by a quote.")inletrecspacesni=ifi<len&&str.[i]==' 'thenspaces(n+1)(i+1)elseninletrecnonspacesquotedni=ifi<len&&str.[i]!=' 'thenifstr.[i]=='\''&"ed&&(i+1>=len||str.[i+1]==' ')thenifInt.equaln0thenuser_err?loc:(make_loc(i-1)i)Pp.(str"Empty quoted token.")elsenelsenonspacesquoted(n+1)(i+1)elseifquotedthenuser_err?loc:(make_locii)Pp.(str"Spaces are not allowed in (quoted) symbols.")elseninletrecparse_non_formati=letn=nonspacesfalse0iinpush_token(make_loci(i+n-1))(UnpTerminal(String.substrin))(parse_token1(i+n))andparse_quotednki=ifi<lenthenmatchstr.[i]with(* Parse " // " *)|'/'wheni+1<len&&str.[i+1]=='/'->(* We discard the useless n spaces... *)push_token(make_loc(i-n)(i+1))(UnpCutPpFnl)(parse_token1(close_quotationi(i+2)))(* Parse " .. / .. " *)|'/'wheni+1<len->letp=spaces0(i+1)inpush_token(make_loc(i-n)(i+p))(UnpCut(PpBrk(n,p)))(parse_token1(close_quotationi(i+p+1)))|c->(* The spaces are real spaces *)push_white(i-n-1-k)n(matchcwith|'['->ifi+1<lenthenmatchstr.[i+1]with(* Parse " [h .. ", *)|'h'wheni+1<=len&&str.[i+2]=='v'->(parse_boxi(funn->PpHVBn)(i+3))(* Parse " [v .. ", *)|'v'->parse_boxi(funn->PpVBn)(i+2)(* Parse " [ .. ", *)|' '|'\''->parse_boxi(funn->PpHOVBn)(i+1)|_->user_err?loc:(make_locii)Pp.(str"\"v\", \"hv\", \" \" expected after \"[\" in format.")elseuser_err?loc:(make_locii)Pp.(str"\"v\", \"hv\" or \" \" expected after \"[\" in format.")(* Parse "]" *)|']'->((i,[])::parse_token1(close_quotationi(i+1)))(* Parse a non formatting token *)|c->letn=nonspacestrue0iinpush_token(make_loci(i+n-1))(UnpTerminal(String.substr(i-1)(n+2)))(parse_token1(close_quotationi(i+n))))elseifInt.equaln0then[]elseuser_err?loc:(make_loc(len-n)len)Pp.(str"Ending spaces non part of a format annotation.")andparse_boxstartboxi=letn=spaces0iinclose_boxstart(i+n-1)(boxn)(parse_token1(close_quotationi(i+n)))andparse_tokenki=letn=spaces0iinleti=i+ninifi<lenthenmatchstr.[i]with(* Parse a ' *)|'\''wheni+1>=len||str.[i+1]==' '->push_white(i-n)(n-k)(push_token(make_loci(i+1))(UnpTerminal"'")(parse_token1(i+1)))(* Parse the beginning of a quoted expression *)|'\''->parse_quoted(n-k)k(i+1)(* Otherwise *)|_->push_white(i-n)(n-k)(parse_non_formati)elsepush_white(i-n)n[(len,[])]inifnot(String.is_emptystr)thenmatchparse_token00with|[_,l]->l|(i,_)::_->user_err?loc:(make_locii)Pp.(str"Box closed without being opened.")|[]->assertfalseelse[](***********************)(* Analyzing notations *)(* Find non-terminal tokens of notation *)(* To protect alphabetic tokens and quotes from being seen as variables *)letquote_notation_tokenx=letn=String.lengthxinletnorm=CLexer.is_identxinif(n>0&&norm)||(n>2&&x.[0]=='\'')then"'"^x^"'"elsexletis_numeral_in_constrentrysymbs=matchentry,List.filter(functionBreak_->false|_->true)symbswith|InConstrEntry,([Terminal"-";Terminalx]|[Terminalx])->NumTok.Unsigned.parse_stringx<>None|_->falseletanalyze_notation_tokens~onlyprinting~infixentrydf=letdf=ifinfixthenquote_notation_tokendfelsedfinlet{recvars;mainvars;symbols}asres=decompose_raw_notationdfin(* don't check for nonlinearity if printing only, see Bug 5526 *)(ifnotonlyprintingthenmatchList.duplicatesId.equal(mainvars@List.mapsndrecvars)with|id::_->user_err(str"Variable "++Id.printid++str" occurs more than once.")|_->());letisnumeral=is_numeral_in_constrentrysymbolsinres,isnumeralletadjust_symbolsvarsnotation_symbols=letx=Namegen.next_ident_away(Id.of_string"x")varsinlety=Namegen.next_ident_away(Id.of_string"y")(Id.Set.addxvars)inletnotation_symbols={recvars=notation_symbols.recvars;mainvars=x::notation_symbols.mainvars@[y];symbols=NonTerminalx::notation_symbols.symbols@[NonTerminaly];}inx,y,notation_symbolsletadjust_reserved_infix_notationnotation_symbols=letvars=Id.Set.of_list(List.map_filter(functionNonTerminalx->Somex|_->None)notation_symbols.symbols)inlet_,_,notation_symbols=adjust_symbolsvarsnotation_symbolsinnotation_symbolsletadjust_infix_notationdfnotation_symbolsc=letvars=names_of_constr_exprcinletx,y,notation_symbols=adjust_symbolsvarsnotation_symbolsinletdf=Id.to_stringx^" "^df^" "^Id.to_stringyinletinject_varx=CAst.make@@CRef(qualid_of_identx,None)inletmetas=[inject_varx;inject_vary]inletc=mkAppC(c,metas)indf,notation_symbols,cletwarn_unexpected_primitive_token_modifier=CWarnings.create~name:"primitive-token-modifier"~category:CWarnings.CoreCategories.parsing(fun()->str"Notations for numbers are primitive; skipping this modifier.")letcheck_no_syntax_modifiers_for_numeral=function|[]->()|l->List.iter(function{CAst.loc}->warn_unexpected_primitive_token_modifier?loc())lleterror_not_same_scopexy=user_err(str"Variables "++Id.printx++str" and "++Id.printy++str" must be in the same scope.")(**********************************************************************)(* Build pretty-printing rules *)letpr_notation_entry=function|InConstrEntry->str"constr"|InCustomEntrys->str"custom "++strsletprecedence_of_position_and_levelfrom_level=function|NumLeveln,BorderProd(b,Somea)->(letopenGramlib.Gramextinmatcha,bwith|RightA,Left->LevelLtn|RightA,Right->LevelLen|LeftA,Left->LevelLen|LeftA,Right->LevelLtn|NonA,_->LevelLtn),Someb|NumLeveln,_->LevelLen,None|NextLevel,_->LevelLtfrom_level,None|DefaultLevel,_->LevelSome,None(** Computing precedences of non-terminals for parsing *)letprecedence_of_entry_type(from_custom,from_level)=function|ETConstr(custom,_,x)whennotation_entry_eqcustomfrom_custom->fst(precedence_of_position_and_levelfrom_levelx)|ETConstr(custom,_,(NumLeveln,_))->LevelLen|ETConstr(custom,_,(NextLevel,_))->user_err(strbrk"\"next level\" is only for sub-expressions in the same entry as where the notation is ("++quote(pr_notation_entrycustom)++strbrk" is different from "++quote(pr_notation_entryfrom_custom)++str").")|ETPattern(_,n)->letn=matchnwithNone->0|Somen->ninLevelLen|_->LevelSome(* should not matter *)letpattern_entry_level=functionNone->0|Somen->n(** Computing precedences for future insertion of parentheses at
the time of printing using hard-wired constr levels *)letunparsing_precedence_of_entry_typefrom_level=function|ETConstr(InConstrEntry,_,x)->(* Possible insertion of parentheses at printing time to deal
with precedence in a constr entry is managed using [prec_less]
in [ppconstr.ml] *)precedence_of_position_and_levelfrom_levelx|ETConstr(custom,_,_)->(* Precedence of printing for a custom entry is managed using
explicit insertion of entry coercions at the time of building
a [constr_expr] *)LevelSome,None|ETPattern(_,n)->(* in constr *)LevelLe(pattern_entry_leveln),None|_->LevelSome,None(* should not matter *)(* Some breaking examples *)(* "x = y" : "x /1 = y" (breaks before any symbol) *)(* "x =S y" : "x /1 =S /1 y" (protect from confusion; each side for symmetry)*)(* "+ {" : "+ {" may breaks reversibility without space but oth. not elegant *)(* "x y" : "x spc y" *)(* "{ x } + { y }" : "{ x } / + { y }" *)(* "< x , y > { z , t }" : "< x , / y > / { z , / t }" *)letstarts_with_left_brackets=letl=String.lengthsinnot(Int.equall0)&&(s.[0]=='{'||s.[0]=='['||s.[0]=='(')letends_with_right_brackets=letl=String.lengthsinnot(Int.equall0)&&(s.[l-1]=='}'||s.[l-1]==']'||s.[l-1]==')')letis_left_brackets=starts_with_left_brackets&¬(ends_with_right_brackets)letis_right_brackets=not(starts_with_left_brackets)&&ends_with_right_bracketsletis_commas=letl=String.lengthsinnot(Int.equall0)&&(s.[0]==','||s.[0]==';')letis_operators=letl=String.lengthsinnot(Int.equall0)&&(s.[0]=='+'||s.[0]=='*'||s.[0]=='='||s.[0]=='-'||s.[0]=='/'||s.[0]=='<'||s.[0]=='>'||s.[0]=='@'||s.[0]=='\\'||s.[0]=='&'||s.[0]=='~'||s.[0]=='$')letis_non_terminal=function|NonTerminal_|SProdList_->true|_->falseletis_next_non_terminalb=function|[]->b|pr::_->is_non_terminalprletis_next_terminal=functionTerminal_::_->true|_->falseletis_next_break=functionBreak_::_->true|_->falseletadd_breaknl=(None,UnpCut(PpBrk(n,0)))::lletadd_break_if_nonenb=function|(_,UnpCut(PpBrk_))::_asl->l|[]whennotb->[]|l->(None,UnpCut(PpBrk(n,0)))::lletcheck_open_binderisopenslm=letpr_token=function|Terminals->strs|Breakn->str"␣"|_->assertfalseinifisopen&¬(List.is_emptysl)thenuser_err(str"as "++Id.printm++str" is a non-closed binder, no such \""++prlist_with_sepspcpr_tokensl++strbrk"\" is allowed to occur.")letunparsing_metavarifromtyps=letx=List.nthtyps(i-1)inletprec,side=unparsing_precedence_of_entry_typefromxinmatchxwith|ETConstr_|ETGlobal|ETBigint->UnpMetaVar(prec,side)|ETPattern_|ETName|ETIdent->UnpBinderMetaVar(prec,NotQuotedPattern)|ETBinderisopen->UnpBinderMetaVar(prec,QuotedPattern)(* Heuristics for building default printing rules *)letindex_ididl=List.indexId.equalidlletmake_hunksetypssymbolsfrom_level=letvars,typs=List.splitetypsinletrecmakeb=function|NonTerminalm::prods->leti=index_idmvarsinletu=unparsing_metavarifrom_leveltypsinifis_next_non_terminalbprodsthen(None,u)::add_break_if_none1b(makebprods)else(None,u)::make_with_spacebprods|Terminals::prodswhen(* true to simulate presence of non-terminal *)b||List.existsis_non_terminalprods->if(is_commas||is_operators)then(* Always a breakable space after comma or separator *)(None,UnpTerminals)::add_break_if_none1b(makebprods)elseifis_right_brackets&&is_next_terminalprodsthen(* Always no space after right bracked, but possibly a break *)(None,UnpTerminals)::add_break_if_none0b(makebprods)elseifis_left_brackets&&is_next_non_terminalbprodsthen(None,UnpTerminals)::makebprodselseifnot(is_next_breakprods)then(* Add rigid space, no break, unless user asked for something *)(None,UnpTerminal(s^" "))::makebprodselse(* Rely on user spaces *)(None,UnpTerminals)::makebprods|Terminals::prods->(* Separate but do not cut a trailing sequence of terminal *)(matchprodswith|Terminal_::_->(None,UnpTerminal(s^" "))::makebprods|_->(None,UnpTerminals)::makebprods)|Breakn::prods->add_breakn(makebprods)|SProdList(m,sl)::prods->leti=index_idmvarsinlettyp=List.nthtyps(i-1)inletprec,side=unparsing_precedence_of_entry_typefrom_leveltypinletsl'=(* If no separator: add a break *)ifList.is_emptyslthenadd_break1[](* We add NonTerminal for simulation but remove it afterwards *)elsemaketrueslinlethunk=matchtypwith|ETConstr_->UnpListMetaVar(prec,List.mapsndsl',side)|ETBinderisopen->check_open_binderisopenslm;UnpBinderListMetaVar(isopen,true,List.mapsndsl')|ETName|ETIdent->UnpBinderListMetaVar(false,true,List.mapsndsl')|ETPattern_->UnpBinderListMetaVar(false,false,List.mapsndsl')|_->assertfalsein(None,hunk)::make_with_spacebprods|[]->[]andmake_with_spacebprods=matchprodswith|Terminals'::prods'->ifis_operators'then(* A rigid space before operator and a breakable after *)(None,UnpTerminal(" "^s'))::add_break_if_none1b(makebprods')elseifis_commas'then(* No space whatsoever before comma *)makebprodselseifis_right_brackets'thenmakebprodselse(* A breakable space between any other two terminals *)add_break_if_none1b(makebprods)|(NonTerminal_|SProdList_)::_->(* A breakable space before a non-terminal *)add_break_if_none1b(makebprods)|Break_::_->(* Rely on user wish *)makebprods|[]->[]inmakefalsesymbols(* Build default printing rules from explicit format *)leterror_format?loc()=user_err?locPp.(str"The format does not match the notation.")letwarn_format_break=CWarnings.create~name:"notation-both-format-and-spaces"~category:CWarnings.CoreCategories.parsing(fun()->strbrk"Discarding format implicitly indicated by multiple spaces in notation because an explicit format modifier is given.")lethas_ldotsl=List.exists(function(_,UnpTerminals)->String.equals(Id.to_stringNotation_ops.ldots_var)|_->false)lletrecsplit_format_at_ldotshd=function|(loc,UnpTerminals)::fmtwhenString.equals(Id.to_stringNotation_ops.ldots_var)->loc,List.revhd,fmt|u::fmt->check_no_ldots_in_boxu;split_format_at_ldots(u::hd)fmt|[]->raise_notraceExitandcheck_no_ldots_in_box=function|(_,UnpBox(_,fmt))->(tryletloc,_,_=split_format_at_ldots[]fmtinuser_err?locPp.(str("The special symbol \"..\" must occur at the same formatting depth than the variables of which it is the ellipse."))withExit->())|_->()leterror_not_same?loc()=user_err?locPp.(str"The format is not the same on the right- and left-hand sides of the special token \"..\".")letfind_prod_list_locsfmtfmt=(* [fmt] is some [UnpTerminal x :: sfmt @ UnpTerminal ".." :: sfmt @ UnpTerminal y :: rest] *)ifList.is_emptysfmtthen(* No separators; we highlight the sequence "x .." *)Loc.merge_opt(fst(List.hdfmt))(fst(List.hd(List.tlfmt)))else(* A separator; we highlight the separating sequence *)Loc.merge_opt(fst(List.hdsfmt))(fst(List.lastsfmt))letis_blanks=letn=String.lengthsinletrecauxis=i>=n||s.[i]=' '&&aux(i+1)sinaux0sletis_formatting=function|(_,UnpCut_)->true|(_,UnpTerminals)->is_blanks|_->falseletrecis_var_in_recursive_format=function|(_,UnpTerminals)whennot(is_blanks)->true|(loc,UnpBox(b,l))->(matchList.filter(funa->not(is_formattinga))lwith|[a]->is_var_in_recursive_formata|_->error_not_same?loc())|_->falseletreccheck_eq_var_upto_name=function|(_,UnpTerminals1),(_,UnpTerminals2)whennot(is_blanks1&&is_blanks2)||s1=s2->()|(_,UnpBox(b1,l1)),(_,UnpBox(b2,l2))whenb1=b2->List.itercheck_eq_var_upto_name(List.combinel1l2)|(_,UnpCutb1),(_,UnpCutb2)whenb1=b2->()|_,(loc,_)->error_not_same?loc()letskip_var_in_recursive_format=function|a::slwhenis_var_in_recursive_formata->a,sl|(loc,_)::_->error_not_same?loc()|[]->assertfalseletread_recursive_formatslfmt=(* Turn [[UnpTerminal s :: some-list @ UnpTerminal ".." :: same-some-list @ UnpTerminal s' :: rest] *)(* into [(some-list,rest)] *)letget_headfmt=letvar,sl=skip_var_in_recursive_formatfmtintryvar,split_format_at_ldots[]slwithExit->error_not_same?loc:(fst(List.last(ifsl=[]thenfmtelsesl)))()inletrecget_tail=function|(loc,a)::sepfmt,(_,b)::fmtwhen(=)ab->get_tail(sepfmt,fmt)(* FIXME *)|[],tail->skip_var_in_recursive_formattail|(loc,_)::_,([]|(_,UnpTerminal_)::_)->error_not_same?loc()|_,(loc,_)::_->error_not_same?loc()inletvar1,(loc,slfmt,fmt)=get_headfmtinletvar2,res=get_tail(slfmt,fmt)incheck_eq_var_upto_name(var1,var2);(* To do, though not so important: check that the names match
the names in the notation *)slfmt,reslethunks_of_format(from_level,(vars,typs))symfmt=letrecaux=function|symbs,(_,(UnpTerminals'asu))::fmtwhenString.equals'(String.make(String.lengths')' ')->letsymbs,l=aux(symbs,fmt)insymbs,u::l|Terminals::symbs,(_,UnpTerminals')::fmtwhenString.equals(String.drop_simple_quotess')->letsymbs,l=aux(symbs,fmt)insymbs,UnpTerminals::l|NonTerminals::symbs,(_,UnpTerminals')::fmtwhenId.equals(Id.of_strings')->leti=index_idsvarsinletsymbs,l=aux(symbs,fmt)insymbs,unparsing_metavarifrom_leveltyps::l|symbs,(_,(UnpCut_asu))::fmt->letsymbs,l=aux(symbs,fmt)insymbs,u::l|SProdList(m,sl)::symbs,fmtwhenhas_ldotsfmt->leti=index_idmvarsinlettyp=List.nthtyps(i-1)inletprec,side=unparsing_precedence_of_entry_typefrom_leveltypinletloc_slfmt,rfmt=read_recursive_formatslfmtinletsl,slfmt=aux(sl,loc_slfmt)inifnot(List.is_emptysl)thenerror_format?loc:(find_prod_list_locloc_slfmtfmt)();letsymbs,l=aux(symbs,rfmt)inlethunk=matchtypwith|ETConstr_->UnpListMetaVar(prec,slfmt,side)|ETBinderisopen->check_open_binderisopenslm;UnpBinderListMetaVar(isopen,true,slfmt)|ETName|ETIdent->UnpBinderListMetaVar(false,true,slfmt)|ETPattern_->UnpBinderListMetaVar(false,false,slfmt)|_->assertfalseinsymbs,hunk::l|symbs,(_,UnpBox(a,b))::fmt->letsymbs',b'=aux(symbs,b)inletsymbs',l=aux(symbs',fmt)insymbs',UnpBox(a,List.map(funx->(None,x))b')::l|symbs,[]->symbs,[]|Break_::symbs,fmt->warn_format_break();aux(symbs,fmt)|_,fmt->error_format?loc:(fst(List.hdfmt))()inmatchauxsymfmtwith|[],l->l|_->error_format()(**********************************************************************)(* Build parsing rules *)letassoc_of_typefromn(_,typ)=precedence_of_entry_type(from,n)typletis_not_small_constr=functionETProdConstr_->true|_->falseletdistributeall=List.map(funl->a@l)ll(* Expand LIST1(t,sep);sep;t;...;t (with the trailing pattern
occurring p times, possibly p=0) into the combination of
t;sep;t;...;t;sep;t (p+1 times)
t;sep;t;...;t;sep;t;sep;t (p+2 times)
...
t;sep;t;...;t;sep;t;...;t;sep;t (p+n times)
t;sep;t;...;t;sep;t;...;t;sep;t;LIST1(t,sep) *)letexpand_list_rulestyptklxnpll=letcamlp5_message_name=Some(add_suffixx("_"^string_of_intn))inletmain=GramConstrNonTerminal(ETProdConstr(s,typ),camlp5_message_name)inlettks=List.map(fun(kw,s)->GramConstrTerminal(kw,s))tklinletrecauxihdsll=ifi<pthenaux(i+1)(main::tks@hds)llelseifInt.equali(p+n)thenlethds=GramConstrListMark(p+n,true,p)::hds@[GramConstrNonTerminal(ETProdConstrList(s,typ,tkl),Somex)]indistributehdsllelsedistribute(GramConstrListMark(i+1,false,p)::hds@[main])ll@aux(i+1)(main::tks@hds)llinaux0[]llletis_constr_typ(s,lev)xetyps=matchList.assocxetypswith(* TODO: factorize these rules with the ones computing the effective
sublevel sent to camlp5, so as to include the case of
DefaultLevel which are valid *)|ETConstr(s',_,(lev',InternalProd|(NumLevel_|NextLevelaslev'),_))->notation_entry_eqss'&&production_level_eqlevlev'|_->falseletinclude_possible_similar_trailing_patterntypetypssll=letrecauxn=function|Terminals::sl,Terminals'::l'whens=s'->auxn(sl,l')|[],NonTerminalx::l'whenis_constr_typtypxetyps->try_auxnl'|Break_::sl,l->auxn(sl,l)|sl,Break_::l->auxn(sl,l)|_->raise_notraceExitandtry_auxnl=tryaux(n+1)(sl,l)withExit->n,lintry_aux0lletprod_entry_type=function|ETIdent->ETProdIdent|ETName->ETProdName|ETGlobal->ETProdGlobal|ETBigint->ETProdBigint|ETBindero->ETProdOneBindero|ETConstr(s,_,p)->ETProdConstr(s,p)|ETPattern(_,n)->ETProdPattern(pattern_entry_leveln)letkeyword_neededneeds=(* Ensure that IDENT articulation terminal symbols are keywords *)matchPcoq.terminalswith|Tok.PIDENT(Somek)->ifneedthenFlags.if_verboseFeedback.msg_info(str"Identifier '"++strk++str"' now a keyword");need|_->matchNumTok.Unsigned.parse_stringswith|Somen->ifneedthenFlags.if_verboseFeedback.msg_info(str"Number '"++NumTok.Unsigned.printn++str"' now a keyword");need|_->trueletmake_production(_,lev,_)etypssymbols=letrecauxneed=function|[]->[[]]|NonTerminalm::l->lettyp=prod_entry_type(List.assocmetyps)indistribute[GramConstrNonTerminal(typ,Somem)](aux(is_not_small_constrtyp)l)|Terminals::l->letkeyword=keyword_neededneedsindistribute[GramConstrTerminal(keyword,s)](auxfalsel)|Break_::l->auxneedl|SProdList(x,sl)::l->lettkl=List.flatten(List.map(functionTerminals->[s]|Break_->[]|_->anomaly(Pp.str"Found a non terminal token in recursive notation separator."))sl)inlettkl=List.map_i(funix->letneed=(i=0)in(keyword_neededneedx,x))0tklinmatchList.assocxetypswith|ETConstr(s,_,(lev,_astyp))->letp,l'=include_possible_similar_trailing_pattern(s,lev)etypssllinexpand_list_rulestyptklx1p(auxtruel')|ETBindero->check_open_binderoslx;lettyp=ifothen(assert(tkl=[]);ETBinderOpen)elseETBinderClosed(None,tkl)indistribute[GramConstrNonTerminal(ETProdBinderListtyp,Somex)](auxfalsel)|ETIdent->distribute[GramConstrNonTerminal(ETProdBinderList(ETBinderClosed(SomeETProdIdent,tkl)),Somex)](auxfalsel)|ETName->distribute[GramConstrNonTerminal(ETProdBinderList(ETBinderClosed(SomeETProdName,tkl)),Somex)](auxfalsel)|ETPattern(st,n)->distribute[GramConstrNonTerminal(ETProdBinderList(ETBinderClosed(Some(ETProdPattern(pattern_entry_leveln)),tkl)),Somex)](auxfalsel)|_->user_errPp.(str"Components of recursive patterns in notation must be terms or binders.")inletneed=(* a leading ident/number factorizes iff at level 0 *)lev<>0inauxneedsymbolsletrecfind_symbolsc_currentc_nextc_last=function|[]->[]|NonTerminalid::sl->letprec=ifnot(List.is_emptysl)thenc_currentelsec_lastin(id,prec)::(find_symbolsc_nextc_nextc_lastsl)|Terminals::sl->find_symbolsc_nextc_nextc_lastsl|Breakn::sl->find_symbolsc_currentc_nextc_lastsl|SProdList(x,_)::sl'->(x,c_next)::(find_symbolsc_nextc_nextc_lastsl')letborder=function|(_,(ETConstr(_,_,(_,BorderProd(_,a)))))::_->a|_->Noneletrecompute_assoctyps=letopenGramlib.Gramextinmatchbordertyps,border(List.revtyps)with|SomeLeftA,SomeRightA->assertfalse|SomeLeftA,_->SomeLeftA|_,SomeRightA->SomeRightA|_->None(**************************************************************************)(* Registration of syntax extensions (parsing/printing, no interpretation)*)letpr_arg_levelfrom(lev,typ)=letpplev=function|LevelLtnwhenInt.equalnfrom->spc()++str"at next level"|LevelLen->spc()++str"at level "++intn|LevelLtn->spc()++str"at level below "++intn|LevelSome->mt()inPpvernac.pr_set_entry_type(fun_->(*TO CHECK*)mt())typ++pplevlevletpr_levelntn(from,fromlevel,args)typs=(matchfromwithInConstrEntry->mt()|InCustomEntrys->str"in "++strs++spc())++str"at level "++intfromlevel++spc()++str"with arguments"++spc()++prlist_with_seppr_comma(pr_arg_levelfromlevel)(List.combineargstyps)leterror_incompatible_levelntnoldprecoldtypsprectyps=user_err(str"Notation "++pr_notationntn++str" is already defined"++spc()++pr_levelntnoldprecoldtyps++spc()++str"while it is now required to be"++spc()++pr_levelntnprectyps++str".")leterror_parsing_incompatible_levelntnntn'oldprecoldtypsprectyps=user_err(str"Notation "++pr_notationntn++str" relies on a parsing rule for "++pr_notationntn'++spc()++str" which is already defined"++spc()++pr_levelntnoldprecoldtyps++spc()++str"while it is now required to be"++spc()++pr_levelntnprectyps++str".")letwarn_incompatible_format=CWarnings.create~name:"notation-incompatible-format"~category:CWarnings.CoreCategories.parsing(fun(specific,ntn)->lethead,scope=matchspecificwith|None->str"Notation",mt()|SomeLastLonelyNotation->str"Lonely notation",mt()|Some(NotationInScopesc)->str"Notation",strbrk(" in scope "^sc)inhead++spc()++pr_notationntn++strbrk" was already defined with a different format"++scope++str".")typesyntax_extension={synext_level:level;synext_nottyps:constr_entry_keylist;synext_notgram:notation_grammaroption;synext_notprint:generic_notation_printing_rulesoption;}typesyntax_rules=|PrimTokenSyntax|SpecificSyntaxofsyntax_extensionletsyntax_rules_iterf=function|PrimTokenSyntax->()|SpecificSyntaxsynext->fsynextletcheck_reserved_formatntnrulesrules'=trylet{notation_printing_reserved=reserved;notation_printing_rules=generic_rules}=rulesinifreserved&&(not(List.for_all2equnparsing_eqrules'.notation_printing_unparsinggeneric_rules.notation_printing_unparsing))thenwarn_incompatible_format(None,ntn)withNot_found->()letspecific_format_to_declare(specific,ntnasspecific_ntn)rules=tryletspecific_rules=Ppextend.find_specific_notation_printing_rulespecific_ntninifnot(List.for_all2equnparsing_eqrules.notation_printing_unparsingspecific_rules.notation_printing_unparsing)then(warn_incompatible_format(Somespecific,ntn);true)elsefalsewithNot_found->truetypesyntax_extension_obj=locality_flag*(notation*syntax_extension)letcheck_and_extend_constr_grammarntnrule=tryletntn_for_grammar=rule.notgram_notationinifnotation_eqntnntn_for_grammarthenraiseNot_found;letprec=rule.notgram_levelinlettyps=rule.notgram_typsinletoldprec=Notation.level_of_notationntn_for_grammarinletoldparsing=trySome(Notgram_ops.grammar_of_notationntn_for_grammar)withNot_found->Noneinletoldtyps=Notgram_ops.non_terminals_of_notationntn_for_grammarinifnot(level_eqprecoldprec)&&oldparsing<>Nonethenerror_parsing_incompatible_levelntnntn_for_grammaroldprecoldtypsprectyps;ifoldparsing=NonethenraiseNot_foundwithNot_found->Egramcoq.extend_constr_grammarruleletcache_one_syntax_extension(ntn,synext)=letprec=synext.synext_levelin(* Check and ensure that the level and the precomputed parsing rule is declared *)letoldparsing=tryletoldprec=Notation.level_of_notationntninletoldparsing=trySome(Notgram_ops.grammar_of_notationntn)withNot_found->Noneinletoldtyps=Notgram_ops.non_terminals_of_notationntninifnot(level_eqprecoldprec&&List.for_all2Extend.constr_entry_key_eqsynext.synext_nottypsoldtyps)&&(oldparsing<>None||synext.synext_notgram=None)thenerror_incompatible_levelntnoldprecoldtypsprecsynext.synext_nottyps;oldparsingwithNot_found->(* Declare the level and the precomputed parsing rule *)let()=Notation.declare_notation_levelntnprecinlet()=Notgram_ops.declare_notation_non_terminalsntnsynext.synext_nottypsinlet()=Option.iter(Notgram_ops.declare_notation_grammarntn)synext.synext_notgraminNonein(* Declare the parsing rule *)beginmatcholdparsing,synext.synext_notgramwith|None,Somegrams->List.iter(check_and_extend_constr_grammarntn)grams|_->(* The grammars rules are canonically derived from the string and the precedence*)()end;(* Printing *)Option.iter(declare_generic_notation_printing_rulesntn)synext.synext_notprintletcache_syntax_extension(_,sy)=cache_one_syntax_extensionsyletsubst_syntax_extension(subst,(local,(ntn,synext)))=(local,(ntn,synext))letclassify_syntax_definition(local,_)=iflocalthenDisposeelseSubstituteletopen_syntax_extensionio=ifInt.equali1thencache_syntax_extensionoletinSyntaxExtension:syntax_extension_obj->obj=declare_object{(default_object"SYNTAX-EXTENSION")withobject_stage=Summary.Stage.Synterp;open_function=simple_open~cat:notation_catopen_syntax_extension;cache_function=cache_syntax_extension;subst_function=subst_syntax_extension;classify_function=classify_syntax_definition}(**************************************************************************)(* Precedences *)(* Interpreting user-provided modifiers *)(* XXX: We could move this to the parser itself *)moduleNotationMods=structtypenotation_modifier={assoc:Gramlib.Gramext.g_assocoption;level:intoption;etyps:(Id.t*simple_constr_prod_entry_key)list;(* common to syn_data below *)format:lstringoption;}letdefault={assoc=None;level=None;etyps=[];format=None;}endexceptionUnknownCustomEntryofstringlet()=CErrors.register_handler@@function|UnknownCustomEntryentry->SomePp.(str"Unknown custom entry: "++strentry++str".")|_->Noneletcheck_custom_entryentry=ifnot(Egramcoq.exists_custom_entryentry)thenraise@@UnknownCustomEntryentryletcheck_entry_type=function|ETConstr(InCustomEntryentry,_,_)->check_custom_entryentry|ETConstr(InConstrEntry,_,_)|ETPattern_|ETIdent|ETGlobal|ETBigint|ETName|ETBinder_->()letinterp_modifiersentrymodl=letopenNotationModsinletrecinterpsubtypsacc=function|[]->subtyps,acc|CAst.{loc;v}::l->matchvwith|SetEntryType(s,typ)->letid=Id.of_stringsincheck_entry_typetyp;ifId.List.mem_associdacc.etypsthenuser_err?loc(strs++str" is already assigned to an entry or constr level.");interpsubtyps{accwithetyps=(id,typ)::acc.etyps;}l|SetItemLevel([],bko,n)->interpsubtypsaccl|SetItemLevel(s::idl,bko,n)->letid=Id.of_stringsinifId.List.mem_associdacc.etypsthenuser_err?loc(strs++str" is already assigned to an entry or constr level.");interp((id,bko,n)::subtyps)acc((CAst.make?loc@@SetItemLevel(idl,bko,n))::l)|SetLeveln->(matchentrywith|InCustomEntrys->ifacc.level<>Nonethenuser_err?loc(str("isolated \"at level "^string_of_intn^"\" unexpected."))elseuser_err?loc(str("use \"in custom "^s^" at level "^string_of_intn^"\"")++spc()++str"rather than"++spc()++str("\"at level "^string_of_intn^"\"")++spc()++str"isolated.")|InConstrEntry->ifacc.level<>Nonethenuser_err?loc(str"A level is already assigned.");interpsubtyps{accwithlevel=Somen;}l)|SetCustomEntry(s,Somen)->(* Note: name of entry already registered in interp_non_syntax_modifiers *)ifacc.level<>Nonethenuser_err?loc(str("isolated \"at level "^string_of_int(Option.getacc.level)^"\" unexpected."));interpsubtyps{accwithlevel=Somen}l|SetAssoca->ifnot(Option.is_emptyacc.assoc)thenuser_err?locPp.(str"An associativity is given more than once.");interpsubtyps{accwithassoc=Somea;}l|SetOnlyParsing|SetOnlyPrinting|SetCustomEntry(_,None)|SetFormat_|SetItemScope_->(* interpreted in interp_non_syntax_modifiers *)assertfalseinletsubtyps,mods=interp[]defaultmodlin(* interpret item levels wrt to main entry *)letextra_etyps=List.map(fun(id,bko,n)->(id,ETConstr(entry,bko,n)))subtypsin(* Temporary hack: "ETName false" (i.e. "ident" in deprecation phase) means "ETIdent" for custom entries *){modswithetyps=extra_etyps@mods.etyps}letcheck_useless_entry_typesrecvarsmainvarsetyps=letvars=let(l1,l2)=List.splitrecvarsinl1@l2@mainvarsinmatchList.filter(fun(x,etyp)->not(List.memxvars))etypswith|(x,_)::_->user_err(Id.printx++str" is unbound in the notation.")|_->()typenotation_main_data={onlyparsing:bool;onlyprinting:bool;deprecation:Deprecation.toption;entry:notation_entry;format:unparsingLoc.locatedlistoption;itemscopes:(Id.t*scope_name)list;}letwarn_only_parsing_reserved_notation=CWarnings.create~name:"irrelevant-reserved-notation-only-parsing"~category:CWarnings.CoreCategories.parsing(fun()->strbrk"The only parsing modifier has no effect in Reserved Notation.")letwarn_only_parsing_discarded_format=CWarnings.create~name:"discarded-format-only-parsing"~category:CWarnings.CoreCategories.parsing(fun()->strbrk"The format modifier has no effect for only-parsing notations.")leterror_onlyparsing_onlyprinting?loc=user_err?loc(str"A notation cannot be both \"only printing\" and \"only parsing\".")letset_onlyparsing?loc~reservedmain_data=ifreservedthen(warn_only_parsing_reserved_notation?loc();main_data)else(ifmain_data.onlyparsingthenuser_err?loc(str"\"only parsing\" is given more than once.");ifmain_data.onlyprintingthenerror_onlyparsing_onlyprinting?loc;{main_datawithonlyparsing=true})letset_onlyprinting?locmain_data=ifmain_data.onlyprintingthenuser_err?loc(str"\"only printing\" is given more than once.");ifmain_data.onlyparsingthenerror_onlyparsing_onlyprinting?loc;{main_datawithonlyprinting=true}letset_custom_entry?locmain_dataentry'=check_custom_entryentry';matchmain_data.entrywith|InConstrEntry->{main_datawithentry=InCustomEntryentry'}|_->user_err?loc(str"\"in custom\" is given more than once.")letwarn_irrelevant_format=CWarnings.create~name:"irrelevant-format-only-parsing"~category:CWarnings.CoreCategories.parsing(fun()->str"The format modifier is irrelevant for only-parsing rules.")letset_format?locmain_dataformat=ifnot(Option.is_emptymain_data.format)thenuser_err?locPp.(str"A format is given more than once.");letformat=ifmain_data.onlyparsingthen(warn_irrelevant_format?loc();None)elseSome(parse_formatformat)in{main_datawithformat}letset_item_scope?locmain_dataidssc=letitemscopes=List.map(funid->(Id.of_stringid,sc))ids@main_data.itemscopesinmatchList.duplicates(fun(id1,_)(id2,_)->Id.equalid1id2)itemscopeswith|(id,_)::_->user_err?loc(str"Notation scope for argument "++Id.printid++str" can be specified only once.")|[]->{main_datawithitemscopes}letinterp_non_syntax_modifiers~reserved~infix~abbrevdeprecationmods=letset(main_data,rest)=CAst.with_loc_val(fun?loc->function|SetOnlyParsing->ifnot(Option.is_emptymain_data.format)then(warn_only_parsing_discarded_format?loc();(main_data,rest))else(set_onlyparsing?loc~reservedmain_data,rest)|SetOnlyPrintingwhennotabbrev->(set_onlyprinting?locmain_data,rest)|SetCustomEntry(entry,None)whennotabbrev->(set_custom_entry?locmain_dataentry,rest)|SetCustomEntry(entry,Some_)asxwhennotabbrev->(set_custom_entrymain_dataentry,CAst.make?locx::rest)|SetEntryType_wheninfix->user_err?locPp.(str"Unexpected entry type in infix notation.")|SetItemLevel_wheninfix->user_err?locPp.(str"Unexpected entry level in infix notation.")|SetFormat(TextFormats)whennotabbrev->(set_format?locmain_datas,rest)|SetItemScope(ids,sc)->(set_item_scope?locmain_dataidssc,rest)|modif->(main_data,(CAst.make?locmodif)::rest))inletmain_data={onlyparsing=false;onlyprinting=false;deprecation;entry=InConstrEntry;format=None;itemscopes=[]}inList.fold_leftset(main_data,[])mods(* Check if an interpretation can be used for printing a cases printing *)lethas_no_binders_type=List.for_all(fun(_,(_,typ))->matchtypwith|NtnTypeBinder_|NtnTypeBinderList_->false|NtnTypeConstr|NtnTypeConstrList->true)(* Compute precedences from modifiers (or find default ones) *)letset_entry_typefromnetyps(x,typ)=letmake_levns=matchtypwith|BorderProd_->NumLeveln|InternalProd->DefaultLevelinlettyp=trymatchList.assocxetyps,typwith|ETConstr(s,bko,DefaultLevel),_->ifnotation_entry_eqfromsthenETConstr(s,bko,(make_levns,typ))elseETConstr(s,bko,(DefaultLevel,typ))|ETConstr(s,bko,n),BorderProd(left,_)->ETConstr(s,bko,(n,BorderProd(left,None)))|ETConstr(s,bko,n),InternalProd->ETConstr(s,bko,(n,InternalProd))|ETPattern(b,n),_->ETPattern(b,n)|(ETIdent|ETName|ETBigint|ETGlobal|ETBinder_asx),_->xwithNot_found->ETConstr(from,None,(make_levnfrom,typ))in(x,typ)letjoin_auxiliary_recursive_typesrecvarsetyps=List.fold_right(fun(x,y)typs->letxtyp=trySome(List.assocxetyps)withNot_found->Noneinletytyp=trySome(List.assocyetyps)withNot_found->Noneinmatchxtyp,ytypwith|None,None->typs|Some_,None->typs|None,Someytyp->(x,ytyp)::typs|Somextyp,Someytypwhen(=)xtypytyp->typs(* FIXME *)|Somextyp,Someytyp->user_err(strbrk"In "++Id.printx++str" .. "++Id.printy++strbrk", both ends have incompatible types."))recvarsetypsletinternalization_type_of_entry_type=function|ETBinder_|ETConstr(_,Some_,_)->NtnInternTypeOnlyBinder|ETConstr(_,None,_)|ETBigint|ETGlobal|ETIdent|ETName|ETPattern_->NtnInternTypeAnyNoneletmake_internalization_varsrecvarsmaintyps=letmaintyps=List.map(on_sndinternalization_type_of_entry_type)maintypsinletextratyps=List.map(fun(x,y)->(y,List.assocxmaintyps))recvarsinmaintyps@extratypsletmake_interpretation_typeisrecisbindingdefault_if_bindingtyp=matchtyp,isrecwith(* Parsed as constr, but interpreted as a specific kind of binder *)|ETConstr(_,Somebk,_),true->NtnTypeBinderList(NtnBinderParsedAsConstrbk)|ETConstr(_,Somebk,_),false->NtnTypeBinder(NtnBinderParsedAsConstrbk)(* Parsed as constr list but interpreted as the default kind of binder *)|ETConstr(_,None,_),truewhenisbinding->NtnTypeBinderList(NtnBinderParsedAsConstrdefault_if_binding)|ETConstr(_,None,_),falsewhenisbinding->NtnTypeBinder(NtnBinderParsedAsConstrdefault_if_binding)(* Parsed as constr, interpreted as constr *)|ETConstr(_,None,_),true->NtnTypeConstrList|ETConstr(_,None,_),false->NtnTypeConstr(* Different way of parsing binders, maybe interpreted also as
constr, but conventionally internally binders *)|ETIdent,true->NtnTypeBinderList(NtnBinderParsedAsSomeBinderKindAsIdent)|ETIdent,false->NtnTypeBinder(NtnBinderParsedAsSomeBinderKindAsIdent)|ETName,true->NtnTypeBinderList(NtnBinderParsedAsSomeBinderKindAsName)|ETName,false->NtnTypeBinder(NtnBinderParsedAsSomeBinderKindAsName)(* Parsed as ident/pattern, primarily interpreted as binder; maybe strict at printing *)|ETPattern(ppstrict,_),true->NtnTypeBinderList(NtnBinderParsedAsSomeBinderKind(ifppstrictthenAsStrictPatternelseAsAnyPattern))|ETPattern(ppstrict,_),false->NtnTypeBinder(NtnBinderParsedAsSomeBinderKind(ifppstrictthenAsStrictPatternelseAsAnyPattern))|ETBinder_,true->NtnTypeBinderListNtnBinderParsedAsBinder|ETBinder_,false->NtnTypeBinderNtnBinderParsedAsBinder(* Others *)|ETBigint,true|ETGlobal,true->NtnTypeConstrList|ETBigint,false|ETGlobal,false->NtnTypeConstrletentry_relative_level_of_constr_prod_entryfrom_level=function|ETConstr(entry,_,(_,y))asx->letside=matchywithBorderProd(side,_)->Someside|_->Nonein(entry,(precedence_of_entry_typefrom_levelx,side))|_->InConstrEntry,(LevelSome,None)(*??*)letmake_interpretation_vars(* For binders, default is to parse only as an ident *)?(default_if_binding=AsName)recvarsallvars(from,level,_)typs=leteq_subscope(sc1,l1)(sc2,l2)=List.equalString.equalsc1sc2&&List.equalString.equall1l2inletcheck(x,y)=let(_,scope1)=Id.Map.findxallvarsinlet(_,scope2)=Id.Map.findyallvarsinifnot(eq_subscopescope1scope2)thenerror_not_same_scopexyinlet()=List.itercheckrecvarsinletuseless_recvars=List.mapsndrecvarsinletmainvars=Id.Map.filter(funx_->not(Id.List.memxuseless_recvars))allvarsinId.Map.mapi(funx(isonlybinding,sc)->lettyp=Id.List.assocxtypsin((entry_relative_level_of_constr_prod_entry(from,level)typ,sc),make_interpretation_type(Id.List.mem_assocxrecvars)isonlybindingdefault_if_bindingtyp))mainvarsletcheck_rule_productivityl=ifList.for_all(functionNonTerminal_|Break_->true|_->false)lthenuser_errPp.(str"A notation must include at least one symbol.");if(matchlwithSProdList_::_->true|_->false)thenuser_errPp.(str"A recursive notation must start with at least one symbol.")letwarn_notation_bound_to_variable=CWarnings.create~name:"notation-bound-to-variable"~category:CWarnings.CoreCategories.parsing(fun()->strbrk"This notation will not be used for printing as it is bound to a single variable.")letwarn_non_reversible_notation=CWarnings.create~name:"non-reversible-notation"~category:CWarnings.CoreCategories.parsing(function|APrioriReversible->assertfalse|HasLtac->strbrk"This notation contains Ltac expressions: it will not be used for printing."|NonInjectiveids->letn=List.lengthidsinstrbrk(String.pluraln"Variable")++spc()++pr_enumId.printids++spc()++strbrk(ifn>1then"do"else"does")++str" not occur in the right-hand side."++spc()++strbrk"The notation will not be used for printing as it is not reversible.")letis_coercionleveltyps=matchlevel,typswith|Some(custom,n,_),[_,e]->(matche,customwith|ETConstr_,_->letentry=(custom,n)inletentry_relative=entry_relative_level_of_constr_prod_entryentryeinifis_coercionentryentry_relativethenSome(IsEntryCoercion(entry,entry_relative))elseNone|ETGlobal,InCustomEntrys->Some(IsEntryGlobal(s,n))|ETIdent,InCustomEntrys->Some(IsEntryIdent(s,n))|_->None)|Some_,_->assertfalse|None,_->Noneletprintabilityleveltypsvarsonlyparsingreversibility=function|NVaridwhenreversibility=APrioriReversible&&List.mem_assoc_fId.equalidvars->letcoe=is_coercionleveltypsinletonlyparsing=ifnotonlyparsing&&Option.is_emptycoethen(warn_notation_bound_to_variable();true)elseonlyparsinginonlyparsing,coe|_->(ifnotonlyparsing&&reversibility<>APrioriReversiblethen(warn_non_reversible_notationreversibility;true)elseonlyparsing),Noneletfind_precedencecustomlevetypssymbolsonlyprint=letfirst_symbol=letrecaux=function|Break_::t->auxt|h::t->Someh|[]->Noneinauxsymbolsinletlast_is_terminal()=letrecauxb=function|Break_::t->auxbt|Terminal_::t->auxtruet|_::t->auxfalset|[]->binauxfalsesymbolsinmatchfirst_symbolwith|None->[],0|Some(NonTerminalx)->lettest()=ifonlyprintthenifOption.is_emptylevthenuser_errPp.(str"Explicit level needed in only-printing mode when the level of the leftmost non-terminal is given.")else[],Option.getlevelseuser_errPp.(str"The level of the leftmost non-terminal cannot be changed.")in(trymatchList.assocxetyps,customwith|ETConstr(s,_,(NumLevel_|NextLevel)),s'whens=s'->test()|(ETIdent|ETName|ETBigint|ETGlobal),_->beginmatchlevwith|None->([fun()->Flags.if_verbose(Feedback.msg_info?loc:None)(strbrk"Setting notation at level 0.")],0)|Some0->([],0)|_->user_errPp.(str"A notation starting with an atomic expression must be at level 0.")end|(ETPattern_|ETBinder_),InConstrEntrywhennotonlyprint->(* Don't know exactly if we can make sense of this case *)user_errPp.(str"Binders or patterns not supported in leftmost position.")|(ETPattern_|ETBinder_|ETConstr_),_->(* Give a default ? *)ifOption.is_emptylevthenuser_errPp.(str"Need an explicit level.")else[],Option.getlevwithNot_found->ifOption.is_emptylevthenuser_errPp.(str"A left-recursive notation must have an explicit level.")else[],Option.getlev)|Some(Terminal_)whenlast_is_terminal()->ifOption.is_emptylevthen([fun()->Flags.if_verbose(Feedback.msg_info?loc:None)(strbrk"Setting notation at level 0.")],0)else[],Option.getlev|Some_->ifOption.is_emptylevthenuser_errPp.(str"Cannot determine the level.");[],Option.getlevletcheck_curly_brackets_notation_exists()=trylet_=Notation.level_of_notation(InConstrEntry,"{ _ }")in()withNot_found->user_errPp.(str"Notations involving patterns of the form \"{ _ }\" are treated \n\
specially and require that the notation \"{ _ }\" is already reserved.")(* Remove patterns of the form "{ _ }", unless it is the "{ _ }" notation *)letremove_curly_bracketsl=letrecskip_breakacc=function|Break_asbr::l->skip_break(br::acc)l|l->List.revacc,linletrecauxdeb=function|[]->[]|Terminal"{"ast1::l->letbr,next=skip_break[]lin(matchnextwith|NonTerminal_asx::l'->letbr',next'=skip_break[]l'in(matchnext'with|Terminal"}"ast2::l''->ifdeb&&List.is_emptyl''then[t1;x;t2]elsebegincheck_curly_brackets_notation_exists();x::auxfalsel''end|l1->t1::br@x::br'@auxfalsel1)|l0->t1::auxfalsel0)|x::l->x::auxfalselinauxtruellethas_implicit_formatsymbols=List.exists(functionBreak_->true|_->false)symbols(* Because of the special treatment for { }, the grammar rule sent
to the parser may be different than what the user sees; e.g. for
"{ A } + { B }", it is "A + B" which is sent to the parser *)typesyn_pa_data={ntn_for_grammar:notation;prec_for_grammar:level;typs_for_grammar:constr_entry_keylist;need_squash:bool;}moduleSynData=structtypesubentry_types=(Id.t*constr_entry_key)list(* XXX: Document *)typesyn_data={(* XXX: Callback to printing, must remove *)msgs:(unit->unit)list;(* Notation data for parsing *)level:level;subentries:subentry_types;pa_syntax_data:subentry_types*symbollist;pp_syntax_data:subentry_types*symbollist;not_data:syn_pa_data;}endletfind_subentry_typesfromnassocetypssymbols=lettyps=find_symbols(BorderProd(Left,assoc))(InternalProd)(BorderProd(Right,assoc))symbolsinletsy_typs=List.map(set_entry_typefromnetyps)typsinletprec=List.map(assoc_of_typefromn)sy_typsinsy_typs,precletcheck_locality_compatibilitylocalcustomi_typs=ifnotlocalthenletsubcustom=List.map_filter(function_,ETConstr(InCustomEntrys,_,_)->Somes|_->None)i_typsinletallcustoms=matchcustomwithInCustomEntrys->s::subcustom|_->subcustominList.iter(funs->ifEgramcoq.locality_of_custom_entrysthenuser_err(strbrk"Notation has to be declared local as it depends on custom entry "++strs++strbrk" which is local."))(List.uniquizeallcustoms)letcompute_syntax_data~localmain_datanotation_symbolsntnmods=letopenSynDatainletopenNotationModsinifmain_data.itemscopes<>[]thenuser_err(str"General notations don't support 'in scope'.");let{recvars;mainvars;symbols}=notation_symbolsinletassoc=Option.appendmods.assoc(SomeGramlib.Gramext.NonA)inlet_=check_useless_entry_typesrecvarsmainvarsmods.etypsin(* Notations for interp and grammar *)letmsgs,n=find_precedencemain_data.entrymods.levelmods.etypssymbolsmain_data.onlyprintinginletsymbols_for_grammar=ifmain_data.entry=InConstrEntrythenremove_curly_bracketssymbolselsesymbolsinletneed_squash=not(List.equalNotation.symbol_eqsymbolssymbols_for_grammar)inletntn_for_grammar=ifneed_squashthenmake_notation_keymain_data.entrysymbols_for_grammarelsentninifmain_data.entry=InConstrEntry&¬main_data.onlyprintingthencheck_rule_productivitysymbols_for_grammar;(* To globalize... *)letetyps=join_auxiliary_recursive_typesrecvarsmods.etypsinletsy_typs,prec=find_subentry_typesmain_data.entrynassocetypssymbolsinletsy_typs_for_grammar,prec_for_grammar=ifneed_squashthenfind_subentry_typesmain_data.entrynassocetypssymbols_for_grammarelsesy_typs,precincheck_locality_compatibilitylocalmain_data.entrysy_typs;letpa_sy_data=(sy_typs_for_grammar,symbols_for_grammar)inletpp_sy_data=(sy_typs,symbols)inletsy_fulldata={ntn_for_grammar;prec_for_grammar=(main_data.entry,n,prec_for_grammar);typs_for_grammar=List.mapsndsy_typs_for_grammar;need_squash}in(* Return relevant data for interpretation and for parsing/printing *){msgs;level=(main_data.entry,n,prec);subentries=sy_typs;pa_syntax_data=pa_sy_data;pp_syntax_data=pp_sy_data;not_data=sy_fulldata;}(**********************************************************************)(* Registration of notations interpretation *)typenotation_obj={notobj_local:bool;notobj_scope:scope_nameoption;notobj_interp:interpretation;notobj_coercion:entry_coercion_kindoption;notobj_use:notation_useoption;notobj_deprecation:Deprecation.toption;notobj_notation:notation*notation_location;notobj_specific_pp_rules:notation_printing_rulesoption;notobj_also_in_cases_pattern:bool;}letload_notation_commonsilently_define_scope_if_undefined_nobj=(* When the default shall be to require that a scope already exists *)(* the call to ensure_scope will have to be removed *)ifsilently_define_scope_if_undefinedthen(* Don't warn if the scope is not defined: *)(* there was already a warning at "cache" time *)Option.iterNotation.declare_scopenobj.notobj_scopeelseOption.iterNotation.ensure_scopenobj.notobj_scopeletload_notation=load_notation_commontrueletopen_notationinobj=ifInt.equali1thenbeginletscope=nobj.notobj_scopeinlet(ntn,df)=nobj.notobj_notationinletpat=nobj.notobj_interpinletdeprecation=nobj.notobj_deprecationinletscope=matchscopewithNone->LastLonelyNotation|Somesc->NotationInScopescinletalso_in_cases_pattern=nobj.notobj_also_in_cases_patternin(* Declare the notation *)(matchnobj.notobj_usewith|Someuse->Notation.declare_notation(scope,ntn)patdf~use~also_in_cases_patternnobj.notobj_coerciondeprecation|None->());(* Declare specific format if any *)(matchnobj.notobj_specific_pp_ruleswith|Somepp_sy->ifspecific_format_to_declare(scope,ntn)pp_sythenPpextend.declare_specific_notation_printing_rules(scope,ntn)pp_sy|None->())endletcache_notationo=load_notation_commonfalse1o;open_notation1oletsubst_notation(subst,nobj)={nobjwithnotobj_interp=subst_interpretationsubstnobj.notobj_interp;}letclassify_notationnobj=ifnobj.notobj_localthenDisposeelseSubstituteletinNotation:notation_obj->obj=declare_object{(default_object"NOTATION")withopen_function=simple_open~cat:notation_catopen_notation;cache_function=cache_notation;subst_function=subst_notation;load_function=load_notation;classify_function=classify_notation}(**********************************************************************)(* Registration of interpretation scopes opening/closing *)letopen_scopei(local,op,sc)=ifInt.equali1thenifopthenNotation.open_scopescelseNotation.close_scopescletcache_scopeo=open_scope1oletsubst_scope(subst,sc)=scletdischarge_scope(local,_,_aso)=iflocalthenNoneelseSomeoletclassify_scope(local,_,_)=iflocalthenDisposeelseSubstituteletinScope:bool*bool*scope_name->obj=declare_object{(default_object"SCOPE")withcache_function=cache_scope;open_function=simple_open~cat:notation_catopen_scope;subst_function=subst_scope;discharge_function=discharge_scope;classify_function=classify_scope}letopen_close_scopelocal~to_opensc=Lib.add_leaf(inScope(local,to_open,normalize_scopesc))(**********************************************************************)letwith_lib_stk_protectionfx=letfs=Lib.Interp.freeze()intryleta=fxinLib.Interp.unfreezefs;awithreraise->letreraise=Exninfo.capturereraiseinlet()=Lib.Interp.unfreezefsinExninfo.iraisereraiseletwith_syntax_protectionfx=with_lib_stk_protection(Pcoq.with_grammar_rule_protection(with_notation_protectionf))x(**********************************************************************)(* Recovering existing syntax *)exceptionNoSyntaxRuleletrecover_notation_syntaxntn=tryletprec=Notation.level_of_notationntninletpa_typs=Notgram_ops.non_terminals_of_notationntninletpa_rule=trySome(Notgram_ops.grammar_of_notationntn)withNot_found->Noneinletpp_rule=trySome(find_generic_notation_printing_rulentn)withNot_found->Nonein{synext_level=prec;synext_nottyps=pa_typs;synext_notgram=pa_rule;synext_notprint=pp_rule;}withNot_found->raiseNoSyntaxRuleletrecover_squash_syntaxsy=letsq=recover_notation_syntax(InConstrEntry,"{ _ }")inmatchsq.synext_notgramwith|Somegram->sy::gram|None->raiseNoSyntaxRule(**********************************************************************)(* Main entry point for building parsing and printing rules *)letmake_pa_rule(typs,symbols)parsing_data=let{ntn_for_grammar;prec_for_grammar;typs_for_grammar;need_squash}=parsing_datainletassoc=recompute_assoctypsinletprod=make_productionprec_for_grammartypssymbolsinletsy={notgram_level=prec_for_grammar;notgram_assoc=assoc;notgram_notation=ntn_for_grammar;notgram_prods=prod;notgram_typs=typs_for_grammar;}in(* By construction, the rule for "{ _ }" is declared, but we need to
redeclare it because the file where it is declared needs not be open
when the current file opens (especially in presence of -nois) *)ifneed_squashthenrecover_squash_syntaxsyelse[sy]letmake_pp_rulelevel(typs,symbols)fmt=matchfmtwith|None->lethunks=make_hunkstypssymbolslevelinifList.exists(function_,(UnpCut(PpBrk_)|UnpListMetaVar_)->true|_->false)hunksthen[UnpBox(PpHOVB0,hunks)]else(* Optimization to work around what seems an ocaml Format bug (see Mantis #7804/#7807) *)List.mapsndhunks(* drop locations which are dummy *)|Somefmt->hunks_of_format(level,List.splittyps)(symbols,fmt)letmake_parsing_rulesmain_data(sd:SynData.syn_data)=letopenSynDatainifmain_data.onlyprintingthenNoneelseSome(make_pa_rulesd.pa_syntax_datasd.not_data)letmake_generic_printing_rulesreservedmain_datantnsd=letopenSynDatainletcustom,level,_=sd.levelinletmake_rulerule={notation_printing_reserved=reserved;notation_printing_rules={notation_printing_unparsing=rule;notation_printing_level=level;}}intryletrules=(Ppextend.find_generic_notation_printing_rulentn)inmatchmain_data.formatwith|Nonewhennot(has_implicit_format(sndsd.pp_syntax_data))->(* No intent to define a format, we reuse the existing generic rules *)Somerules|_->letrules'=make_rule(make_pp_rulelevelsd.pp_syntax_datamain_data.format)incheck_reserved_formatntnrulesrules'.notation_printing_rules;Somerules'withNot_found->Some(make_rule(make_pp_rulelevelsd.pp_syntax_datamain_data.format))letmake_syntax_rulesreservedmain_datantnsd=letopenSynDatain(* Prepare the parsing and printing rules *)letpa_rules=make_parsing_rulesmain_datasdinletpp_rules=make_generic_printing_rulesreservedmain_datantnsdin{synext_level=sd.level;synext_nottyps=List.mapsndsd.subentries;synext_notgram=pa_rules;synext_notprint=pp_rules;}(**********************************************************************)(* Main entry point for building specific printing rules *)letmake_specific_printing_rulesetypssymbolslevelpp_ruleformat=matchlevelwith|None->None|Some(_,level,_)->matchformat,pp_rulewith|None,Some_whennot(has_implicit_formatsymbols)->None|_->Some{notation_printing_unparsing=make_pp_rulelevel(etyps,symbols)format;notation_printing_level=level;}(**********************************************************************)(* Miscellaneous *)letwarn_unused_interpretation=CWarnings.create~name:"unused-notation"~category:CWarnings.CoreCategories.parsing(funb->strbrk"interpretation is used neither for printing nor for parsing, "++(ifbthenstrbrk"the declaration could be replaced by \"Reserved Notation\"."elsestrbrk"the declaration could be removed."))letmake_usereservedonlyparseonlyprint=matchonlyparse,onlyprintwith|false,false->SomeParsingAndPrinting|true,false->SomeOnlyParsing|false,true->SomeOnlyPrinting|true,true->warn_unused_interpretationreserved;None(**********************************************************************)(* Main functions about notations *)letmake_notation_interpretation~localmain_datanotation_symbolsntnsyntax_rulesdfenv?(impls=empty_internalization_env)cscope=let{recvars;mainvars;symbols}=notation_symbolsin(* Recover types of variables and pa/pp rules; redeclare them if needed *)letlevel,i_typs,main_data,sy_pp_rules=matchsyntax_ruleswith|PrimTokenSyntax->None,[],main_data,None|SpecificSyntaxsy->(* If the only printing flag has been explicitly requested, put it back *)letmain_data={main_datawithonlyprinting=main_data.onlyprinting||(sy.synext_notgram=None&¬main_data.onlyparsing)}inSomesy.synext_level,List.combinemainvarssy.synext_nottyps,main_data,sy.synext_notprintin(* Declare interpretation *)letsy_pp_rules=make_specific_printing_rulesi_typssymbolslevelsy_pp_rulesmain_data.formatinletpath=(Lib.library_dp(),Lib.current_dirpathtrue)inletdf'=ntn,(path,df)inleti_vars=make_internalization_varsrecvarsi_typsinletnenv={ninterp_var_type=Id.Map.of_listi_vars;ninterp_rec_vars=Id.Map.of_listrecvars;}inlet(acvars,ac,reversibility)=interp_notation_constrenv~implsnenvcinletplevel=matchlevelwithSome(from,level,l)->(from,level,l)|None(* numeral: irrelevant )*)->(InConstrEntry,0,[])inletinterp=make_interpretation_varsrecvarsacvarspleveli_typsinletmap(x,_)=trySome(x,Id.Map.findxinterp)withNot_found->Noneinletvars=List.map_filtermapi_varsin(* Order of elements is important here! *)letalso_in_cases_pattern=has_no_binders_typevarsinletonlyparsing,coe=printabilityleveli_typsvarsmain_data.onlyparsingreversibilityacinletmain_data={main_datawithonlyparsing}inletuse=make_usefalseonlyparsingmain_data.onlyprintingin{notobj_local=local;notobj_scope=scope;notobj_use=use;notobj_interp=(vars,ac);notobj_coercion=coe;notobj_deprecation=main_data.deprecation;notobj_notation=df';notobj_specific_pp_rules=sy_pp_rules;notobj_also_in_cases_pattern=also_in_cases_pattern;}(* Notations without interpretation (Reserved Notation) *)letadd_reserved_notation~local~infix({CAst.loc;v=df},mods)=letopenSynDatainlet(main_data,mods)=interp_non_syntax_modifiers~reserved:true~infix~abbrev:falseNonemodsinletmods=interp_modifiersmain_data.entrymodsinletnotation_symbols,isnumeral=analyze_notation_tokens~onlyprinting:main_data.onlyprinting~infixmain_data.entrydfinletnotation_symbols=ifinfixthenadjust_reserved_infix_notationnotation_symbolselsenotation_symbolsinletntn=make_notation_keymain_data.entrynotation_symbols.symbolsinifisnumeralthenuser_err?loc(str"Notations for numbers are primitive and need not be reserved.");letsd=compute_syntax_data~localmain_datanotation_symbolsntnmodsinletsynext=make_syntax_rulestruemain_datantnsdinList.iter(funf->f())sd.msgs;Lib.add_leaf(inSyntaxExtension(local,(ntn,synext)))typenotation_interpretation_decl=notation_declaration*notation_main_data*notation_symbols*notation*syntax_rules(* Notations associated to a where clause *)letprepare_where_notationntn_decl=let{ntn_decl_string={CAst.loc;v=df};ntn_decl_interp=c;ntn_decl_modifiers=modifiers;ntn_decl_scope=sc;}=ntn_declinlet(main_data,mods)=interp_non_syntax_modifiers~reserved:false~infix:false~abbrev:falseNonemodifiersinmatchmodswith|_::_->CErrors.user_err(str"Only modifiers not affecting parsing are supported here.")|[]->letnotation_symbols,isnumeral=analyze_notation_tokens~onlyprinting:main_data.onlyprinting~infix:falsemain_data.entrydfinletntn=make_notation_keymain_data.entrynotation_symbols.symbolsinletsyntax_rules=ifisnumeralthenPrimTokenSyntaxelsetrySpecificSyntax(recover_notation_syntaxntn)withNoSyntaxRule->user_errPp.(str"Parsing rule for this notation has to be previously declared.")in(ntn_decl,main_data,notation_symbols,ntn,syntax_rules)letadd_notation_interpretation~localenv(ntn_decl,main_data,notation_symbols,ntn,syntax_rules)=let{ntn_decl_string={CAst.loc;v=df};ntn_decl_interp=c;ntn_decl_scope=sc}=ntn_declinletnotation=make_notation_interpretation~localmain_datanotation_symbolsntnsyntax_rulesdfenvcscinLib.add_leaf(inNotationnotation);Dumpglob.dump_notation(CAst.make?locntn)sctrue(* interpreting a where clause *)letset_notation_for_interpretationenvimpls(ntn_decl,main_data,notation_symbols,ntn,syntax_rules)=let{ntn_decl_string={CAst.loc;v=df};ntn_decl_interp=c;ntn_decl_scope=sc}=ntn_declinletnotation=make_notation_interpretation~local:truemain_datanotation_symbolsntnsyntax_rulesdfenv~implscscinLib.add_leaf(inNotationnotation);Option.iter(funsc->Lib.add_leaf(inScope(false,true,sc)))scletbuild_notation_syntax~local~infixdeprecationntn_decl=let{ntn_decl_string={CAst.loc;v=df};ntn_decl_modifiers=modifiers;ntn_decl_interp=c}=ntn_declin(* Extract the modifiers not affecting the parsing rule *)let(main_data,syntax_modifiers)=interp_non_syntax_modifiers~reserved:false~infix~abbrev:falsedeprecationmodifiersin(* Extract the modifiers not affecting the parsing rule *)letnotation_symbols,isnumeral=analyze_notation_tokens~onlyprinting:main_data.onlyprinting~infixmain_data.entrydfin(* Add variables on both sides if an infix notation *)letdf,notation_symbols,c=ifinfixthenadjust_infix_notationdfnotation_symbolscelsedf,notation_symbols,cin(* Build the canonical identifier of the syntactic part of the notation *)letntn=make_notation_keymain_data.entrynotation_symbols.symbolsinletsyntax_rules=ifisnumeralthen(check_no_syntax_modifiers_for_numeralsyntax_modifiers;PrimTokenSyntax)elsematchsyntax_modifierswith|[]->(* No syntax data: try to rely on a previously declared rule *)(trySpecificSyntax(recover_notation_syntaxntn)withNoSyntaxRule->(* Try to determine a default syntax rule *)letsd=compute_syntax_data~localmain_datanotation_symbolsntnNotationMods.defaultinSpecificSyntax(make_syntax_rulesfalsemain_datantnsd))|_->letmods=interp_modifiersmain_data.entrysyntax_modifiersinletsd=compute_syntax_data~localmain_datanotation_symbolsntnmodsinSpecificSyntax(make_syntax_rulesfalsemain_datantnsd)inmain_data,notation_symbols,ntn,syntax_rules,c,dfletadd_notation_syntax~local~infixdeprecationntn_decl=(* Build or rebuild the syntax rules *)letmain_data,notation_symbols,ntn,syntax_rules,c,df=build_notation_syntax~local~infixdeprecationntn_declin(* Declare syntax *)syntax_rules_iter(funsy->Lib.add_leaf(inSyntaxExtension(local,(ntn,sy))))syntax_rules;letntn_decl_string=CAst.make?loc:ntn_decl.ntn_decl_string.CAst.locdfinletntn_decl={ntn_declwithntn_decl_interp=c;ntn_decl_string}inntn_decl,main_data,notation_symbols,ntn,syntax_rules(**********************************************************************)(* Scopes, delimiters and classes bound to scopes *)typescope_command=|ScopeDeclare|ScopeDelimAddofstring|ScopeDelimRemove|ScopeClassesofadd_scope_whereoption*scope_classlistletload_scope_command_commonsilently_define_scope_if_undefined_(local,scope,o)=letdeclare_scope_if_needed=ifsilently_define_scope_if_undefinedthenNotation.declare_scopeelseNotation.ensure_scopeinmatchowith|ScopeDeclare->Notation.declare_scopescope(* When the default shall be to require that a scope already exists *)(* the call to declare_scope_if_needed will have to be removed below *)|ScopeDelimAdddlm->declare_scope_if_neededscope|ScopeDelimRemove->declare_scope_if_neededscope|ScopeClasses_->declare_scope_if_neededscopeletload_scope_command=load_scope_command_commontrueletopen_scope_commandi(local,scope,o)=ifInt.equali1thenmatchowith|ScopeDeclare->()|ScopeDelimAdddlm->Notation.declare_delimitersscopedlm|ScopeDelimRemove->Notation.remove_delimitersscope|ScopeClasses(where,cl)->List.iter(Notation.declare_scope_classscope?where)clletcache_scope_commando=load_scope_command_commonfalse1o;open_scope_command1oletsubst_scope_command(subst,(local,scope,oasx))=matchowith|ScopeClasses(where,cl)->letenv=Global.env()inletcl'=List.map_filter(subst_scope_classenvsubst)clinletcl'=ifList.for_all2eq(==)clcl'thenclelsecl'inlocal,scope,ScopeClasses(where,cl')|_->xletclassify_scope_command(local,_,_)=iflocalthenDisposeelseSubstituteletinScopeCommand:locality_flag*scope_name*scope_command->obj=declare_object{(default_object"DELIMITERS")withcache_function=cache_scope_command;open_function=simple_open~cat:notation_catopen_scope_command;load_function=load_scope_command;subst_function=subst_scope_command;classify_function=classify_scope_command}letdeclare_scopelocalscope=Lib.add_leaf(inScopeCommand(local,scope,ScopeDeclare))letadd_delimiterslocalscopekey=Lib.add_leaf(inScopeCommand(local,scope,ScopeDelimAddkey))letremove_delimiterslocalscope=Lib.add_leaf(inScopeCommand(local,scope,ScopeDelimRemove))letadd_class_scopelocalscopewherecl=Lib.add_leaf(inScopeCommand(local,scope,ScopeClasses(where,cl)))letinterp_abbreviation_modifiersdeprecationmodl=letmods,skipped=interp_non_syntax_modifiers~reserved:false~infix:false~abbrev:truedeprecationmodlinifskipped<>[]then(letmodifier=List.hdskippedinuser_err?loc:modifier.CAst.loc(str"Abbreviations don't support "++Ppvernac.pr_syntax_modifiermodifier));(mods.onlyparsing,mods.itemscopes)letadd_abbreviation~localdeprecationenvident(vars,c)modl=let(only_parsing,scopes)=interp_abbreviation_modifiersdeprecationmodlinletvars=List.map(funv->v,List.assoc_optvscopes)varsinletacvars,pat,reversibility=matchvars,intern_name_aliascwith|[],Some(r,u)->(* Check if abbreviation to a name and avoid early insertion of
maximal implicit arguments *)Id.Map.empty,NRef(r,u),APrioriReversible|_->letfoldaccu(id,scope)=Id.Map.addid(NtnInternTypeAnyscope)accuinleti_vars=List.fold_leftfoldId.Map.emptyvarsinletnenv={ninterp_var_type=i_vars;ninterp_rec_vars=Id.Map.empty;}ininterp_notation_constrenvnenvcinletlevel_arg=NumLevel9(* level of arguments of an application *)inletin_pat(id,_)=(id,ETConstr(Constrexpr.InConstrEntry,None,(level_arg,InternalProd)))inletlevel=(InConstrEntry,0,[])inletinterp=make_interpretation_vars~default_if_binding:AsAnyPattern[]acvarslevel(List.mapin_patvars)inletvars=List.map(fun(x,_)->(x,Id.Map.findxinterp))varsinletalso_in_cases_pattern=has_no_binders_typevarsinletonlyparsing=only_parsing||fst(printabilityNone[]varsfalsereversibilitypat)inAbbreviation.declare_abbreviation~local~also_in_cases_patterndeprecationident~onlyparsing(vars,pat)(**********************************************************************)(* Activating/deactivating notations *)letload_notation_toggle__=()letopen_notation_toggle_(local,(on,all,pat))=letenv=Global.env()inletsigma=Evd.from_envenvintoggle_notations~on~all(Constrextern.without_symbols(Printer.pr_glob_constr_envenvsigma))patletcache_notation_toggleo=load_notation_toggle1o;open_notation_toggle1oletsubst_notation_toggle(subst,(local,(on,all,pat)))=let{notation_entry_pattern;interp_rule_key_pattern;use_pattern;scope_pattern;interpretation_pattern}=patinletinterpretation_pattern=Option.map(subst_interpretationsubst)interpretation_patterninletinterp_rule_key_pattern=interp_rule_key_patternin(local,(on,all,{notation_entry_pattern;interp_rule_key_pattern;use_pattern;scope_pattern;interpretation_pattern}))letclassify_notation_toggle(local,_)=iflocalthenDisposeelseSubstituteletinNotationActivation:locality_flag*(bool*bool*notation_query_pattern)->obj=declare_object{(default_object"NOTATION-TOGGLE")withcache_function=cache_notation_toggle;open_function=simple_openopen_notation_toggle;load_function=load_notation_toggle;subst_function=subst_notation_toggle;classify_function=classify_notation_toggle}letdeclare_notation_togglelocal~on~alls=Lib.add_leaf(inNotationActivation(local,(on,all,s)))(**********************************************************************)(* Declaration of custom entry *)letwarn_custom_entry=CWarnings.create~name:"custom-entry-overridden"~category:CWarnings.CoreCategories.parsing(funs->strbrk"Custom entry "++strs++strbrk" has been overridden.")letload_custom_entry_(local,s)=ifEgramcoq.exists_custom_entrysthenwarn_custom_entryselseEgramcoq.create_custom_entry~localsletcache_custom_entryo=load_custom_entry1oletsubst_custom_entry(subst,x)=xletclassify_custom_entry(local,s)=iflocalthenDisposeelseSubstituteletinCustomEntry:locality_flag*string->obj=declare_object{(default_object"CUSTOM-ENTRIES")withobject_stage=Summary.Stage.Synterp;cache_function=cache_custom_entry;load_function=load_custom_entry;subst_function=subst_custom_entry;classify_function=classify_custom_entry}letdeclare_custom_entrylocals=ifEgramcoq.exists_custom_entrysthenuser_errPp.(str"Custom entry "++strs++str" already exists.")elseLib.add_leaf(inCustomEntry(local,s))