12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886(************************************************************************)(* * 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_termopenNotation_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)letpr_registered_grammarname=letgram=Pcoq.find_grammars_by_namenameinmatchgramwith|[]->user_errPp.(str"Unknown or unprintable grammar entry.")|entries->letpr_one(Pcoq.AnyEntrye)=str"Entry "++str(Pcoq.Entry.namee)++str" is"++fnl()++pr_entryeinprlistpr_oneentriesletpr_grammar=function|"constr"|"term"|"binder_constr"->str"Entry constr is"++fnl()++pr_entryPcoq.Constr.constr++str"and lconstr is"++fnl()++pr_entryPcoq.Constr.lconstr++str"where binder_constr is"++fnl()++pr_entryPcoq.Constr.binder_constr++str"and term is"++fnl()++pr_entryPcoq.Constr.term|"pattern"->pr_entryPcoq.Constr.pattern|"vernac"->str"Entry vernac_control is"++fnl()++pr_entryPvernac.Vernac_.vernac_control++str"Entry command is"++fnl()++pr_entryPvernac.Vernac_.command++str"Entry syntax is"++fnl()++pr_entryPvernac.Vernac_.syntax++str"Entry gallina is"++fnl()++pr_entryPvernac.Vernac_.gallina++str"Entry gallina_ext is"++fnl()++pr_entryPvernac.Vernac_.gallina_ext|name->pr_registered_grammarnameletpr_custom_grammarname=pr_registered_grammar("custom:"^name)(**********************************************************************)(* 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:"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 subentries 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 *)(** 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(matchnwithSomen->n|None->0),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,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:"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,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.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(matchnwithNone->0|Somen->n)letkeyword_neededneeds=(* Ensure that IDENT articulation terminal symbols are keywords *)matchCLexer.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)elseETBinderClosedtklindistribute[GramConstrNonTerminal(ETProdBinderListtyp,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:"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)||rules'.notation_printing_extra<>generic_rules.notation_printing_extra)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)||rules.notation_printing_extra<>specific_rules.notation_printing_extrathen(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.subentries_of_notationntn_for_grammarinifnot(Notation.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.subentries_of_notationntninifnot(Notation.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_subentriesntnsynext.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")withopen_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;extra:(string*string)list;}letdefault={assoc=None;level=None;etyps=[];format=None;extra=[];}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;extra:(string*string)list;itemscopes:(Id.t*scope_name)list;}letwarn_only_parsing_reserved_notation=CWarnings.create~name:"irrelevant-reserved-notation-only-parsing"~category:"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:"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:"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_extra_format?locmain_data(k,s)=ifList.mem_assockmain_data.extrathenuser_err?locPp.(str"A format for "++strk++str" is given more than once.");letextra=ifmain_data.onlyparsingthen(warn_irrelevant_format?loc();main_data.extra)else(k,s.CAst.v)::main_data.extrain{main_datawithextra}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&&List.is_emptymain_data.extra)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)|SetFormat(ExtraFormat(k,s))whennotabbrev->(set_extra_format?locmain_data(k,s),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;extra=[];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_->NtnInternTypeOnlyBinder|ETConstr_|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_typeisrecisonlybindingdefault_if_binding=function(* Parsed as constr list *)|ETConstr(_,None,_)whenisrec->NtnTypeConstrList(* Parsed as constr, but interpreted as a binder *)|ETConstr(_,Somebk,_)->NtnTypeBinder(NtnBinderParsedAsConstrbk)|ETConstr(_,None,_)whenisonlybinding->NtnTypeBinder(NtnBinderParsedAsConstrdefault_if_binding)(* Parsed as constr, interpreted as constr *)|ETConstr(_,None,_)->NtnTypeConstr(* Others *)|ETIdent->NtnTypeBinderNtnParsedAsIdent|ETName->NtnTypeBinderNtnParsedAsName|ETPattern(ppstrict,_)->NtnTypeBinder(NtnParsedAsPatternppstrict)(* Parsed as ident/pattern, primarily interpreted as binder; maybe strict at printing *)|ETBigint|ETGlobal->NtnTypeConstr|ETBinder_->ifisrecthenNtnTypeBinderListelseNtnTypeBinderNtnParsedAsBinderletsubentry_of_constr_prod_entryfrom_level=function(* Specific 8.2 approximation *)|ETConstr(InCustomEntrys,_,x)->letn=matchfst(precedence_of_position_and_levelfrom_levelx)with|LevelLtn->n-1|LevelLen->n|LevelSome->max_intinInCustomEntryLevel(s,n)(* level and use of parentheses for coercion is hard-wired for "constr";
we don't remember the level *)|ETConstr(InConstrEntry,_,_)->InConstrEntrySomeLevel|_->InConstrEntrySomeLevelletmake_interpretation_vars(* For binders, default is to parse only as an ident *)?(default_if_binding=AsName)recvarslevelallvarstyps=leteq_subscope(sc1,l1)(sc2,l2)=Option.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((subentry_of_constr_prod_entryleveltyp,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:"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:"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_,_->letcustomkey=make_notation_entry_levelcustomninletsubentry=subentry_of_constr_prod_entryneinifnotation_entry_level_eqsubentrycustomkeythenNoneelseSome(IsEntryCoercionsubentry)|ETGlobal,InCustomEntrys->Some(IsEntryGlobal(s,n))|ETIdent,InCustomEntrys->Some(IsEntryIdent(s,n))|_->None)|Some_,_->assertfalse|None,_->Noneletprintabilityleveltypsonlyparsingreversibility=function|NVar_whenreversibility=APrioriReversible->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}(**********************************************************************)letwith_lib_stk_protectionfx=letfs=Lib.freeze()intryleta=fxinLib.unfreezefs;awithreraise->letreraise=Exninfo.capturereraiseinlet()=Lib.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.subentries_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;notation_printing_extra=main_data.extra;}}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 *)letmerge_extraextra1extra2=List.fold_left(funextras(k,s)->(k,s)::List.remove_assockextras)extra1extra2letmake_specific_printing_rulesetypssymbolslevelpp_rule(format,new_extra)=matchlevelwith|None->None|Some(_,level,_)->letold_extra=matchpp_rulewithSome{notation_printing_rules={notation_printing_extra}}->notation_printing_extra|None->[]inmatchformat,new_extra,pp_rulewith|None,[],Some_whennot(has_implicit_formatsymbols)->None|_->Some{notation_printing_unparsing=make_pp_rulelevel(etyps,symbols)format;notation_printing_level=level;notation_printing_extra=merge_extraold_extranew_extra;}(**********************************************************************)(* Miscellaneous *)letwarn_unused_interpretation=CWarnings.create~name:"unused-notation"~category:"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 *)letto_mapl=letfoldaccu(x,v)=Id.Map.addxvaccuinList.fold_leftfoldId.Map.emptylletmake_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}inSomesy.synext_level,List.combinemainvarssy.synext_nottyps,main_data,sy.synext_notprintin(* Declare interpretation *)letsy_pp_rules=make_specific_printing_rulesi_typssymbolslevelsy_pp_rules(main_data.format,main_data.extra)inletpath=(Lib.library_dp(),Lib.current_dirpathtrue)inletdf'=ntn,(path,df)inleti_vars=make_internalization_varsrecvarsi_typsinletnenv={ninterp_var_type=to_mapi_vars;ninterp_rec_vars=to_maprecvars;}inlet(acvars,ac,reversibility)=interp_notation_constrenv~implsnenvcinletplevel=matchlevelwithSome(from,level,l)->level|None(* numeral: irrelevant )*)->0inletinterp=make_interpretation_varsrecvarsplevelacvarsi_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_typsmain_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)))(* Notations associated to a where clause *)typewhere_decl_notation=decl_notation*notation_main_data*notation_symbols*notation*syntax_rulesletprepare_where_notationdecl_ntn=let{decl_ntn_string={CAst.loc;v=df};decl_ntn_interp=c;decl_ntn_modifiers=modifiers;decl_ntn_scope=sc;}=decl_ntninlet(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(decl_ntn,main_data,notation_symbols,ntn,syntax_rules)letadd_notation_interpretation~localenv(decl_ntn,main_data,notation_symbols,ntn,syntax_rules)=let{decl_ntn_string={CAst.loc;v=df};decl_ntn_interp=c;decl_ntn_scope=sc}=decl_ntninletnotation=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(decl_ntn,main_data,notation_symbols,ntn,syntax_rules)=let{decl_ntn_string={CAst.loc;v=df};decl_ntn_interp=c;decl_ntn_scope=sc}=decl_ntninletnotation=make_notation_interpretation~local:truemain_datanotation_symbolsntnsyntax_rulesdfenv~implscscinsyntax_rules_iter(funsy->Lib.add_leaf(inSyntaxExtension(true,(ntn,sy))))syntax_rules;Lib.add_leaf(inNotationnotation);Option.iter(funsc->Notation.open_close_scope(false,true,sc))sc(* Main entry point for command Notation *)letadd_notation~local~infixdeprecationenvc({CAst.loc;v=df},modifiers)sc=(* 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.symbolsin(* Build or rebuild the syntax rules *)letsyntax_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)in(* Build the interpretation *)letnotation=make_notation_interpretation~localmain_datanotation_symbolsntnsyntax_rulesdfenvcscin(* Declare both syntax and interpretation *)syntax_rules_iter(funsy->Lib.add_leaf(inSyntaxExtension(local,(ntn,sy))))syntax_rules;Lib.add_leaf(inNotationnotation);(* Dump the location of the notation for coqdoc *)Dumpglob.dump_notation(CAst.make?locntn)sctrue(* Main entry point for Format Notation *)letadd_notation_extra_printing_ruledfkv=letnotk=let{symbols},isnumeral=analyze_notation_tokens~onlyprinting:true~infix:falseInConstrEntrydfinifisnumeralthenuser_err(str"Notations for numbers are primitive.");make_notation_keyInConstrEntrysymbolsinadd_notation_extra_printing_rulenotkkv(**********************************************************************)(* Scopes, delimiters and classes bound to scopes *)typescope_command=|ScopeDeclare|ScopeDelimAddofstring|ScopeDelimRemove|ScopeClassesofscope_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|ScopeClassescl->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|ScopeClassescl->List.iter(Notation.declare_scope_classscope)clletcache_scope_commando=load_scope_command_commonfalse1o;open_scope_command1oletsubst_scope_command(subst,(local,scope,oasx))=matchowith|ScopeClassescl->letenv=Global.env()inletcl'=List.map_filter(subst_scope_classenvsubst)clinletcl'=ifList.for_all2eq(==)clcl'thenclelsecl'inlocal,scope,ScopeClassescl'|_->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_scopelocalscopecl=Lib.add_leaf(inScopeCommand(local,scope,ScopeClassescl))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_constrenvnenvcinletin_pat(id,_)=(id,ETConstr(Constrexpr.InConstrEntry,None,(NextLevel,InternalProd)))inletinterp=make_interpretation_vars~default_if_binding:AsNameOrPattern[]0acvars(List.mapin_patvars)inletvars=List.map(fun(x,_)->(x,Id.Map.findxinterp))varsinletalso_in_cases_pattern=has_no_binders_typevarsinletonlyparsing=only_parsing||fst(printabilityNone[]falsereversibilitypat)inAbbreviation.declare_abbreviation~local~also_in_cases_patterndeprecationident~onlyparsing(vars,pat)(**********************************************************************)(* Declaration of custom entry *)letwarn_custom_entry=CWarnings.create~name:"custom-entry-overridden"~category:"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")withcache_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))