123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694(*---------------------------------------------------------------------------
Copyright (c) 2016 The b0 programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)openB00_serialk_textmoduleJson=struct(* JSON text *)typeloc=Tloc.ttypemem=(string*loc)*tandt=[`Nullofloc|`Boolofbool*loc|`Floatoffloat*loc|`Stringofstring*loc|`Aoftlist*loc|`Oofmemlist*loc]letloc_nil=Tloc.nilletloc=function|`Nulll|`Bool(_,l)|`Float(_,l)|`String(_,l)|`A(_,l)|`O(_,l)->l(* Constructors *)letnull=`Nullloc_nilletboolb=`Bool(b,loc_nil)letfloatf=`Float(f,loc_nil)letstrings=`String(s,loc_nil)letarrayvs=`A(vs,loc_nil)letmemnv=((n,loc_nil),v)letobjmems=`O(mems,loc_nil)(* Accessors *)letkind=function|`Null_->"null"|`Bool_->"bool"|`Float_->"float"|`String_->"string"|`A_->"array"|`O_->"object"leterr_expexpfnd=Format.asprintf"%a: %s but expected %s"Tloc.pp(locfnd)(kindfnd)expleterr_exp_null=err_exp"null"leterr_exp_bool=err_exp"bool"leterr_exp_float=err_exp"number"leterr_exp_string=err_exp"string"leterr_exp_array=err_exp"array"leterr_exp_obj=err_exp"object"leterre=Erroreletto_null=function`Null_->Ok()|j->err(err_exp_nullj)letto_bool=function`Bool(b,_)->Okb|j->err(err_exp_boolj)letto_float=function`Float(f,_)->Okf|j->err(err_exp_floatj)letto_string=function`String(s,_)->Oks|j->err(err_exp_stringj)letto_array=function`A(vs,_)->Okvs|j->err(err_exp_arrayj)letto_obj=function`O(mems,_)->Okmems|j->err(err_exp_objj)leterr=invalid_argletget_null=function`Null_->()|j->err(err_exp_nullj)letget_bool=function`Bool(b,_)->b|j->err(err_exp_boolj)letget_float=function`Float(f,_)->f|j->err(err_exp_floatj)letget_string=function`String(s,_)->s|j->err(err_exp_stringj)letget_array=function`A(vs,_)->vs|j->err(err_exp_arrayj)letget_obj=function`O(mems,_)->mems|j->err(err_exp_objj)(* Decode *)(* FIXME add positions and reuse Tlex. *)typedecoder={t:Buffer.t;i:string;mutablepos:int;}letdecoders={t=Buffer.create255;i=s;pos=0}letacceptd=d.pos<-d.pos+1[@@ocaml.inline]lettresetd=Buffer.resetd.t[@@ocaml.inline]lettacceptd=Buffer.add_chard.td.i.[d.pos];acceptd;[@@ocaml.inline]lettaddcdc=Buffer.add_chard.tc[@@ocaml.inline]lettaddudu=Tdec.buffer_add_uchard.tulettokend=Buffer.contentsd.t[@@ocaml.inline]leteoid=d.pos=String.lengthd.i[@@ocaml.inline]letbyted=matcheoidwith|true->0xFFF|false->Char.coded.i.[d.pos][@@ocaml.inline]leterrdfmt=Format.kasprintf(funs->raise_notrace(Failures))("%d: "^^fmt)d.posletpp_byteppfd=matchbytedwith|0xFFF->Format.fprintfppf"end of input"|b->Format.fprintfppf"%C"(Char.chrb)typeutf_8_case=|L1|L2|L3_E0|L3_E1_EC_or_EE_EF|L3_ED|L4_F0|L4_F1_F3|L4_F4|Eletutf_8_case=(*
(* See https://tools.ietf.org/html/rfc3629#section-4 *)
Printf.printf "[|";
for i = 0 to 255 do
if i mod 16 = 0 then Printf.printf "\n";
if 0x00 <= i && i <= 0x7F then Printf.printf "L1; " else
if 0xC2 <= i && i <= 0xDF then Printf.printf "L2; " else
if 0xE0 = i then Printf.printf "L3_E0; " else
if 0xE1 <= i && i <= 0xEC || 0xEE <= i && i <= 0xEF
then Printf.printf "L3_E1_EC_or_EE_EF; " else
if 0xED = i then Printf.printf "L3_ED;" else
if 0xF0 = i then Printf.printf "L4_F0; " else
if 0xF1 <= i && i <= 0xF3 then Printf.printf "L4_F1_F3; " else
if 0xF4 = i then Printf.printf "L4_F4; " else
Printf.printf "E; "
done;
Printf.printf "\n|]"
*)[|L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;L1;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;E;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L2;L3_E0;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L3_ED;L3_E1_EC_or_EE_EF;L3_E1_EC_or_EE_EF;L4_F0;L4_F1_F3;L4_F1_F3;L4_F1_F3;L4_F4;E;E;E;E;E;E;E;E;E;E;E;|]lettaccept_utf_8d=leterrd=errd"expected UTF-8 byte found: %a"pp_bytedinletb=bytedinletaccept_taild=if(bytedlsr6=0b10)thentacceptdelseerrd[@@ocaml.inline]inmatchutf_8_case.(b)with|L1->tacceptd|L2->tacceptd;accept_taild|L3_E0->tacceptd;if(byted-0xA0<0xBF-0xA0)thentacceptdelseerrd;accept_taild|L3_E1_EC_or_EE_EF->tacceptd;accept_taild;accept_taild|L3_ED->tacceptd;if(byted-0x80<0x9F-0x80)thentacceptdelseerrd;accept_taild|L4_F0->tacceptd;if(byted-0x90<0xBF-0x90)thentacceptdelseerrd;accept_taild;accept_taild|L4_F1_F3->tacceptd;accept_taild;accept_taild;accept_taild;|L4_F4->tacceptd;if(byted-0x80<0x8F-0x80)thentacceptdelseerrd;|E->errdletaccept_bytesdbytes=(* first byte already checked *)letmax=String.lengthbytes-1inletrecloopi=matchi>maxwith|true->()|false->matchChar.codebytes.[i]=bytedwith|true->acceptd;loop(i+1)|false->errd"expected %C found: %a while parsing '%s'"bytes.[i]pp_bytedbytesinacceptd;loop1letrecskip_wsd=matchbytedwith|0x20|0x09|0x0A|0x0D->acceptd;skip_wsd|_->()letparse_trued=accept_bytesd"true";`Bool(true,loc_nil)letparse_falsed=accept_bytesd"false";`Bool(false,loc_nil)letparse_nulld=accept_bytesd"null";`Nullloc_nilletparse_numberd=(* not fully compliant *)letconvd=try`Float(float_of_string(tokend),loc_nil)with|Failuree->errd"could not parse a float from: %S"(tokend)inletrectaccept_non_sepd=matchbytedwith|0x20|0x09|0x0A|0x0D|0x2C|0x5D|0x7D|0xFFF->convd|_->tacceptd;taccept_non_sepdintresetd;tacceptd;taccept_non_sepdletrecparse_uescapedhiucount=letpp_ucpppfd=Format.fprintfppf"U+%04X"dinleterr_not_lodu=errd"not a low surrogate %a"pp_ucpuinleterr_lodu=errd"lone low surrogate %a"pp_ucpuinleterr_hidu=errd"lone high surrogate %a"pp_ucpuinmatchcount>0with|true->beginmatchbytedwith|cwhen0x30<=c&&c<=0x39->acceptd;parse_uescapedhi(u*16+c-0x30)(count-1)|cwhen0x41<=c&&c<=0x46->acceptd;parse_uescapedhi(u*16+c-0x37)(count-1)|cwhen0x61<=c&&c<=0x66->acceptd;parse_uescapedhi(u*16+c-0x57)(count-1)|c->errd"expected hex digit found: %C"(Char.chrc)end|false->matchhiwith|Somehi->(* combine high and low surrogate into scalar value. *)ifu<0xDC00||u>0xDFFFthenerr_not_loduelseletu=((((hiland0x3FF)lsl10)lor(uland0x3FF))+0x10000)intaddud(Uchar.unsafe_of_intu)|None->ifu<0xD800||u>0xDFFFthentaddud(Uchar.unsafe_of_intu)elseifu>0xDBFFthenerr_loduelsematchbytedwith|0x5C->acceptd;beginmatchbytedwith|0x75->acceptd;parse_uescaped(Someu)04|_->err_hiduend|_->err_hiduletparse_stringd=letparse_escaped=matchbytedwith|(0x22|0x5C|0x2Fasb)->taddcd(Char.chrb);acceptd;|0x62->taddcd'\x08';acceptd;|0x66->taddcd'\x0C';acceptd;|0x6E->taddcd'\x0A';acceptd;|0x72->taddcd'\x0D';acceptd;|0x74->taddcd'\x09';acceptd;|0x75->acceptd;parse_uescapedNone04|_->errd"expected escape found: %a"pp_bytedinletrecloopd=matchbytedwith|0x5C(* '\' *)->acceptd;parse_escaped;loopd|0x22(* '"' *)->acceptd;`String((tokend),loc_nil)|0xFFF->errd"unclosed string"|_->taccept_utf_8d;loopdinacceptd;tresetd;loopdletrecparse_objectd=match(acceptd;skip_wsd;byted)with|0x7D(* '}' *)->acceptd;`O([],loc_nil)|_->letparse_named=let`Stringname=match(skip_wsd;byted)with|0x22(* '"' *)->parse_stringd|_->errd"expected '\"' found: %a"pp_bytedinskip_wsd;nameinletrecloopaccd=letname=parse_namedinmatchbytedwith|0x3A(* ':' *)->letv=(acceptd;parse_valued)inbeginmatchbytedwith|0x2C(* ',' *)->acceptd;loop((name,v)::acc)d|0x7D(* '}' *)->acceptd;`O(List.rev((name,v)::acc),loc_nil)|_->errd"expected ',' or '}' found: %a"pp_bytedend|_->errd"expected ':' found: %a"pp_bytedinloop[]dandparse_arrayd=match(acceptd;skip_wsd;byted)with|0x5D(* ']' *)->acceptd;`A([],loc_nil)|_->letrecloopaccd=letv=parse_valuedinmatchbytedwith|0x2C(* ',' *)->acceptd;loop(v::acc)d|0x5D(* ']' *)->acceptd;`A(List.rev(v::acc),loc_nil)|_->errd"expected ',' or ']' found: %a"pp_bytedinloop[]dandparse_valued:t=letv=match(skip_wsd;byted)with|0x22(* '"' *)->parse_stringd|0x74(* 't' *)->parse_trued|0x66(* 'f' *)->parse_falsed|0x6E(* 'n' *)->parse_nulld|0x7B(* '{' *)->parse_objectd|0x5B(* '[' *)->parse_arrayd|0x2D(* '-' *)->parse_numberd|bwhen0x30(* '0' *)<=b&&b<=0x39(* '9' *)->parse_numberd|_->errd"expected a JSON value found: %a"pp_bytedinskip_wsd;vletof_string?(file=Tloc.no_file)s=tryletd=decodersinletv=parse_valuedinmatchbytedwith|0xFFF(* eoi *)->Okv|_->errd"expected end of input found: %a"pp_bytedwith|Failuree->Errore(* JSON generation *)moduleG=struct(* Not T.R. we could CPS. *)typeenc={mutablesep:bool;b:Buffer.t}typet=enc->unitletaddccenc=Buffer.add_charenc.bcletaddssenc=Buffer.add_stringenc.bsletadds_escsenc=letis_control=function'\x00'..'\x1F'|'\x7F'->true|_->falseinletlen=String.lengthsinletmax_idx=len-1inletflushbstarti=ifstart<lenthenBuffer.add_substringbsstart(i-start);inletrecloopstarti=matchi>max_idxwith|true->flushenc.bstarti|false->letnext=i+1inmatchString.getsiwith|'"'->flushenc.bstarti;adds"\\\""enc;loopnextnext|'\\'->flushenc.bstarti;adds"\\\\"enc;loopnextnext|cwhenis_controlc->flushenc.bstarti;adds(Format.asprintf"\\u%04X"(Char.codec))enc;loopnextnext|c->loopstartnextinloop00letnullenc=adds"null"encletboolbenc=adds(ifbthen"true"else"false")encletintienc=adds(string_of_inti)encletfloatfenc=adds(Format.asprintf"%.16g"f)encletstringsenc=addc'"'enc;adds_escsenc;addc'"'encletnosepenc=enc.sep<-falseletsepenc=enc.sepletset_sepsepenc=enc.sep<-sepletif_sepenc=ifnotenc.septhenenc.sep<-trueelseaddc','enctypearray=tletarrayenc=()letarray_endelsenc=letsep=sepencinaddc'['enc;nosepenc;elsenc;addc']'enc;set_sepsepencletelearrenc=arrenc;if_sepenc;eencletel_ifcearrenc=ifcthenel(e())arrencelsearrenctypeobj=tletobjenc=()letobj_endmemsenc=letsep=sepencinaddc'{'enc;nosepenc;memsenc;addc'}'enc;set_sepsepencletmemmvobjenc=objenc;if_sepenc;stringmenc;addc':'enc;vencletmem_ifcmvobjenc=ifcthenmemm(v())objencelseobjenc(* Derived generators *)letstrffmt=Format.kasprintfstringfmtletlistelvl=array_end(List.fold_left(funav->el(elvv)a)arrayl)letoptionsomeo=matchowithNone->null|Somev->somevletrecjson=function|`Null_->null|`Bool(b,_)->boolb|`Float(f,_)->floatf|`String(s,_)->strings|`A(a,_)->array_end@@List.fold_left(funae->el(jsone)a)arraya|`O(o,_)->obj_end@@List.fold_left(funo((m,_),v)->memm(jsonv)o)objo(* Output generated values *)letbuffer_addbg=g{sep=true;b}letto_stringg=letb=Buffer.create65535in(buffer_addbg;Buffer.contentsb)endletto_stringv=G.to_string(G.jsonv)letppppf(v:t)=(* FIXME not T.R. *)letpp_stringppfs=(* FIXME quick & dirty escaping *)Format.pp_print_stringppf(G.to_string(G.json((`String(s,loc_nil)))))inletpp_commappf()=Format.(pp_print_charppf',';pp_print_spaceppf())inletrecloopppf=function|`Null_->Format.pp_print_stringppf"null"|`Bool(b,_)->Format.pp_print_stringppf(ifbthen"true"else"false")|`Float(f,_)->Format.fprintfppf"%.16g"f|`String(s,_)->pp_stringppfs|`A(a,_)->Format.pp_open_boxppf1;Format.pp_print_charppf'[';Format.pp_print_list~pp_sep:pp_commaloopppfa;Format.pp_print_charppf']';Format.pp_close_boxppf();|`O(o,_)->letpp_memppf((m,_),v)=Format.pp_open_boxppf1;pp_stringppfm;Format.pp_print_charppf':';Format.pp_print_spaceppf();loopppfv;Format.pp_close_boxppf();inFormat.pp_open_vboxppf1;Format.pp_print_charppf'{';Format.pp_print_list~pp_sep:pp_commapp_memppfo;Format.pp_print_charppf'}';Format.pp_close_boxppf();inloopppfvendmoduleJsong=Json.GmoduleJsonq=structmoduleSset=Set.Make(String)moduleSmap=Map.Make(String)letpp_quoteppfs=Format.fprintfppf"'%s'"sletpp_mem=pp_quotetypepath=(* Paths in JSON values, array and object member traversals. *)([`A|`Oofstring]*Json.loc)list(* in reverse order *)letpath_to_stringp=letseg=function`A->"[]"|`On->"."^ninString.concat""(List.rev_mapsegp)letpath_to_trace?(pp_mem=pp_mem)p=letseg=function|`A,l->Format.asprintf"%a: in array"Tloc.ppl|`Om,l->Format.asprintf"%a: in key %a"Tloc.pplpp_memminString.concat"\n"(List.mapsegp)(* Errors *)exceptionErrofpath*Tloc.t*stringleterrplmsg=raise_notrace(Err(p,l,msg))leterrfplfmt=Format.kasprintf(errpl)fmtleterr_expexppfnd=errfp(Json.locfnd)"found %s but expected %s"(Json.kindfnd)expleterr_exp_null=err_exp"null"leterr_exp_bool=err_exp"bool"leterr_exp_float=err_exp"number"leterr_exp_string=err_exp"string"leterr_exp_array=err_exp"array"leterr_exp_obj=err_exp"object"leterr_empty_arraypl=errfpl"unexpected empty array"leterr_miss_mempln=errfpl"member %a unbound in object"pp_memnleterr_to_string?pp_memplocmsg=letpp_linesppfs=Format.fprintfppf"@[<v>%a@]"(Format.pp_print_listFormat.pp_print_string)(String.split_on_char'\n's)inmatchpwith|[]->Format.asprintf"%a:@\n%a"Tloc.pplocpp_linesmsg|p->Format.asprintf"%a:@\n%a@\n @[%a@]"Tloc.pplocpp_linesmsgpp_lines(path_to_tracep)(* Queries *)type'at=path->Json.t->'aletqueryqs=tryOk(q[]s)with|Err(p,l,m)->Error(err_to_stringplm)(* Succeeding and failing queries *)letsucceedvpj=vletfailmsgpj=errp(Json.locj)msgletfailffmt=Format.kasprintffailfmt(* Query combinators *)letappfqqpj=fqpj(qpj)let($)=appletpairq0q1pj=letv0=q0pjinv0,q1pjletbindqfpj=f(qpj)pjletmapfqpj=f(qpj)letsomeqpj=Some(qpj)(* JSON queries *)letfold~null~bool~float~string~array~objp=function|`Null_asj->nullpj|`Bool_asj->boolpj|`Float_asj->floatpj|`String_asj->stringpj|`A_asj->arraypj|`O_asj->objpjletpartial_fold?null?bool?float?string?array?obj()pj=letwith_qqpj=matchqwith|None->letkindk=functionNone->""|Some_->kinletkinds=[kind"null"null;kind"bool"bool;kind"number"float;kind"string"string;kind"array"array;kind"obj"obj]inletkinds=List.filter(funs->s<>"")kindsinletkinds=String.concat", "kindsin(* FIXME use error messages from Err_msg *)letkinds=ifkinds=""then"nothing"else"one of "^kindsinerr_expkindspj|Someq->qpjinmatchjwith|`Null_asj->with_qnullpj|`Bool_asj->with_qboolpj|`Float_asj->with_qfloatpj|`String_asj->with_qstringpj|`A_asj->with_qarraypj|`O_asj->with_qobjpjletjsonps=sletlocps=Json.locsletwith_locqps=(qps),Json.locs(* Nulls *)letis_nullp=function`Null_->true|j->falseletnullp=function`Null_->()|j->err_exp_nullpjletnullableqp=function`Null_->None|j->Some(qpj)(* Atomic values *)letboolp=function`Bool(b,_)->b|j->err_exp_boolpjletfloatp=function`Float(f,_)->f|j->err_exp_floatpjletint=maptruncatefloatletstringp=function`String(s,_)->s|j->err_exp_stringpjletstring_to~kindparsep=function|`String(s,_)asj->(matchparseswithOkv->v|Errorm->failmpj)|j->err_expkindpjletenum~kindssp=function|`String(s,_)whenSset.memsss->s|`String(s,l)->letss=Sset.elementsssinlethint,ss=matchTdec.err_suggestssswith|[]->Tdec.pp_must_be,ss|ss->Tdec.pp_did_you_mean,ssinletkindppf()=Format.pp_print_stringppfkindinletpp_v=Format.pp_print_stringinerrfpl"%a"(Tdec.pp_unknown'~kindpp_v~hint)(s,ss)|j->err_expkindpjletenum_map~kindsmp=function|`String(s,l)->beginmatchSmap.findssmwith|v->v|exceptionNot_found->letss=Smap.fold(funk_acc->k::acc)sm[]inlethint,ss=matchTdec.err_suggestssswith|[]->Tdec.pp_must_be,ss|ss->Tdec.pp_did_you_mean,ssinletkindppf()=Format.pp_print_stringppfkindinletpp_v=Format.pp_print_stringinerrfpl"%a"(Tdec.pp_unknown'~kindpp_v~hint)(s,ss)end|j->err_expkindpj(* Array *)letis_empty_arrayp=function`A(a,_)->a=[]|j->err_exp_arraypjlethdqp=function|`A([],l)->err_empty_arraypl|`A(v::_,l)->q((`A,l)::p)v|j->err_exp_arraypjlettlqp=function|`A([],l)->err_empty_arraypl|`A(_::[],l)->qp(`A([],Tloc.to_endl))|`A(_::(v::_asa),l)->letl=Tloc.restart~at:(Tloc.to_start(Json.locv))linqp(`A(a,l))|j->err_exp_arraypjletnth?absentnqp=function|`A(vs,l)->letp=(`A,l)::pinletk,vs=ifn<0then-n-1,List.revvselsen,vsinletrecloopk=function|v::vswhenk=0->qpv|_::vs->loop(k-1)vs|[]->matchabsentwith|None->errfpl"%d: no such index in array"n|Someabsent->absentinloopkvs|j->err_exp_arraypjletfold_arrayfqaccp=function|`A(vs,l)->letp=(`A,l)::pinletaddpaccv=f(qpv)accinList.fold_left(addp)accvs|j->err_exp_arraypjletarrayqv=mapList.rev(fold_array(funvacc->v::acc)qv[])(* Objects *)letrecmem_findn=function|((n',_),j)::mswhenString.equaln'n->Somej|_::ms->mem_findnms|[]->Noneletmem:string->'at->'at=funnqp->function|`O(ms,l)->beginmatchmem_findnmswith|None->err_miss_mempln|Somej->q((`On,l)::p)jend|j->err_exp_objpjletopt_memnq~absentp=function|`O(ms,l)->beginmatchmem_findnmswith|None->absent|Somej->q((`On,l)::p)jend|j->err_exp_objpjletmem_dom~validatep=function|`O(ms,l)->letadd_mem=matchvalidatewith|None->funacc((n,_),_)->Sset.addnacc|Somedom->funacc((n,_),_)->matchSset.memndomwith|true->Sset.addnacc|false->letns=Sset.elementsdominlethint,ss=matchTdec.err_suggestnsnwith|[]->Tdec.pp_must_be,ns|ss->Tdec.pp_did_you_mean,ssinletkindppf()=Format.pp_print_stringppf"member"inletpp_v=Format.pp_print_stringinerrfpl"%a"(Tdec.pp_unknown'~kindpp_v~hint)(n,ss)inList.fold_leftadd_memSset.emptyms|j->err_exp_objpjend(*---------------------------------------------------------------------------
Copyright (c) 2016 The b0 programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)