package parseff

  1. Overview
  2. Docs

Convenience combinators

Convenience combinators provide ready-to-use parsers for common patterns like digits, letters, and whitespace.

Character classes

digit

Parseff.digit parses a decimal digit (0-9) and returns its integer value.

val digit : unit -> int
(* Parse two-digit number *)
let two_digits () =
  let a = Parseff.digit () in
  let b = Parseff.digit () in
  a * 10 + b

(* Matches "42" -> 42 *)

letter

Parseff.letter parses an ASCII letter (a-z or A-Z).

val letter : unit -> char
let initial () = Parseff.letter ()

(* Matches "A" -> 'A' *)
(* Matches "z" -> 'z' *)
(* Fails on "1" *)

alphanum

Parseff.alphanum parses an alphanumeric character (letter or digit).

val alphanum : unit -> char
let username () =
  let first = Parseff.letter () in
  let rest = Parseff.many Parseff.alphanum () in
  String.make 1 first ^ String.of_seq (List.to_seq rest)

(* Matches "user123" -> "user123" *)
(* Fails on "123user" (must start with letter) *)

any_char

Parseff.any_char parses any character. Fails only at end of input.

val any_char : unit -> char
(* Skip past a single unknown character *)
let skip_one () =
  let _ = Parseff.any_char () in
  ()

(* Peek at the next character without consuming it *)
let peek () = Parseff.look_ahead Parseff.any_char

Whitespace

is_whitespace

Parseff.is_whitespace returns true for whitespace characters (space, tab, newline, carriage return). This is a predicate, not a parser.

val is_whitespace : char -> bool
let trim_parser () =
  Parseff.skip_while Parseff.is_whitespace;
  let value =
    Parseff.take_while1
      (fun c -> not (Parseff.is_whitespace c))
      ~label:"value"
  in
  Parseff.skip_while Parseff.is_whitespace;
  value

(* Matches "  hello  " -> "hello" *)

whitespace

Parseff.whitespace parses zero or more whitespace characters. Returns the matched string. Always succeeds (returns empty string if no whitespace).

val whitespace : unit -> string
let spaced_values () =
  let a = Parseff.digit () in
  let _ = Parseff.whitespace () in
  let b = Parseff.digit () in
  (a, b)

(* Matches "1 2" -> (1, 2) *)
(* Matches "1    2" -> (1, 2) *)
(* Matches "12" -> (1, 2) *)

whitespace1

Parseff.whitespace1 parses one or more whitespace characters. Fails if no whitespace found.

val whitespace1 : unit -> string
let words () =
  Parseff.sep_by1
    (fun () ->
      Parseff.take_while1
        (fun c -> not (Parseff.is_whitespace c))
        ~label:"word")
    (fun () -> Parseff.whitespace1 ())
    ()

(* Matches "hello world" -> ["hello"; "world"] *)
(* Fails on "helloworld" (no whitespace separator) *)

skip_whitespace

Parseff.skip_whitespace skips zero or more whitespace characters (returns unit). More efficient than Parseff.whitespace when you don't need the matched string.

val skip_whitespace : unit -> unit
(* Parse comma-separated list with flexible spacing *)
let flexible_list () =
  let _ = Parseff.char '[' in
  Parseff.skip_whitespace ();
  let values =
    Parseff.sep_by
      (fun () ->
        Parseff.skip_whitespace ();
        let n = Parseff.digit () in
        Parseff.skip_whitespace ();
        n)
      (fun () -> Parseff.char ',')
      ()
  in
  Parseff.skip_whitespace ();
  let _ = Parseff.char ']' in
  values

(* All of these parse to [1; 2; 3]: *)
(* "[1,2,3]" *)
(* "[ 1 , 2 , 3 ]" *)
(* "[  1  ,  2  ,  3  ]" *)

Tip: Always use skip_whitespace instead of whitespace when you don't need the matched string:

(* allocates a string you don't need *)
let _ = Parseff.whitespace () in
parse_value ()

(* skips without allocating *)
Parseff.skip_whitespace ();
parse_value ()