123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510type('a,'b,'c)ep={embed:'a->'b;project:'b->'a;is:'c->bool}type('a,'b,'c)synonym_for_ep=('a,'b,'c)ep={embed:'a->'b;project:'b->'a;is:'c->bool}moduletypeS=sigtype'auserdata'typesrcloctypeinitstatemodulerecLuaValueBase:sigtypevalue=Nil|Numberoffloat|Stringofstring|Functionofsrcloc*func|Userdataofuserdata|Tableoftableandfunc=valuelist->valuelistandtable=valueLuahash.tanduserdata=valueuserdata'valeq:value->value->boolendandLuahashKey:sigtypetvalhash:t->intvalequal:t->t->boolendandLuahash:Hashtbl.Swithtypekey=LuaValueBase.valuetypevalue=LuaValueBase.valueandfunc=valuelist->valuelist(* can also side-effect state *)andtable=valueLuahash.tanduserdata=valueuserdata'andstate={globals:table;fallbacks:(string,value)Hashtbl.t;mutablecallstack:activationlist;mutablecurrentloc:Luasrcmap.locationoption(* supersedes top of stack *);startup:initstate}andactivation=srcloc*Luasrcmap.locationoptionvalcaml_func:func->value(* each result unique *)vallua_func:file:string->linedefined:int->func->valuevalsrcloc:file:string->linedefined:int->srcloc(* must NOT be reused *)valeq:value->value->boolvalto_string:value->stringvalactivation_strings:state->activation->stringlisttypeobjname=Fallbackofstring|Globalofstring|Elementofstring*valuevalobjname:state->value->objnameoption(* 'fallback', 'global', or 'element', name *)valstate:unit->state(* empty state, without even fallbacks *)valat_init:state->stringlist->unit(* run code at startup time *)valinitcode:state->(string->unit)->unit(* for the implementation only *)moduleTable:sigvalcreate:int->tablevalfind:table->key:value->value(* returns Nil if not found *)valbind:table->key:value->data:value->unitvalof_list:(string*value)list->tablevalnext:valueLuahash.t->value->(value*value)valfirst:valueLuahash.t->value*valueendexceptionProjectionofvalue*stringvalprojection:value->string->'atype('a,'b,'c)ep=('a,'b,'c)synonym_for_ep={embed:'a->'b;project:'b->'a;is:'c->bool}type'amap=('a,value,value)eptype'amapf(* used to build function maps that curry/uncurry *)valfloat:floatmapvalint:intmapvalbool:boolmapvalstring:stringmapvaluserdata:userdatamapvalunit:unitmapvaloption:'amap->'aoptionmapvaldefault:'a->'amap->'amapvallist:'amap->'alistmap(* does not project nil *)valoptlist:'amap->'alistmap(* projects nil to empty list *)valvalue:valuemapvaltable:tablemapvalrecord:'amap->(string*'a)listmapvalenum:string->(string*'a)list->'amapval(-->):'amap->'bmap->('a->'b)mapval(**->):'amap->'bmapf->('a->'b)mapfvalresult:'amap->'amapfvalresultvs:valuelistmapf(* functions returning value lists*)valresultpair:'amap->'bmap->('a*'b)mapfvaldots_arrow:'amap->'bmap->('alist->'b)mapf(* varargs functions *)valresults:('a->valuelist)->(valuelist->'a)->'amapf(* 'a represents multiple results (general case) *)valfunc:'amapf->'amap(* function *)valclosure:'amapf->'amap(* function or table+apply method *)valefunc:'amapf->'a->value(* efunc f = (closure f).embed *)typealt(* an alternative *)valalt:'amapf->'a->alt(* create an alternative *)valchoose:altlist->value(* dispatch on type/number of args *)val(<|>):'amap->'amap->'amapval(<@):'amap->('a->'b)->'bmap(* apply continuation after project *)endmoduletypeUSERDATA=sigtype'at(* type parameter will be Lua value *)valtname:string(* name of this type, for projection errors *)valeq:('a->'a->bool)->'at->'at->boolvalto_string:('a->string)->'at->stringendmoduleMake(U:USERDATA):Swithtype'auserdata'='aU.t=structtype'auserdata'='aU.ttypesrcloc=int*string*int(* unique id, filename, linedefined *)modulerecLuaValueBase:sigtypevalue=Nil|Numberoffloat|Stringofstring|Functionofsrcloc*func|Userdataofuserdata|Tableoftableandfunc=valuelist->valuelistandtable=valueLuahash.tanduserdata=valueuserdata'valeq:value->value->boolend=structtypevalue=Nil|Numberoffloat|Stringofstring|Functionofsrcloc*func|Userdataofuserdata|Tableoftableandfunc=valuelist->valuelistandtable=valueLuahash.tanduserdata=valueuserdata'letreceqxy=matchx,ywith|Nil,Nil->true|Numberx,Numbery->x=y|Stringx,Stringy->x=y|Userdatax,Userdatay->U.eqeqxy|Tablex,Tabley->x==y|Function((x,_,_),_),Function((y,_,_),_)->x=y|_,_->falseendandLuahashKey:sigtypetvalhash:t->intvalequal:t->t->boolend=structtypet=LuaValueBase.valuelethash=Hashtbl.hashletequal=LuaValueBase.eqendandLuahash:Hashtbl.Swithtypekey=LuaValueBase.value=Hashtbl.Make(LuahashKey)includeLuaValueBasetypestate={globals:table;fallbacks:(string,value)Hashtbl.t;mutablecallstack:activationlist;mutablecurrentloc:Luasrcmap.locationoption(* supersedes top of stack *);startup:initstate}andinitstate={mutableinit_strings:(string->unit)->unit;mutableinitialized:bool}andactivation=srcloc*Luasrcmap.locationoptionmoduleTable=struct(* open LuaValueBase *)letcreate=Luahash.createletfindt~key:k=tryLuahash.findtkwithNot_found->Nilletbindt~key:k~data:v=matchvwith|Nil->Luahash.removetk|_->Luahash.replacetkvletof_listl=lett=create(List.lengthl)inlet_=List.iter(fun(k,v)->bindt~key:(Stringk)~data:v)lintletnexthkey=letrecauxhskey=matchhs()with|Seq.Cons((k,_),f)->ifeqkkeythenbeginletn=f()inmatchnwith|Seq.Cons((k',v'),_)->(k',v')|Seq.Nil->raiseNot_foundendelseauxfkey|Seq.Nil->raiseNot_foundinlethash_seq=Luahash.to_seqhinauxhash_seqkeyletfirsth=lethash_seq=Luahash.to_seqhinmatchhash_seq()with|Seq.Cons((k,v),_)->(k,v)|Seq.Nil->raiseNot_foundendletsrcloc=letn=ref0infun~file~linedefined:line->(n:=!n+1;(!n,file,line))letlua_func~file~linedefined:linef=Function(srcloc~file~linedefined:line,f)letcaml_func=lua_func~file:"(OCaml)"~linedefined:(-1)letluastring_of_floatx=lets=string_of_floatxinifString.gets(String.lengths-1)='.'thenString.subs0(String.lengths-1)elsesletrecto_string=function|Nil->"nil"|Numberx->luastring_of_floatx|Strings->s|Function(_,_)->"function"|Userdatau->U.to_stringto_stringu|Table_->"table"typeobjname=Fallbackofstring|Globalofstring|Elementofstring*valueletkey_matchingitertneedle=letr=refNoneiniter(funkv->ifeqneedlevthenr:=Somekelse())t;!rletobjnamegneedle=matchkey_matchingHashtbl.iterg.fallbacksneedlewith|Somes->Some(Fallbacks)|None->matchkey_matchingLuahash.iterg.globalsneedlewith|Some(Strings)->Some(Globals)|_->letr=refNoneinLuahash.iter(funkv->match!rwith|None->(matchk,vwith|Stringn,Tablet->(matchkey_matchingLuahash.itertneedlewith|Somev->r:=Some(Element(n,v))|None->())|_,_->())|Some_->())g.globals;!rletactivation_stringsg((_uid,file,line)assrcloc,current)=letfirsttail=matchobjnameg(Function(srcloc,fun_->assertfalse))with|Some(Fallbackn)->"`"::n::"' fallback"::tail|Some(Globaln)->"function "::n::tail|Some(Element(t,Stringn))->"function "::t::"."::n::tail|Some(Element(t,v))->"function "::t::"["::to_stringv::"]"::tail|None->"unknown function"::tailinletlast=matchcurrentwith|None->" defined in file "::file::(ifline>0then[" at line ";string_of_intline]else[])(* | Some (f, l, c) when f = file ->
[" at line "; string_of_int l; " column "; string_of_int c]
*)|Some(f,l,c)->[" in file ";f;", line ";string_of_intl;" column ";string_of_intc]inmatchlinewith|0->"main of "::file::last|-1->first[" ";file]|_->firstlastexceptionProjectionofvalue*stringletprojectionvs=raise(Projection(v,s))type('a,'b,'c)ep=('a,'b,'c)synonym_for_ep={embed:'a->'b;project:'b->'a;is:'c->bool}type'amap=('a,value,value)eptype'amapf=('a,valuelist->valuelist,valuelist)epletuserdata={embed=(funx->Userdatax);project=(functionUserdatax->x|v->raise(Projection(v,U.tname)));is=(functionUserdata_->true|_->false)}letstring={embed=(funs->Strings);project=(functionStrings->s|Numberx->luastring_of_floatx|v->raise(Projection(v,"string")));is=(functionString_|Number_->true|_->false)}letis_float_literals=tryLuafloat.length(Lexing.from_strings)=String.lengthswithFailure_->falseletpervasive_float=floatletfloat={embed=(funx->Numberx);project=(functionNumberx->x|Stringswhenis_float_literals->float_of_strings|v->raise(Projection(v,"float")));is=(functionNumber_->true|Strings->is_float_literals|_->false)}letto_intx=letn=truncatexinifpervasive_floatn=xthennelseraise(Projection(Numberx,"int"))letint={embed=(funn->Number(pervasive_floatn));project=(functionNumberx->to_intx|v->raise(Projection(v,"int")));is=(functionNumberx->pervasive_float(truncatex)=x|_->false)}letbool={embed=(funb->ifbthenString"t"elseNil);project=(functionNil->false|_->true);is=(fun_->true)}letunit={embed=(fun()->Nil);project=(functionNil->()|v->raise(Projection(v,"unit")));is=(functionNil->true|_->false)}letenumtypenamepairs={embed=(funv'->tryString(fst(List.find(fun(_,v)->v=v')pairs))withNot_found->assertfalse);project=(functionStringk->(tryList.assockpairswithNot_found->raise(Projection(Stringk,typename)))|v->raise(Projection(v,typename)));is=(functionStringk->List.mem_assockpairs|_->false)}letoptiont={embed=(functionNone->Nil|Somex->t.embedx);project=(functionNil->None|v->Some(t.projectv));is=(functionNil->true|v->t.isv)}letdefaultdt={embed=t.embed;project=(functionNil->d|v->t.projectv);is=(functionNil->true|v->t.isv)}letlist(ty:'amap)=lettablel=letn=List.lengthlinlett=Table.createninletrecset_elemsnext=function|[]->()|e::es->(Table.bindt~key:(Numbernext)~data:(ty.embede);set_elems(next+.1.0)es)in(set_elems1.0l;Tablet)inletuntable(t:table)=letn=Luahash.lengthtinletrecelemsi=ifi>nthen[]elsety.project(Table.findt~key:(Number(pervasive_floati)))::elems(i+1)inelems1in{embed=table;project=(functionTablet->untablet|v->raise(Projection(v,"list")));is=(functionTable_->true|_->false)}letoptlistty=default[](listty)letvalue={embed=(funx->x);project=(funx->x);is=(fun_->true)}lettable={embed=(funx->Tablex);project=(functionTablet->t|v->raise(Projection(v,"table")));is=(functionTable_->true|_->false)}letprojectRecordtyv=matchvwith|Tablet->letrecaddpairs(k,v)=(string.projectv,ty.projectv)::tryaddpairs(Table.nexttk)withNot_found->[]in(tryaddpairs(Table.firstt)withNot_found->[])|_->raise(Projection(v,"table (as record)"))letrecordty={embed=(funpairs->Table(Table.of_list(List.map(fun(k,v)->(k,ty.embedv))pairs)));project=projectRecordty;is=table.is}lettake1=function(* take one value from a list of arguments *)|[]->Nil|h::_->hlet(-->)argresult={embed=(funf->caml_func(funargs->[result.embed(f(arg.project(take1args)))]));project=(functionFunction(_,f)->funx->result.project(take1(f[arg.embedx]))|v->raise(Projection(v,"function")));is=(functionFunction(_,_)->true|_->false)}let(**->)(firstarg:'amap)(lastargs:'bmapf):('a->'b)mapf=letapply(f:'a->'b)args=leth,t=matchargswith[]->Nil,[]|h::t->h,tinletf=f(firstarg.projecth)inlastargs.embedftinletunappf'=fun(x:'a)->lastargs.project(functiont->f'(firstarg.embedx::t))in(* function can match even if args are defaulted, but not if too many args *)letisargs=leth,t=matchargswith[]->Nil,[]|h::t->h,tinfirstarg.ish&&lastargs.istin{embed=apply;project=unapp;is=is}letresults(a_to_values:'a->valuelist)(a_of_values:valuelist->'a)={embed=(fun(a:'a)->fun_lua_args->a_to_valuesa);project=(funf_lua->(a_of_values(f_lua[]):'a));is=(function[]->true|_::_->false)}let(<<)fg=funx->f(gx)letresultr=results(funv->[r.embedv])(r.project<<take1)letresultvs=results(funl->l)(funl->l)letresultpairab=letem(x,y)=[a.embedx;b.embedy]inletprvs=letx,y=matchvswith|[]->Nil,Nil|[x]->x,Nil|x::y::_->x,yin(a.projectx,b.projecty)inresultsemprletdots_arrow(varargs:'amap)(result:'bmap):('alist->'b)mapf=letapply(f:'alist->'b)=fun(args:valuelist)->[result.embed(f(List.mapvarargs.projectargs))]inletunapp(f':valuelist->valuelist)=fun(args:'alist)->result.project(take1(f'(List.mapvarargs.embedargs)))in{embed=apply;project=unapp;is=List.for_allvarargs.is}letfunc(arrow:'amapf):('amap)={embed=(fun(f:'a)->caml_func(arrow.embedf));project=(functionFunction(_,f)->(arrow.projectf:'a)|v->raise(Projection(v,"function")));is=(functionFunction(_,_)->true|_->false)}letclosure(arrow:'amapf):('amap)={embed=(fun(f:'a)->caml_func(arrow.embedf));project=(functionFunction(_,f)->(arrow.projectf:'a)|Tabletasv->(letf=tryTable.findt~key:(String"apply")withNot_found->raise(Projection(v,"function"))inmatchfwith|Function(_,f)->arrow.project(funvs->f(v::vs))|v->raise(Projection(v,"'apply' element of table as function")))|v->raise(Projection(v,"function")));is=(functionFunction(_,_)->true|Tablet->(trymatchTable.findt~key:(String"apply")with|Function(_,_)->true|_->falsewithNot_found->false)|_->false)}letefunctf=(closuret).embedftypealt=(valuelist->valuelist)*(valuelist->bool)letalttf=(t.embedf,t.is)letchoosealts=letrunargs=letf=tryfst(List.find(fun(_,is)->isargs)alts)withNot_found->letargs=(listvalue).embedargsinraise(Projection(args,"arguments matching alternatives"))infargsincaml_funcrunlet(<|>)tt'={project=(funv->ift.isvthent.projectvelset'.projectv);embed=t'.embed;is=(funv->t.isv||t'.isv)}let(<@)tk={project=(funv->k(t.projectv));embed=(fun_->assertfalse);is=t.is}moduleStringList=structletempty_=()letof_listlf=List.iterflletappendl1l2f=l1f;l2fendletstate()={globals=Table.create50;fallbacks=Hashtbl.create10;callstack=[];currentloc=None;startup={init_strings=StringList.empty;initialized=false;}}letat_initgss=ifg.startup.initializedthen(prerr_endline"Internal Lua-ML error: called at_init after initialiation was complete";exit(1))elseg.startup.init_strings<-StringList.appendg.startup.init_strings(StringList.of_listss)letinitcodeg=ifg.startup.initializedthen(prerr_endline"Internal Lua-ML error: a naughty client called initcode";exit(1))elseletcode=g.startup.init_stringsinbeging.startup.initialized<-true;g.startup.init_strings<-StringList.empty;codeendend