123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401(*
* Copyright (c) 2013 Thomas Gazagnaire <thomas@gazagnaire.org>
*
* Permission to use, copy, modify, and 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.
*)(* From http://erratique.ch/software/jsonm/doc/Jsonm.html#datamodel *)typevalue=[`Null|`Boolofbool|`Floatoffloat|`Stringofstring|`Aofvaluelist|`Oof(string*value)list]typet=[`Aofvaluelist|`Oof(string*value)list]letvalue:t->value=funt->(t:>value)moduleList=structincludeList(* Tail-recursive List.map *)letmapfl=rev(rev_mapfl)endtypeerror_location=(int*int)*(int*int)typeread_value_error=[|`Erroroferror_location*Jsonm.error|`Unexpectedof[`Lexemeoferror_location*Jsonm.lexeme*string|`End_of_input]]typeread_error=[read_value_error|`Not_a_tofvalue]letjson_of_srcsrc:(value,[>read_value_error])result=letd=Jsonm.decodersrcinletexceptionAbortofread_value_errorinletmoduleStack=structtypet=|In_arrayofvaluelist*t|In_objectofstring*(string*value)list*t|Emptyendinletloc()=Jsonm.decoded_rangedinletdec()=matchJsonm.decodedwith|`Lexemel->l|`Errore->raise(Abort(`Error(loc(),e)))|`End->raise(Abort (`Unexpected`End_of_input))|`Await->assertfalseinletrecvaluelstack=matchlwith|`Os->obj[]stack|`As->arr[]stack|`Null|`Bool_|`String_|`Float_asl->continuelstack|_->raise(Abort(`Unexpected(`Lexeme(loc(),l,"value"))))andarrso_farstack=matchdec()with|`Ae->continue(`A(List.revso_far))stack|l->letstack=Stack.In_array(so_far,stack)invaluelstackandobjso_farstack=matchdec()with|`Oe->continue(`O(List.revso_far))stack|`Namen->letstack=Stack.In_object(n,so_far,stack)invalue(dec())stack|l->raise(Abort(`Unexpected(`Lexeme(loc(),l,"object fields"))))andcontinuevstack=matchstackwith|Stack.In_array(vs,stack)->letso_far=(v::vs)inarrso_farstack|Stack.In_object(n,ms,stack)->letso_far=((n,v)::ms)inobjso_farstack|Stack.Empty->vintryOk(value(dec())Empty)withAbort(#read_value_erroraserr)->Errorerrletvalue_to_dst?(minify=true)dstjson=letmoduleStack=structtypet=|In_arrayofvaluelist*t|In_objectof(string*value)list*t|Emptyendinletencel=ignore(Jsonm.encodee(`Lexemel))inletrectvestack=matchvwith|`Avs->ence`As;arrvsestack|`Oms->ence`Os;objmsestackandvaluevestack=matchvwith|`Null|`Bool_|`Float_|`String_asv->encev;continueestack|#tasx->t(x:>t)estackandarrvsestack=matchvswith|v::vs'->letstack=Stack.In_array(vs',stack)invaluevestack|[]->ence`Ae;continueestackandobjmsestack=matchmswith|(n,v)::ms->ence(`Namen);letstack=Stack.In_object(ms,stack)invaluevestack|[]->ence`Oe;continueestackandcontinueestack=matchstackwith|Stack.In_array(vs,stack)->arrvsestack|Stack.In_object(ms,stack)->objmsestack|Stack.Empty->()inlete=Jsonm.encoder~minifydstinvaluejsoneStack.Empty;ignore(Jsonm.encodee`End)letvalue_to_buffer?minifybufjson=value_to_dst?minify(`Bufferbuf)jsonletto_buffer?minifybufjson=value_to_buffer?minifybuf(json:>value)letvalue_to_string?minifyjson=letbuf=Buffer.create1024invalue_to_buffer?minifybufjson;Buffer.contentsbufletto_string?minifyjson=value_to_string?minify(json:>value)letvalue_to_channel?minifyocjson=value_to_dst?minify(`Channeloc)jsonletto_channel?minifyocjson=value_to_channel?minifyoc(json:>value)exceptionParse_errorofvalue*stringletparse_errortfmt=Printf.kprintf(funmsg->raise(Parse_error(t,msg)))fmtletwrapt=`A[t]letunwrap=function|`A[t]->t|v->parse_error(v:>value)"Not unwrappable"letread_error_description:[<read_error]->string=function|`Error(_loc,err)->Format.asprintf"%a"Jsonm.pp_errorerr|`Unexpected`End_of_input->Format.sprintf"Unexpected end of input"|`Unexpected(`Lexeme(_loc,_l,expectation))->Format.sprintf"Unexpected input when parsing a %s"expectation|`Not_a_t_value->"We expected a well-formed JSON document (array or object)"letread_error_location:[<read_error]->error_locationoption=function|`Error(loc,_)->Someloc|`Unexpected`End_of_input->None|`Unexpected(`Lexeme(loc,_l,_expectation))->Someloc|`Not_a_t_value->Noneletvalue_from_src_resultsrc=json_of_srcsrcletvalue_from_srcsrc=matchvalue_from_src_resultsrcwith|Okt->t|Errore->parse_error`Null"JSON.of_buffer %s"(read_error_descriptione)letvalue_from_string_resultstr=value_from_src_result(`Stringstr)letvalue_from_stringstr=value_from_src(`Stringstr)letvalue_from_channel_resultchan=value_from_src_result(`Channelchan)letvalue_from_channelchan=value_from_src(`Channelchan)letensure_document_result:[>value]->([>t],[>read_error])result=function|#tast->Okt|value->Error(`Not_a_tvalue)letensure_document:[>value]->[>t]=function|#tast->t|t->raise(Parse_error(t,"not a valid JSON array/object"))letfrom_stringstr=value_from_stringstr|>ensure_documentletfrom_channelchan=value_from_channelchan|>ensure_documentletfrom_string_resultstr=Result.bind(value_from_string_resultstr)ensure_document_resultletfrom_channel_resultchan=Result.bind(value_from_channel_resultchan)ensure_document_result(* unit *)letunit()=`Nullletget_unit=function|`Null->()|j->parse_errorj"Ezjsonm.get_unit"(* bool *)letboolb=`Boolbletget_bool=function|`Boolb->b|j->parse_errorj"Ezjsonm.get_bool"(* string *)letstrings=`Stringsletget_string=function|`Strings->s|j->parse_errorj"Ezjsonm.get_string"(* int *)letinti=`Float(float_of_inti)letint32i=`Float(Int32.to_floati)letint64i=`Float(Int64.to_floati)letget_int=function|`Floatf->int_of_floatf|j->parse_errorj"Ezjsonm.get_int"letget_int32=function|`Floatf->Int32.of_floatf|j->parse_errorj"Ezjsonm.get_int32"letget_int64=function|`Floatf->Int64.of_floatf|j->parse_errorj"Ezjsonm.get_int64"(* float *)letfloatf=`Floatfletget_float=function|`Floatf->f|j->parse_errorj"Ezjsonm.get_float"(* list *)letlistfnl=`A(List.mapfnl)letget_listfn=function|`Aks->List.mapfnks|j->parse_errorj"Ezjsonm.get_list"(* string lists *)letstringsstrings=liststringstringsletget_strings=get_listget_string(* options *)letoptionfn=function|None->`Null|Somex->`A[fnx]letget_optionfn=function|`Null->None|`A[j]->Some(fnj)|j->parse_errorj"Ezjsonm.get_option"(* dict *)letdictd=`Odletget_dict=function|`Od->d|j->parse_errorj"Ezjsonm.get_dict"(* pairs *)letpairfkfv(k,v)=`A[fkk;fvv]letget_pairfkfv=function|`A[k;v]->(fkk,fvv)|j->parse_errorj"Ezjsonm.get_pair"(* triple *)lettriplefafbfc(a,b,c)=`A[faa;fbb;fcc]letget_triplefafbfc=function|`A[a;b;c]->(faa,fbb,fcc)|j->parse_errorj"Ezjsonm.get_triple"letmemtpath=letrecauxjp=matchp,jwith|[],_->true|h::tl,`Oo->List.mem_assocho&&aux(List.assocho)tl|_->falseinauxtpathletfindtpath=letrecauxjp=matchp,jwith|[],j->j|h::tl,`Oo->aux(List.assocho)tl|_->raiseNot_foundinauxtpathletfind_opttpath=trySome(findtpath)withNot_found->Noneletmap_dictfdictlabel=letrecauxacc=function|[]->beginmatchf`Nullwith|None->List.revacc|Somej->List.rev_appendacc[label,j]end|(l,j)ase::dict->ifl=labelthenmatchfjwith|None->List.rev_appendaccdict|Somej->List.rev_appendacc((l,j)::dict)elseaux(e::acc)dictinaux[]dictletmapftpath=letrecauxt=function|[]->ft|h::tl->matchtwith|`Od->Some(`O(map_dict(funt->auxttl)dh))|_->Noneinmatchauxtpathwith|None->raiseNot_found|Somej->jletupdatetpathv=map(fun_->v)tpathexceptionNot_utf8letis_valid_utf8str=tryUutf.String.fold_utf_8(fun__->function|`Malformed_->raiseNot_utf8|_->())()str;truewithNot_utf8->falseletencode_stringstr=ifis_valid_utf8strthenstringstrelselet`Hexh=Hex.of_stringstrin`O["hex",stringh]letdecode_string=function|`Stringstr->Somestr|`O["hex",`Stringstr]->Some(Hex.to_string(`Hexstr))|_->Noneletdecode_string_exnj=matchdecode_stringjwith|Somes->s|None->parse_errorj"Ezjsonm.decode_string_exn"letrecof_sexp=function|Sexplib0.Sexp.Atomx->encode_stringx|Sexplib0.Sexp.Listl->listof_sexplletvalue_of_sexp=of_sexplett_of_sexps=matchvalue_of_sexpswith|`Ax->`Ax|`Ox->`Ox|_->failwith"Ezjsonm: t_of_sexp encountered a value (fragment) rather than a t"letrecto_sexpjson=matchdecode_stringjsonwith|Somes->Sexplib0.Sexp.Atoms|None->matchjsonwith|`Al->Sexplib0.Sexp.List(List.mapto_sexpl)|_->parse_errorjson"Ezjsonm.to_sexp"letsexp_of_value=to_sexpletsexp_of_tt=sexp_of_value@@valuet