Page
Library
Module
Module type
Parameter
Class
Class type
Source
Use diagnostics when you want to keep parsing and report multiple validation problems instead of stopping at the first one.
The result types for diagnostics-aware parsing:
type 'd diagnostic = { pos : int; diagnostic : 'd }A non-fatal diagnostic emitted during parsing.
type ('e, 'd) error_with_diagnostics = {
pos : int;
error : 'e;
diagnostics : 'd diagnostic list;
}An error with collected diagnostics.
type ('a, 'e, 'd) result_with_diagnostics =
('a * 'd diagnostic list, ('e, 'd) error_with_diagnostics) resultParse outcome with diagnostics in both success and failure cases.
warnParseff.warn records a non-fatal diagnostic at the current parser position.
val warn : 'd -> unitwarn_atParseff.warn_at records a non-fatal diagnostic at an explicit position.
val warn_at : pos:int -> 'd -> unitparse_until_endParseff.parse_until_end runs a parser on a string, enforces full input consumption implicitly, and returns both:
val parse_until_end :
?max_depth:int ->
string ->
(unit -> 'a) ->
('a, [> `Expected of string | `Unexpected_end_of_input | `Depth_limit_exceeded of string ], 'd)
result_with_diagnosticsOk (value, diagnostics) on successError { pos; error; diagnostics } on failureSemantically, this is equivalent to running your parser and then calling Parseff.end_of_input once more at the end. It does not change how explicit Parseff.end_of_input calls behave inside your parser.
parse_source_until_endParseff.parse_source_until_end is the streaming equivalent of Parseff.parse_until_end for Parseff.Source.t inputs.
val parse_source_until_end :
?max_depth:int ->
Source.t ->
(unit -> 'a) ->
('a, [> `Expected of string | `Unexpected_end_of_input | `Depth_limit_exceeded of string ], 'd)
result_with_diagnosticsIn plain terms: parse_source_until_end always does one extra Parseff.end_of_input check after your parser returns.
If your parser already calls Parseff.end_of_input itself, that call keeps the same behavior as before. The runner just adds a final safety check.
type validation = [ `Octet_out_of_range of int ]
let number_lenient () =
let start = Parseff.position () in
let digits = Parseff.many1 Parseff.digit () in
let n = List.fold_left (fun acc d -> (acc * 10) + d) 0 digits in
if n > 255 then Parseff.warn_at ~pos:start (`Octet_out_of_range n);
n
let ip_address_lenient () =
let a = number_lenient () in
let _ = Parseff.char '.' in
let b = number_lenient () in
let _ = Parseff.char '.' in
let c = number_lenient () in
let _ = Parseff.char '.' in
let d = number_lenient () in
(a, b, c, d)
let () =
let outcome =
Parseff.parse_until_end "999.2.3.256" ip_address_lenient
in
match outcome with
| Ok ((a, b, c, d), diagnostics) ->
Printf.printf "Parsed: %d.%d.%d.%d\n" a b c d;
List.iter
(fun ({ pos; diagnostic } : validation Parseff.diagnostic) ->
match diagnostic with
| `Octet_out_of_range n ->
Printf.printf " at %d: octet %d out of range (0-255)\n" pos n)
diagnostics
| Error { pos; error = `Expected msg; diagnostics } ->
Printf.printf "Parse error at %d: %s\n" pos msg;
List.iter
(fun ({ pos; diagnostic } : validation Parseff.diagnostic) ->
match diagnostic with
| `Octet_out_of_range n ->
Printf.printf " noted at %d: octet %d out of range\n" pos n)
diagnostics
| Error { pos; error = `Unexpected_end_of_input; _ } ->
Printf.printf "Unexpected end of input at %d\n" pos
| Error _ -> Printf.printf "Parse failed\n"Diagnostics are transactional with parser control flow:
Parseff.or_: diagnostics from failed branches are rolled backParseff.many: diagnostics from the final failing attempt are rolled backParseff.look_ahead: diagnostics are rolled backThis prevents diagnostics from leaking out of speculative branches.