Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file CCSexp.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398(* This file is free software, part of containers. See file "license" for more details. *)(** {1 Simple S-expression parsing/printing} *)openCCShims_type'aor_error=('a,string)resulttype'asequence=('a->unit)->unittype'agen=unit->'aoptionmoduletypeSEXP=CCSexp_intf.SEXPmoduletypeS=CCSexp_intf.Sletequal_string(a:string)b=Stdlib.(=)abletcompare_string(a:string)b=Stdlib.compareabmoduleMake(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)letatomx=Sexp.atomxletof_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)(** {2 Printing} *)let_with_outfilenamef=letoc=open_outfilenameintryletx=focinclose_outoc;xwithe->close_outoc;raisee(* shall we escape the string because of one of its chars? *)let_must_escapes=tryfori=0toString.lengths-1doletc=String.unsafe_getsiinmatchcwith|' '|')'|'('|'"'|';'|'\\'|'\n'|'\t'|'\r'->raiseExit|_whenChar.codec>127->raiseExit(* non-ascii *)|_->()done;falsewithExit->trueletrecto_bufbt=Sexp.match_t~atom:(funs->if_must_escapesthenPrintf.bprintfb"\"%s\""(String.escapeds)elseBuffer.add_stringbs)~list:(function|[]->Buffer.add_stringb"()"|[x]->Printf.bprintfb"(%a)"to_bufx|l->Buffer.add_charb'(';List.iteri(funit'->(ifi>0thenBuffer.add_charb' ';to_bufbt'))l;Buffer.add_charb')')letto_stringt=letb=Buffer.create128into_bufbt;Buffer.contentsbletrecppfmtt=Sexp.match_t~atom:(funs->if_must_escapesthenFormat.fprintffmt"\"%s\""(String.escapeds)elseFormat.pp_print_stringfmts)~list:(function|[]->Format.pp_print_stringfmt"()"|[x]->Format.fprintffmt"@[<hov2>(%a)@]"ppx|l->Format.fprintffmt"@[<hov1>(";List.iteri(funit'->(ifi>0thenFormat.fprintffmt"@ ";ppfmtt'))l;Format.fprintffmt")@]")letrecpp_noindentfmtt=Sexp.match_t~atom:(funs->if_must_escapesthenFormat.fprintffmt"\"%s\""(String.escapeds)elseFormat.pp_print_stringfmts)~list:(function|[]->Format.pp_print_stringfmt"()"|[x]->Format.fprintffmt"(%a)"pp_noindentx|l->Format.pp_print_charfmt'(';List.iteri(funit'->(ifi>0thenFormat.pp_print_charfmt' ';pp_noindentfmtt'))l;Format.pp_print_charfmt')')letto_chanoct=letfmt=Format.formatter_of_out_channelocinppfmtt;Format.pp_print_flushfmt()letto_file_seqfilenameseq=_with_outfilename(funoc->seq(funt->to_chanoct;output_charoc'\n'))letto_filefilenamet=to_file_seqfilename(funk->kt)(** {2 Parsing} *)let_with_infilenamef=letic=open_infilenameintryletx=ficinclose_inic;xwithe->close_inic;Error(Printexc.to_stringe)(** A parser of ['a] can return [Yield x] when it parsed a value,
or [Fail e] when a parse error was encountered, or
[End] if the input was empty *)type'aparse_result=|Yieldof'a|Failofstring|EndmoduleDecoder=structmoduleL=CCSexp_lextypet={buf:Lexing.lexbuf;mutablecur_tok:L.tokenoption;(* current token *)}letcur(t:t):L.token=matcht.cur_tokwith|SomeL.EOI->assertfalse|Somet->t|None->(* fetch token *)lettok=L.tokent.bufint.cur_tok<-Sometok;tokletjunkt=t.cur_tok<-Noneletof_lexbufbuf={buf;cur_tok=None;}exceptionE_endexceptionE_errorofint*int*stringletpair_of_pos_p=letopenLexinginp.pos_lnum,p.pos_cnum-p.pos_bolleterror_lexbufmsg=letstart=Lexing.lexeme_start_plexbufinletline,col=pair_of_pos_startinraise(E_error(line,col,msg))letnext(t:t)=letopenLexinginletrecexpr()=matchcurtwith|L.EOI->raiseE_end|L.SEXP_COMMENT->junkt;let_u=expr()in(* discard next sexp *)expr()|L.ATOMs->junkt;beginmatchSexp.make_locwith|None->Sexp.atoms|Somef->(* build a position for this token *)letloc=f(pair_of_pos_t.buf.lex_start_p)(pair_of_pos_t.buf.lex_curr_p)t.buf.lex_curr_p.pos_fnameinSexp.atom_with_loc~locsend|L.LIST_OPEN->letpos_start=t.buf.lex_curr_pinjunkt;letl=lst[]inbeginmatchcurtwith|L.LIST_CLOSE->junkt;beginmatchSexp.make_locwith|None->Sexp.listl|Somef->letloc=f(pair_of_pos_pos_start)(pair_of_pos_t.buf.lex_curr_p)t.buf.lex_curr_p.pos_fnameinSexp.list_with_loc~loclend|_->error_t.buf"expected ')'"end|L.LIST_CLOSE->error_t.buf"expected expression"andlstacc=matchcurtwith|L.LIST_CLOSE->List.revacc|L.LIST_OPEN|L.ATOM_|L.SEXP_COMMENT->letsub=expr()inlst(sub::acc)|L.EOI->error_t.buf"unexpected EOI"intryYield(expr())with|E_end->End|E_error(line,col,msg)|CCSexp_lex.Error(line,col,msg)->Fail(Printf.sprintf"parse error at %d:%d: %s"linecolmsg)letto_list(d:t):_or_error=letreciteracc=matchnextdwith|End->Ok(List.revacc)|Yieldx->iter(x::acc)|Faile->Erroreintryiter[]withe->Error(Printexc.to_stringe)endletdec_next_(d:Decoder.t):_or_error=matchDecoder.nextdwith|End->Error"unexpected end of file"|Yieldx->Okx|Fails->Errorsletparse_strings:tor_error=letbuf=Lexing.from_stringsinletd=Decoder.of_lexbufbufindec_next_dletparse_string_lists:tlistor_error=letbuf=Lexing.from_stringsinletd=Decoder.of_lexbufbufinDecoder.to_listdletset_file_?filebuf=letopenLexinginmatchfilewith|Somes->buf.lex_start_p<-{buf.lex_start_pwithpos_fname=s}|None->()letparse_chan_?fileic:sexpor_error=letbuf=Lexing.from_channelicinset_file_?filebuf;letd=Decoder.of_lexbufbufindec_next_dletparse_chan_list_?fileic=letbuf=Lexing.from_channelicinset_file_?filebuf;letd=Decoder.of_lexbufbufinDecoder.to_listdletparse_chanic=parse_chan_icletparse_chan_listic=parse_chan_list_icletparse_chan_genic=letbuf=Lexing.from_channelicinletd=Decoder.of_lexbufbufinfun()->matchDecoder.nextdwith|End->None|Faile->Some(Errore)|Yieldx->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->begintryList.for_all2equall1l2withInvalid_argument_->falseend|`Atom_,_|`List_,_->falseletreccompare_listab=matcha,bwith|[],[]->0|[],_::_->-1|_::_,[]->1|x::xs,y::ys->beginmatchcomparexywith|0->compare_listxsys|c->cendandcompareab=matcha,bwith|`Atoms1,`Atoms2->compare_strings1s2|`Listl1,`Listl2->compare_listl1l2|`Atom_,_->-1|`List_,_->1include(Make(structtypet_=ttypet=t_typeloc=unitletmake_loc=Noneletatomx=`Atomxletlistx=`Listxletatom_with_loc~loc:_s=atomsletlist_with_loc~loc:_l=listlletmatch_x~atom~list=matchxwith|`Atomx->atomx|`Listl->listlend):Swithtypet:=t)(*$T
CCResult.to_opt (parse_string "(abc d/e/f \"hello \\\" () world\" )") <> None
CCResult.to_opt (parse_string "(abc ( d e ffff ) \"hello/world\")") <> None
CCResult.to_opt (parse_string "\"\123\bcoucou\"") <> None
*)(*$= & ~printer:(function Ok x -> to_string x | Error e -> "error " ^ e)
(parse_string "(a b)") (Ok (`List [`Atom "a"; `Atom "b"]))
(parse_string "(a\n ;coucou\n b)") (Ok (`List [`Atom "a"; `Atom "b"]))
(parse_string "(a #; (foo bar\n (1 2 3)) b)") (Ok (`List [`Atom "a"; `Atom "b"]))
(parse_string "#; (a b) (c d)") (Ok (`List [`Atom "c"; `Atom "d"]))
(parse_string "#; (a b) 1") (Ok (`Atom "1"))
*)(*$= & ~printer:(function Ok x -> String.concat ";" @@ List.map to_string x | Error e -> "error " ^ e)
(parse_string_list "(a b)(c)") (Ok [`List [`Atom "a"; `Atom "b"]; `List [`Atom "c"]])
(parse_string_list " ") (Ok [])
(parse_string_list "(a\n ;coucou\n b)") (Ok [`List [`Atom "a"; `Atom "b"]])
(parse_string_list "#; (a b) (c d) e ") (Ok [`List [`Atom "c"; `Atom "d"]; `Atom "e"])
(parse_string_list "#; (a b) 1") (Ok [`Atom "1"])
*)(*$inject
let sexp_gen =
let mkatom a = `Atom a and mklist l = `List l in
let atom = Q.Gen.(map mkatom (string_size ~gen:printable (1 -- 30))) in
let gen = Q.Gen.(
sized (fix
(fun self n st -> match n with
| 0 -> atom st
| _ ->
frequency
[ 1, atom
; 2, map mklist (list_size (0 -- 10) (self (n/10)))
] st
)
)) in
let rec small = function
| `Atom s -> String.length s
| `List l -> List.fold_left (fun n x->n+small x) 0 l
and print = function
| `Atom s -> Printf.sprintf "`Atom \"%s\"" s
| `List l -> "`List " ^ Q.Print.list print l
and shrink = function
| `Atom s -> Q.Iter.map mkatom (Q.Shrink.string s)
| `List l -> Q.Iter.map mklist (Q.Shrink.list ~shrink l)
in
Q.make ~print ~small ~shrink gen
let rec sexp_valid = function
| `Atom "" -> false
| `Atom _ -> true
| `List l -> List.for_all sexp_valid l
*)(*$Q & ~count:100
sexp_gen (fun s -> sexp_valid s ==> (to_string s |> parse_string = Ok s))
*)letatoms:t=`Atoms