123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300openSexplibopenSexplib.ConvopenPgx_auxtypet=stringoption[@@derivingsexp_of]exceptionConversion_failureofstring[@@derivingsexp]letconvert_failuretype_s=Conversion_failure(Printf.sprintf"Unable to convert to %s: %s"type_s)|>raiseletrequiredf=function|Somex->fx|None->raise(Conversion_failure"Expected not-null but got null")letopt=Option.bindletnull=Noneletof_bool=function|true->Some"t"|false->Some"f"letto_bool'=function|"t"->true|"f"->false|s->convert_failure"bool"sletto_bool_exn=requiredto_bool'letto_bool=Option.mapto_bool'letof_float'f=matchclassify_floatfwith|FP_infinitewhenf>0.->"Infinity"|FP_infinitewhenf<0.->"-Infinity"|FP_nan->"NaN"|_->string_of_floatfletof_floatf=Some(of_float'f)letto_float't=matchString.lowercase_asciitwith|"infinity"->infinity|"-infinity"->neg_infinity|"nan"->nan|_->tryfloat_of_stringtwithFailure_->convert_failure"float"tletto_float_exn=requiredto_float'letto_float=Option.mapto_float'typehstore=(string*stringoption)list[@@derivingsexp]letof_hstorehstore=letstring_of_quotedstr="\""^str^"\""inletstring_of_mapping(key,value)=letkey_str=string_of_quotedkeyandvalue_str=matchvaluewith|Somev->string_of_quotedv|None->"NULL"inkey_str^"=>"^value_strinSome(String.join", "(List.mapstring_of_mappinghstore))letto_hstore'str=letexpecttargetstream=ifList.exists(func->c<>Stream.nextstream)targetthenconvert_failure"hstore"strinletparse_quotedstream=letrecloopaccumstream=matchStream.nextstreamwith|'"'->String.implode(List.revaccum)(* FIXME: Slashes don't seem to round-trip properly *)|'\\'->loop(Stream.nextstream::accum)stream|x->loop(x::accum)streaminexpect['"']stream;loop[]streaminletparse_valuestream=matchStream.peekstreamwith|Some'N'->(expect['N';'U';'L';'L']stream;None)|_->Some(parse_quotedstream)inletparse_mappingstream=letkey=parse_quotedstreaminexpect['=';'>']stream;letvalue=parse_valuestreamin(key,value)inletparse_mainstream=letrecloopaccumstream=letmapping=parse_mappingstreaminmatchStream.peekstreamwith|Some_->(expect[',';' ']stream;loop(mapping::accum)stream)|None->mapping::accuminmatchStream.peekstreamwith|Some_->loop[]stream|None->[]inparse_main(Stream.of_stringstr)letto_hstore_exn=requiredto_hstore'letto_hstore=Option.mapto_hstore'typeinet=Unix.inet_addr*intletsexp_of_inet(addr,mask)=[%sexp_of:string*int](Unix.string_of_inet_addraddr,mask)letof_inet(addr,mask)=lethostmask=ifUnix.domain_of_sockaddr(Unix.ADDR_INET(addr,1))=Unix.PF_INET6then128else32inletaddr=Unix.string_of_inet_addraddrinifmask=hostmaskthenSomeaddrelseifmask>=0&&mask<hostmaskthenSome(addr^"/"^string_of_intmask)elseinvalid_arg"mask"letto_inet'=letre=letopenRein[group([rep(compl[set":./"]);group(set":.");rep1(compl[char'/'])]|>seq);opt(seq[char'/';group(rep1any)])]|>seq|>compileinfunstr->tryletsubs=Re.execrestrinletaddr=Unix.inet_addr_of_string(Re.getsubs1)in(* optional match *)letmask=try(Re.getsubs3)withNot_found->""inifmask=""then(addr,(if(Re.getsubs2)="."then32else128))else(addr,int_of_stringmask)with_->convert_failure"inet"strletto_inet_exn=requiredto_inet'letto_inet=Option.mapto_inet'letof_inti=Some(string_of_inti)letto_int't=tryint_of_stringtwithFailure_->convert_failure"int"tletto_int_exn=requiredto_int'letto_int=Option.mapto_int'letof_int32i=Some(Int32.to_stringi)letto_int32't=tryInt32.of_stringtwithFailure_->convert_failure"int32"tletto_int32_exn=requiredto_int32'letto_int32=Option.mapto_int32'letof_int64i=Some(Int64.to_stringi)letto_int64't=tryInt64.of_stringtwithFailure_->convert_failure"int64"tletto_int64_exn=requiredto_int64'letto_int64=Option.mapto_int64'letescape_stringstr=letbuf=Buffer.create128infori=0toString.lengthstr-1domatchstr.[i]with|'"'|'\\'asx->Buffer.add_charbuf'\\';Buffer.add_charbufx|x->Buffer.add_charbufxdone;Buffer.contentsbufletof_list(xs:tlist)=letbuf=Buffer.create128inBuffer.add_charbuf'{';letadderix=ifi>0thenBuffer.add_charbuf',';matchxwith|Somex->letx=escape_stringxinBuffer.add_charbuf'"';Buffer.add_stringbufx;Buffer.add_charbuf'"'|None->Buffer.add_stringbuf"NULL"inList.iteriadderxs;Buffer.add_charbuf'}';Some(Buffer.contentsbuf)letto_list'str=letn=String.lengthstrinifn=0||str.[0]<>'{'||str.[n-1]<>'}'thenconvert_failure"list"str;letstr=String.substr1(n-2)inletbuf=Buffer.create128inletadd_fieldaccum=letx=Buffer.contentsbufinBuffer.clearbuf;letfield=ifx="NULL"thenNoneelseletn=String.lengthxinifn>=2&&x.[0]='"'thenSome(String.subx1(n-2))elseSomexinfield::accuminletloop(accum,quoted,escaped)=function|'\\'whennotescaped->(accum,quoted,true)|'"'whennotescaped->Buffer.add_charbuf'"';(accum,notquoted,false)|','whennotescaped&¬quoted->(add_fieldaccum,false,false)|x->Buffer.add_charbufx;(accum,quoted,false)inlet(accum,_,_)=String.fold_leftloop([],false,false)strinletaccum=ifBuffer.lengthbuf=0thenaccumelseadd_fieldaccuminList.revaccumletto_list_exn=requiredto_list'letto_list=Option.mapto_list'typepoint=float*float[@@derivingsexp]letof_point(x,y)=letx=of_float'xinlety=of_float'yinSome(Printf.sprintf"(%s,%s)"xy)letto_point'=letpoint_re=letopenReinletpart=seq[repspace;group(repany);repspace]in[repspace;char'(';part;char',';part;char')';repspace]|>seq|>whole_string|>compileinfunstr->tryletsubs=Re.execpoint_restrin(float_of_string(Re.getsubs1),float_of_string(Re.getsubs2))with|e->Printexc.to_stringe|>print_endline;convert_failure"point"strletto_point_exn=requiredto_point'letto_point=Option.mapto_point'letof_stringt=Sometletto_string_exn=required(funt->t)letto_stringt=tletunit=Some""letto_unit'=function|""->()|t->convert_failure"unit"tletto_unit_exn=requiredto_unit'letto_unit=Option.mapto_unit'typeuuid=Uuidm.tletsexp_of_uuidu=Uuidm.to_stringu|>sexp_of_stringletof_uuids=Some(Uuidm.to_strings)letto_uuid't=matchUuidm.of_stringtwith|Someu->u|None->convert_failure"uuid"tletto_uuid_exn=requiredto_uuid'letto_uuid=Option.mapto_uuid'