123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208(* 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.
*)openScheme(* See Scheme_syntax.mli *)exceptionErrorofstring*Sexpr.spanletfail(x:Sexpr.t)fmt=Printf.ksprintf(funmsg->raise(Error(msg,x.span)))fmt(*****************************************************************************)(* Quote *)(*****************************************************************************)letrecdatum(x:Sexpr.t):Scheme.t=matchx.datumwith|Intn->Intn|Floatf->Realf|Strs->Strs|Syms->Syms|Charc->Charc|Boolb->Boolb|List(xs,tail)->List.fold_right(funxrest->Pair(datumx,rest))xs(matchtailwithSomet->datumt|None->Nil)|Vectorxs->Vector(Array.of_list(List.mapdatumxs))(*****************************************************************************)(* Helpers *)(*****************************************************************************)letmk(desc:desc)(span:Sexpr.span):expr={desc;span}letquotevspan=mk(Quotev)span(* a built-in called by the rewriting: the procedure itself, not its
name, so a program's own list or cons can't change what `(...)
means *)letprim(name:string)(span:Sexpr.span):expr=quote(Proc(Primname))spanletname_of(x:Sexpr.t)(what:string):string=matchx.datumwithSyms->s|_->failx"%s: expected a name, but found %s"what(Sexpr.to_stringx)(* the parts of a list form, or an error on (a . b) *)letparts(x:Sexpr.t):Sexpr.tlist=matchx.datumwithList(xs,None)->xs|_->failx"bad syntax: %s"(Sexpr.to_stringx)letis_define(x:Sexpr.t):bool=matchx.datumwithList({datum=Sym"define";_}::_,None)->true|_->false(*****************************************************************************)(* Expressions *)(*****************************************************************************)letrecexpr(x:Sexpr.t):expr=letsp=x.spaninmatchx.datumwith|Int_|Float_|Str_|Char_|Bool_|Vector_->quote(datumx)sp|Syms->mk(Vars)sp|List([],None)->failx"(): expected a function after the open parenthesis, but nothing's there"|List(_,Some_)->failx"bad syntax: a dotted list is not an expression"|List(({datum=Symhead;_}ash)::args,None)->formxhheadargs|List(f::args,None)->mk(App(exprf,List.mapexprargs))sp(* a list whose head is a symbol: a special form, or a call *)andform(x:Sexpr.t)(h:Sexpr.t)(head:string)(args:Sexpr.tlist):expr=letsp=x.spaninmatch(head,args)with|"quote",[d]->quote(datumd)sp|"quote",_->failx"quote: expected one thing after quote"|"quasiquote",[d]->quasid|("lambda"|"λ"),formals::body->mk(Lambda(lambdax""formalsbody))sp|("lambda"|"λ"),[]->failx"lambda: expected the variables, then the body"|"if",[c;a;b]->mk(If(exprc,expra,exprb))sp|"if",[c;a]->mk(If(exprc,expra,quoteVoidsp))sp|"if",_->failx"if: expected a question and two answers, but found %d parts"(List.lengthargs)|"set!",[v;e]->mk(Set(name_ofv"set!",expre))sp|"set!",_->failx"set!: expected a variable and an expression"|"begin",[]->quoteVoidsp|"begin",es->mk(Seq(List.mapexpres))sp|"let",{datum=Symname;_}::bindings::body->(* the named let: a loop *)letvars,inits=bind_listbindingsinletloop={params=vars;rest=None;locals=[];body=[];name}inletloop=mk(Lambda(body_ofx{loopwithbody=[]}body))spinletf=mk(Lambda{params=[];rest=None;locals=[name];body=[mk(Set(name,loop))sp;mk(Varname)sp];name=""})spinmk(App(mk(App(f,[]))sp,inits))sp|"let",bindings::body->letvars,inits=bind_listbindingsinmk(App(mk(Lambda(body_ofx{params=vars;rest=None;locals=[];body=[];name=""}body))sp,inits))sp|"let*",bindings::body->(matchpartsbindingswith|[]|[_]->formxh"let"args|b::more->formxh"let"[Sexpr.make(List([b],None))bindings.span;Sexpr.make(List(Sexpr.sym"let*"h.span::Sexpr.make(List(more,None))bindings.span::body,None))sp])|"letrec",bindings::body|"letrec*",bindings::body->letdefs=List.map(funb->matchpartsbwith[v;e]->Sexpr.make(List([Sexpr.sym"define"b.span;v;e],None))b.span|_->failb"letrec: expected [name expression]")(partsbindings)inmk(App(mk(Lambda(body_ofx{params=[];rest=None;locals=[];body=[];name=""}(defs@body)))sp,[]))sp|("let"|"let*"|"letrec"),[]->failx"%s: expected the bindings, then the body"head|"and",[]->quote(Booltrue)sp|"and",[a]->expra|"and",a::rest->mk(If(expra,formxh"and"rest,quote(Boolfalse)sp))sp|"or",[]->quote(Boolfalse)sp|"or",[a]->expra|"or",a::rest->lett=mk(Var" t")spinmk(App(mk(Lambda{params=[" t"];rest=None;locals=[];body=[mk(If(t,t,formxh"or"rest))sp];name=""})sp,[expra]))sp|"cond",clauses->condxclauses|"when",c::body->mk(If(exprc,mk(Seq(List.mapexprbody))sp,quoteVoidsp))sp|"unless",c::body->mk(If(exprc,quoteVoidsp,mk(Seq(List.mapexprbody))sp))sp|"case",key::clauses->(* (case k [(d ...) a] [else b]): k once, then memv down the clauses *)lett=mk(Var" t")spinletrecgocs=matchcswith|[]->quoteVoidsp|c::rest->(matchpartscwith|{datum=Sym"else";_}::body->mk(Seq(List.mapexprbody))c.span|ds::body->mk(If(mk(App(prim"memv"c.span,[t;quote(datumds)ds.span]))c.span,mk(Seq(List.mapexprbody))c.span,gorest))c.span|[]->failc"case: expected a clause")inmk(App(mk(Lambda{params=[" t"];rest=None;locals=[];body=[goclauses];name=""})sp,[exprkey]))sp|"big-bang",w::clauses->letclausec=matchpartscwith[{datum=Symname;_};e]->(name,expre)|_->failc"big-bang: expected a clause such as [on-tick f]"inmk(Big_bang(exprw,List.mapclauseclauses))sp|("define"|"define-struct"),_->failx"%s: found a definition that is not at the top level"head|_->mk(App(exprh,List.mapexprargs))sp(* [(x e) ...]: the variables and their expressions *)andbind_list(bindings:Sexpr.t):stringlist*exprlist=List.split(List.map(funb->matchpartsbwith[v;e]->(name_ofv"let",expre)|_->failb"let: expected a variable and an expression")(partsbindings))(* (lambda formals body ...): (a b), (a . rest), or a single name for all *)andlambda(x:Sexpr.t)(name:string)(formals:Sexpr.t)(body:Sexpr.tlist):lambda=letparams,rest=matchformals.datumwith|Symr->([],Somer)|List(ps,tail)->(List.map(funp->name_ofp"lambda")ps,Option.map(funt->name_oft"lambda")tail)|_->failformals"lambda: expected the variables, but found %s"(Sexpr.to_stringformals)inbody_ofx{params;rest;locals=[];body=[];name}body(* a body: its defines become its own variables, set in turn *)andbody_of(x:Sexpr.t)(l:lambda)(forms:Sexpr.tlist):lambda=ifList.for_allis_defineformsthenfailx"expected an expression for the body, but nothing's there";letone(f:Sexpr.t):stringlist*expr=ifis_definefthenmatchdefinitionfwith|{desc=Define(name,e);span}->([name],mk(Set(name,e))span)|_->failf"define: bad syntax"else([],exprf)inletlocals,body=List.split(List.maponeforms)in{lwithlocals=List.concatlocals;body}andcond(x:Sexpr.t)(clauses:Sexpr.tlist):expr=matchclauseswith|[]->(* no question was true: the teaching languages' error, R5RS's
unspecified value *)mk(App(prim"cond-fell-through"x.span,[]))x.span|c::rest->(matchpartscwith|[{datum=Sym"else";_}]->failc"cond: expected an answer after else"|{datum=Sym"else";_}::body->mk(Seq(List.mapexprbody))c.span|[q]->lett=mk(Var" t")c.spaninmk(App(mk(Lambda{params=[" t"];rest=None;locals=[];body=[mk(If(t,t,condxrest))c.span];name=""})c.span,[exprq]))c.span|q::body->mk(If(exprq,mk(Seq(List.mapexprbody))c.span,condxrest))c.span|[]->failc"cond: expected a clause with a question and an answer, but found an empty part")(* `d: data, and the ,e inside it computed *)andquasi(d:Sexpr.t):expr=letsp=d.spaninmatchd.datumwith|List([{datum=Sym"unquote";_};e],None)->expre|List(xs,tail)->letlast=matchtailwithSomet->quasit|None->quoteNilspinList.fold_right(fun(x:Sexpr.t)rest->matchx.datumwith|List([{datum=Sym"unquote-splicing";_};e],None)->mk(App(prim"append"x.span,[expre;rest]))x.span|_->mk(App(prim"cons"x.span,[quasix;rest]))x.span)xslast|_->quote(datumd)sp(*****************************************************************************)(* Definitions *)(*****************************************************************************)anddefinition(x:Sexpr.t):expr=matchpartsxwith|[_;{datum=Symname;_};e]->mk(Define(name,matchexprewith{desc=Lambdal;span}->{desc=Lambda{lwithname};span}|e->e))x.span|_::({datum=List(({datum=Symname;_})::params,tail);_}asheader)::body->ifbody=[]thenfailx"define: expected an expression for the function's body, but nothing's there";letformals=Sexpr.make(List(params,tail))header.spaninmk(Define(name,mk(Lambda(lambdaxnameformalsbody))x.span))x.span|[_;v]->failx"define: expected an expression after the name %s, but nothing's there"(Sexpr.to_stringv)|_->failx"define: expected a name, or a name and its arguments in parentheses"lettop(x:Sexpr.t):expr=matchx.datumwith|List({datum=Sym"define";_}::_,None)->definitionx|List([{datum=Sym"define-struct";_};name;fields],None)->mk(Define_struct(name_ofname"define-struct",List.map(funf->name_off"define-struct")(partsfields)))x.span|List({datum=Sym"define-struct";_}::_,None)->failx"define-struct: expected a name and the fields in parentheses"|_->exprx