123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297(* This file is free software, part of containers. See file "license" for more details. *)(** {1 Simple S-expression parsing/printing} *)type'aor_error=('a,string)resulttype'agen=unit->'aoptionmoduletypeSEXP=CCSexp_intf.BASIC_SEXPmoduletypeS=CCSexp_intf.S0letequal_string(a:string)b=Stdlib.(=)abletcompare_string(a:string)b=Stdlib.compareablet_with_infilenamef=letic=open_infilenameintryletx=ficinclose_inic;xwithe->close_inic;Error(Printexc.to_stringe)let_with_outfilenamef=letoc=open_outfilenameintryletx=focinclose_outoc;xwithe->close_outoc;raiseemoduleMake(Sexp:SEXP)=structtypet=Sexp.ttypesexp=tletatom=Sexp.atomletlist=Sexp.listletof_intx=Sexp.atom(string_of_intx)letof_floatx=Sexp.atom(string_of_floatx)letof_boolx=Sexp.atom(string_of_boolx)letof_unit=Sexp.list[]letof_listl=Sexp.listlletof_rev_listl=Sexp.list(List.revl)letof_pair(x,y)=Sexp.list[x;y]letof_triple(x,y,z)=Sexp.list[x;y;z]letof_quad(x,y,z,u)=Sexp.list[x;y;z;u]letof_variantnameargs=Sexp.list(Sexp.atomname::args)letof_fieldnamet=Sexp.list[Sexp.atomname;t]letof_recordl=Sexp.list(List.map(fun(n,x)->of_fieldnx)l)(** {3 Printing} *)letrecto_bufbt=Sexp.match_t~atom:(funs->Printf.bprintfb"%d:%s"(String.lengths)s)~list:(function|[]->Buffer.add_stringb"()"|[x]->Printf.bprintfb"(%a)"to_bufx|l->Buffer.add_charb'(';List.iter(to_bufb)l;Buffer.add_charb')')letto_stringt=letb=Buffer.create128into_bufbt;Buffer.contentsbletrecpp_noindentfmtt=Sexp.match_t~atom:(funs->Format.fprintffmt"%d:%s"(String.lengths)s)~list:(function|[]->Format.pp_print_stringfmt"()"|[x]->Format.fprintffmt"(%a)"pp_noindentx|l->Format.fprintffmt"(";List.iter(pp_noindentfmt)l;Format.fprintffmt")")letpp=pp_noindentletrecto_chanoct=Sexp.match_t~atom:(funs->Printf.fprintfoc"%d:%s"(String.lengths)s)~list:(function|[]->output_stringoc"()"|[x]->Printf.fprintfoc"(%a)"to_chanx|l->output_charoc'(';List.iter(to_chanoc)l;output_charoc')')letto_file_iterfilenameiter=_with_outfilename(funoc->iter(funt->to_chanoct))letto_filefilenamet=to_file_iterfilename(funk->kt)(** {3 Parsing} *)moduletypeINPUT=sigexceptionEOFvalread_char:unit->charvalread_string:int->stringendmoduleDecoder(I:INPUT)=structlet[@inline]is_num_c=Char.codec>=Char.code'0'&&Char.codec<=Char.code'9'let[@inline]as_num_c=Char.codec-Char.code'0'letnext_():sexpor_error*bool=letrecread_string_lenn=matchI.read_char()with|cwhenis_num_c->read_string_len((n*10)+as_num_c)|':'->lets=I.read_stringninatoms|_->failwith"expected string length"andeat_colon()=matchI.read_char()with|':'->()|_->failwith"expected ':'"andread_in_parenacc=matchI.read_char()with|')'->list(List.revacc)|cwhenis_num_c->letsexp=read_string_len(as_num_c)inread_in_paren(sexp::acc)|'('->letsexp=read_in_paren[]inread_in_paren(sexp::acc)|_->failwith"expected list of sexprs"in(* read a S-expr *)trymatchI.read_char()with|exceptionI.EOF->Error"unexpected EOF",true|'('->Ok(read_in_paren[]),false|'0'->eat_colon();Ok(atom""),false|cwhenis_num_c->Ok(read_string_len(as_num_c)),false|_->Error"unexpected char, expected toplevel sexpr",falsewithFailuree->Errore,falseletto_list():_or_error=letreciteracc=matchnext_()with|Error_,true->Ok(List.revacc)|Okx,_->iter(x::acc)|(Error_asres),_->resintryiter[]withe->Error(Printexc.to_stringe)let[@inline]next_or_error():_or_error=fst(next_())end[@@inline]moduleDecoder_str(X:sigvals:stringend)=Decoder(structexceptionEOFleti=ref0letn=String.lengthX.sletread_char()=if!i>=nthenraise_notraceEOF;letc=String.unsafe_getX.s!iinincri;cletread_stringlen=if!i+len>nthenraise_notraceEOF;letres=String.subX.s!ilenini:=!i+len;resend)[@@inline]letparse_strings:tor_error=letmoduleD=Decoder_str(structlets=send)inD.next_or_error()letparse_string_lists:tlistor_error=letmoduleD=Decoder_str(structlets=send)inD.to_list()moduleDecoder_ic(X:sigvalic:in_channelend)=Decoder(structexceptionEOF=End_of_filelet[@inline]read_char()=input_charX.icletread_stringn=matchnwith|0->""|1->String.make1(read_char())|_->letbuf=Bytes.maken'\000'inleti=ref0inwhile!i<ndoletlen=inputX.icbuf!i(n-!i)ini:=!i+lendone;Bytes.unsafe_to_stringbufend)[@@inline]letparse_chan_?fileic:sexpor_error=letmoduleD=Decoder_ic(structletic=icend)inmatchD.next_or_error(),filewith|Errors,Somefile->Error(Printf.sprintf"%s in '%s'"sfile)|r,_->rletparse_chan_list_?fileic=letmoduleD=Decoder_ic(structletic=icend)inmatchD.to_list(),filewith|Errors,Somefile->Error(Printf.sprintf"%s in '%s'"sfile)|r,_->rletparse_chanic=parse_chan_icletparse_chan_listic=parse_chan_list_icletparse_chan_genic=letmoduleD=Decoder_ic(structletic=icend)infun()->matchD.next_()with|_,true->None|Errore,_->Some(Errore)|Okx,_->Some(Okx)letparse_filefilename=_with_infilename(parse_chan_~file:filename)letparse_file_listfilename=_with_infilename(parse_chan_list_~file:filename)endtypet=[`Atomofstring|`Listoftlist]letrecequalab=matcha,bwith|`Atoms1,`Atoms2->equal_strings1s2|`Listl1,`Listl2->(tryList.for_all2equall1l2withInvalid_argument_->false)|`Atom_,_|`List_,_->falseletreccompare_listab=matcha,bwith|[],[]->0|[],_::_->-1|_::_,[]->1|x::xs,y::ys->(matchcomparexywith|0->compare_listxsys|c->c)andcompareab=matcha,bwith|`Atoms1,`Atoms2->compare_strings1s2|`Listl1,`Listl2->compare_listl1l2|`Atom_,_->-1|`List_,_->1moduleBasic_=structtypenonrect=tletatomx=`Atomxletlistx=`Listxletmatch_x~atom~list=matchxwith|`Atomx->atomx|`Listl->listlendinclude(Make(Basic_):Swithtypet:=t)