123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110open!StdunemoduleAtom=AtommoduleTemplate=TemplatemoduleSyntax=Syntaxtypesyntax=Syntax.t=Jbuild|Dunetypet=|AtomofAtom.t|Quoted_stringofstring|Listoftlist|TemplateofTemplate.tletatom_or_quoted_strings=ifAtom.is_valid_dunesthenAtom(Atom.of_strings)elseQuoted_stringsletatoms=Atom(Atom.of_strings)letunsafe_atom_of_strings=atomsletrecto_stringt~syntax=matchtwith|Atoma->Atom.printasyntax|Quoted_strings->Escape.quoteds~syntax|Listl->Printf.sprintf"(%s)"(List.mapl~f:(to_string~syntax)|>String.concat~sep:" ")|Templatet->Template.to_stringt~syntaxletrecppsyntaxppf=function|Atoms->Format.pp_print_stringppf(Atom.printssyntax)|Quoted_strings->Format.pp_print_stringppf(Escape.quoted~syntaxs)|List[]->Format.pp_print_stringppf"()"|List(first::rest)->Format.pp_open_boxppf1;Format.pp_print_stringppf"(";Format.pp_open_hvboxppf0;ppsyntaxppffirst;List.iterrest~f:(funsexp->Format.pp_print_spaceppf();ppsyntaxppfsexp);Format.pp_close_boxppf();Format.pp_print_stringppf")";Format.pp_close_boxppf()|Templatet->Template.ppsyntaxppftletpp_quoted=letrecloop=function|Atom(As)ast->ifAtom.is_valid_dunesthentelseQuoted_strings|Listxs->List(List.map~f:loopxs)|(Quoted_string_|Template_)ast->tinfunppft->ppDuneppf(loopt)letpp_print_quoted_stringppfs=letsyntax=DuneinifString.containss'\n'thenbeginmatchString.splits~on:'\n'with|[]->Format.pp_print_stringppf(Escape.quoted~syntaxs)|first::rest->Format.fprintfppf"@[<hv 1>\"@{<atom>%s"(Escape.escaped~syntaxfirst);List.iterrest~f:(funs->Format.fprintfppf"@,\\n%s"(Escape.escaped~syntaxs));Format.fprintfppf"@}\"@]"endelseFormat.pp_print_stringppf(Escape.quoted~syntaxs)letrecpp_split_stringsppf=function|Atoms->Format.pp_print_stringppf(Atom.printsSyntax.Dune)|Quoted_strings->pp_print_quoted_stringppfs|List[]->Format.pp_print_stringppf"()"|List(first::rest)->Format.pp_open_boxppf1;Format.pp_print_stringppf"(";Format.pp_open_hvboxppf0;pp_split_stringsppffirst;List.iterrest~f:(funsexp->Format.pp_print_spaceppf();pp_split_stringsppfsexp);Format.pp_close_boxppf();Format.pp_print_stringppf")";Format.pp_close_boxppf()|Templatet->Template.pp_split_stringsppfttypeformatter_state=|In_atom|In_makefile_action|In_makefile_stuffletprepare_formatterppf=letstate=ref[]inFormat.pp_set_mark_tagsppftrue;letofuncs=Format.pp_get_formatter_out_functionsppf()inlettfuncs=Format.pp_get_formatter_tag_functionsppf()inFormat.pp_set_formatter_tag_functionsppf{tfuncswithmark_open_tag=(function|"atom"->state:=In_atom::!state;""|"makefile-action"->state:=In_makefile_action::!state;""|"makefile-stuff"->state:=In_makefile_stuff::!state;""|s->tfuncs.mark_open_tags);mark_close_tag=(function|"atom"|"makefile-action"|"makefile-stuff"->state:=List.tl!state;""|s->tfuncs.mark_close_tags)};Format.pp_set_formatter_out_functionsppf{ofuncswithout_newline=(fun()->match!statewith|[In_atom;In_makefile_action]->ofuncs.out_string"\\\n\t"03|[In_atom]->ofuncs.out_string"\\\n"02|[In_makefile_action]->ofuncs.out_string" \\\n\t"04|[In_makefile_stuff]->ofuncs.out_string" \\\n"03|[]->ofuncs.out_string"\n"01|_->assertfalse);out_spaces=(funn->ofuncs.out_spaces(match!statewith|In_atom::_->max0(n-2)|_->n))}moduleAst=structtypedune_lang=ttypet=|AtomofLoc.t*Atom.t|Quoted_stringofLoc.t*string|TemplateofTemplate.t|ListofLoc.t*tlistletatom_or_quoted_stringlocs=matchatom_or_quoted_stringswith|Atoma->Atom(loc,a)|Quoted_strings->Quoted_string(loc,s)|Template_|List_->assertfalseletloc(Atom(loc,_)|Quoted_string(loc,_)|List(loc,_)|Template{loc;_})=locletrecremove_locst:dune_lang=matchtwith|Templatet->Template(Template.remove_locst)|Atom(_,s)->Atoms|Quoted_string(_,s)->Quoted_strings|List(_,l)->List(List.mapl~f:remove_locs)endletrecadd_loct~loc:Ast.t=matchtwith|Atoms->Atom(loc,s)|Quoted_strings->Quoted_string(loc,s)|Listl->List(loc,List.mapl~f:(add_loc~loc))|Templatet->Template{twithloc}moduleCst=structmoduleComment=Lexer_shared.Token.Commenttypet=|AtomofLoc.t*Atom.t|Quoted_stringofLoc.t*string|TemplateofTemplate.t|ListofLoc.t*tlist|CommentofLoc.t*Comment.tletloc(Atom(loc,_)|Quoted_string(loc,_)|List(loc,_)|Template{loc;_}|Comment(loc,_))=locletfetch_legacy_commentst~file_contents=letrecloopt=matchtwith|Template_|Quoted_string_|Atom_|Comment(_,Lines_)->t|List(loc,l)->List(loc,List.mapl~f:loop)|Comment(loc,Legacy)->letstart=loc.start.pos_cnuminletstop=loc.stop.pos_cnuminlets=iffile_contents.[start]='#'&&file_contents.[start+1]='|'thenString.subfile_contents~pos:(start+2)~len:(stop-start-4)elseString.subfile_contents~pos:start~len:(stop-start)inComment(loc,Lines(String.splits~on:'\n'))inlooptletrecabstract:t->Ast.toption=function|Atom(loc,atom)->Some(Atom(loc,atom))|Quoted_string(loc,s)->Some(Quoted_string(loc,s))|Templatet->Some(Templatet)|List(loc,l)->Some(List(loc,List.filter_map~f:abstractl))|Comment_->Noneletrecconcrete:Ast.t->t=function|Atom(loc,atom)->Atom(loc,atom)|Quoted_string(loc,s)->Quoted_string(loc,s)|Templatet->Templatet|List(loc,l)->List(loc,List.map~f:concretel)letto_sexpc=abstractc|>Option.map~f:Ast.remove_locsletextract_comments=letrecloopacc=function|Atom_|Quoted_string_|Template_->acc|List(_,l)->List.fold_leftl~init:acc~f:loop|Comment(loc,comment)->(loc,comment)::accinList.fold_left~init:[]~f:looplettokenizets=lettokens=ref[]inletemitloc(token:Lexer.Token.t)=tokens:=(loc,token)::!tokensinletreciter=function|Atom(loc,s)->emitloc(Atoms)|Quoted_string(loc,s)->emitloc(Quoted_strings)|Template({loc;_}astemplate)->emitloc(Templatetemplate)|Comment(loc,comment)->emitloc(Commentcomment)|List(loc,l)->emit{locwithstop={loc.startwithpos_cnum=loc.start.pos_cnum+1}}Lparen;List.iterl~f:iter;emit{locwithstart={loc.stopwithpos_cnum=loc.stop.pos_cnum-1}}RpareninList.iterts~f:iter;List.rev!tokensendmoduleParse_error=structincludeLexer.Errorletloct:Loc.t={start=t.start;stop=t.stop}letmessaget=t.messageendexceptionParse_error=Lexer.ErrormoduleLexer=LexermoduleParser=structleterror(loc:Loc.t)message=raise(Parse_error{start=loc.start;stop=loc.stop;message})moduleMode=structtype'at=|Single:Ast.tt|Many:Ast.tlistt|Many_as_one:Ast.ttletmake_result:typea.at->Lexing.lexbuf->Ast.tlist->a=funtlexbufsexps->matchtwith|Single->beginmatchsexpswith|[sexp]->sexp|[]->error(Loc.of_lexbuflexbuf)"no s-expression found in input"|_::sexp::_->error(Ast.locsexp)"too many s-expressions found in input"end|Many->sexps|Many_as_one->matchsexpswith|[]->List(Loc.in_file(Path.of_stringlexbuf.lex_curr_p.pos_fname),[])|x::l->letlast=List.fold_leftl~init:x~f:(fun_x->x)inletloc={(Ast.locx)withstop=(Ast.loclast).stop}inList(loc,x::l)end(* To avoid writing two parsers, one for the Cst and one for the
Ast, we write only one that work for both.
The natural thing to do would be to have parser that produce
[Cst.t] value and drop comment for the [Ast.t] one. However the
most used parser is the one producing Ast one, so it is the one
we want to go fast. As a result, we encode comment as special
[Ast.t] values and decode them for the [Cst.t] parser.
We could also do clever things with GADTs, but it will add type
variables everywhere which is annoying. *)letreccst_of_encoded_ast(x:Ast.t):Cst.t=matchxwith|Templatet->Templatet|Quoted_string(loc,s)->Quoted_string(loc,s)|List(loc,l)->List(loc,List.mapl~f:cst_of_encoded_ast)|Atom(loc,(Asasatom))->matchs.[0]with|'\000'->Comment(loc,Lines(String.drops1|>String.split~on:'\n'))|'\001'->Comment(loc,Legacy)|_->Atom(loc,atom)letrecloopwith_commentsdepthlexerlexbufacc=match(lexer~with_commentslexbuf:Lexer.Token.t)with|Atoma->letloc=Loc.of_lexbuflexbufinloopwith_commentsdepthlexerlexbuf(Ast.Atom(loc,a)::acc)|Quoted_strings->letloc=Loc.of_lexbuflexbufinloopwith_commentsdepthlexerlexbuf(Quoted_string(loc,s)::acc)|Templatet->letloc=Loc.of_lexbuflexbufinloopwith_commentsdepthlexerlexbuf(Template{twithloc}::acc)|Lparen->letstart=Lexing.lexeme_start_plexbufinletsexps=loopwith_comments(depth+1)lexerlexbuf[]inletstop=Lexing.lexeme_end_plexbufinloopwith_commentsdepthlexerlexbuf(List({start;stop},sexps)::acc)|Rparen->ifdepth=0thenerror(Loc.of_lexbuflexbuf)"right parenthesis without matching left parenthesis";List.revacc|Sexp_comment->letsexps=letloc=Loc.of_lexbuflexbufinmatchloopwith_commentsdepthlexerlexbuf[]with|commented::sexps->ifnotwith_commentsthensexpselseAtom(Ast.loccommented,Atom.of_string"\001")::sexps|[]->errorloc"s-expression missing after #;"inList.rev_appendaccsexps|Eof->ifdepth>0thenerror(Loc.of_lexbuflexbuf)"unclosed parenthesis at end of input";List.revacc|Commentcomment->ifnotwith_commentsthenloopfalsedepthlexerlexbufaccelsebeginletloc=Loc.of_lexbuflexbufinletencoded=matchcommentwith|Lineslines->"\000"^String.concatlines~sep:"\n"|Legacy->"\001"inloopwith_commentsdepthlexerlexbuf(Atom(loc,Atom.of_stringencoded)::acc)endletparse~mode?(lexer=Lexer.token)lexbuf=loopfalse0lexerlexbuf[]|>Mode.make_resultmodelexbufletparse_cst?(lexer=Lexer.token)lexbuf=looptrue0lexerlexbuf[]|>List.map~f:cst_of_encoded_astendletinsert_commentscstscomments=(* To insert the comments, we tokenize the csts, reconciliate the
token streams and parse the result again. This is not the fastest
implementation, but at least it is simple. *)letcompare(a,_)(b,_)=Int.comparea.Loc.start.pos_cnumb.Loc.start.pos_cnuminletrecreconciliateacctokens1tokens2=matchtokens1,tokens2with|[],l|l,[]->List.rev_appendaccl|tok1::rest1,tok2::rest2->matchcomparetok1tok2with|Eq|Lt->reconciliate(tok1::acc)rest1tokens2|Gt->reconciliate(tok2::acc)tokens1rest2inlettokens=reconciliate[](Cst.tokenizecsts)(List.sortcomments~compare|>List.map~f:(fun(loc,comment)->(loc,Lexer.Token.Commentcomment)))inlettokens=reftokensinletlexer~with_comments:_(lb:Lexing.lexbuf)=match!tokenswith|[]->lb.lex_curr_p<-lb.lex_start_p;Lexer.Token.Eof|({start;stop},tok)::rest->tokens:=rest;lb.lex_start_p<-start;lb.lex_curr_p<-stop;tokinParser.parse_cst(Lexing.from_string"")~lexerletlexbuf_from_string~fnamestr=letlb=Lexing.from_stringstrinlb.lex_curr_p<-{pos_fname=fname;pos_lnum=1;pos_bol=0;pos_cnum=0};lbletparse_string~fname~mode?lexerstr=letlb=lexbuf_from_string~fnamestrinParser.parse~mode?lexerlbletparse_cst_string~fname?lexerstr=letlb=lexbuf_from_string~fnamestrinParser.parse_cst?lexerlbtypedune_lang=tmoduleEncoder=structtypenonrec'at='a->tletunit()=List[]letcharc=atom_or_quoted_string(String.make1c)letstring=atom_or_quoted_stringletintn=Atom(Atom.of_intn)letfloatf=Atom(Atom.of_floatf)letboolb=Atom(Atom.of_boolb)letpairfafb(a,b)=List[faa;fbb]lettriplefafbfc(a,b,c)=List[faa;fbb;fcc]letlistfl=List(List.mapl~f)letarrayfa=listf(Array.to_lista)letsexpx=xletoptionf=function|None->List[]|Somex->List[fx]letrecordl=List(List.mapl~f:(fun(n,v)->List[Atom(Atom.of_stringn);v]))typefield=|Absent|Normalofstring*dune_lang|Inlined_listofstring*dune_langlistletfieldnamef?(equal=(=))?defaultv=matchdefaultwith|None->Normal(name,fv)|Somed->ifequaldvthenAbsentelseNormal(name,fv)letfield_onamefv=matchvwith|None->Absent|Somev->Normal(name,fv)letfield_bnamev=ifvthenInlined_list(name,[])elseAbsentletfield_lnamefl=matchlwith|[]->Absent|_->Inlined_list(name,List.mapl~f)letfield_inamefx=matchfxwith|[]->Absent|l->Inlined_list(name,l)letrecord_fields(l:fieldlist)=List.filter_mapl~f:(function|Absent->None|Normal(name,v)->Some(List[Atom(Atom.of_stringname);v])|Inlined_list(name,l)->Some(List(Atom(Atom.of_stringname)::l)))letunknown_=unsafe_atom_of_string"<unknown>"endmoduleDecoder=structtypeast=Ast.t=|AtomofLoc.t*Atom.t|Quoted_stringofLoc.t*string|TemplateofTemplate.t|ListofLoc.t*astlisttypehint={on:string;candidates:stringlist}exceptionDecoderofLoc.t*string*hintoptionletof_sexp_error?hintlocmsg=raise(Decoder(loc,msg,hint))letof_sexp_errorf?hintlocfmt=Printf.ksprintf(funmsg->of_sexp_errorloc?hintmsg)fmtletno_templates?hintlocfmt=Printf.ksprintf(funmsg->of_sexp_errorloc?hint("No variables allowed "^msg))fmtmoduleName=structmoduleT=structtypet=stringletcompareab=letalen=String.lengthaandblen=String.lengthbinmatchInt.comparealenblenwith|Eq->String.compareab|ne->neendincludeTmoduleMap=Map.Make(T)endmoduleFields=structmoduleUnparsed=structtypet={values:Ast.tlist;entry:Ast.t;prev:toption(* Previous occurrence of this field *)}endtypet={unparsed:Unparsed.tName.Map.t;known:stringlist}letconsumenamestate={unparsed=Name.Map.removestate.unparsedname;known=name::state.known}letadd_knownnamestate={statewithknown=name::state.known}letunparsed_ast{unparsed;_}=letrecloopacc=function|[]->acc|x::xs->beginmatchx.Unparsed.prevwith|None->loop(x.entry::acc)xs|Somep->loop(x.entry::acc)(p::xs)endinloop[](Name.Map.valuesunparsed)|>List.sort~compare:(funab->Int.compare(Ast.loca).start.pos_cnum(Ast.locb).start.pos_cnum)endtypefields=Fields.ttypevalues=Ast.tlist(* Arguments are:
- the location of the whole list
- the first atom when parsing a constructor or a field
- the universal map holding the user context
*)type'kindcontext=|Values:Loc.t*stringoption*Univ_map.t->valuescontext|Fields:Loc.t*stringoption*Univ_map.t->Fields.tcontexttype('a,'kind)parser='kindcontext->'kind->'a*'kindtype'at=('a,values)parsertype'afields_parser=('a,Fields.t)parserletreturnx_ctxstate=(x,state)let(>>=)tfctxstate=letx,state=tctxstateinfxctxstatelet(>>|)tfctxstate=letx,state=tctxstatein(fx,state)let(>>>)abctxstate=let(),state=actxstateinbctxstateletmapt~f=t>>|flettry_tfctxstate=trytctxstatewithexn->fexnctxstateletget_user_context:typek.kcontext->Univ_map.t=function|Values(_,_,uc)->uc|Fields(_,_,uc)->ucletgetkeyctxstate=(Univ_map.find(get_user_contextctx)key,state)letget_allctxstate=(get_user_contextctx,state)letset:typeabk.aUniv_map.Key.t->a->(b,k)parser->(b,k)parser=funkeyvtctxstate->matchctxwith|Values(loc,cstr,uc)->t(Values(loc,cstr,Univ_map.adduckeyv))state|Fields(loc,cstr,uc)->t(Fields(loc,cstr,Univ_map.adduckeyv))stateletset_many:typeak.Univ_map.t->(a,k)parser->(a,k)parser=funmaptctxstate->matchctxwith|Values(loc,cstr,uc)->t(Values(loc,cstr,Univ_map.superposeucmap))state|Fields(loc,cstr,uc)->t(Fields(loc,cstr,Univ_map.superposeucmap))stateletloc:typek.kcontext->k->Loc.t*k=functxstate->matchctxwith|Values(loc,_,_)->(loc,state)|Fields(loc,_,_)->(loc,state)letat_eos:typek.kcontext->k->bool=functxstate->matchctxwith|Values_->state=[]|Fields_->Name.Map.is_emptystate.unparsedleteosctxstate=(at_eosctxstate,state)letif_eos~then_~else_ctxstate=ifat_eosctxstatethenthen_ctxstateelseelse_ctxstateletrepeat:'at->'alistt=letreclooptaccctxl=matchlwith|[]->(List.revacc,[])|_->letx,l=tctxlinloopt(x::acc)ctxlinfuntctxstate->loopt[]ctxstateletresult:typeak.kcontext->a*k->a=functx(v,state)->matchctxwith|Values(_,cstr,_)->beginmatchstatewith|[]->v|sexp::_->matchcstrwith|None->of_sexp_errorf(Ast.locsexp)"This value is unused"|Somes->of_sexp_errorf(Ast.locsexp)"Too many argument for %s"send|Fields_->beginmatchName.Map.choosestate.unparsedwith|None->v|Some(name,{entry;_})->letname_loc=matchentrywith|List(_,s::_)->Ast.locs|_->assertfalseinof_sexp_errorf~hint:{on=name;candidates=state.known}name_loc"Unknown field %s"nameendletparsetcontextsexp=letctx=Values(Ast.locsexp,None,context)inresultctx(tctx[sexp])letcapturectxstate=letft=resultctx(tctxstate)in(f,[])letend_of_list(Values(loc,cstr,_))=matchcstrwith|None->letloc={locwithstart=loc.stop}inof_sexp_errorfloc"Premature end of list"|Somes->of_sexp_errorfloc"Not enough arguments for %s"s[@@inlinenever]letnextfctxsexps=matchsexpswith|[]->end_of_listctx|sexp::sexps->(fsexp,sexps)[@@inlinealways]letnext_with_user_contextfctxsexps=matchsexpswith|[]->end_of_listctx|sexp::sexps->(f(get_user_contextctx)sexp,sexps)[@@inlinealways]letpeek_ctxsexps=matchsexpswith|[]->(None,sexps)|sexp::_->(Somesexp,sexps)[@@inlinealways]letpeek_exnctxsexps=matchsexpswith|[]->end_of_listctx|sexp::_->(sexp,sexps)[@@inlinealways]letjunk=nextignoreletjunk_everything:typek.(unit,k)parser=functxstate->matchctxwith|Values_->((),[])|Fields_->((),{statewithunparsed=Name.Map.empty})letkeywordkwd=next(function|Atom(_,s)whenAtom.to_strings=kwd->()|sexp->of_sexp_errorf(Ast.locsexp)"'%s' expected"kwd)letmatch_keywordl~fallback=peek>>=function|Some(Atom(_,As))->beginmatchList.assoclswith|Somet->junk>>>t|None->fallbackend|_->fallbackletuntil_keywordkwd~before~after=letrecloopacc=peek>>=function|None->return(List.revacc,None)|Some(Atom(_,As))whens=kwd->junk>>>after>>=funx->return(List.revacc,Somex)|_->before>>=funx->loop(x::acc)inloop[]letplain_stringf=next(function|Atom(loc,As)|Quoted_string(loc,s)->f~locs|Template{loc;_}|List(loc,_)->of_sexp_errorloc"Atom or quoted string expected")letentert=next_with_user_context(funucsexp->matchsexpwith|List(loc,l)->letctx=Values(loc,None,uc)inresultctx(tctxl)|sexp->of_sexp_error(Ast.locsexp)"List expected")letif_list~then_~else_=peek_exn>>=function|List_->then_|_->else_letif_paren_colon_form~then_~else_=peek_exn>>=function|List(_,Atom(loc,As)::_)whenString.is_prefixs~prefix:":"->letname=String.drops1inenter(junk>>=fun()->then_>>|funf->f(loc,name))|_->else_letfixf=letrecp=lazy(fr)andrast=(Lazy.forcep)astinrletloc_between_states:typek.kcontext->k->k->Loc.t=functxstate1state2->matchctxwith|Values_->beginmatchstate1with|sexp::restwhenrest==state2->(* common case *)Ast.locsexp|[]->let(Values(loc,_,_))=ctxin{locwithstart=loc.stop}|sexp::rest->letloc=Ast.locsexpinletrecsearchlastl=ifl==state2then{locwithstop=(Ast.loclast).stop}elsematchlwith|[]->let(Values(loc,_,_))=ctxin{(Ast.locsexp)withstop=loc.stop}|sexp::rest->searchsexprestinsearchsexprestend|Fields_->letparsed=Name.Map.mergestate1.unparsedstate2.unparsed~f:(fun_keybeforeafter->matchbefore,afterwith|Some_,None->before|_->None)inmatchName.Map.valuesparsed|>List.map~f:(funf->Ast.locf.Fields.Unparsed.entry)|>List.sort~compare:(funab->Int.comparea.Loc.start.pos_cnumb.start.pos_cnum)with|[]->let(Fields(loc,_,_))=ctxinloc|first::l->letlast=List.fold_leftl~init:first~f:(fun_x->x)in{firstwithstop=last.stop}letlocatedtctxstate1=letx,state2=tctxstate1in((loc_between_statesctxstate1state2,x),state2)letraw=nextFn.idletunit=next(function|List(_,[])->()|sexp->of_sexp_error(Ast.locsexp)"() expected")letbasicdescf=next(function|Template{loc;_}|List(loc,_)|Quoted_string(loc,_)->of_sexp_errorfloc"%s expected"desc|Atom(loc,s)->matchf(Atom.to_strings)with|Result.Error()->of_sexp_errorfloc"%s expected"desc|Okx->x)letstring=plain_string(fun~loc:_x->x)letchar=plain_string(fun~locx->ifString.lengthx=1thenx.[0]elseof_sexp_errorfloc"character expected")letint=basic"Integer"(funs->matchint_of_stringswith|x->Okx|exception_->Result.Error())letfloat=basic"Float"(funs->matchfloat_of_stringswith|x->Okx|exception_->Result.Error())letpairab=enter(a>>=funa->b>>=funb->return(a,b))lettripleabc=enter(a>>=funa->b>>=funb->c>>=func->return(a,b,c))letlistt=enter(repeatt)letarrayt=listt>>|Array.of_listletoptiont=enter(eos>>=function|true->returnNone|false->t>>|Option.some)letfind_cstrcstrslocnamectxvalues=matchList.assoccstrsnamewith|Somet->resultctx(tctxvalues)|None->of_sexp_errorfloc~hint:{on=name;candidates=List.mapcstrs~f:fst}"Unknown constructor %s"nameletsumcstrs=next_with_user_context(funucsexp->matchsexpwith|Atom(loc,As)->find_cstrcstrslocs(Values(loc,Somes,uc))[]|Template{loc;_}|Quoted_string(loc,_)->of_sexp_errorloc"Atom expected"|List(loc,[])->of_sexp_errorloc"Non-empty list expected"|List(loc,name::args)->matchnamewith|Quoted_string(loc,_)|List(loc,_)|Template{loc;_}->of_sexp_errorloc"Atom expected"|Atom(s_loc,As)->find_cstrcstrss_locs(Values(loc,Somes,uc))args)letenumcstrs=next(function|Quoted_string(loc,_)|Template{loc;_}|List(loc,_)->of_sexp_errorloc"Atom expected"|Atom(loc,As)->matchList.assoccstrsswith|Somevalue->value|None->of_sexp_errorfloc~hint:{on=s;candidates=List.mapcstrs~f:fst}"Unknown value %s"s)letbool=enum[("true",true);("false",false)]letmap_validatet~fctxstate1=letx,state2=tctxstate1inmatchfxwith|Result.Okx->(x,state2)|Errormsg->letloc=loc_between_statesctxstate1state2inof_sexp_errorfloc"%s"msgletfield_missinglocname=of_sexp_errorfloc"field %s missing"name[@@inlinenever]letfield_present_too_many_times_nameentries=matchentrieswith|_::second::_->of_sexp_errorf(Ast.locsecond)"Field %S is present too many times"name|_->assertfalseletmultiple_occurrences?(on_dup=field_present_too_many_times)ucnamelast=letreccollectacc(x:Fields.Unparsed.t)=letacc=x.entry::accinmatchx.prevwith|None->acc|Someprev->collectaccprevinon_dupucname(collect[]last)[@@inlinenever]letfind_single?on_dupuc(state:Fields.t)name=letres=Name.Map.findstate.unparsednamein(matchreswith|Some({prev=Some_;_}aslast)->multiple_occurrencesucnamelast?on_dup|_->());resletfieldname?default?on_dupt(Fields(loc,_,uc))state=matchfind_singleucstatename?on_dupwith|Some{values;entry;_}->letctx=Values(Ast.locentry,Somename,uc)inletx=resultctx(tctxvalues)in(x,Fields.consumenamestate)|None->matchdefaultwith|Somev->(v,Fields.add_knownnamestate)|None->field_missinglocnameletfield_oname?on_dupt(Fields(_,_,uc))state=matchfind_singleucstatename?on_dupwith|Some{values;entry;_}->letctx=Values(Ast.locentry,Somename,uc)inletx=resultctx(tctxvalues)in(Somex,Fields.consumenamestate)|None->(None,Fields.add_knownnamestate)letfield_b_genfield_gen?check?on_dupname=field_genname?on_dup(Option.valuecheck~default:(return())>>=fun()->eos>>=function|true->returntrue|_->bool)letfield_b=field_b_gen(field~default:false)letfield_o_b=field_b_genfield_oletmulti_fieldnamet(Fields(_,_,uc))(state:Fields.t)=letrecloopacc(field:Fields.Unparsed.toption)=matchfieldwith|None->acc|Some{values;prev;entry}->letctx=Values(Ast.locentry,Somename,uc)inletx=resultctx(tctxvalues)inloop(x::acc)previnletres=loop[](Name.Map.findstate.unparsedname)in(res,Fields.consumenamestate)letfieldst(Values(loc,cstr,uc))sexps=letunparsed=List.fold_leftsexps~init:Name.Map.empty~f:(funaccsexp->matchsexpwith|List(_,name_sexp::values)->beginmatchname_sexpwith|Atom(_,Aname)->Name.Map.addaccname{Fields.Unparsed.values;entry=sexp;prev=Name.Map.findaccname}|List(loc,_)|Quoted_string(loc,_)|Template{loc;_}->of_sexp_errorloc"Atom expected"end|_->of_sexp_error(Ast.locsexp)"S-expression of the form (<name> <values>...) expected")inletctx=Fields(loc,cstr,uc)inletx=resultctx(tctx{Fields.unparsed;known=[]})in(x,[])letleftover_fields(Fields(_,_,_))state=(Fields.unparsed_aststate,{Fields.known=state.known@Name.Map.keysstate.unparsed;unparsed=Name.Map.empty})letrecordt=enter(fieldst)typekind=|ValuesofLoc.t*stringoption|FieldsofLoc.t*stringoptionletkind:typek.kcontext->k->kind*k=functxstate->matchctxwith|Values(loc,cstr,_)->(Values(loc,cstr),state)|Fields(loc,cstr,_)->(Fields(loc,cstr),state)let(let*)=(>>=)let(let+)=(>>|)let(and+)abctxstate=leta,state=actxstateinletb,state=bctxstatein((a,b),state)endmoduletypeConv=sigtypetvaldecode:tDecoder.tvalencode:tEncoder.tendletrecto_sexp=function|Atom(Aa)->Sexp.Atoma|Lists->List(List.maps~f:to_sexp)|Quoted_strings->Sexp.Atoms|Templatet->List[Atom"template";Atom(Template.to_string~syntax:Dunet)]moduleIo=structletload?lexerpath~mode=Io.with_lexbuf_from_filepath~f:(Parser.parse~mode?lexer)end