123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331(* Claude Code
*
* Copyright (C) 2026 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* (LGPL) as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*)(* See St_parse.mli *)openSt_astmoduleL=St_lexerexceptionErrorofint*string(*****************************************************************************)(* The tokens, one at a time *)(*****************************************************************************)typest={toks:L.tokenarray;mutablei:int}letpeek(p:st):L.kind=p.toks.(p.i).kindletpeek2(p:st):L.kind=ifp.i+1<Array.lengthp.toksthenp.toks.(p.i+1).kindelseL.Eoflettok(p:st):L.token=p.toks.(p.i)letadvance(p:st):unit=ifp.i<Array.lengthp.toks-1thenp.i<-p.i+1letfail(p:st)(msg:string)=raise(Error((tokp).start,msg))letlast_stop(p:st):int=ifp.i=0then0elsep.toks.(p.i-1).stopletexpect(p:st)(k:L.kind)(msg:string):unit=ifpeekp=kthenadvancepelsefailpmsgletname(p:st)(msg:string):string=matchpeekpwith|L.Namen->advancep;n|_->failpmsg(*****************************************************************************)(* Literals *)(*****************************************************************************)letrecliteral_array(p:st):literal=(* after "#(" or a nested "(" *)letitems=ref[]inletrecgo()=matchpeekpwith|L.Rparen->advancep|L.Eof->failp") expected"|_->items:=array_itemp::!items;go()ingo();L_array(List.rev!items)andarray_item(p:st):literal=lett=tokpinmatcht.kindwith|L.Inti->advancep;L_inti|L.Large(n,b)->advancep;L_large(n,b)|L.Floatf->advancep;L_floatf|L.Charc->advancep;L_charc|L.Strings->advancep;L_strings|L.Symbols->advancep;L_symbols|L.Name"nil"->advancep;L_nil|L.Name"true"->advancep;L_true|L.Name"false"->advancep;L_false|L.Namen->advancep;L_symboln|L.Binaryb->advancep;L_symbolb|L.Bar->advancep;L_symbol"|"|L.Keywordk->(* at:put: is two keyword tokens, touching *)advancep;letrecmoreaccstop=match(tokp).kindwith|L.Keywordkwhen(tokp).start=stop->letstop=(tokp).stopinadvancep;more(acc^k)stop|_->accinL_symbol(morekt.stop)|L.Lparen|L.Array_start->advancep;literal_arrayp|_->failp"literal expected"letliteral_of_token(p:st):literaloption=matchpeekpwith|L.Inti->advancep;Some(L_inti)|L.Large(n,b)->advancep;Some(L_large(n,b))|L.Floatf->advancep;Some(L_floatf)|L.Charc->advancep;Some(L_charc)|L.Strings->advancep;Some(L_strings)|L.Symbols->advancep;Some(L_symbols)|L.Array_start->advancep;Some(literal_arrayp)|_->None(*****************************************************************************)(* Expressions *)(*****************************************************************************)letmkestartstop={e;pos=(start,stop)}letrecexpression(p:st):expr=match(peekp,peek2p)with|L.Namev,L.Assign->letstart=(tokp).startinadvancep;advancep;letx=expressionpinmk(Assign(v,x))start(sndx.pos)|_->cascadepandcascade(p:st):expr=letx=keyword_exprpinifpeekp<>L.Semicolonthenxelsematchx.ewith|Send(r,sel,args)->letfirst=(sel,args,x.pos)inletmsgs=ref[first]inwhilepeekp=L.Semicolondoadvancep;msgs:=messagep::!msgsdone;mk(Cascade(r,List.rev!msgs))(fstx.pos)(last_stopp)|_->failp"Cascading not expected"(* one message of a cascade, after its ";" *)andmessage(p:st):string*exprlist*pos=letstart=(tokp).startinmatchpeekpwith|L.Namesel->advancep;(sel,[],(start,last_stopp))|L.Binarysel->advancep;letarg=unary_exprpin(sel,[arg],(start,last_stopp))|L.Bar->advancep;letarg=unary_exprpin("|",[arg],(start,last_stopp))|L.Keyword_->letsel=Buffer.create16andargs=ref[]inwhilematchpeekpwithL.Keyword_->true|_->falsedo(matchpeekpwithL.Keywordk->Buffer.add_stringselk|_->());advancep;args:=binary_exprp::!argsdone;(Buffer.contentssel,List.rev!args,(start,last_stopp))|_->failp"Message expected"andkeyword_expr(p:st):expr=letr=binary_exprpinmatchpeekpwith|L.Keyword_->letsel=Buffer.create16andargs=ref[]inwhilematchpeekpwithL.Keyword_->true|_->falsedo(matchpeekpwithL.Keywordk->Buffer.add_stringselk|_->());advancep;args:=binary_exprp::!argsdone;mk(Send(r,Buffer.contentssel,List.rev!args))(fstr.pos)(last_stopp)|_->randbinary_expr(p:st):expr=letrecgo(r:expr)=matchpeekpwith|L.Binarysel->advancep;letarg=unary_exprpingo(mk(Send(r,sel,[arg]))(fstr.pos)(sndarg.pos))|L.Bar->advancep;letarg=unary_exprpingo(mk(Send(r,"|",[arg]))(fstr.pos)(sndarg.pos))|_->ringo(unary_exprp)andunary_expr(p:st):expr=letrecgo(r:expr)=matchpeekpwith|L.Namesel->letstop=(tokp).stopinadvancep;go(mk(Send(r,sel,[]))(fstr.pos)stop)|_->ringo(primaryp)andprimary(p:st):expr=letstart=(tokp).startinmatchliteral_of_tokenpwith|Somel->mk(Litl)start(last_stopp)|None->(matchpeekpwith|L.Namev->advancep;mk(Varv)start(last_stopp)|L.Lparen->advancep;letx=expressionpinexpectpL.Rparen") expected";(* the parentheses count in the position: the debugger
* highlights what the person wrote *){xwithpos=(start,last_stopp)}|L.Lbracket->blockp|_->failp"Argument expected")andblock(p:st):expr=letstart=(tokp).startinadvancep;letargs=ref[]inwhilepeekp=L.Colondoadvancep;args:=namep"Argument name expected"::!argsdone;if!args<>[]then(matchpeekpwithL.Bar->advancep|L.Rbracket->()|_->failp"Vertical bar expected");lettemps=temporariespinletbody=statementspinexpectpL.Rbracket"Period or right bracket expected";mk(Block(List.rev!args,temps,body))start(last_stopp)andtemporaries(p:st):stringlist=ifpeekp<>L.Barthen[]elsebeginadvancep;lettemps=ref[]inwhilematchpeekpwithL.Name_->true|_->falsedotemps:=namep""::!tempsdone;expectpL.Bar"Vertical bar expected";List.rev!tempsendandstatements(p:st):stmtlist=letrecgoacc=matchpeekpwith|L.Rbracket|L.Eof->List.revacc|L.Caret->letstart=(tokp).startinadvancep;letx=expressionpinlets=Return(x,(start,sndx.pos))inifpeekp=L.Periodthenadvancep;(matchpeekpwithL.Rbracket|L.Eof->()|_->failp"Nothing more expected");List.rev(s::acc)|_->(letx=expressionpinmatchpeekpwith|L.Period->advancep;go(Exprx::acc)|L.Rbracket|L.Eof->List.rev(Exprx::acc)|_->failp"Nothing more expected")ingo[](*****************************************************************************)(* Methods *)(*****************************************************************************)letpattern(p:st):string*stringlist=matchpeekpwith|L.Namesel->advancep;(sel,[])|L.Binarysel->advancep;(sel,[namep"Argument name expected"])|L.Bar->advancep;("|",[namep"Argument name expected"])|L.Keyword_->letsel=Buffer.create16andargs=ref[]inwhilematchpeekpwithL.Keyword_->true|_->falsedo(matchpeekpwithL.Keywordk->Buffer.add_stringselk|_->());advancep;args:=namep"Argument name expected"::!argsdone;(Buffer.contentssel,List.rev!args)|_->failp"Message pattern expected"(* <primitive: 60> *)letprimitive(p:st):intoption=match(peekp,peek2p)with|L.Binary"<",L.Keyword"primitive:"->(advancep;advancep;matchpeekpwith|L.Intn->advancep;expectp(L.Binary">")"> expected";Somen|_->failp"Integer expected")|_->Nonelettokens(text:string):st=try{toks=L.tokenizetext;i=0}withL.Error(pos,msg)->raise(Error(pos,msg))letparse_body(p:st)(selector:string)(args:stringlist):method_=(* the primitive may come before or after the temporaries *)letprim1=primitivepinlettemps=temporariespinletprim2=primitivepinletbody=statementspinifpeekp<>L.Eofthenfailp"Nothing more expected";letprimitive=matchprim1withSome_->prim1|None->prim2in{selector;args;temps;primitive;body}letparse_method(text:string):method_=letp=tokenstextinletselector,args=patternpinparse_bodypselectorargsletparse_doit(text:string):method_=letp=tokenstextinparse_bodyp"DoIt"[]letparse_literal(text:string):literaloption=matchtokenstextwith|p->(matchliteral_of_tokenpwithSomelwhenpeekp=L.Eof->Somel|_->None|exceptionError_->None)|exceptionError_->Noneletselector_of(text:string):stringoption=matchtokenstextwith|p->(trySome(fst(patternp))withError_->None)|exceptionError_->None