mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 03:05:28 -05:00
add CCParse.{char_fold, chars_fold_map}
useful for non-trivial lexing
This commit is contained in:
parent
c63a2b7b37
commit
88fe234a4c
2 changed files with 140 additions and 2 deletions
|
|
@ -160,11 +160,11 @@ module Error = struct
|
||||||
let msg self = self.msg()
|
let msg self = self.msg()
|
||||||
let to_string self =
|
let to_string self =
|
||||||
let line,col = line_and_column self in
|
let line,col = line_and_column self in
|
||||||
Printf.sprintf "at line %d, char %d:\n%s" line col (self.msg())
|
Printf.sprintf "at line %d, char %d: %s" line col (self.msg())
|
||||||
|
|
||||||
let pp out self =
|
let pp out self =
|
||||||
let line,col = line_and_column self in
|
let line,col = line_and_column self in
|
||||||
Format.fprintf out "at line %d, char %d:@ %s" line col (self.msg())
|
Format.fprintf out "@[<hv>at line %d, char %d:@ %s@]" line col (self.msg())
|
||||||
end
|
end
|
||||||
|
|
||||||
type 'a or_error = ('a, Error.t) result
|
type 'a or_error = ('a, Error.t) result
|
||||||
|
|
@ -429,6 +429,65 @@ let chars1_if ?descr p = {
|
||||||
~err
|
~err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exception Fold_fail of state * string
|
||||||
|
|
||||||
|
let chars_fold ~f acc0 = {
|
||||||
|
run=fun st ~ok ~err ->
|
||||||
|
let i0 = st.i in
|
||||||
|
let i = ref i0 in
|
||||||
|
let acc = ref acc0 in
|
||||||
|
let continue = ref true in
|
||||||
|
try
|
||||||
|
while !continue do
|
||||||
|
let st = {st with i = !i} in
|
||||||
|
if is_done st then (
|
||||||
|
continue := false;
|
||||||
|
) else (
|
||||||
|
let c = cur st in
|
||||||
|
match f !acc c with
|
||||||
|
| `Continue acc' ->
|
||||||
|
incr i;
|
||||||
|
acc := acc'
|
||||||
|
| `Stop -> continue := false;
|
||||||
|
| `Consume_and_stop -> incr i; continue := false
|
||||||
|
| `Fail msg -> raise (Fold_fail (st,msg))
|
||||||
|
)
|
||||||
|
done;
|
||||||
|
ok {st with i= !i} !acc
|
||||||
|
with Fold_fail (st,msg) -> err (mk_error_ st (const_str_ msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
let chars_fold_map ~f acc0 = {
|
||||||
|
run=fun st ~ok ~err ->
|
||||||
|
let i0 = st.i in
|
||||||
|
let i = ref i0 in
|
||||||
|
let acc = ref acc0 in
|
||||||
|
let continue = ref true in
|
||||||
|
let buf = Buffer.create 16 in
|
||||||
|
try
|
||||||
|
while !continue do
|
||||||
|
let st = {st with i = !i} in
|
||||||
|
if is_done st then (
|
||||||
|
continue := false;
|
||||||
|
) else (
|
||||||
|
let c = cur st in
|
||||||
|
match f !acc c with
|
||||||
|
| `Continue acc' ->
|
||||||
|
incr i;
|
||||||
|
acc := acc'
|
||||||
|
| `Yield (acc', c') ->
|
||||||
|
incr i;
|
||||||
|
acc := acc';
|
||||||
|
Buffer.add_char buf c';
|
||||||
|
| `Stop -> continue := false;
|
||||||
|
| `Consume_and_stop -> incr i; continue := false
|
||||||
|
| `Fail msg -> raise (Fold_fail (st,msg))
|
||||||
|
)
|
||||||
|
done;
|
||||||
|
ok {st with i= !i} (!acc, Buffer.contents buf)
|
||||||
|
with Fold_fail (st,msg) -> err (mk_error_ st (const_str_ msg))
|
||||||
|
}
|
||||||
|
|
||||||
let skip_chars p : _ t =
|
let skip_chars p : _ t =
|
||||||
let rec self = {
|
let rec self = {
|
||||||
run=fun st ~ok ~err ->
|
run=fun st ~ok ~err ->
|
||||||
|
|
@ -901,3 +960,29 @@ module U = struct
|
||||||
p3 >>= fun x3 ->
|
p3 >>= fun x3 ->
|
||||||
string stop *> return (x1,x2,x3)
|
string stop *> return (x1,x2,x3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Debug_ = struct
|
||||||
|
let trace_fail name p = {
|
||||||
|
run=fun st ~ok ~err ->
|
||||||
|
p.run st ~ok
|
||||||
|
~err:(fun e ->
|
||||||
|
Printf.eprintf "trace %s: fail with %s\n%!" name (Error.to_string e);
|
||||||
|
err e)
|
||||||
|
}
|
||||||
|
|
||||||
|
let trace_ ~both name ~print p = {
|
||||||
|
run=fun st ~ok ~err ->
|
||||||
|
p.run st
|
||||||
|
~ok:(fun st x ->
|
||||||
|
Printf.eprintf "trace %s: parsed %s\n%!" name (print x);
|
||||||
|
ok st x)
|
||||||
|
~err:(fun e ->
|
||||||
|
if both then (
|
||||||
|
Printf.eprintf "trace %s: fail with %s\n%!" name (Error.to_string e);
|
||||||
|
);
|
||||||
|
err e)
|
||||||
|
}
|
||||||
|
|
||||||
|
let trace_success name ~print p = trace_ ~both:false name ~print p
|
||||||
|
let trace_success_or_fail name ~print p = trace_ ~both:true name ~print p
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,43 @@ val chars1_if : ?descr:string -> (char -> bool) -> string t
|
||||||
(** Like {!chars_if}, but only non-empty strings.
|
(** Like {!chars_if}, but only non-empty strings.
|
||||||
@param descr describes what kind of character was expected *)
|
@param descr describes what kind of character was expected *)
|
||||||
|
|
||||||
|
val chars_fold :
|
||||||
|
f:('acc -> char ->
|
||||||
|
[`Continue of 'acc | `Consume_and_stop | `Stop | `Fail of string]) ->
|
||||||
|
'acc ->
|
||||||
|
'acc t
|
||||||
|
(** [chars_fold f acc0] folds over characters of the input.
|
||||||
|
Each char [c] is passed, along with the current accumulator, to [f];
|
||||||
|
[f] can either:
|
||||||
|
|
||||||
|
- stop, by returning [`Stop]. In this case the current accumulator
|
||||||
|
is returned, and [c] is not consumed.
|
||||||
|
- consume char and stop, by returning [`Consume_and_stop].
|
||||||
|
- fail, by returning [`Fail msg]. In this case the parser fails
|
||||||
|
with the given message.
|
||||||
|
- continue, by returning [`Continue acc]. The parser continues to the
|
||||||
|
next char with the new accumulator.
|
||||||
|
|
||||||
|
This is a generalization of of {!chars_if} that allows one to transform
|
||||||
|
characters on the fly, skip some, handle escape sequences, etc.
|
||||||
|
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
|
val chars_fold_map :
|
||||||
|
f:('acc -> char ->
|
||||||
|
[`Continue of 'acc | `Yield of 'acc * char
|
||||||
|
| `Consume_and_stop | `Stop | `Fail of string]) ->
|
||||||
|
'acc ->
|
||||||
|
('acc * string) t
|
||||||
|
(** Same as {!char_fold} but with the following differences:
|
||||||
|
|
||||||
|
- returns a string along with the accumulator. The string is built from
|
||||||
|
characters returned by [`Yield].
|
||||||
|
- new case [`Yield (acc, c)] adds [c] to the returned string
|
||||||
|
and continues parsing with [acc].
|
||||||
|
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
val endline : char t
|
val endline : char t
|
||||||
(** Parse '\n'. *)
|
(** Parse '\n'. *)
|
||||||
|
|
||||||
|
|
@ -471,3 +508,19 @@ module U : sig
|
||||||
(** Parse a triple using OCaml syntactic conventions.
|
(** Parse a triple using OCaml syntactic conventions.
|
||||||
The default is "(a, b, c)". *)
|
The default is "(a, b, c)". *)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
(** Debugging utils.
|
||||||
|
{b EXPERIMENTAL} *)
|
||||||
|
module Debug_ : sig
|
||||||
|
val trace_fail : string -> 'a t -> 'a t
|
||||||
|
(** [trace_fail name p] behaves like [p], but prints the error message of [p]
|
||||||
|
on stderr whenever [p] fails.
|
||||||
|
@param name used as a prefix of all trace messages. *)
|
||||||
|
|
||||||
|
val trace_success : string -> print:('a -> string) -> 'a t -> 'a t
|
||||||
|
(** [trace_success name ~print p] behaves like [p], but
|
||||||
|
prints successful runs of [p] using [print]. *)
|
||||||
|
|
||||||
|
val trace_success_or_fail : string -> print:('a -> string) -> 'a t -> 'a t
|
||||||
|
(** Trace both error or success *)
|
||||||
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue