diff --git a/examples/id_sexp.ml b/examples/id_sexp.ml index 3280466d..75f87a6e 100644 --- a/examples/id_sexp.ml +++ b/examples/id_sexp.ml @@ -3,7 +3,7 @@ let () = if Array.length Sys.argv <> 2 then failwith "usage: id_sexp file"; let f = Sys.argv.(1) in - let s = Sexp.parse_l_file f in + let s = Sexp.L.of_file f in match s with | `Ok l -> List.iter diff --git a/misc/sexp.ml b/misc/sexp.ml index 3cfd4b46..e65d6f76 100644 --- a/misc/sexp.ml +++ b/misc/sexp.ml @@ -33,14 +33,35 @@ type t = | Atom of string | List of t list -let eq a b = a = b +let equal a b = a = b let compare a b = Pervasives.compare a b let hash a = Hashtbl.hash a +let _with_in filename f = + let ic = open_in filename in + try + let x = f ic in + close_in ic; + x + with e -> + close_in ic; + `Error (Printexc.to_string e) + +let _with_out filename f = + let oc = open_out filename in + try + let x = f oc in + close_out oc; + x + with e -> + close_out oc; + raise e + (** {2 Serialization (encoding)} *) +(* shall we escape the string because of one of its chars? *) let _must_escape s = try for i = 0 to String.length s - 1 do @@ -101,17 +122,13 @@ let to_chan oc t = print fmt t; Format.pp_print_flush fmt () -let seq_to_file filename seq = - let oc = open_out filename in - try - seq - (fun t -> to_chan oc t; output_char oc '\n'); - close_out oc - with e -> - close_out oc; - raise e +let to_file_seq filename seq = + _with_out filename + (fun oc -> + seq (fun t -> to_chan oc t; output_char oc '\n') + ) -let to_file filename t = seq_to_file filename (fun k -> k t) +let to_file filename t = to_file_seq filename (fun k -> k t) (** {2 Deserialization (decoding)} *) @@ -472,51 +489,66 @@ let parse_chan ?bufsize ic = (** {6 Blocking} *) -let parse1_chan ic = +let of_chan ic = ParseGen.head (parse_chan ic) -let parse1_string s = +let of_string s = ParseGen.head (parse_string s) -let parse_l_chan ?bufsize ic = - ParseGen.to_list (parse_chan ?bufsize ic) +let of_file f = + _with_in f of_chan -let parse_l_file ?bufsize filename = - let ic = open_in filename in - try - let l = parse_l_chan ?bufsize ic in - close_in ic; - l - with e -> - close_in ic; - `Error (Printexc.to_string e) +module L = struct + let to_buf b l = + List.iter (to_buf b) l -let parse_l_string s = - ParseGen.to_list (parse_string s) + let to_string l = + let b = Buffer.create 32 in + to_buf b l; + Buffer.contents b -let parse_l_gen g = - ParseGen.to_list (parse_gen g) + let to_chan oc l = + let fmt = Format.formatter_of_out_channel oc in + List.iter (Format.fprintf fmt "%a@." print) l; + Format.pp_print_flush fmt () -exception OhNoes of string -exception StopNaow + let to_file filename l = + _with_out filename (fun oc -> to_chan oc l) -let parse_l_seq seq = - let src = Source.Manual.make () in - let ps = mk_ps (Source.Manual.to_src src) in - let l = ref [] in - (* read as many expressions as possible *) - let rec _nexts () = match _next ps with - | `Ok x -> l := x :: !l; _nexts () - | `Error e -> raise (OhNoes e) - | `End -> raise StopNaow - | `Await -> () - in - try - seq - (fun s -> Source.Manual.feed src s 0 (String.length s); _nexts ()); - Source.Manual.reached_end src; - _nexts (); - `Ok (List.rev !l) - with - | OhNoes msg -> `Error msg - | StopNaow -> `Ok (List.rev !l) + let of_chan ?bufsize ic = + ParseGen.to_list (parse_chan ?bufsize ic) + + let of_file ?bufsize filename = + _with_in filename + (fun ic -> of_chan ?bufsize ic) + + let of_string s = + ParseGen.to_list (parse_string s) + + let of_gen g = + ParseGen.to_list (parse_gen g) + + exception OhNoes of string + exception StopNaow + + let of_seq seq = + let src = Source.Manual.make () in + let ps = mk_ps (Source.Manual.to_src src) in + let l = ref [] in + (* read as many expressions as possible *) + let rec _nexts () = match _next ps with + | `Ok x -> l := x :: !l; _nexts () + | `Error e -> raise (OhNoes e) + | `End -> raise StopNaow + | `Await -> () + in + try + seq + (fun s -> Source.Manual.feed src s 0 (String.length s); _nexts ()); + Source.Manual.reached_end src; + _nexts (); + `Ok (List.rev !l) + with + | OhNoes msg -> `Error msg + | StopNaow -> `Ok (List.rev !l) +end diff --git a/misc/sexp.mli b/misc/sexp.mli index 319dd495..50950b53 100644 --- a/misc/sexp.mli +++ b/misc/sexp.mli @@ -23,7 +23,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) -(** {1 Simple S-expression parsing/printing} *) +(** {1 Simple and efficient S-expression parsing/printing} + +@since NEXT_RELEASE *) type 'a or_error = [ `Ok of 'a | `Error of string ] type 'a sequence = ('a -> unit) -> unit @@ -35,15 +37,21 @@ type t = | Atom of string | List of t list -val eq : t -> t -> bool +val equal : t -> t -> bool val compare : t -> t -> int val hash : t -> int (** {2 Serialization (encoding)} *) val to_buf : Buffer.t -> t -> unit + val to_string : t -> string + val to_file : string -> t -> unit + +val to_file_seq : string -> t sequence -> unit +(** Print the given sequence of expressions to a file *) + val to_chan : out_channel -> t -> unit val print : Format.formatter -> t -> unit @@ -52,16 +60,12 @@ val print : Format.formatter -> t -> unit val print_noindent : Format.formatter -> t -> unit (** Raw, direct printing as compact as possible *) -val seq_to_file : string -> t sequence -> unit -(** Print the given sequence of expressions to a file *) - (** {2 Deserialization (decoding)} *) type 'a parse_result = ['a or_error | `End ] type 'a partial_result = [ 'a parse_result | `Await ] -(** {6 Streaming Parsing} *) - +(** {6 Source of characters} *) module Source : sig type individual_char = | NC_yield of char @@ -105,6 +109,8 @@ module Source : sig val of_gen : string gen -> t end +(** {6 Streaming Lexer} +splits the input into opening parenthesis, closing ones, and atoms *) module Lexer : sig type t (** A streaming lexer, that parses atomic chunks of S-expressions (atoms @@ -148,7 +154,8 @@ module ParseGen : sig val take : int -> 'a t -> 'a t end -(** {6 Stream Parser} *) +(** {6 Stream Parser} +Returns a lazy stream of S-expressions. *) val parse_string : string -> t ParseGen.t (** Parse a string *) @@ -159,20 +166,37 @@ val parse_chan : ?bufsize:int -> in_channel -> t ParseGen.t val parse_gen : string gen -> t ParseGen.t (** Parse chunks of string *) -(** {6 Blocking} *) +(** {6 Blocking API} +Parse one S-expression from some source. *) -val parse1_chan : in_channel -> t or_error +val of_chan : in_channel -> t or_error +(** Parse a S-expression from the given channel. Can read more data than + necessary, so don't use this if you need finer-grained control (e.g. + to read something else {b after} the S-exp) *) -val parse1_string : string -> t or_error +val of_string : string -> t or_error -val parse_l_chan : ?bufsize:int -> in_channel -> t list or_error -(** Parse values from a channel. *) +val of_file : string -> t or_error +(** Open the file and read a S-exp from it *) -val parse_l_file : ?bufsize:int -> string -> t list or_error -(** Parse a file *) +(** {6 Lists of S-exps} *) -val parse_l_string : string -> t list or_error +module L : sig + val to_buf : Buffer.t -> t list -> unit -val parse_l_gen : string gen -> t list or_error + val to_string : t list -> string -val parse_l_seq : string sequence -> t list or_error + val to_file : string -> t list -> unit + + val to_chan : out_channel -> t list -> unit + + val of_chan : ?bufsize:int -> in_channel -> t list or_error + + val of_file : ?bufsize:int -> string -> t list or_error + + val of_string : string -> t list or_error + + val of_gen : string gen -> t list or_error + + val of_seq : string sequence -> t list or_error +end