feat(CCParse): add take_until_success

this reads a slice using the given parser to parse the end delimiter
(e.g "end gpg signature" 😉)
This commit is contained in:
Simon Cruanes 2023-04-07 11:45:12 -04:00
parent ea0e4473a8
commit 84173382db
No known key found for this signature in database
GPG key ID: EBFFF6F283F3A2B4
3 changed files with 50 additions and 0 deletions

View file

@ -600,6 +600,36 @@ let take len : slice t =
)); ));
} }
let take_until_success p : (slice * _) t =
{
run =
(fun st ~ok ~err ->
let i = ref st.i in
let st_after_p = ref st in
let continue = ref true in
let res = ref None in
while !continue && !i < st.j do
let st' = { st with i = !i } in
p.run st'
~ok:(fun new_st x ->
(* success *)
res := Some x;
continue := false;
(* parsing will continue where [p] left off *)
st_after_p := new_st)
~err:(fun _ -> incr i)
done;
match !res with
| None ->
err
(mk_error_ st (const_str_ "take_until_success: no position worked"))
| Some x ->
let slice = { st with j = !i } in
ok !st_after_p (slice, x));
}
let any_char_n len : _ t = take len >|= Slice.to_string let any_char_n len : _ t = take len >|= Slice.to_string
let exact s = let exact s =

View file

@ -304,6 +304,15 @@ val chars_fold_transduce :
@since 3.6 *) @since 3.6 *)
val take_until_success : 'a t -> (slice * 'a) t
(** [take_until_success p] accumulates characters of the input into a slice,
until [p] successfully parses a value [x]; then it returns [slice, x].
{b NOTE} performance wise, if [p] does a lot of work at each position,
this can be costly (thing naive substring search if [p] is [string "very long needle"]).
@since NEXT_RELEASE *)
val take : int -> slice t val take : int -> slice t
(** [take len] parses exactly [len] characters from the input. (** [take len] parses exactly [len] characters from the input.
Fails if the input doesn't contain at least [len] chars. Fails if the input doesn't contain at least [len] chars.

View file

@ -287,3 +287,14 @@ eq
~printer:Q.Print.(errpp (pair int int)) ~printer:Q.Print.(errpp (pair int int))
(Ok (1, 2)) (Ok (1, 2))
U.(parse_string (pair int int) "(1 , 2 )") U.(parse_string (pair int int) "(1 , 2 )")
;;
eq
~printer:Q.Print.(errpp (pair (pair string string) string))
(Ok (("!this is the text between!", "LOL"), " and a lot of other stuff"))
(parse_string
(string "COUCOU"
*> let* slice, x = take_until_success (string "LOL") in
let+ rest = take_if (fun _ -> true) <* eoi in
(Slice.to_string slice, x), Slice.to_string rest)
"COUCOU!this is the text between!LOL and a lot of other stuff")