···19192020(** Hexadecimal digit *)
2121let is_hex c =
2222- (c >= '0' && c <= '9') ||
2323- (c >= 'a' && c <= 'f') ||
2424- (c >= 'A' && c <= 'F')
2222+ (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
25232624(** Alphabetic character *)
2727-let is_alpha c =
2828- (c >= 'a' && c <= 'z') ||
2929- (c >= 'A' && c <= 'Z')
2525+let is_alpha c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
30263127(** Alphanumeric character *)
3228let is_alnum c = is_alpha c || is_digit c
···3430(** YAML indicator characters *)
3531let is_indicator c =
3632 match c with
3737- | '-' | '?' | ':' | ',' | '[' | ']' | '{' | '}'
3838- | '#' | '&' | '*' | '!' | '|' | '>' | '\'' | '"'
3939- | '%' | '@' | '`' -> true
3333+ | '-' | '?' | ':' | ',' | '[' | ']' | '{' | '}' | '#' | '&' | '*' | '!' | '|'
3434+ | '>' | '\'' | '"' | '%' | '@' | '`' ->
3535+ true
4036 | _ -> false
41374238(** Flow context indicator characters *)
4339let is_flow_indicator c =
4444- match c with
4545- | ',' | '[' | ']' | '{' | '}' -> true
4646- | _ -> false
4040+ match c with ',' | '[' | ']' | '{' | '}' -> true | _ -> false
+5-19
lib/chomping.ml
···66(** Block scalar chomping indicators *)
7788type t =
99- | Strip (** Remove final line break and trailing empty lines *)
99+ | Strip (** Remove final line break and trailing empty lines *)
1010 | Clip (** Keep final line break, remove trailing empty lines (default) *)
1111 | Keep (** Keep final line break and trailing empty lines *)
12121313-let to_string = function
1414- | Strip -> "strip"
1515- | Clip -> "clip"
1616- | Keep -> "keep"
1717-1818-let pp fmt t =
1919- Format.pp_print_string fmt (to_string t)
2020-2121-let of_char = function
2222- | '-' -> Some Strip
2323- | '+' -> Some Keep
2424- | _ -> None
2525-2626-let to_char = function
2727- | Strip -> Some '-'
2828- | Clip -> None
2929- | Keep -> Some '+'
3030-1313+let to_string = function Strip -> "strip" | Clip -> "clip" | Keep -> "keep"
1414+let pp fmt t = Format.pp_print_string fmt (to_string t)
1515+let of_char = function '-' -> Some Strip | '+' -> Some Keep | _ -> None
1616+let to_char = function Strip -> Some '-' | Clip -> None | Keep -> Some '+'
3117let equal a b = a = b
+16-20
lib/document.ml
···1313 implicit_end : bool;
1414}
15151616-let make
1717- ?(version : (int * int) option)
1818- ?(tags : (string * string) list = [])
1919- ?(implicit_start = true)
2020- ?(implicit_end = true)
2121- root =
1616+let make ?(version : (int * int) option) ?(tags : (string * string) list = [])
1717+ ?(implicit_start = true) ?(implicit_end = true) root =
2218 { version; tags; root; implicit_start; implicit_end }
23192420let version t = t.version
···2622let root t = t.root
2723let implicit_start t = t.implicit_start
2824let implicit_end t = t.implicit_end
2929-3025let with_version version t = { t with version = Some version }
3126let with_tags tags t = { t with tags }
3227let with_root root t = { t with root = Some root }
···3429let pp fmt t =
3530 Format.fprintf fmt "@[<v 2>document(@,";
3631 (match t.version with
3737- | Some (maj, min) -> Format.fprintf fmt "version=%d.%d,@ " maj min
3838- | None -> ());
3232+ | Some (maj, min) -> Format.fprintf fmt "version=%d.%d,@ " maj min
3333+ | None -> ());
3934 if t.tags <> [] then begin
4035 Format.fprintf fmt "tags=[";
4141- List.iteri (fun i (h, p) ->
4242- if i > 0 then Format.fprintf fmt ", ";
4343- Format.fprintf fmt "%s -> %s" h p
4444- ) t.tags;
3636+ List.iteri
3737+ (fun i (h, p) ->
3838+ if i > 0 then Format.fprintf fmt ", ";
3939+ Format.fprintf fmt "%s -> %s" h p)
4040+ t.tags;
4541 Format.fprintf fmt "],@ "
4642 end;
4743 Format.fprintf fmt "implicit_start=%b,@ " t.implicit_start;
4844 Format.fprintf fmt "implicit_end=%b,@ " t.implicit_end;
4945 (match t.root with
5050- | Some root -> Format.fprintf fmt "root=%a" Yaml.pp root
5151- | None -> Format.fprintf fmt "root=<empty>");
4646+ | Some root -> Format.fprintf fmt "root=%a" Yaml.pp root
4747+ | None -> Format.fprintf fmt "root=<empty>");
5248 Format.fprintf fmt "@]@,)"
53495450let equal a b =
5555- Option.equal ( = ) a.version b.version &&
5656- List.equal ( = ) a.tags b.tags &&
5757- Option.equal Yaml.equal a.root b.root &&
5858- a.implicit_start = b.implicit_start &&
5959- a.implicit_end = b.implicit_end
5151+ Option.equal ( = ) a.version b.version
5252+ && List.equal ( = ) a.tags b.tags
5353+ && Option.equal Yaml.equal a.root b.root
5454+ && a.implicit_start = b.implicit_start
5555+ && a.implicit_end = b.implicit_end
+63-107
lib/eio/yamlrw_eio.ml
···5566(** Yamlrw Eio - Streaming YAML parsing and emitting with Eio
7788- This module provides Eio-compatible streaming YAML parsing and emitting.
99- It uses bytesrw adapters to convert Eio sources/sinks to the standard
1010- YAML scanner/parser/emitter, eliminating code duplication. *)
88+ This module provides Eio-compatible streaming YAML parsing and emitting. It
99+ uses bytesrw adapters to convert Eio sources/sinks to the standard YAML
1010+ scanner/parser/emitter, eliminating code duplication. *)
11111212open Yamlrw
1313···3030 Scanner.of_input input
31313232 (** Create a parser from an Eio flow *)
3333- let parser_of_flow flow =
3434- Parser.of_scanner (scanner_of_flow flow)
3333+ let parser_of_flow flow = Parser.of_scanner (scanner_of_flow flow)
35343635 (** Parse a JSON-compatible value from an Eio flow.
37363837 @param resolve_aliases Whether to expand aliases (default: true)
3938 @param max_nodes Maximum nodes during alias expansion (default: 10M)
4039 @param max_depth Maximum alias nesting depth (default: 100) *)
4141- let value
4242- ?(resolve_aliases = true)
4040+ let value ?(resolve_aliases = true)
4341 ?(max_nodes = Yaml.default_max_alias_nodes)
4444- ?(max_depth = Yaml.default_max_alias_depth)
4545- flow =
4242+ ?(max_depth = Yaml.default_max_alias_depth) flow =
4643 let parser = parser_of_flow flow in
4747- Loader.value_of_parser
4848- ~resolve_aliases ~max_nodes ~max_depth
4949- (fun () -> Parser.next parser)
4444+ Loader.value_of_parser ~resolve_aliases ~max_nodes ~max_depth (fun () ->
4545+ Parser.next parser)
50465147 (** Parse a full YAML value from an Eio flow.
5248···5551 @param resolve_aliases Whether to expand aliases (default: false)
5652 @param max_nodes Maximum nodes during alias expansion (default: 10M)
5753 @param max_depth Maximum alias nesting depth (default: 100) *)
5858- let yaml
5959- ?(resolve_aliases = false)
5454+ let yaml ?(resolve_aliases = false)
6055 ?(max_nodes = Yaml.default_max_alias_nodes)
6161- ?(max_depth = Yaml.default_max_alias_depth)
6262- flow =
5656+ ?(max_depth = Yaml.default_max_alias_depth) flow =
6357 let parser = parser_of_flow flow in
6464- Loader.yaml_of_parser
6565- ~resolve_aliases ~max_nodes ~max_depth
6666- (fun () -> Parser.next parser)
5858+ Loader.yaml_of_parser ~resolve_aliases ~max_nodes ~max_depth (fun () ->
5959+ Parser.next parser)
67606861 (** Parse multiple YAML documents from an Eio flow. *)
6962 let documents flow =
···72657366 (** {2 Event-Based Streaming} *)
74676868+ type event_reader = { parser : Parser.t }
7569 (** A streaming event reader backed by a flow *)
7676- type event_reader = {
7777- parser : Parser.t;
7878- }
79708080- (** Create an event reader from an Eio flow.
8181- This reads data incrementally as events are requested. *)
8282- let event_reader flow =
8383- { parser = parser_of_flow flow }
7171+ (** Create an event reader from an Eio flow. This reads data incrementally as
7272+ events are requested. *)
7373+ let event_reader flow = { parser = parser_of_flow flow }
84748585- (** Get the next event from an event reader.
8686- Returns [None] when parsing is complete. *)
8787- let next_event reader =
8888- Parser.next reader.parser
7575+ (** Get the next event from an event reader. Returns [None] when parsing is
7676+ complete. *)
7777+ let next_event reader = Parser.next reader.parser
89789079 (** Iterate over all events from a flow.
9180···127116 @param encoding Output encoding (default: UTF-8)
128117 @param scalar_style Preferred scalar style (default: Any)
129118 @param layout_style Preferred layout style (default: Any) *)
130130- let value
131131- ?(encoding = `Utf8)
132132- ?(scalar_style = `Any)
133133- ?(layout_style = `Any)
134134- flow
135135- (v : value) =
136136- let config = { Emitter.default_config with encoding; scalar_style; layout_style } in
119119+ let value ?(encoding = `Utf8) ?(scalar_style = `Any) ?(layout_style = `Any)
120120+ flow (v : value) =
121121+ let config =
122122+ { Emitter.default_config with encoding; scalar_style; layout_style }
123123+ in
137124 let writer = Bytesrw_eio.bytes_writer_of_flow flow in
138125 Serialize.value_to_writer ~config writer v
139126···142129 @param encoding Output encoding (default: UTF-8)
143130 @param scalar_style Preferred scalar style (default: Any)
144131 @param layout_style Preferred layout style (default: Any) *)
145145- let yaml
146146- ?(encoding = `Utf8)
147147- ?(scalar_style = `Any)
148148- ?(layout_style = `Any)
149149- flow
150150- (v : yaml) =
151151- let config = { Emitter.default_config with encoding; scalar_style; layout_style } in
132132+ let yaml ?(encoding = `Utf8) ?(scalar_style = `Any) ?(layout_style = `Any)
133133+ flow (v : yaml) =
134134+ let config =
135135+ { Emitter.default_config with encoding; scalar_style; layout_style }
136136+ in
152137 let writer = Bytesrw_eio.bytes_writer_of_flow flow in
153138 Serialize.yaml_to_writer ~config writer v
154139···158143 @param scalar_style Preferred scalar style (default: Any)
159144 @param layout_style Preferred layout style (default: Any)
160145 @param resolve_aliases Whether to expand aliases (default: true) *)
161161- let documents
162162- ?(encoding = `Utf8)
163163- ?(scalar_style = `Any)
164164- ?(layout_style = `Any)
165165- ?(resolve_aliases = true)
166166- flow
167167- docs =
168168- let config = { Emitter.default_config with encoding; scalar_style; layout_style } in
146146+ let documents ?(encoding = `Utf8) ?(scalar_style = `Any)
147147+ ?(layout_style = `Any) ?(resolve_aliases = true) flow docs =
148148+ let config =
149149+ { Emitter.default_config with encoding; scalar_style; layout_style }
150150+ in
169151 let writer = Bytesrw_eio.bytes_writer_of_flow flow in
170152 Serialize.documents_to_writer ~config ~resolve_aliases writer docs
171153172154 (** {2 Event-Based Streaming} *)
173155156156+ type event_writer = { emitter : Emitter.t }
174157 (** A streaming event writer that writes directly to a flow *)
175175- type event_writer = {
176176- emitter : Emitter.t;
177177- }
178158179179- (** Create an event writer that writes directly to a flow.
180180- Events are written incrementally as they are emitted.
159159+ (** Create an event writer that writes directly to a flow. Events are written
160160+ incrementally as they are emitted.
181161182162 @param encoding Output encoding (default: UTF-8)
183163 @param scalar_style Preferred scalar style (default: Any)
184164 @param layout_style Preferred layout style (default: Any) *)
185185- let event_writer
186186- ?(encoding = `Utf8)
187187- ?(scalar_style = `Any)
188188- ?(layout_style = `Any)
189189- flow =
190190- let config = { Emitter.default_config with encoding; scalar_style; layout_style } in
165165+ let event_writer ?(encoding = `Utf8) ?(scalar_style = `Any)
166166+ ?(layout_style = `Any) flow =
167167+ let config =
168168+ { Emitter.default_config with encoding; scalar_style; layout_style }
169169+ in
191170 let writer = Bytesrw_eio.bytes_writer_of_flow flow in
192171 { emitter = Emitter.of_writer ~config writer }
193172194173 (** Emit a single event to the writer. *)
195195- let emit ew ev =
196196- Emitter.emit ew.emitter ev
174174+ let emit ew ev = Emitter.emit ew.emitter ev
197175198176 (** Flush the writer by sending end-of-data. *)
199199- let flush ew =
200200- Emitter.flush ew.emitter
177177+ let flush ew = Emitter.flush ew.emitter
201178202179 (** Emit events from a list to a flow. *)
203180 let emit_all flow events =
···209186(** {1 Convenience Functions} *)
210187211188(** Read a value from a file path *)
212212-let of_file
213213- ?(resolve_aliases = true)
189189+let of_file ?(resolve_aliases = true)
214190 ?(max_nodes = Yaml.default_max_alias_nodes)
215215- ?(max_depth = Yaml.default_max_alias_depth)
216216- ~fs
217217- path =
191191+ ?(max_depth = Yaml.default_max_alias_depth) ~fs path =
218192 Eio.Path.with_open_in Eio.Path.(fs / path) @@ fun flow ->
219193 Read.value ~resolve_aliases ~max_nodes ~max_depth flow
220194221195(** Read full YAML from a file path *)
222222-let yaml_of_file
223223- ?(resolve_aliases = false)
196196+let yaml_of_file ?(resolve_aliases = false)
224197 ?(max_nodes = Yaml.default_max_alias_nodes)
225225- ?(max_depth = Yaml.default_max_alias_depth)
226226- ~fs
227227- path =
198198+ ?(max_depth = Yaml.default_max_alias_depth) ~fs path =
228199 Eio.Path.with_open_in Eio.Path.(fs / path) @@ fun flow ->
229200 Read.yaml ~resolve_aliases ~max_nodes ~max_depth flow
230201231202(** Read documents from a file path *)
232203let documents_of_file ~fs path =
233233- Eio.Path.with_open_in Eio.Path.(fs / path) @@ fun flow ->
234234- Read.documents flow
204204+ Eio.Path.with_open_in Eio.Path.(fs / path) @@ fun flow -> Read.documents flow
235205236206(** Write a value to a file path *)
237237-let to_file
238238- ?(encoding = `Utf8)
239239- ?(scalar_style = `Any)
240240- ?(layout_style = `Any)
241241- ~fs
242242- path
243243- v =
244244- Eio.Path.with_open_out ~create:(`Or_truncate 0o644) Eio.Path.(fs / path) @@ fun flow ->
245245- Write.value ~encoding ~scalar_style ~layout_style flow v
207207+let to_file ?(encoding = `Utf8) ?(scalar_style = `Any) ?(layout_style = `Any)
208208+ ~fs path v =
209209+ Eio.Path.with_open_out ~create:(`Or_truncate 0o644) Eio.Path.(fs / path)
210210+ @@ fun flow -> Write.value ~encoding ~scalar_style ~layout_style flow v
246211247212(** Write full YAML to a file path *)
248248-let yaml_to_file
249249- ?(encoding = `Utf8)
250250- ?(scalar_style = `Any)
251251- ?(layout_style = `Any)
252252- ~fs
253253- path
254254- v =
255255- Eio.Path.with_open_out ~create:(`Or_truncate 0o644) Eio.Path.(fs / path) @@ fun flow ->
256256- Write.yaml ~encoding ~scalar_style ~layout_style flow v
213213+let yaml_to_file ?(encoding = `Utf8) ?(scalar_style = `Any)
214214+ ?(layout_style = `Any) ~fs path v =
215215+ Eio.Path.with_open_out ~create:(`Or_truncate 0o644) Eio.Path.(fs / path)
216216+ @@ fun flow -> Write.yaml ~encoding ~scalar_style ~layout_style flow v
257217258218(** Write documents to a file path *)
259259-let documents_to_file
260260- ?(encoding = `Utf8)
261261- ?(scalar_style = `Any)
262262- ?(layout_style = `Any)
263263- ?(resolve_aliases = true)
264264- ~fs
265265- path
266266- docs =
267267- Eio.Path.with_open_out ~create:(`Or_truncate 0o644) Eio.Path.(fs / path) @@ fun flow ->
268268- Write.documents ~encoding ~scalar_style ~layout_style ~resolve_aliases flow docs
219219+let documents_to_file ?(encoding = `Utf8) ?(scalar_style = `Any)
220220+ ?(layout_style = `Any) ?(resolve_aliases = true) ~fs path docs =
221221+ Eio.Path.with_open_out ~create:(`Or_truncate 0o644) Eio.Path.(fs / path)
222222+ @@ fun flow ->
223223+ Write.documents ~encoding ~scalar_style ~layout_style ~resolve_aliases flow
224224+ docs
+66-52
lib/eio/yamlrw_eio.mli
···5566(** Yamlrw Eio - Streaming YAML parsing and emitting with Eio
7788- This library provides Eio-based streaming support for YAML parsing
99- and emitting. It uses bytesrw adapters that read/write directly to
1010- Eio flows, with bytesrw handling internal buffering.
88+ This library provides Eio-based streaming support for YAML parsing and
99+ emitting. It uses bytesrw adapters that read/write directly to Eio flows,
1010+ with bytesrw handling internal buffering.
11111212 {2 Quick Start}
1313···2424 Eio_main.run @@ fun env ->
2525 let fs = Eio.Stdenv.fs env in
2626 Eio.Path.with_open_out Eio.Path.(fs / "output.yaml") @@ fun flow ->
2727- Yaml_eio.Write.value flow (`O [("name", `String "test")])
2727+ Yaml_eio.Write.value flow (`O [ ("name", `String "test") ])
2828 ]}
29293030 Stream events incrementally:
···3232 Eio_main.run @@ fun env ->
3333 let fs = Eio.Stdenv.fs env in
3434 Eio.Path.with_open_in Eio.Path.(fs / "data.yaml") @@ fun flow ->
3535- Yaml_eio.Read.iter_events (fun event span ->
3636- Format.printf "Event at %a@." Yamlrw.Span.pp span
3737- ) flow
3535+ Yaml_eio.Read.iter_events
3636+ (fun event span -> Format.printf "Event at %a@." Yamlrw.Span.pp span)
3737+ flow
3838 ]}
39394040 {2 Streaming Architecture}
41414242 This library uses bytesrw for direct I/O with Eio flows:
43434444- - {b Reading}: Data is read directly from the flow as the
4545- parser requests it. Bytesrw handles internal buffering.
4444+ - {b Reading}: Data is read directly from the flow as the parser requests
4545+ it. Bytesrw handles internal buffering.
46464747- - {b Writing}: Output is written directly to the flow.
4848- Bytesrw handles chunking and buffering. *)
4747+ - {b Writing}: Output is written directly to the flow. Bytesrw handles
4848+ chunking and buffering. *)
49495050(** {1 Types} *)
5151···6666module Read : sig
6767 (** Parse YAML from Eio flows.
68686969- All functions read data incrementally from the underlying flow,
7070- without loading the entire file into memory first. *)
6969+ All functions read data incrementally from the underlying flow, without
7070+ loading the entire file into memory first. *)
71717272 (** {2 High-Level Parsing} *)
7373···7575 ?resolve_aliases:bool ->
7676 ?max_nodes:int ->
7777 ?max_depth:int ->
7878- _ Eio.Flow.source -> value
7878+ _ Eio.Flow.source ->
7979+ value
7980 (** Parse a JSON-compatible value from an Eio flow.
80818182 @param resolve_aliases Whether to expand aliases (default: true)
···8687 ?resolve_aliases:bool ->
8788 ?max_nodes:int ->
8889 ?max_depth:int ->
8989- _ Eio.Flow.source -> yaml
9090+ _ Eio.Flow.source ->
9191+ yaml
9092 (** Parse a full YAML value from an Eio flow.
91939294 By default, aliases are NOT resolved, preserving the document structure.
···101103 (** {2 Event-Based Streaming} *)
102104103105 type event_reader
104104- (** A streaming event reader backed by a flow.
105105- Events are parsed incrementally as requested. *)
106106+ (** A streaming event reader backed by a flow. Events are parsed incrementally
107107+ as requested. *)
106108107109 val event_reader : _ Eio.Flow.source -> event_reader
108110 (** Create an event reader from an Eio flow. *)
109111110112 val next_event : event_reader -> Yamlrw.Event.spanned option
111111- (** Get the next event from an event reader.
112112- Returns [None] when parsing is complete. *)
113113+ (** Get the next event from an event reader. Returns [None] when parsing is
114114+ complete. *)
113115114116 val iter_events :
115115- (event -> Yamlrw.Span.t -> unit) ->
116116- _ Eio.Flow.source -> unit
117117+ (event -> Yamlrw.Span.t -> unit) -> _ Eio.Flow.source -> unit
117118 (** Iterate over all events from a flow. *)
118119119119- val fold_events :
120120- ('a -> event -> 'a) -> 'a ->
121121- _ Eio.Flow.source -> 'a
120120+ val fold_events : ('a -> event -> 'a) -> 'a -> _ Eio.Flow.source -> 'a
122121 (** Fold over all events from a flow. *)
123122124124- val iter_documents :
125125- (document -> unit) ->
126126- _ Eio.Flow.source -> unit
123123+ val iter_documents : (document -> unit) -> _ Eio.Flow.source -> unit
127124 (** Iterate over documents from a flow, calling [f] for each document. *)
128125129129- val fold_documents :
130130- ('a -> document -> 'a) -> 'a ->
131131- _ Eio.Flow.source -> 'a
126126+ val fold_documents : ('a -> document -> 'a) -> 'a -> _ Eio.Flow.source -> 'a
132127 (** Fold over documents from a flow. *)
133128end
134129···145140 ?encoding:Yamlrw.Encoding.t ->
146141 ?scalar_style:Yamlrw.Scalar_style.t ->
147142 ?layout_style:Yamlrw.Layout_style.t ->
148148- _ Eio.Flow.sink -> value -> unit
143143+ _ Eio.Flow.sink ->
144144+ value ->
145145+ unit
149146 (** Write a JSON-compatible value to an Eio flow.
150147151148 @param encoding Output encoding (default: UTF-8)
···156153 ?encoding:Yamlrw.Encoding.t ->
157154 ?scalar_style:Yamlrw.Scalar_style.t ->
158155 ?layout_style:Yamlrw.Layout_style.t ->
159159- _ Eio.Flow.sink -> yaml -> unit
156156+ _ Eio.Flow.sink ->
157157+ yaml ->
158158+ unit
160159 (** Write a full YAML value to an Eio flow.
161160162161 @param encoding Output encoding (default: UTF-8)
···168167 ?scalar_style:Yamlrw.Scalar_style.t ->
169168 ?layout_style:Yamlrw.Layout_style.t ->
170169 ?resolve_aliases:bool ->
171171- _ Eio.Flow.sink -> document list -> unit
170170+ _ Eio.Flow.sink ->
171171+ document list ->
172172+ unit
172173 (** Write multiple YAML documents to an Eio flow.
173174174175 @param encoding Output encoding (default: UTF-8)
···179180 (** {2 Event-Based Streaming} *)
180181181182 type event_writer
182182- (** A streaming event writer backed by a flow.
183183- Events are written incrementally to the underlying flow. *)
183183+ (** A streaming event writer backed by a flow. Events are written
184184+ incrementally to the underlying flow. *)
184185185186 val event_writer :
186187 ?encoding:Yamlrw.Encoding.t ->
187188 ?scalar_style:Yamlrw.Scalar_style.t ->
188189 ?layout_style:Yamlrw.Layout_style.t ->
189189- _ Eio.Flow.sink -> event_writer
190190- (** Create an event writer that writes directly to a flow.
191191- Events are written incrementally as they are emitted.
190190+ _ Eio.Flow.sink ->
191191+ event_writer
192192+ (** Create an event writer that writes directly to a flow. Events are written
193193+ incrementally as they are emitted.
192194193195 @param encoding Output encoding (default: UTF-8)
194196 @param scalar_style Preferred scalar style (default: Any)
···211213 ?max_nodes:int ->
212214 ?max_depth:int ->
213215 fs:_ Eio.Path.t ->
214214- string -> value
216216+ string ->
217217+ value
215218(** Read a value from a file path.
216219217217- @param fs The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
220220+ @param fs
221221+ The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
218222219223val yaml_of_file :
220224 ?resolve_aliases:bool ->
221225 ?max_nodes:int ->
222226 ?max_depth:int ->
223227 fs:_ Eio.Path.t ->
224224- string -> yaml
228228+ string ->
229229+ yaml
225230(** Read full YAML from a file path.
226231227227- @param fs The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
232232+ @param fs
233233+ The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
228234229229-val documents_of_file :
230230- fs:_ Eio.Path.t ->
231231- string -> document list
235235+val documents_of_file : fs:_ Eio.Path.t -> string -> document list
232236(** Read documents from a file path.
233237234234- @param fs The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
238238+ @param fs
239239+ The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
235240236241val to_file :
237242 ?encoding:Yamlrw.Encoding.t ->
238243 ?scalar_style:Yamlrw.Scalar_style.t ->
239244 ?layout_style:Yamlrw.Layout_style.t ->
240245 fs:_ Eio.Path.t ->
241241- string -> value -> unit
246246+ string ->
247247+ value ->
248248+ unit
242249(** Write a value to a file path.
243250244244- @param fs The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
251251+ @param fs
252252+ The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
245253246254val yaml_to_file :
247255 ?encoding:Yamlrw.Encoding.t ->
248256 ?scalar_style:Yamlrw.Scalar_style.t ->
249257 ?layout_style:Yamlrw.Layout_style.t ->
250258 fs:_ Eio.Path.t ->
251251- string -> yaml -> unit
259259+ string ->
260260+ yaml ->
261261+ unit
252262(** Write full YAML to a file path.
253263254254- @param fs The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
264264+ @param fs
265265+ The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
255266256267val documents_to_file :
257268 ?encoding:Yamlrw.Encoding.t ->
···259270 ?layout_style:Yamlrw.Layout_style.t ->
260271 ?resolve_aliases:bool ->
261272 fs:_ Eio.Path.t ->
262262- string -> document list -> unit
273273+ string ->
274274+ document list ->
275275+ unit
263276(** Write documents to a file path.
264277265265- @param fs The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
278278+ @param fs
279279+ The filesystem path (e.g., [Eio.Stdenv.fs env] or [Eio.Stdenv.cwd env]) *)
+209-211
lib/emitter.ml
···5566(** Emitter - converts YAML data structures to string output
7788- The emitter can write to either a Buffer (default) or directly to a
99- bytesrw Bytes.Writer for streaming output. *)
88+ The emitter can write to either a Buffer (default) or directly to a bytesrw
99+ Bytes.Writer for streaming output. *)
10101111type config = {
1212 encoding : Encoding.t;
···1717 canonical : bool;
1818}
19192020-let default_config = {
2121- encoding = `Utf8;
2222- scalar_style = `Any;
2323- layout_style = `Any;
2424- indent = 2;
2525- width = 80;
2626- canonical = false;
2727-}
2020+let default_config =
2121+ {
2222+ encoding = `Utf8;
2323+ scalar_style = `Any;
2424+ layout_style = `Any;
2525+ indent = 2;
2626+ width = 80;
2727+ canonical = false;
2828+ }
28292930type state =
3031 | Initial
3132 | Stream_started
3233 | Document_started
3333- | In_block_sequence of int (* indent level *)
3434+ | In_block_sequence of int (* indent level *)
3435 | In_block_mapping_key of int
3536 | In_block_mapping_value of int
3636- | In_block_mapping_first_key of int (* first key after "- ", no indent needed *)
3737+ | In_block_mapping_first_key of
3838+ int (* first key after "- ", no indent needed *)
3739 | In_flow_sequence
3840 | In_flow_mapping_key
3941 | In_flow_mapping_value
···4143 | Stream_ended
42444345(** Output sink - either a Buffer or a bytesrw Writer *)
4444-type sink =
4545- | Buffer_sink of Buffer.t
4646- | Writer_sink of Bytesrw.Bytes.Writer.t
4646+type sink = Buffer_sink of Buffer.t | Writer_sink of Bytesrw.Bytes.Writer.t
47474848type t = {
4949 config : config;
···5555 mutable need_separator : bool;
5656}
57575858-let create ?(config = default_config) () = {
5959- config;
6060- sink = Buffer_sink (Buffer.create 1024);
6161- state = Initial;
6262- states = [];
6363- indent = 0;
6464- flow_level = 0;
6565- need_separator = false;
6666-}
5858+let create ?(config = default_config) () =
5959+ {
6060+ config;
6161+ sink = Buffer_sink (Buffer.create 1024);
6262+ state = Initial;
6363+ states = [];
6464+ indent = 0;
6565+ flow_level = 0;
6666+ need_separator = false;
6767+ }
67686869(** Create an emitter that writes directly to a Bytes.Writer *)
6969-let of_writer ?(config = default_config) writer = {
7070- config;
7171- sink = Writer_sink writer;
7272- state = Initial;
7373- states = [];
7474- indent = 0;
7575- flow_level = 0;
7676- need_separator = false;
7777-}
7070+let of_writer ?(config = default_config) writer =
7171+ {
7272+ config;
7373+ sink = Writer_sink writer;
7474+ state = Initial;
7575+ states = [];
7676+ indent = 0;
7777+ flow_level = 0;
7878+ need_separator = false;
7979+ }
78807981let contents t =
8082 match t.sink with
8183 | Buffer_sink buf -> Buffer.contents buf
8282- | Writer_sink _ -> "" (* No accumulated content for writer sink *)
8484+ | Writer_sink _ -> "" (* No accumulated content for writer sink *)
83858486let reset t =
8587 (match t.sink with
8686- | Buffer_sink buf -> Buffer.clear buf
8787- | Writer_sink _ -> ());
8888+ | Buffer_sink buf -> Buffer.clear buf
8989+ | Writer_sink _ -> ());
8890 t.state <- Initial;
8991 t.states <- [];
9092 t.indent <- 0;
···107109108110let write_indent t =
109111 if t.indent <= 8 then
110110- for _ = 1 to t.indent do write_char t ' ' done
111111- else
112112- write t (String.make t.indent ' ')
112112+ for _ = 1 to t.indent do
113113+ write_char t ' '
114114+ done
115115+ else write t (String.make t.indent ' ')
113116114114-let write_newline t =
115115- write_char t '\n'
117117+let write_newline t = write_char t '\n'
116118117119let push_state t s =
118120 t.states <- t.state :: t.states;
···123125 | s :: rest ->
124126 t.state <- s;
125127 t.states <- rest
126126- | [] ->
127127- t.state <- Stream_ended
128128+ | [] -> t.state <- Stream_ended
128129129129-(** Escape a string for double-quoted output.
130130- Uses a buffer to batch writes instead of character-by-character. *)
130130+(** Escape a string for double-quoted output. Uses a buffer to batch writes
131131+ instead of character-by-character. *)
131132let escape_double_quoted value =
132133 let len = String.length value in
133134 (* Check if any escaping is needed *)
···140141 done;
141142 if not !needs_escape then value
142143 else begin
143143- let buf = Buffer.create (len + len / 4) in
144144+ let buf = Buffer.create (len + (len / 4)) in
144145 for i = 0 to len - 1 do
145146 match value.[i] with
146147 | '"' -> Buffer.add_string buf "\\\""
···148149 | '\n' -> Buffer.add_string buf "\\n"
149150 | '\r' -> Buffer.add_string buf "\\r"
150151 | '\t' -> Buffer.add_string buf "\\t"
151151- | c when c < ' ' -> Buffer.add_string buf (Printf.sprintf "\\x%02x" (Char.code c))
152152+ | c when c < ' ' ->
153153+ Buffer.add_string buf (Printf.sprintf "\\x%02x" (Char.code c))
152154 | c -> Buffer.add_char buf c
153155 done;
154156 Buffer.contents buf
···159161 if not (String.contains value '\'') then value
160162 else begin
161163 let len = String.length value in
162162- let buf = Buffer.create (len + len / 8) in
164164+ let buf = Buffer.create (len + (len / 8)) in
163165 for i = 0 to len - 1 do
164166 let c = value.[i] in
165165- if c = '\'' then Buffer.add_string buf "''"
166166- else Buffer.add_char buf c
167167+ if c = '\'' then Buffer.add_string buf "''" else Buffer.add_char buf c
167168 done;
168169 Buffer.contents buf
169170 end
170171171172(** Write scalar with appropriate quoting *)
172173let write_scalar t ?(style = `Any) value =
173173- match (match style with `Any -> Quoting.choose_style value | s -> s) with
174174- | `Plain | `Any ->
175175- write t value
174174+ match match style with `Any -> Quoting.choose_style value | s -> s with
175175+ | `Plain | `Any -> write t value
176176 | `Single_quoted ->
177177 write_char t '\'';
178178 write t (escape_single_quoted value);
···184184 | `Literal ->
185185 write t "|";
186186 write_newline t;
187187- String.split_on_char '\n' value |> List.iter (fun line ->
188188- write_indent t;
189189- write t line;
190190- write_newline t
191191- )
187187+ String.split_on_char '\n' value
188188+ |> List.iter (fun line ->
189189+ write_indent t;
190190+ write t line;
191191+ write_newline t)
192192 | `Folded ->
193193 write t ">";
194194 write_newline t;
195195- String.split_on_char '\n' value |> List.iter (fun line ->
196196- write_indent t;
197197- write t line;
198198- write_newline t
199199- )
195195+ String.split_on_char '\n' value
196196+ |> List.iter (fun line ->
197197+ write_indent t;
198198+ write t line;
199199+ write_newline t)
200200201201(** Write anchor if present *)
202202let write_anchor t anchor =
···221221222222let emit t (ev : Event.t) =
223223 match ev with
224224- | Event.Stream_start _ ->
225225- t.state <- Stream_started
226226-227227- | Event.Stream_end ->
228228- t.state <- Stream_ended
229229-224224+ | Event.Stream_start _ -> t.state <- Stream_started
225225+ | Event.Stream_end -> t.state <- Stream_ended
230226 | Event.Document_start { version; implicit } ->
231227 if not implicit then begin
232228 (match version with
233233- | Some (maj, min) ->
234234- write t (Printf.sprintf "%%YAML %d.%d\n" maj min)
235235- | None -> ());
229229+ | Some (maj, min) -> write t (Printf.sprintf "%%YAML %d.%d\n" maj min)
230230+ | None -> ());
236231 write t "---";
237232 write_newline t
238233 end;
239234 t.state <- Document_started
240240-241235 | Event.Document_end { implicit } ->
242236 if not implicit then begin
243237 write t "...";
244238 write_newline t
245239 end;
246240 t.state <- Document_ended
247247-248241 | Event.Alias { anchor } ->
249242 if t.flow_level > 0 then begin
250243 if t.need_separator then write t ", ";
251244 t.need_separator <- true;
252245 write_char t '*';
253246 write t anchor
254254- end else begin
255255- (match t.state with
256256- | In_block_sequence _ ->
257257- write_indent t;
258258- write t "- *";
259259- write t anchor;
260260- write_newline t
261261- | In_block_mapping_key _ ->
262262- write_indent t;
263263- write_char t '*';
264264- write t anchor;
265265- write t ": ";
266266- t.state <- In_block_mapping_value t.indent
267267- | In_block_mapping_value indent ->
268268- write_char t '*';
269269- write t anchor;
270270- write_newline t;
271271- t.state <- In_block_mapping_key indent
272272- | _ ->
273273- write_char t '*';
274274- write t anchor;
275275- write_newline t)
247247+ end
248248+ else begin
249249+ match t.state with
250250+ | In_block_sequence _ ->
251251+ write_indent t;
252252+ write t "- *";
253253+ write t anchor;
254254+ write_newline t
255255+ | In_block_mapping_key _ ->
256256+ write_indent t;
257257+ write_char t '*';
258258+ write t anchor;
259259+ write t ": ";
260260+ t.state <- In_block_mapping_value t.indent
261261+ | In_block_mapping_value indent ->
262262+ write_char t '*';
263263+ write t anchor;
264264+ write_newline t;
265265+ t.state <- In_block_mapping_key indent
266266+ | _ ->
267267+ write_char t '*';
268268+ write t anchor;
269269+ write_newline t
276270 end
277277-278271 | Event.Scalar { anchor; tag; value; plain_implicit; style; _ } ->
279272 if t.flow_level > 0 then begin
280280- (match t.state with
281281- | In_flow_mapping_key ->
282282- if t.need_separator then write t ", ";
283283- write_anchor t anchor;
284284- write_tag t ~implicit:plain_implicit tag;
285285- write_scalar t ~style value;
286286- write t ": ";
287287- t.need_separator <- false;
288288- t.state <- In_flow_mapping_value
289289- | In_flow_mapping_value ->
290290- if t.need_separator then begin
291291- (* We just finished a nested structure (array/mapping),
273273+ match t.state with
274274+ | In_flow_mapping_key ->
275275+ if t.need_separator then write t ", ";
276276+ write_anchor t anchor;
277277+ write_tag t ~implicit:plain_implicit tag;
278278+ write_scalar t ~style value;
279279+ write t ": ";
280280+ t.need_separator <- false;
281281+ t.state <- In_flow_mapping_value
282282+ | In_flow_mapping_value ->
283283+ if t.need_separator then begin
284284+ (* We just finished a nested structure (array/mapping),
292285 so this scalar is the next key, not a value *)
293293- write t ", ";
294294- write_anchor t anchor;
295295- write_tag t ~implicit:plain_implicit tag;
296296- write_scalar t ~style value;
297297- write t ": ";
298298- t.need_separator <- false;
299299- t.state <- In_flow_mapping_value
300300- end else begin
301301- (* Normal value scalar *)
302302- write_anchor t anchor;
303303- write_tag t ~implicit:plain_implicit tag;
304304- write_scalar t ~style value;
305305- t.need_separator <- true;
306306- t.state <- In_flow_mapping_key
307307- end
308308- | _ ->
309309- if t.need_separator then write t ", ";
310310- t.need_separator <- true;
311311- write_anchor t anchor;
312312- write_tag t ~implicit:plain_implicit tag;
313313- write_scalar t ~style value)
314314- end else begin
286286+ write t ", ";
287287+ write_anchor t anchor;
288288+ write_tag t ~implicit:plain_implicit tag;
289289+ write_scalar t ~style value;
290290+ write t ": ";
291291+ t.need_separator <- false;
292292+ t.state <- In_flow_mapping_value
293293+ end
294294+ else begin
295295+ (* Normal value scalar *)
296296+ write_anchor t anchor;
297297+ write_tag t ~implicit:plain_implicit tag;
298298+ write_scalar t ~style value;
299299+ t.need_separator <- true;
300300+ t.state <- In_flow_mapping_key
301301+ end
302302+ | _ ->
303303+ if t.need_separator then write t ", ";
304304+ t.need_separator <- true;
305305+ write_anchor t anchor;
306306+ write_tag t ~implicit:plain_implicit tag;
307307+ write_scalar t ~style value
308308+ end
309309+ else begin
315310 match t.state with
316311 | In_block_sequence _ ->
317312 write_indent t;
···347342 write_scalar t ~style value;
348343 write_newline t
349344 end
350350-351345 | Event.Sequence_start { anchor; tag; implicit; style } ->
352346 let use_flow = style = `Flow || t.flow_level > 0 in
353347 if t.flow_level > 0 then begin
354354- (match t.state with
355355- | In_flow_mapping_key ->
356356- if t.need_separator then write t ", ";
357357- write_anchor t anchor;
358358- write_tag t ~implicit tag;
359359- write_char t '[';
360360- t.flow_level <- t.flow_level + 1;
361361- t.need_separator <- false;
362362- push_state t In_flow_mapping_value; (* After ] we'll be in value position but sequence handles it *)
363363- t.state <- In_flow_sequence
364364- | In_flow_mapping_value ->
365365- write_anchor t anchor;
366366- write_tag t ~implicit tag;
367367- write_char t '[';
368368- t.flow_level <- t.flow_level + 1;
369369- t.need_separator <- false;
370370- push_state t In_flow_mapping_key;
371371- t.state <- In_flow_sequence
372372- | _ ->
373373- if t.need_separator then write t ", ";
374374- write_anchor t anchor;
375375- write_tag t ~implicit tag;
376376- write_char t '[';
377377- t.flow_level <- t.flow_level + 1;
378378- t.need_separator <- false;
379379- push_state t In_flow_sequence)
380380- end else begin
348348+ match t.state with
349349+ | In_flow_mapping_key ->
350350+ if t.need_separator then write t ", ";
351351+ write_anchor t anchor;
352352+ write_tag t ~implicit tag;
353353+ write_char t '[';
354354+ t.flow_level <- t.flow_level + 1;
355355+ t.need_separator <- false;
356356+ push_state t In_flow_mapping_value;
357357+ (* After ] we'll be in value position but sequence handles it *)
358358+ t.state <- In_flow_sequence
359359+ | In_flow_mapping_value ->
360360+ write_anchor t anchor;
361361+ write_tag t ~implicit tag;
362362+ write_char t '[';
363363+ t.flow_level <- t.flow_level + 1;
364364+ t.need_separator <- false;
365365+ push_state t In_flow_mapping_key;
366366+ t.state <- In_flow_sequence
367367+ | _ ->
368368+ if t.need_separator then write t ", ";
369369+ write_anchor t anchor;
370370+ write_tag t ~implicit tag;
371371+ write_char t '[';
372372+ t.flow_level <- t.flow_level + 1;
373373+ t.need_separator <- false;
374374+ push_state t In_flow_sequence
375375+ end
376376+ else begin
381377 match t.state with
382378 | In_block_sequence _ ->
383379 write_indent t;
···389385 t.flow_level <- t.flow_level + 1;
390386 t.need_separator <- false;
391387 push_state t In_flow_sequence
392392- end else begin
388388+ end
389389+ else begin
393390 write_newline t;
394391 push_state t (In_block_sequence t.indent);
395392 t.indent <- t.indent + t.config.indent
···423420 (* Save key state to return to after flow sequence *)
424421 t.state <- In_block_mapping_key indent;
425422 push_state t In_flow_sequence
426426- end else begin
423423+ end
424424+ else begin
427425 write_newline t;
428426 (* Save key state to return to after nested sequence *)
429427 t.state <- In_block_mapping_key indent;
···438436 t.flow_level <- t.flow_level + 1;
439437 t.need_separator <- false;
440438 push_state t In_flow_sequence
441441- end else begin
439439+ end
440440+ else begin
442441 push_state t (In_block_sequence t.indent);
443442 t.state <- In_block_sequence t.indent
444443 end
445444 end
446446-447445 | Event.Sequence_end ->
448446 if t.flow_level > 0 then begin
449447 write_char t ']';
···451449 t.need_separator <- true;
452450 pop_state t;
453451 (* Write newline if returning to block context *)
454454- (match t.state with
455455- | In_block_mapping_key _ | In_block_sequence _ -> write_newline t
456456- | _ -> ())
457457- end else begin
452452+ match t.state with
453453+ | In_block_mapping_key _ | In_block_sequence _ -> write_newline t
454454+ | _ -> ()
455455+ end
456456+ else begin
458457 t.indent <- t.indent - t.config.indent;
459458 pop_state t
460459 end
461461-462460 | Event.Mapping_start { anchor; tag; implicit; style } ->
463461 let use_flow = style = `Flow || t.flow_level > 0 in
464462 if t.flow_level > 0 then begin
465465- (match t.state with
466466- | In_flow_mapping_key ->
467467- if t.need_separator then write t ", ";
468468- write_anchor t anchor;
469469- write_tag t ~implicit tag;
470470- write_char t '{';
471471- t.flow_level <- t.flow_level + 1;
472472- t.need_separator <- false;
473473- push_state t In_flow_mapping_value;
474474- t.state <- In_flow_mapping_key
475475- | In_flow_mapping_value ->
476476- write_anchor t anchor;
477477- write_tag t ~implicit tag;
478478- write_char t '{';
479479- t.flow_level <- t.flow_level + 1;
480480- t.need_separator <- false;
481481- push_state t In_flow_mapping_key;
482482- t.state <- In_flow_mapping_key
483483- | _ ->
484484- if t.need_separator then write t ", ";
485485- write_anchor t anchor;
486486- write_tag t ~implicit tag;
487487- write_char t '{';
488488- t.flow_level <- t.flow_level + 1;
489489- t.need_separator <- false;
490490- push_state t In_flow_mapping_key)
491491- end else begin
463463+ match t.state with
464464+ | In_flow_mapping_key ->
465465+ if t.need_separator then write t ", ";
466466+ write_anchor t anchor;
467467+ write_tag t ~implicit tag;
468468+ write_char t '{';
469469+ t.flow_level <- t.flow_level + 1;
470470+ t.need_separator <- false;
471471+ push_state t In_flow_mapping_value;
472472+ t.state <- In_flow_mapping_key
473473+ | In_flow_mapping_value ->
474474+ write_anchor t anchor;
475475+ write_tag t ~implicit tag;
476476+ write_char t '{';
477477+ t.flow_level <- t.flow_level + 1;
478478+ t.need_separator <- false;
479479+ push_state t In_flow_mapping_key;
480480+ t.state <- In_flow_mapping_key
481481+ | _ ->
482482+ if t.need_separator then write t ", ";
483483+ write_anchor t anchor;
484484+ write_tag t ~implicit tag;
485485+ write_char t '{';
486486+ t.flow_level <- t.flow_level + 1;
487487+ t.need_separator <- false;
488488+ push_state t In_flow_mapping_key
489489+ end
490490+ else begin
492491 match t.state with
493492 | In_block_sequence _ ->
494493 write_indent t;
···500499 t.flow_level <- t.flow_level + 1;
501500 t.need_separator <- false;
502501 push_state t In_flow_mapping_key
503503- end else begin
502502+ end
503503+ else begin
504504 (* Don't write newline - first key goes on same line as "- " *)
505505 push_state t (In_block_sequence t.indent);
506506 t.indent <- t.indent + t.config.indent;
···535535 (* Save key state to return to after flow mapping *)
536536 t.state <- In_block_mapping_key indent;
537537 push_state t In_flow_mapping_key
538538- end else begin
538538+ end
539539+ else begin
539540 write_newline t;
540541 (* Save key state to return to after nested mapping *)
541542 t.state <- In_block_mapping_key indent;
···550551 t.flow_level <- t.flow_level + 1;
551552 t.need_separator <- false;
552553 push_state t In_flow_mapping_key
553553- end else begin
554554+ end
555555+ else begin
554556 push_state t (In_block_mapping_key t.indent);
555557 t.state <- In_block_mapping_key t.indent
556558 end
557559 end
558558-559560 | Event.Mapping_end ->
560561 if t.flow_level > 0 then begin
561562 write_char t '}';
···563564 t.need_separator <- true;
564565 pop_state t;
565566 (* Write newline if returning to block context *)
566566- (match t.state with
567567- | In_block_mapping_key _ | In_block_sequence _ -> write_newline t
568568- | _ -> ())
569569- end else begin
567567+ match t.state with
568568+ | In_block_mapping_key _ | In_block_sequence _ -> write_newline t
569569+ | _ -> ()
570570+ end
571571+ else begin
570572 t.indent <- t.indent - t.config.indent;
571573 pop_state t
572574 end
573575574574-(** Access to the underlying buffer for advanced use.
575575- Returns None if emitter is writing to a Writer instead of Buffer. *)
576576+(** Access to the underlying buffer for advanced use. Returns None if emitter is
577577+ writing to a Writer instead of Buffer. *)
576578let buffer t =
577577- match t.sink with
578578- | Buffer_sink buf -> Some buf
579579- | Writer_sink _ -> None
579579+ match t.sink with Buffer_sink buf -> Some buf | Writer_sink _ -> None
580580581581(** Get config *)
582582let config t = t.config
583583584584(** Check if emitter is writing to a Writer *)
585585let is_streaming t =
586586- match t.sink with
587587- | Writer_sink _ -> true
588588- | Buffer_sink _ -> false
586586+ match t.sink with Writer_sink _ -> true | Buffer_sink _ -> false
589587590588(** Flush the writer sink (no-op for buffer sink) *)
591589let flush t =
+12-20
lib/encoding.ml
···5566(** Character encoding detection and handling *)
7788-type t = [
99- | `Utf8
1010- | `Utf16be
1111- | `Utf16le
1212- | `Utf32be
1313- | `Utf32le
1414-]
88+type t = [ `Utf8 | `Utf16be | `Utf16le | `Utf32be | `Utf32le ]
1591610let to_string = function
1711 | `Utf8 -> "UTF-8"
···2014 | `Utf32be -> "UTF-32BE"
2115 | `Utf32le -> "UTF-32LE"
22162323-let pp fmt t =
2424- Format.pp_print_string fmt (to_string t)
1717+let pp fmt t = Format.pp_print_string fmt (to_string t)
25182626-(** Detect encoding from BOM or first bytes.
2727- Returns (encoding, bom_length) *)
1919+(** Detect encoding from BOM or first bytes. Returns (encoding, bom_length) *)
2820let detect s =
2921 let len = String.length s in
3022 if len = 0 then (`Utf8, 0)
···3527 let b3 = if len > 3 then Char.code s.[3] else 0 in
3628 match (b0, b1, b2, b3) with
3729 (* BOM patterns *)
3838- | (0xEF, 0xBB, 0xBF, _) -> (`Utf8, 3)
3939- | (0xFE, 0xFF, _, _) -> (`Utf16be, 2)
4040- | (0xFF, 0xFE, 0x00, 0x00) -> (`Utf32le, 4)
4141- | (0xFF, 0xFE, _, _) -> (`Utf16le, 2)
4242- | (0x00, 0x00, 0xFE, 0xFF) -> (`Utf32be, 4)
3030+ | 0xEF, 0xBB, 0xBF, _ -> (`Utf8, 3)
3131+ | 0xFE, 0xFF, _, _ -> (`Utf16be, 2)
3232+ | 0xFF, 0xFE, 0x00, 0x00 -> (`Utf32le, 4)
3333+ | 0xFF, 0xFE, _, _ -> (`Utf16le, 2)
3434+ | 0x00, 0x00, 0xFE, 0xFF -> (`Utf32be, 4)
4335 (* Content pattern detection (no BOM) *)
4444- | (0x00, 0x00, 0x00, b3) when b3 <> 0x00 -> (`Utf32be, 0)
4545- | (b0, 0x00, 0x00, 0x00) when b0 <> 0x00 -> (`Utf32le, 0)
4646- | (0x00, b1, _, _) when b1 <> 0x00 -> (`Utf16be, 0)
4747- | (b0, 0x00, _, _) when b0 <> 0x00 -> (`Utf16le, 0)
3636+ | 0x00, 0x00, 0x00, b3 when b3 <> 0x00 -> (`Utf32be, 0)
3737+ | b0, 0x00, 0x00, 0x00 when b0 <> 0x00 -> (`Utf32le, 0)
3838+ | 0x00, b1, _, _ when b1 <> 0x00 -> (`Utf16be, 0)
3939+ | b0, 0x00, _, _ when b0 <> 0x00 -> (`Utf16le, 0)
4840 | _ -> (`Utf8, 0)
49415042let equal a b = a = b
+135-105
lib/error.ml
···7788 Comprehensive error reporting for YAML parsing and emission.
991010- This module provides detailed error types that correspond to various
1111- failure modes in YAML processing, as specified in the
1010+ This module provides detailed error types that correspond to various failure
1111+ modes in YAML processing, as specified in the
1212 {{:https://yaml.org/spec/1.2.2/}YAML 1.2.2 specification}.
13131414 Each error includes:
···1717 - A context stack showing where the error occurred
1818 - Optional source text for error display
19192020- See also {{:https://yaml.org/spec/1.2.2/#31-processes}Section 3.1 (Processes)}
2121- for background on the YAML processing model. *)
2020+ See also
2121+ {{:https://yaml.org/spec/1.2.2/#31-processes}Section 3.1 (Processes)} for
2222+ background on the YAML processing model. *)
22232324(** {2 Error Classification}
2425···3132 (* Scanner errors - see {{:https://yaml.org/spec/1.2.2/#51-character-set}Section 5.1} *)
3233 | Unexpected_character of char
3334 (** Invalid character in input. See
3434- {{:https://yaml.org/spec/1.2.2/#51-character-set}Section 5.1 (Character Set)}. *)
3535- | Unexpected_eof
3636- (** Premature end of input. *)
3535+ {{:https://yaml.org/spec/1.2.2/#51-character-set}Section 5.1
3636+ (Character Set)}. *)
3737+ | Unexpected_eof (** Premature end of input. *)
3738 | Invalid_escape_sequence of string
3839 (** Invalid escape in double-quoted string. See
3939- {{:https://yaml.org/spec/1.2.2/#57-escaped-characters}Section 5.7 (Escaped Characters)}. *)
4040+ {{:https://yaml.org/spec/1.2.2/#57-escaped-characters}Section 5.7
4141+ (Escaped Characters)}. *)
4042 | Invalid_unicode_escape of string
4143 (** Invalid Unicode escape sequence (\uXXXX or \UXXXXXXXX). *)
4244 | Invalid_hex_escape of string
4345 (** Invalid hexadecimal escape sequence (\xXX). *)
4446 | Invalid_tag of string
4547 (** Malformed tag syntax. See
4646- {{:https://yaml.org/spec/1.2.2/#681-node-tags}Section 6.8.1 (Node Tags)}. *)
4848+ {{:https://yaml.org/spec/1.2.2/#681-node-tags}Section 6.8.1 (Node
4949+ Tags)}. *)
4750 | Invalid_anchor of string
4851 (** Malformed anchor name. See
4949- {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section 3.2.2.2 (Anchors and Aliases)}. *)
5252+ {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section
5353+ 3.2.2.2 (Anchors and Aliases)}. *)
5054 | Invalid_alias of string
5155 (** Malformed alias reference. See
5252- {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section 3.2.2.2 (Anchors and Aliases)}. *)
5656+ {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section
5757+ 3.2.2.2 (Anchors and Aliases)}. *)
5358 | Invalid_comment
5459 (** Comment not properly separated from content. See
5555- {{:https://yaml.org/spec/1.2.2/#62-comments}Section 6.2 (Comments)}. *)
6060+ {{:https://yaml.org/spec/1.2.2/#62-comments}Section 6.2 (Comments)}.
6161+ *)
5662 | Unclosed_single_quote
5763 (** Unterminated single-quoted scalar. See
5858- {{:https://yaml.org/spec/1.2.2/#72-single-quoted-style}Section 7.2 (Single-Quoted Style)}. *)
6464+ {{:https://yaml.org/spec/1.2.2/#72-single-quoted-style}Section 7.2
6565+ (Single-Quoted Style)}. *)
5966 | Unclosed_double_quote
6067 (** Unterminated double-quoted scalar. See
6161- {{:https://yaml.org/spec/1.2.2/#73-double-quoted-style}Section 7.3 (Double-Quoted Style)}. *)
6868+ {{:https://yaml.org/spec/1.2.2/#73-double-quoted-style}Section 7.3
6969+ (Double-Quoted Style)}. *)
6270 | Unclosed_flow_sequence
6371 (** Missing closing bracket \] for flow sequence. See
6464- {{:https://yaml.org/spec/1.2.2/#742-flow-sequences}Section 7.4.2 (Flow Sequences)}. *)
7272+ {{:https://yaml.org/spec/1.2.2/#742-flow-sequences}Section 7.4.2 (Flow
7373+ Sequences)}. *)
6574 | Unclosed_flow_mapping
6675 (** Missing closing brace \} for flow mapping. See
6767- {{:https://yaml.org/spec/1.2.2/#743-flow-mappings}Section 7.4.3 (Flow Mappings)}. *)
7676+ {{:https://yaml.org/spec/1.2.2/#743-flow-mappings}Section 7.4.3 (Flow
7777+ Mappings)}. *)
6878 | Invalid_indentation of int * int
6979 (** Incorrect indentation level (expected, got). See
7070- {{:https://yaml.org/spec/1.2.2/#61-indentation-spaces}Section 6.1 (Indentation Spaces)}. *)
8080+ {{:https://yaml.org/spec/1.2.2/#61-indentation-spaces}Section 6.1
8181+ (Indentation Spaces)}. *)
7182 | Invalid_flow_indentation
7283 (** Content in flow collection must be indented. See
7373- {{:https://yaml.org/spec/1.2.2/#74-flow-styles}Section 7.4 (Flow Styles)}. *)
8484+ {{:https://yaml.org/spec/1.2.2/#74-flow-styles}Section 7.4 (Flow
8585+ Styles)}. *)
7486 | Tab_in_indentation
7587 (** Tab character used for indentation (only spaces allowed). See
7676- {{:https://yaml.org/spec/1.2.2/#61-indentation-spaces}Section 6.1 (Indentation Spaces)}. *)
8888+ {{:https://yaml.org/spec/1.2.2/#61-indentation-spaces}Section 6.1
8989+ (Indentation Spaces)}. *)
7790 | Invalid_block_scalar_header of string
7891 (** Malformed block scalar header (| or >). See
7979- {{:https://yaml.org/spec/1.2.2/#81-block-scalar-styles}Section 8.1 (Block Scalar Styles)}. *)
9292+ {{:https://yaml.org/spec/1.2.2/#81-block-scalar-styles}Section 8.1
9393+ (Block Scalar Styles)}. *)
8094 | Invalid_quoted_scalar_indentation of string
8195 (** Incorrect indentation in quoted scalar. *)
8296 | Invalid_directive of string
8397 (** Malformed directive. See
8484- {{:https://yaml.org/spec/1.2.2/#68-directives}Section 6.8 (Directives)}. *)
9898+ {{:https://yaml.org/spec/1.2.2/#68-directives}Section 6.8
9999+ (Directives)}. *)
85100 | Invalid_yaml_version of string
86101 (** Unsupported YAML version in %YAML directive. See
8787- {{:https://yaml.org/spec/1.2.2/#681-yaml-directives}Section 6.8.1 (YAML Directives)}. *)
102102+ {{:https://yaml.org/spec/1.2.2/#681-yaml-directives}Section 6.8.1
103103+ (YAML Directives)}. *)
88104 | Invalid_tag_directive of string
89105 (** Malformed %TAG directive. See
9090- {{:https://yaml.org/spec/1.2.2/#682-tag-directives}Section 6.8.2 (TAG Directives)}. *)
106106+ {{:https://yaml.org/spec/1.2.2/#682-tag-directives}Section 6.8.2 (TAG
107107+ Directives)}. *)
91108 | Reserved_directive of string
92109 (** Reserved directive name. See
9393- {{:https://yaml.org/spec/1.2.2/#683-reserved-directives}Section 6.8.3 (Reserved Directives)}. *)
110110+ {{:https://yaml.org/spec/1.2.2/#683-reserved-directives}Section 6.8.3
111111+ (Reserved Directives)}. *)
94112 | Illegal_flow_key_line
95113 (** Key and colon must be on same line in flow context. See
9696- {{:https://yaml.org/spec/1.2.2/#743-flow-mappings}Section 7.4.3 (Flow Mappings)}. *)
114114+ {{:https://yaml.org/spec/1.2.2/#743-flow-mappings}Section 7.4.3 (Flow
115115+ Mappings)}. *)
97116 | Block_sequence_disallowed
98117 (** Block sequence entries not allowed in this context. See
9999- {{:https://yaml.org/spec/1.2.2/#82-block-collection-styles}Section 8.2 (Block Collection Styles)}. *)
100100-118118+ {{:https://yaml.org/spec/1.2.2/#82-block-collection-styles}Section 8.2
119119+ (Block Collection Styles)}. *)
101120 (* Parser errors - see {{:https://yaml.org/spec/1.2.2/#3-processing-yaml-information}Section 3 (Processing)} *)
102102- | Unexpected_token of string
103103- (** Unexpected token in event stream. *)
121121+ | Unexpected_token of string (** Unexpected token in event stream. *)
104122 | Expected_document_start
105123 (** Expected document start marker (---). See
106106- {{:https://yaml.org/spec/1.2.2/#912-document-markers}Section 9.1.2 (Document Markers)}. *)
124124+ {{:https://yaml.org/spec/1.2.2/#912-document-markers}Section 9.1.2
125125+ (Document Markers)}. *)
107126 | Expected_document_end
108127 (** Expected document end marker (...). See
109109- {{:https://yaml.org/spec/1.2.2/#912-document-markers}Section 9.1.2 (Document Markers)}. *)
128128+ {{:https://yaml.org/spec/1.2.2/#912-document-markers}Section 9.1.2
129129+ (Document Markers)}. *)
110130 | Expected_block_entry
111131 (** Expected block sequence entry marker (-). See
112112- {{:https://yaml.org/spec/1.2.2/#821-block-sequences}Section 8.2.1 (Block Sequences)}. *)
132132+ {{:https://yaml.org/spec/1.2.2/#821-block-sequences}Section 8.2.1
133133+ (Block Sequences)}. *)
113134 | Expected_key
114135 (** Expected mapping key. See
115115- {{:https://yaml.org/spec/1.2.2/#822-block-mappings}Section 8.2.2 (Block Mappings)}. *)
136136+ {{:https://yaml.org/spec/1.2.2/#822-block-mappings}Section 8.2.2
137137+ (Block Mappings)}. *)
116138 | Expected_value
117139 (** Expected mapping value after colon. See
118118- {{:https://yaml.org/spec/1.2.2/#822-block-mappings}Section 8.2.2 (Block Mappings)}. *)
119119- | Expected_node
120120- (** Expected a YAML node. *)
121121- | Expected_scalar
122122- (** Expected a scalar value. *)
140140+ {{:https://yaml.org/spec/1.2.2/#822-block-mappings}Section 8.2.2
141141+ (Block Mappings)}. *)
142142+ | Expected_node (** Expected a YAML node. *)
143143+ | Expected_scalar (** Expected a scalar value. *)
123144 | Expected_sequence_end
124145 (** Expected closing bracket \] for flow sequence. See
125125- {{:https://yaml.org/spec/1.2.2/#742-flow-sequences}Section 7.4.2 (Flow Sequences)}. *)
146146+ {{:https://yaml.org/spec/1.2.2/#742-flow-sequences}Section 7.4.2 (Flow
147147+ Sequences)}. *)
126148 | Expected_mapping_end
127149 (** Expected closing brace \} for flow mapping. See
128128- {{:https://yaml.org/spec/1.2.2/#743-flow-mappings}Section 7.4.3 (Flow Mappings)}. *)
150150+ {{:https://yaml.org/spec/1.2.2/#743-flow-mappings}Section 7.4.3 (Flow
151151+ Mappings)}. *)
129152 | Duplicate_anchor of string
130153 (** Anchor name defined multiple times. See
131131- {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section 3.2.2.2 (Anchors and Aliases)}. *)
154154+ {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section
155155+ 3.2.2.2 (Anchors and Aliases)}. *)
132156 | Undefined_alias of string
133157 (** Alias references non-existent anchor. See
134134- {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section 3.2.2.2 (Anchors and Aliases)}. *)
158158+ {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section
159159+ 3.2.2.2 (Anchors and Aliases)}. *)
135160 | Alias_cycle of string
136161 (** Circular reference in alias chain. See
137137- {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section 3.2.2.2 (Anchors and Aliases)}. *)
162162+ {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section
163163+ 3.2.2.2 (Anchors and Aliases)}. *)
138164 | Multiple_documents
139165 (** Multiple documents found when single document expected. See
140140- {{:https://yaml.org/spec/1.2.2/#912-document-markers}Section 9.1.2 (Document Markers)}. *)
166166+ {{:https://yaml.org/spec/1.2.2/#912-document-markers}Section 9.1.2
167167+ (Document Markers)}. *)
141168 | Mapping_key_too_long
142169 (** Mapping key exceeds maximum length (1024 characters). *)
143143-144170 (* Loader errors - see {{:https://yaml.org/spec/1.2.2/#31-processes}Section 3.1 (Processes)} *)
145171 | Invalid_scalar_conversion of string * string
146146- (** Cannot convert scalar value to target type (value, target type).
147147- See {{:https://yaml.org/spec/1.2.2/#103-core-schema}Section 10.3 (Core Schema)}. *)
172172+ (** Cannot convert scalar value to target type (value, target type). See
173173+ {{:https://yaml.org/spec/1.2.2/#103-core-schema}Section 10.3 (Core
174174+ Schema)}. *)
148175 | Type_mismatch of string * string
149176 (** Value has wrong type for operation (expected, got). *)
150177 | Unresolved_alias of string
151151- (** Alias encountered during conversion but not resolved.
152152- See {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section 3.2.2.2 (Anchors and Aliases)}. *)
153153- | Key_not_found of string
154154- (** Mapping key not found. *)
178178+ (** Alias encountered during conversion but not resolved. See
179179+ {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section
180180+ 3.2.2.2 (Anchors and Aliases)}. *)
181181+ | Key_not_found of string (** Mapping key not found. *)
155182 | Alias_expansion_node_limit of int
156156- (** Alias expansion exceeded maximum node count (protection against billion laughs attack).
157157- See {{:https://yaml.org/spec/1.2.2/#321-processes}Section 3.2.1 (Processes)}.
183183+ (** Alias expansion exceeded maximum node count (protection against
184184+ billion laughs attack). See
185185+ {{:https://yaml.org/spec/1.2.2/#321-processes}Section 3.2.1
186186+ (Processes)}.
158187159159- The "billion laughs attack" (also known as an XML bomb) is a denial-of-service
160160- attack where a small YAML document expands to enormous size through recursive
161161- alias expansion. This limit prevents such attacks. *)
188188+ The "billion laughs attack" (also known as an XML bomb) is a
189189+ denial-of-service attack where a small YAML document expands to
190190+ enormous size through recursive alias expansion. This limit prevents
191191+ such attacks. *)
162192 | Alias_expansion_depth_limit of int
163163- (** Alias expansion exceeded maximum nesting depth (protection against deeply nested aliases).
164164- See {{:https://yaml.org/spec/1.2.2/#321-processes}Section 3.2.1 (Processes)}. *)
165165-193193+ (** Alias expansion exceeded maximum nesting depth (protection against
194194+ deeply nested aliases). See
195195+ {{:https://yaml.org/spec/1.2.2/#321-processes}Section 3.2.1
196196+ (Processes)}. *)
166197 (* Emitter errors *)
167198 | Invalid_encoding of string
168199 (** Invalid character encoding specified. See
169169- {{:https://yaml.org/spec/1.2.2/#51-character-set}Section 5.1 (Character Set)}. *)
200200+ {{:https://yaml.org/spec/1.2.2/#51-character-set}Section 5.1
201201+ (Character Set)}. *)
170202 | Scalar_contains_invalid_chars of string
171203 (** Scalar contains characters invalid for chosen style. *)
172172- | Anchor_not_set
173173- (** Attempted to emit alias before anchor was defined. *)
204204+ | Anchor_not_set (** Attempted to emit alias before anchor was defined. *)
174205 | Invalid_state of string
175206 (** Emitter in invalid state for requested operation. *)
176176-177207 (* Generic *)
178178- | Custom of string
179179- (** Custom error message. *)
208208+ | Custom of string (** Custom error message. *)
180209181181-(** {2 Error Value}
182182-183183- Full error information including classification, location, and context. *)
184210type t = {
185185- kind : kind;
186186- (** The specific error classification. *)
211211+ kind : kind; (** The specific error classification. *)
187212 span : Span.t option;
188213 (** Source location where the error occurred (if available). *)
189214 context : string list;
···191216 source : string option;
192217 (** Source text for displaying the error in context. *)
193218}
219219+(** {2 Error Value}
194220221221+ Full error information including classification, location, and context. *)
222222+223223+exception Yamlrw_error of t
195224(** {2 Exception}
196225197226 The main exception type raised by all yamlrw operations.
198227199199- All parsing, loading, and emitting errors are reported by raising
200200- this exception with detailed error information. *)
201201-exception Yamlrw_error of t
228228+ All parsing, loading, and emitting errors are reported by raising this
229229+ exception with detailed error information. *)
202230203231let () =
204232 Printexc.register_printer (function
205233 | Yamlrw_error e ->
206206- let loc = match e.span with
234234+ let loc =
235235+ match e.span with
207236 | None -> ""
208237 | Some span -> " at " ^ Span.to_string span
209238 in
210210- Some (Printf.sprintf "Yamlrw_error: %s%s"
211211- (match e.kind with Custom s -> s | _ -> "error") loc)
239239+ Some
240240+ (Printf.sprintf "Yamlrw_error: %s%s"
241241+ (match e.kind with Custom s -> s | _ -> "error")
242242+ loc)
212243 | _ -> None)
213244214245(** {2 Error Construction} *)
···219250 @param context Context stack (defaults to empty)
220251 @param source Source text
221252 @param kind Error classification *)
222222-let make ?span ?(context=[]) ?source kind =
223223- { kind; span; context; source }
253253+let make ?span ?(context = []) ?source kind = { kind; span; context; source }
224254225255(** [raise ?span ?context ?source kind] constructs and raises an error.
226256···248278 @param span Source span
249279 @param kind Error classification
250280 @raise Yamlrw_error *)
251251-let raise_span span kind =
252252- raise ~span kind
281281+let raise_span span kind = raise ~span kind
253282254254-(** [with_context ctx f] executes [f ()] and adds [ctx] to any raised error's context.
283283+(** [with_context ctx f] executes [f ()] and adds [ctx] to any raised error's
284284+ context.
255285256286 This is useful for tracking the processing path through nested structures.
257287258288 @param ctx Context description (e.g., "parsing mapping key")
259289 @param f Function to execute *)
260290let with_context ctx f =
261261- try f () with
262262- | Yamlrw_error e ->
263263- Stdlib.raise (Yamlrw_error { e with context = ctx :: e.context })
291291+ try f ()
292292+ with Yamlrw_error e ->
293293+ Stdlib.raise (Yamlrw_error { e with context = ctx :: e.context })
264294265295(** {2 Error Formatting} *)
266296···274304 | Invalid_tag s -> Printf.sprintf "invalid tag: %s" s
275305 | Invalid_anchor s -> Printf.sprintf "invalid anchor: %s" s
276306 | Invalid_alias s -> Printf.sprintf "invalid alias: %s" s
277277- | Invalid_comment -> "comments must be separated from other tokens by whitespace"
307307+ | Invalid_comment ->
308308+ "comments must be separated from other tokens by whitespace"
278309 | Unclosed_single_quote -> "unclosed single quote"
279310 | Unclosed_double_quote -> "unclosed double quote"
280311 | Unclosed_flow_sequence -> "unclosed flow sequence '['"
···285316 | Tab_in_indentation -> "tab character in indentation"
286317 | Invalid_block_scalar_header s ->
287318 Printf.sprintf "invalid block scalar header: %s" s
288288- | Invalid_quoted_scalar_indentation s ->
289289- Printf.sprintf "%s" s
319319+ | Invalid_quoted_scalar_indentation s -> Printf.sprintf "%s" s
290320 | Invalid_directive s -> Printf.sprintf "invalid directive: %s" s
291321 | Invalid_yaml_version s -> Printf.sprintf "invalid YAML version: %s" s
292322 | Invalid_tag_directive s -> Printf.sprintf "invalid TAG directive: %s" s
293323 | Reserved_directive s -> Printf.sprintf "reserved directive: %s" s
294294- | Illegal_flow_key_line -> "key and ':' must be on the same line in flow context"
295295- | Block_sequence_disallowed -> "block sequence entries are not allowed in this context"
324324+ | Illegal_flow_key_line ->
325325+ "key and ':' must be on the same line in flow context"
326326+ | Block_sequence_disallowed ->
327327+ "block sequence entries are not allowed in this context"
296328 | Unexpected_token s -> Printf.sprintf "unexpected token: %s" s
297329 | Expected_document_start -> "expected document start '---'"
298330 | Expected_document_end -> "expected document end '...'"
···329361330362 Includes error kind, source location (if available), and context stack. *)
331363let to_string t =
332332- let loc = match t.span with
333333- | None -> ""
334334- | Some span -> " at " ^ Span.to_string span
364364+ let loc =
365365+ match t.span with None -> "" | Some span -> " at " ^ Span.to_string span
335366 in
336336- let ctx = match t.context with
367367+ let ctx =
368368+ match t.context with
337369 | [] -> ""
338370 | ctxs -> " (in " ^ String.concat " > " (List.rev ctxs) ^ ")"
339371 in
340372 kind_to_string t.kind ^ loc ^ ctx
341373342374(** [pp fmt t] pretty-prints an error to a formatter. *)
343343-let pp fmt t =
344344- Format.fprintf fmt "Yamlrw error: %s" (to_string t)
375375+let pp fmt t = Format.fprintf fmt "Yamlrw error: %s" (to_string t)
345376346377(** [pp_with_source ~source fmt t] pretty-prints an error with source context.
347378348348- Shows the error message followed by the relevant source line with
349349- a caret (^) pointing to the error location.
379379+ Shows the error message followed by the relevant source line with a caret
380380+ (^) pointing to the error location.
350381351382 @param source The source text
352383 @param fmt Output formatter
353384 @param t The error to display *)
354385let pp_with_source ~source fmt t =
355355-let extract_line source line_num =
356356- let lines = String.split_on_char '\n' source in
357357- if line_num >= 1 && line_num <= List.length lines then
358358- Some (List.nth lines (line_num - 1))
359359- else
360360- None
386386+ let extract_line source line_num =
387387+ let lines = String.split_on_char '\n' source in
388388+ if line_num >= 1 && line_num <= List.length lines then
389389+ Some (List.nth lines (line_num - 1))
390390+ else None
361391 in
362392363393 pp fmt t;
364394 match t.span with
365395 | None -> ()
366366- | Some span ->
396396+ | Some span -> (
367397 match extract_line source span.start.line with
368398 | None -> ()
369399 | Some line ->
370400 Format.fprintf fmt "\n %d | %s\n" span.start.line line;
371401 let padding = String.make (span.start.column - 1) ' ' in
372372- Format.fprintf fmt " | %s^" padding
402402+ Format.fprintf fmt " | %s^" padding)
···5566(** Character input source with lookahead, based on Bytes.Reader.t
7788- This module wraps a bytesrw [Bytes.Reader.t] to provide character-by-character
99- access with lookahead for the YAML scanner. Uses bytesrw's sniff and push_back
1010- for efficient lookahead without excessive copying.
88+ This module wraps a bytesrw [Bytes.Reader.t] to provide
99+ character-by-character access with lookahead for the YAML scanner. Uses
1010+ bytesrw's sniff and push_back for efficient lookahead without excessive
1111+ copying.
11121213 The same input type works with any reader source: strings, files, channels,
1314 or streaming sources like Eio. *)
14151516open Bytesrw
16171717-(** Re-export character classification *)
1818include Char_class
1919+(** Re-export character classification *)
19202021type t = {
2122 reader : Bytes.Reader.t;
2222- mutable current_slice : Bytes.Slice.t option; (** Current slice being consumed *)
2323- mutable slice_pos : int; (** Position within current slice *)
2424- mutable position : Position.t; (** Line/column tracking *)
2323+ mutable current_slice : Bytes.Slice.t option;
2424+ (** Current slice being consumed *)
2525+ mutable slice_pos : int; (** Position within current slice *)
2626+ mutable position : Position.t; (** Line/column tracking *)
2527}
26282729(** Ensure we have a current slice. Returns true if data available. *)
···3335 if Bytes.Slice.is_eod slice then begin
3436 t.current_slice <- None;
3537 false
3636- end else begin
3838+ end
3939+ else begin
3740 t.current_slice <- Some slice;
3841 t.slice_pos <- 0;
3942 true
···50535154(** Create input from a Bytes.Reader.t *)
5255let of_reader ?(initial_position = Position.initial) reader =
5353- let t = {
5454- reader;
5555- current_slice = None;
5656- slice_pos = 0;
5757- position = initial_position;
5858- } in
5656+ let t =
5757+ { reader; current_slice = None; slice_pos = 0; position = initial_position }
5858+ in
5959 (* Use sniff for BOM detection - this is exactly what sniff is for *)
6060 let sample = Bytes.Reader.sniff 4 t.reader in
6161 let bom_len =
6262- if String.length sample >= 3 &&
6363- sample.[0] = '\xEF' &&
6464- sample.[1] = '\xBB' &&
6565- sample.[2] = '\xBF'
6666- then 3 (* UTF-8 BOM *)
6262+ if
6363+ String.length sample >= 3
6464+ && sample.[0] = '\xEF'
6565+ && sample.[1] = '\xBB'
6666+ && sample.[2] = '\xBF'
6767+ then 3 (* UTF-8 BOM *)
6768 else 0
6869 in
6970 (* Skip BOM if present *)
7070- if bom_len > 0 then
7171- Bytes.Reader.skip bom_len t.reader;
7171+ if bom_len > 0 then Bytes.Reader.skip bom_len t.reader;
7272 t
73737474(** Create input from a string *)
···7777 of_reader reader
78787979let position t = t.position
8080-8181-let is_eof t =
8282- not (ensure_slice t)
8383-8484-let peek t =
8585- if ensure_slice t then
8686- peek_current t
8787- else
8888- None
8080+let is_eof t = not (ensure_slice t)
8181+let peek t = if ensure_slice t then peek_current t else None
89829083let peek_exn t =
9184 match peek t with
···112105 let sample_offset = n - slice_remaining in
113106 if sample_offset < String.length sample then
114107 Some sample.[sample_offset]
115115- else
116116- None
108108+ else None
117109 end
118118- | None ->
119119- if n < String.length sample then
120120- Some sample.[n]
121121- else
122122- None
110110+ | None -> if n < String.length sample then Some sample.[n] else None
123111 end
124112125113(** Peek at up to n characters as a string *)
···139127 let needed_from_reader = n - slice_remaining in
140128 let sample = Bytes.Reader.sniff needed_from_reader t.reader in
141129 let buf = Buffer.create n in
142142- Buffer.add_subbytes buf slice_bytes (slice_first + t.slice_pos) slice_remaining;
130130+ Buffer.add_subbytes buf slice_bytes
131131+ (slice_first + t.slice_pos)
132132+ slice_remaining;
143133 Buffer.add_string buf sample;
144134 Buffer.contents buf
145135 end
146146- | None ->
147147- if ensure_slice t then
148148- peek_string t n
149149- else
150150- ""
136136+ | None -> if ensure_slice t then peek_string t n else ""
151137 end
152138153139(** Consume next character *)
···161147 t.slice_pos <- t.slice_pos + 1;
162148 t.position <- Position.advance_char c t.position;
163149 (* Check if we've exhausted this slice *)
164164- if t.slice_pos >= Bytes.Slice.length slice then
165165- t.current_slice <- None;
150150+ if t.slice_pos >= Bytes.Slice.length slice then t.current_slice <- None;
166151 Some c
167152 | None -> None
168168- end else
169169- None
153153+ end
154154+ else None
170155171156let next_exn t =
172157 match next t with
···181166let skip_while t pred =
182167 let rec loop () =
183168 match peek t with
184184- | Some c when pred c -> ignore (next t); loop ()
169169+ | Some c when pred c ->
170170+ ignore (next t);
171171+ loop ()
185172 | _ -> ()
186173 in
187174 loop ()
188175189176(** Check if next char satisfies predicate *)
190190-let next_is pred t =
191191- match peek t with
192192- | None -> false
193193- | Some c -> pred c
177177+let next_is pred t = match peek t with None -> false | Some c -> pred c
194178195179let next_is_break t = next_is is_break t
196180let next_is_blank t = next_is is_blank t
···209193 if len < 3 then false
210194 else
211195 let prefix = String.sub s 0 3 in
212212- (prefix = "---" || prefix = "...") &&
213213- (len = 3 || is_whitespace s.[3])
196196+ (prefix = "---" || prefix = "...") && (len = 3 || is_whitespace s.[3])
214197 end
215198216199(** Consume line break, handling \r\n as single break *)
217200let consume_break t =
218201 match peek t with
219219- | Some '\r' ->
202202+ | Some '\r' -> (
220203 ignore (next t);
221221- (match peek t with
222222- | Some '\n' -> ignore (next t)
223223- | _ -> ())
224224- | Some '\n' ->
225225- ignore (next t)
204204+ match peek t with Some '\n' -> ignore (next t) | _ -> ())
205205+ | Some '\n' -> ignore (next t)
226206 | _ -> ()
227207228208(** Get remaining content from current position *)
···230210 let buf = Buffer.create 256 in
231211 (* Add current slice remainder *)
232212 (match t.current_slice with
233233- | Some slice ->
234234- let bytes = Bytes.Slice.bytes slice in
235235- let first = Bytes.Slice.first slice in
236236- let remaining = Bytes.Slice.length slice - t.slice_pos in
237237- if remaining > 0 then
238238- Buffer.add_subbytes buf bytes (first + t.slice_pos) remaining
239239- | None -> ());
213213+ | Some slice ->
214214+ let bytes = Bytes.Slice.bytes slice in
215215+ let first = Bytes.Slice.first slice in
216216+ let remaining = Bytes.Slice.length slice - t.slice_pos in
217217+ if remaining > 0 then
218218+ Buffer.add_subbytes buf bytes (first + t.slice_pos) remaining
219219+ | None -> ());
240220 (* Add remaining from reader *)
241221 Bytes.Reader.add_to_buffer buf t.reader;
242222 Buffer.contents buf
···253233 Some (Stdlib.Bytes.get bytes (first + t.slice_pos - 1))
254234 | _ -> None
255235256256-(** Get a sample of the source for encoding detection.
257257- Uses sniff to peek without consuming. *)
236236+(** Get a sample of the source for encoding detection. Uses sniff to peek
237237+ without consuming. *)
258238let source t =
259239 (* First check current slice *)
260240 match t.current_slice with
···268248 Bytes.Reader.sniff 4 t.reader
269249270250(** Get the byte position in the underlying stream *)
271271-let byte_pos t =
272272- Bytes.Reader.pos t.reader
251251+let byte_pos t = Bytes.Reader.pos t.reader
+7-18
lib/layout_style.ml
···5566(** Collection layout styles *)
7788-type t = [
99- | `Any (** Let emitter choose *)
1010- | `Block (** Indentation-based *)
1111- | `Flow (** Inline with brackets *)
1212-]
88+type t =
99+ [ `Any (** Let emitter choose *)
1010+ | `Block (** Indentation-based *)
1111+ | `Flow (** Inline with brackets *) ]
13121414-let to_string = function
1515- | `Any -> "any"
1616- | `Block -> "block"
1717- | `Flow -> "flow"
1818-1919-let pp fmt t =
2020- Format.pp_print_string fmt (to_string t)
2121-1313+let to_string = function `Any -> "any" | `Block -> "block" | `Flow -> "flow"
1414+let pp fmt t = Format.pp_print_string fmt (to_string t)
2215let equal a b = a = b
23162417let compare a b =
2525- let to_int = function
2626- | `Any -> 0
2727- | `Block -> 1
2828- | `Flow -> 2
2929- in
1818+ let to_int = function `Any -> 0 | `Block -> 1 | `Flow -> 2 in
3019 Int.compare (to_int a) (to_int b)
+138-162
lib/loader.ml
···3131 mutable doc_implicit_start : bool;
3232}
33333434-let create_state () = {
3535- stack = [];
3636- current = None;
3737- documents = [];
3838- doc_version = None;
3939- doc_implicit_start = true;
4040-}
3434+let create_state () =
3535+ {
3636+ stack = [];
3737+ current = None;
3838+ documents = [];
3939+ doc_version = None;
4040+ doc_implicit_start = true;
4141+ }
41424243(** Process a single event *)
4344let rec process_event state (ev : Event.spanned) =
4445 match ev.event with
4546 | Event.Stream_start _ -> ()
4646-4747 | Event.Stream_end -> ()
4848-4948 | Event.Document_start { version; implicit } ->
5049 state.doc_version <- version;
5150 state.doc_implicit_start <- implicit
5252-5351 | Event.Document_end { implicit } ->
5454- let doc = Document.make
5555- ?version:state.doc_version
5656- ~implicit_start:state.doc_implicit_start
5757- ~implicit_end:implicit
5858- state.current
5252+ let doc =
5353+ Document.make ?version:state.doc_version
5454+ ~implicit_start:state.doc_implicit_start ~implicit_end:implicit
5555+ state.current
5956 in
6057 state.documents <- doc :: state.documents;
6158 state.current <- None;
6259 state.doc_version <- None;
6360 state.doc_implicit_start <- true
6464-6561 | Event.Alias { anchor } ->
6662 let node : Yaml.t = `Alias anchor in
6763 add_node state node
6868-6969- | Event.Scalar { anchor; tag; value; plain_implicit; quoted_implicit; style } ->
7070- let scalar = Scalar.make
7171- ?anchor ?tag
7272- ~plain_implicit ~quoted_implicit
7373- ~style value
6464+ | Event.Scalar { anchor; tag; value; plain_implicit; quoted_implicit; style }
6565+ ->
6666+ let scalar =
6767+ Scalar.make ?anchor ?tag ~plain_implicit ~quoted_implicit ~style value
7468 in
7569 let node : Yaml.t = `Scalar scalar in
7670 add_node state node
7777-7871 | Event.Sequence_start { anchor; tag; implicit; style } ->
7979- let frame = Sequence_frame {
8080- anchor; tag; implicit; style;
8181- items = [];
8282- } in
7272+ let frame = Sequence_frame { anchor; tag; implicit; style; items = [] } in
8373 state.stack <- frame :: state.stack
8484-8585- | Event.Sequence_end ->
8686- (match state.stack with
8787- | Sequence_frame { anchor; tag; implicit; style; items } :: rest ->
8888- let seq = Sequence.make ?anchor ?tag ~implicit ~style (List.rev items) in
8989- let node : Yaml.t = `A seq in
9090- state.stack <- rest;
9191- add_node state node
9292- | _ -> Error.raise (Invalid_state "unexpected sequence end"))
9393-7474+ | Event.Sequence_end -> (
7575+ match state.stack with
7676+ | Sequence_frame { anchor; tag; implicit; style; items } :: rest ->
7777+ let seq =
7878+ Sequence.make ?anchor ?tag ~implicit ~style (List.rev items)
7979+ in
8080+ let node : Yaml.t = `A seq in
8181+ state.stack <- rest;
8282+ add_node state node
8383+ | _ -> Error.raise (Invalid_state "unexpected sequence end"))
9484 | Event.Mapping_start { anchor; tag; implicit; style } ->
9595- let frame = Mapping_frame {
9696- anchor; tag; implicit; style;
9797- pairs = [];
9898- pending_key = None;
9999- } in
8585+ let frame =
8686+ Mapping_frame
8787+ { anchor; tag; implicit; style; pairs = []; pending_key = None }
8888+ in
10089 state.stack <- frame :: state.stack
101101-102102- | Event.Mapping_end ->
103103- (match state.stack with
104104- | Mapping_frame { anchor; tag; implicit; style; pairs; pending_key = None } :: rest ->
105105- let map = Mapping.make ?anchor ?tag ~implicit ~style (List.rev pairs) in
106106- let node : Yaml.t = `O map in
107107- state.stack <- rest;
108108- add_node state node
109109- | Mapping_frame { pending_key = Some _; _ } :: _ ->
110110- Error.raise (Invalid_state "mapping ended with pending key")
111111- | _ -> Error.raise (Invalid_state "unexpected mapping end"))
9090+ | Event.Mapping_end -> (
9191+ match state.stack with
9292+ | Mapping_frame
9393+ { anchor; tag; implicit; style; pairs; pending_key = None }
9494+ :: rest ->
9595+ let map =
9696+ Mapping.make ?anchor ?tag ~implicit ~style (List.rev pairs)
9797+ in
9898+ let node : Yaml.t = `O map in
9999+ state.stack <- rest;
100100+ add_node state node
101101+ | Mapping_frame { pending_key = Some _; _ } :: _ ->
102102+ Error.raise (Invalid_state "mapping ended with pending key")
103103+ | _ -> Error.raise (Invalid_state "unexpected mapping end"))
112104113105(** Add a node to current context *)
114106and add_node state node =
115107 match state.stack with
116116- | [] ->
117117- state.current <- Some node
118118-108108+ | [] -> state.current <- Some node
119109 | Sequence_frame f :: rest ->
120110 state.stack <- Sequence_frame { f with items = node :: f.items } :: rest
121121-122122- | Mapping_frame f :: rest ->
123123- (match f.pending_key with
124124- | None ->
125125- (* This is a key *)
126126- state.stack <- Mapping_frame { f with pending_key = Some node } :: rest
127127- | Some key ->
128128- (* This is a value *)
129129- state.stack <- Mapping_frame {
130130- f with
131131- pairs = (key, node) :: f.pairs;
132132- pending_key = None;
133133- } :: rest)
111111+ | Mapping_frame f :: rest -> (
112112+ match f.pending_key with
113113+ | None ->
114114+ (* This is a key *)
115115+ state.stack <-
116116+ Mapping_frame { f with pending_key = Some node } :: rest
117117+ | Some key ->
118118+ (* This is a value *)
119119+ state.stack <-
120120+ Mapping_frame
121121+ { f with pairs = (key, node) :: f.pairs; pending_key = None }
122122+ :: rest)
134123135124(** Internal: parse all documents from a parser *)
136125let parse_all_documents parser =
···149138150139 @param resolve_aliases Whether to resolve aliases (default true)
151140 @param max_nodes Maximum nodes during alias expansion (default 10M)
152152- @param max_depth Maximum alias nesting depth (default 100)
153153-*)
154154-let value_of_string
155155- ?(resolve_aliases = true)
141141+ @param max_depth Maximum alias nesting depth (default 100) *)
142142+let value_of_string ?(resolve_aliases = true)
156143 ?(max_nodes = Yaml.default_max_alias_nodes)
157157- ?(max_depth = Yaml.default_max_alias_depth)
158158- s =
144144+ ?(max_depth = Yaml.default_max_alias_depth) s =
159145 let docs = parse_all_documents (Parser.of_string s) in
160146 let doc = single_document_or_error docs ~empty:(Document.make None) in
161147 match Document.root doc with
162148 | None -> `Null
163149 | Some yaml ->
164164- Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes ~max_depth yaml
150150+ Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes ~max_depth
151151+ yaml
165152166153(** Load single document as Yaml.
167154168155 @param resolve_aliases Whether to resolve aliases (default false for Yaml.t)
169156 @param max_nodes Maximum nodes during alias expansion (default 10M)
170170- @param max_depth Maximum alias nesting depth (default 100)
171171-*)
172172-let yaml_of_string
173173- ?(resolve_aliases = false)
157157+ @param max_depth Maximum alias nesting depth (default 100) *)
158158+let yaml_of_string ?(resolve_aliases = false)
174159 ?(max_nodes = Yaml.default_max_alias_nodes)
175175- ?(max_depth = Yaml.default_max_alias_depth)
176176- s =
160160+ ?(max_depth = Yaml.default_max_alias_depth) s =
177161 let docs = parse_all_documents (Parser.of_string s) in
178162 let doc = single_document_or_error docs ~empty:(Document.make None) in
179163 match Document.root doc with
180164 | None -> `Scalar (Scalar.make "")
181165 | Some yaml ->
182182- if resolve_aliases then
183183- Yaml.resolve_aliases ~max_nodes ~max_depth yaml
184184- else
185185- yaml
166166+ if resolve_aliases then Yaml.resolve_aliases ~max_nodes ~max_depth yaml
167167+ else yaml
186168187169(** Load all documents *)
188170let documents_of_string s =
···194176195177 @param resolve_aliases Whether to resolve aliases (default true)
196178 @param max_nodes Maximum nodes during alias expansion (default 10M)
197197- @param max_depth Maximum alias nesting depth (default 100)
198198-*)
199199-let value_of_reader
200200- ?(resolve_aliases = true)
179179+ @param max_depth Maximum alias nesting depth (default 100) *)
180180+let value_of_reader ?(resolve_aliases = true)
201181 ?(max_nodes = Yaml.default_max_alias_nodes)
202202- ?(max_depth = Yaml.default_max_alias_depth)
203203- reader =
182182+ ?(max_depth = Yaml.default_max_alias_depth) reader =
204183 let docs = parse_all_documents (Parser.of_reader reader) in
205184 let doc = single_document_or_error docs ~empty:(Document.make None) in
206185 match Document.root doc with
207186 | None -> `Null
208187 | Some yaml ->
209209- Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes ~max_depth yaml
188188+ Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes ~max_depth
189189+ yaml
210190211191(** Load single document as Yaml from a Bytes.Reader.
212192213193 @param resolve_aliases Whether to resolve aliases (default false for Yaml.t)
214194 @param max_nodes Maximum nodes during alias expansion (default 10M)
215215- @param max_depth Maximum alias nesting depth (default 100)
216216-*)
217217-let yaml_of_reader
218218- ?(resolve_aliases = false)
195195+ @param max_depth Maximum alias nesting depth (default 100) *)
196196+let yaml_of_reader ?(resolve_aliases = false)
219197 ?(max_nodes = Yaml.default_max_alias_nodes)
220220- ?(max_depth = Yaml.default_max_alias_depth)
221221- reader =
198198+ ?(max_depth = Yaml.default_max_alias_depth) reader =
222199 let docs = parse_all_documents (Parser.of_reader reader) in
223200 let doc = single_document_or_error docs ~empty:(Document.make None) in
224201 match Document.root doc with
225202 | None -> `Scalar (Scalar.make "")
226203 | Some yaml ->
227227- if resolve_aliases then
228228- Yaml.resolve_aliases ~max_nodes ~max_depth yaml
229229- else
230230- yaml
204204+ if resolve_aliases then Yaml.resolve_aliases ~max_nodes ~max_depth yaml
205205+ else yaml
231206232207(** Load all documents from a Bytes.Reader *)
233208let documents_of_reader reader =
···245220 let rec loop () =
246221 match next_event () with
247222 | None -> None
248248- | Some ev ->
223223+ | Some ev -> (
249224 process_event state ev;
250225 match ev.event with
251251- | Event.Document_end _ ->
252252- (match state.documents with
253253- | doc :: _ ->
254254- state.documents <- [];
255255- Some (extract doc)
256256- | [] -> None)
226226+ | Event.Document_end _ -> (
227227+ match state.documents with
228228+ | doc :: _ ->
229229+ state.documents <- [];
230230+ Some (extract doc)
231231+ | [] -> None)
257232 | Event.Stream_end -> None
258258- | _ -> loop ()
233233+ | _ -> loop ())
259234 in
260235 loop ()
261236···267242268243 @param resolve_aliases Whether to resolve aliases (default true)
269244 @param max_nodes Maximum nodes during alias expansion (default 10M)
270270- @param max_depth Maximum alias nesting depth (default 100)
271271-*)
272272-let load_value
273273- ?(resolve_aliases = true)
245245+ @param max_depth Maximum alias nesting depth (default 100) *)
246246+let load_value ?(resolve_aliases = true)
274247 ?(max_nodes = Yaml.default_max_alias_nodes)
275275- ?(max_depth = Yaml.default_max_alias_depth)
276276- parser =
277277- load_generic (fun doc ->
278278- match Document.root doc with
279279- | None -> `Null
280280- | Some yaml ->
281281- Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes ~max_depth yaml
282282- ) parser
248248+ ?(max_depth = Yaml.default_max_alias_depth) parser =
249249+ load_generic
250250+ (fun doc ->
251251+ match Document.root doc with
252252+ | None -> `Null
253253+ | Some yaml ->
254254+ Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes
255255+ ~max_depth yaml)
256256+ parser
283257284258(** Load single Yaml from parser *)
285259let load_yaml parser =
286286- load_generic (fun doc ->
287287- Document.root doc |> Option.value ~default:(`Scalar (Scalar.make ""))
288288- ) parser
260260+ load_generic
261261+ (fun doc ->
262262+ Document.root doc |> Option.value ~default:(`Scalar (Scalar.make "")))
263263+ parser
289264290265(** Load single Document from parser *)
291291-let load_document parser =
292292- load_generic Fun.id parser
266266+let load_document parser = load_generic Fun.id parser
293267294268(** Iterate over documents *)
295269let iter_documents f parser =
296270 let rec loop () =
297271 match load_document parser with
298272 | None -> ()
299299- | Some doc -> f doc; loop ()
273273+ | Some doc ->
274274+ f doc;
275275+ loop ()
300276 in
301277 loop ()
302278303279(** Fold over documents *)
304280let fold_documents f init parser =
305281 let rec loop acc =
306306- match load_document parser with
307307- | None -> acc
308308- | Some doc -> loop (f acc doc)
282282+ match load_document parser with None -> acc | Some doc -> loop (f acc doc)
309283 in
310284 loop init
285285+311286312287(** Load single Value from event source.
313288314289 @param resolve_aliases Whether to resolve aliases (default true)
315290 @param max_nodes Maximum nodes during alias expansion (default 10M)
316316- @param max_depth Maximum alias nesting depth (default 100)
317317-*)
318318-let value_of_parser
319319- ?(resolve_aliases = true)
291291+ @param max_depth Maximum alias nesting depth (default 100) *)
292292+let value_of_parser ?(resolve_aliases = true)
320293 ?(max_nodes = Yaml.default_max_alias_nodes)
321321- ?(max_depth = Yaml.default_max_alias_depth)
322322- next_event =
323323- match load_generic_fn (fun doc ->
324324- match Document.root doc with
325325- | None -> `Null
326326- | Some yaml ->
327327- Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes ~max_depth yaml
328328- ) next_event with
294294+ ?(max_depth = Yaml.default_max_alias_depth) next_event =
295295+ match
296296+ load_generic_fn
297297+ (fun doc ->
298298+ match Document.root doc with
299299+ | None -> `Null
300300+ | Some yaml ->
301301+ Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes
302302+ ~max_depth yaml)
303303+ next_event
304304+ with
329305 | Some v -> v
330306 | None -> `Null
331307···333309334310 @param resolve_aliases Whether to resolve aliases (default false)
335311 @param max_nodes Maximum nodes during alias expansion (default 10M)
336336- @param max_depth Maximum alias nesting depth (default 100)
337337-*)
338338-let yaml_of_parser
339339- ?(resolve_aliases = false)
312312+ @param max_depth Maximum alias nesting depth (default 100) *)
313313+let yaml_of_parser ?(resolve_aliases = false)
340314 ?(max_nodes = Yaml.default_max_alias_nodes)
341341- ?(max_depth = Yaml.default_max_alias_depth)
342342- next_event =
343343- match load_generic_fn (fun doc ->
344344- match Document.root doc with
345345- | None -> `Scalar (Scalar.make "")
346346- | Some yaml ->
347347- if resolve_aliases then
348348- Yaml.resolve_aliases ~max_nodes ~max_depth yaml
349349- else
350350- yaml
351351- ) next_event with
315315+ ?(max_depth = Yaml.default_max_alias_depth) next_event =
316316+ match
317317+ load_generic_fn
318318+ (fun doc ->
319319+ match Document.root doc with
320320+ | None -> `Scalar (Scalar.make "")
321321+ | Some yaml ->
322322+ if resolve_aliases then
323323+ Yaml.resolve_aliases ~max_nodes ~max_depth yaml
324324+ else yaml)
325325+ next_event
326326+ with
352327 | Some v -> v
353328 | None -> `Scalar (Scalar.make "")
354329355330(** Load single Document from event source *)
356356-let document_of_parser next_event =
357357- load_generic_fn Fun.id next_event
331331+let document_of_parser next_event = load_generic_fn Fun.id next_event
358332359333(** Load all documents from event source *)
360334let documents_of_parser next_event =
···373347 let rec loop () =
374348 match document_of_parser next_event with
375349 | None -> ()
376376- | Some doc -> f doc; loop ()
350350+ | Some doc ->
351351+ f doc;
352352+ loop ()
377353 in
378354 loop ()
379355
+38-41
lib/mapping.ml
···1313 members : ('k * 'v) list;
1414}
15151616-let make
1717- ?(anchor : string option)
1818- ?(tag : string option)
1919- ?(implicit = true)
2020- ?(style = `Any)
2121- members =
1616+let make ?(anchor : string option) ?(tag : string option) ?(implicit = true)
1717+ ?(style = `Any) members =
2218 { anchor; tag; implicit; style; members }
23192420let members t = t.members
···2622let tag t = t.tag
2723let implicit t = t.implicit
2824let style t = t.style
2929-3025let with_anchor anchor t = { t with anchor = Some anchor }
3126let with_tag tag t = { t with tag = Some tag }
3227let with_style style t = { t with style }
33283434-let map_keys f t = { t with members = List.map (fun (k, v) -> (f k, v)) t.members }
3535-let map_values f t = { t with members = List.map (fun (k, v) -> (k, f v)) t.members }
3636-let map f t = { t with members = List.map (fun (k, v) -> f k v) t.members }
2929+let map_keys f t =
3030+ { t with members = List.map (fun (k, v) -> (f k, v)) t.members }
37313838-let length t = List.length t.members
3232+let map_values f t =
3333+ { t with members = List.map (fun (k, v) -> (k, f v)) t.members }
39343535+let map f t = { t with members = List.map (fun (k, v) -> f k v) t.members }
3636+let length t = List.length t.members
4037let is_empty t = t.members = []
41384239let find pred t =
4340 List.find_opt (fun (k, _) -> pred k) t.members |> Option.map snd
44414545-let find_key pred t =
4646- List.find_opt (fun (k, _) -> pred k) t.members
4747-4848-let mem pred t =
4949- List.exists (fun (k, _) -> pred k) t.members
5050-4242+let find_key pred t = List.find_opt (fun (k, _) -> pred k) t.members
4343+let mem pred t = List.exists (fun (k, _) -> pred k) t.members
5144let keys t = List.map fst t.members
5252-5345let values t = List.map snd t.members
5454-5546let iter f t = List.iter (fun (k, v) -> f k v) t.members
5656-5747let fold f init t = List.fold_left (fun acc (k, v) -> f acc k v) init t.members
58485949let pp pp_key pp_val fmt t =
···6252 Option.iter (Format.fprintf fmt "tag=%s,@ ") t.tag;
6353 Format.fprintf fmt "style=%a,@ " Layout_style.pp t.style;
6454 Format.fprintf fmt "members={@,";
6565- List.iteri (fun i (k, v) ->
6666- if i > 0 then Format.fprintf fmt ",@ ";
6767- Format.fprintf fmt "@[<hv 2>%a:@ %a@]" pp_key k pp_val v
6868- ) t.members;
5555+ List.iteri
5656+ (fun i (k, v) ->
5757+ if i > 0 then Format.fprintf fmt ",@ ";
5858+ Format.fprintf fmt "@[<hv 2>%a:@ %a@]" pp_key k pp_val v)
5959+ t.members;
6960 Format.fprintf fmt "@]@,})"
70617162let equal eq_k eq_v a b =
7272- Option.equal String.equal a.anchor b.anchor &&
7373- Option.equal String.equal a.tag b.tag &&
7474- a.implicit = b.implicit &&
7575- Layout_style.equal a.style b.style &&
7676- List.equal (fun (k1, v1) (k2, v2) -> eq_k k1 k2 && eq_v v1 v2) a.members b.members
6363+ Option.equal String.equal a.anchor b.anchor
6464+ && Option.equal String.equal a.tag b.tag
6565+ && a.implicit = b.implicit
6666+ && Layout_style.equal a.style b.style
6767+ && List.equal
6868+ (fun (k1, v1) (k2, v2) -> eq_k k1 k2 && eq_v v1 v2)
6969+ a.members b.members
77707871let compare cmp_k cmp_v a b =
7972 let c = Option.compare String.compare a.anchor b.anchor in
8080- if c <> 0 then c else
8181- let c = Option.compare String.compare a.tag b.tag in
8282- if c <> 0 then c else
8383- let c = Bool.compare a.implicit b.implicit in
8484- if c <> 0 then c else
8585- let c = Layout_style.compare a.style b.style in
8686- if c <> 0 then c else
8787- let cmp_pair (k1, v1) (k2, v2) =
8888- let c = cmp_k k1 k2 in
8989- if c <> 0 then c else cmp_v v1 v2
9090- in
9191- List.compare cmp_pair a.members b.members
7373+ if c <> 0 then c
7474+ else
7575+ let c = Option.compare String.compare a.tag b.tag in
7676+ if c <> 0 then c
7777+ else
7878+ let c = Bool.compare a.implicit b.implicit in
7979+ if c <> 0 then c
8080+ else
8181+ let c = Layout_style.compare a.style b.style in
8282+ if c <> 0 then c
8383+ else
8484+ let cmp_pair (k1, v1) (k2, v2) =
8585+ let c = cmp_k k1 k2 in
8686+ if c <> 0 then c else cmp_v v1 v2
8787+ in
8888+ List.compare cmp_pair a.members b.members
+266-305
lib/parser.ml
···1010 | Stream_start
1111 | Implicit_document_start
1212 | Document_content
1313- | Document_content_done (* After parsing a node, check for unexpected content *)
1313+ | Document_content_done
1414+ (* After parsing a node, check for unexpected content *)
1415 | Document_end
1516 | Block_sequence_first_entry
1617 | Block_sequence_entry
···3637 mutable tag_directives : (string * string) list;
3738 mutable current_token : Token.spanned option;
3839 mutable finished : bool;
3939- mutable explicit_doc_end : bool; (** True if last doc ended with explicit ... *)
4040- mutable stream_start : bool; (** True if we haven't emitted any documents yet *)
4040+ mutable explicit_doc_end : bool;
4141+ (** True if last doc ended with explicit ... *)
4242+ mutable stream_start : bool;
4343+ (** True if we haven't emitted any documents yet *)
4144}
42454343-let create scanner = {
4444- scanner;
4545- state = Stream_start;
4646- states = [];
4747- version = None;
4848- tag_directives = [
4949- ("!", "!");
5050- ("!!", "tag:yaml.org,2002:");
5151- ];
5252- current_token = None;
5353- finished = false;
5454- explicit_doc_end = false;
5555- stream_start = true;
5656-}
4646+let create scanner =
4747+ {
4848+ scanner;
4949+ state = Stream_start;
5050+ states = [];
5151+ version = None;
5252+ tag_directives = [ ("!", "!"); ("!!", "tag:yaml.org,2002:") ];
5353+ current_token = None;
5454+ finished = false;
5555+ explicit_doc_end = false;
5656+ stream_start = true;
5757+ }
57585859let of_string s = create (Scanner.of_string s)
5960let of_scanner = create
···6465let current_token t =
6566 match t.current_token with
6667 | Some tok -> tok
6767- | None ->
6868+ | None -> (
6869 let tok = Scanner.next t.scanner in
6970 t.current_token <- tok;
7070- match tok with
7171- | Some tok -> tok
7272- | None -> Error.raise Unexpected_eof
7171+ match tok with Some tok -> tok | None -> Error.raise Unexpected_eof)
73727473(** Peek at current token *)
7574let peek_token t =
···8079 t.current_token
81808281(** Skip current token *)
8383-let skip_token t =
8484- t.current_token <- None
8282+let skip_token t = t.current_token <- None
85838684(** Check if current token matches predicate *)
8785let check t pred =
8888- match peek_token t with
8989- | Some tok -> pred tok.token
9090- | None -> false
8686+ match peek_token t with Some tok -> pred tok.token | None -> false
8787+91889289(** Push state onto stack *)
9393-let push_state t s =
9494- t.states <- s :: t.states
9090+let push_state t s = t.states <- s :: t.states
95919692(** Pop state from stack *)
9793let pop_state t =
···115111(** Process directives at document start *)
116112let process_directives t =
117113 t.version <- None;
118118- t.tag_directives <- [("!", "!"); ("!!", "tag:yaml.org,2002:")];
114114+ t.tag_directives <- [ ("!", "!"); ("!!", "tag:yaml.org,2002:") ];
119115120120- while check t (function
121121- | Token.Version_directive _ | Token.Tag_directive _ -> true
122122- | _ -> false)
116116+ while
117117+ check t (function
118118+ | Token.Version_directive _ | Token.Tag_directive _ -> true
119119+ | _ -> false)
123120 do
124121 let tok = current_token t in
125122 skip_token t;
126123 match tok.token with
127124 | Token.Version_directive { major; minor } ->
128125 if t.version <> None then
129129- Error.raise_span tok.span (Invalid_yaml_version "duplicate YAML directive");
126126+ Error.raise_span tok.span
127127+ (Invalid_yaml_version "duplicate YAML directive");
130128 t.version <- Some (major, minor)
131129 | Token.Tag_directive { handle; prefix } ->
132130 (* Skip empty tag directives (these are reserved/unknown directives that were ignored) *)
133133- if handle = "" && prefix = "" then
134134- () (* Ignore reserved directives *)
131131+ if handle = "" && prefix = "" then () (* Ignore reserved directives *)
135132 else begin
136136- if List.mem_assoc handle t.tag_directives &&
137137- handle <> "!" && handle <> "!!" then
138138- Error.raise_span tok.span (Invalid_tag_directive ("duplicate tag handle: " ^ handle));
133133+ if
134134+ List.mem_assoc handle t.tag_directives
135135+ && handle <> "!" && handle <> "!!"
136136+ then
137137+ Error.raise_span tok.span
138138+ (Invalid_tag_directive ("duplicate tag handle: " ^ handle));
139139 t.tag_directives <- (handle, prefix) :: t.tag_directives
140140 end
141141 | _ -> ()
···146146 let anchor = ref None in
147147 let tag = ref None in
148148149149- while check t (function
150150- | Token.Anchor _ | Token.Tag _ -> true
151151- | _ -> false)
149149+ while
150150+ check t (function Token.Anchor _ | Token.Tag _ -> true | _ -> false)
152151 do
153152 let tok = current_token t in
154153 skip_token t;
···172171173172(** Empty scalar event *)
174173let empty_scalar_event ~anchor ~tag span =
175175- Event.Scalar {
176176- anchor;
177177- tag;
178178- value = "";
179179- plain_implicit = tag = None;
180180- quoted_implicit = false;
181181- style = `Plain;
182182- }, span
174174+ ( Event.Scalar
175175+ {
176176+ anchor;
177177+ tag;
178178+ value = "";
179179+ plain_implicit = tag = None;
180180+ quoted_implicit = false;
181181+ style = `Plain;
182182+ },
183183+ span )
183184184185(** Parse stream start *)
185186let parse_stream_start t =
···188189 match tok.token with
189190 | Token.Stream_start encoding ->
190191 t.state <- Implicit_document_start;
191191- Event.Stream_start { encoding }, tok.span
192192- | _ ->
193193- Error.raise_span tok.span (Unexpected_token "expected stream start")
192192+ (Event.Stream_start { encoding }, tok.span)
193193+ | _ -> Error.raise_span tok.span (Unexpected_token "expected stream start")
194194195195(** Parse document start (implicit or explicit) *)
196196let parse_document_start t ~implicit =
···199199 if not implicit then begin
200200 let tok = current_token t in
201201 match tok.token with
202202- | Token.Document_start ->
203203- skip_token t
204204- | _ ->
205205- Error.raise_span tok.span Expected_document_start
202202+ | Token.Document_start -> skip_token t
203203+ | _ -> Error.raise_span tok.span Expected_document_start
206204 end;
207205208208- let span = match peek_token t with
206206+ let span =
207207+ match peek_token t with
209208 | Some tok -> tok.span
210209 | None -> Span.point Position.initial
211210 in
···214213 t.stream_start <- false;
215214 push_state t Document_end;
216215 t.state <- Document_content;
217217- Event.Document_start { version = t.version; implicit }, span
216216+ (Event.Document_start { version = t.version; implicit }, span)
218217219218(** Parse document end *)
220219let parse_document_end t =
221221- let implicit = not (check t (function Token.Document_end -> true | _ -> false)) in
222222- let span = match peek_token t with
220220+ let implicit =
221221+ not (check t (function Token.Document_end -> true | _ -> false))
222222+ in
223223+ let span =
224224+ match peek_token t with
223225 | Some tok -> tok.span
224226 | None -> Span.point Position.initial
225227 in
···229231 (* Track if this document ended explicitly with ... *)
230232 t.explicit_doc_end <- not implicit;
231233 t.state <- Implicit_document_start;
232232- Event.Document_end { implicit }, span
234234+ (Event.Document_end { implicit }, span)
233235234236(** Parse node in various contexts *)
235237let parse_node t ~block ~indentless =
···238240 | Token.Alias name ->
239241 skip_token t;
240242 t.state <- pop_state t;
241241- Event.Alias { anchor = name }, tok.span
242242-243243- | Token.Anchor _ | Token.Tag _ ->
243243+ (Event.Alias { anchor = name }, tok.span)
244244+ | Token.Anchor _ | Token.Tag _ -> (
244245 let anchor, tag = parse_properties t in
245246 let tok = current_token t in
246246- (match tok.token with
247247- | Token.Block_entry when indentless ->
248248- t.state <- Indentless_sequence_entry;
249249- Event.Sequence_start {
250250- anchor; tag;
251251- implicit = tag = None;
252252- style = `Block;
253253- }, tok.span
254254-255255- | Token.Block_sequence_start when block ->
256256- t.state <- Block_sequence_first_entry;
257257- skip_token t;
258258- Event.Sequence_start {
259259- anchor; tag;
260260- implicit = tag = None;
261261- style = `Block;
262262- }, tok.span
263263-264264- | Token.Block_mapping_start when block ->
265265- t.state <- Block_mapping_first_key;
266266- skip_token t;
267267- Event.Mapping_start {
268268- anchor; tag;
269269- implicit = tag = None;
270270- style = `Block;
271271- }, tok.span
272272-273273- | Token.Flow_sequence_start ->
274274- t.state <- Flow_sequence_first_entry;
275275- skip_token t;
276276- Event.Sequence_start {
277277- anchor; tag;
278278- implicit = tag = None;
279279- style = `Flow;
280280- }, tok.span
281281-282282- | Token.Flow_mapping_start ->
283283- t.state <- Flow_mapping_first_key;
284284- skip_token t;
285285- Event.Mapping_start {
286286- anchor; tag;
287287- implicit = tag = None;
288288- style = `Flow;
289289- }, tok.span
290290-291291- | Token.Scalar { style; value } ->
292292- skip_token t;
293293- t.state <- pop_state t;
294294- let plain_implicit = tag = None && style = `Plain in
295295- let quoted_implicit = tag = None && style <> `Plain in
296296- Event.Scalar {
297297- anchor; tag; value;
298298- plain_implicit; quoted_implicit; style;
299299- }, tok.span
300300-301301- | _ ->
302302- (* Empty node *)
303303- t.state <- pop_state t;
304304- empty_scalar_event ~anchor ~tag tok.span)
305305-247247+ match tok.token with
248248+ | Token.Block_entry when indentless ->
249249+ t.state <- Indentless_sequence_entry;
250250+ ( Event.Sequence_start
251251+ { anchor; tag; implicit = tag = None; style = `Block },
252252+ tok.span )
253253+ | Token.Block_sequence_start when block ->
254254+ t.state <- Block_sequence_first_entry;
255255+ skip_token t;
256256+ ( Event.Sequence_start
257257+ { anchor; tag; implicit = tag = None; style = `Block },
258258+ tok.span )
259259+ | Token.Block_mapping_start when block ->
260260+ t.state <- Block_mapping_first_key;
261261+ skip_token t;
262262+ ( Event.Mapping_start
263263+ { anchor; tag; implicit = tag = None; style = `Block },
264264+ tok.span )
265265+ | Token.Flow_sequence_start ->
266266+ t.state <- Flow_sequence_first_entry;
267267+ skip_token t;
268268+ ( Event.Sequence_start
269269+ { anchor; tag; implicit = tag = None; style = `Flow },
270270+ tok.span )
271271+ | Token.Flow_mapping_start ->
272272+ t.state <- Flow_mapping_first_key;
273273+ skip_token t;
274274+ ( Event.Mapping_start
275275+ { anchor; tag; implicit = tag = None; style = `Flow },
276276+ tok.span )
277277+ | Token.Scalar { style; value } ->
278278+ skip_token t;
279279+ t.state <- pop_state t;
280280+ let plain_implicit = tag = None && style = `Plain in
281281+ let quoted_implicit = tag = None && style <> `Plain in
282282+ ( Event.Scalar
283283+ { anchor; tag; value; plain_implicit; quoted_implicit; style },
284284+ tok.span )
285285+ | _ ->
286286+ (* Empty node *)
287287+ t.state <- pop_state t;
288288+ empty_scalar_event ~anchor ~tag tok.span)
306289 | Token.Block_sequence_start when block ->
307290 t.state <- Block_sequence_first_entry;
308291 skip_token t;
309309- Event.Sequence_start {
310310- anchor = None; tag = None;
311311- implicit = true;
312312- style = `Block;
313313- }, tok.span
314314-292292+ ( Event.Sequence_start
293293+ { anchor = None; tag = None; implicit = true; style = `Block },
294294+ tok.span )
315295 | Token.Block_mapping_start when block ->
316296 t.state <- Block_mapping_first_key;
317297 skip_token t;
318318- Event.Mapping_start {
319319- anchor = None; tag = None;
320320- implicit = true;
321321- style = `Block;
322322- }, tok.span
323323-298298+ ( Event.Mapping_start
299299+ { anchor = None; tag = None; implicit = true; style = `Block },
300300+ tok.span )
324301 | Token.Flow_sequence_start ->
325302 t.state <- Flow_sequence_first_entry;
326303 skip_token t;
327327- Event.Sequence_start {
328328- anchor = None; tag = None;
329329- implicit = true;
330330- style = `Flow;
331331- }, tok.span
332332-304304+ ( Event.Sequence_start
305305+ { anchor = None; tag = None; implicit = true; style = `Flow },
306306+ tok.span )
333307 | Token.Flow_mapping_start ->
334308 t.state <- Flow_mapping_first_key;
335309 skip_token t;
336336- Event.Mapping_start {
337337- anchor = None; tag = None;
338338- implicit = true;
339339- style = `Flow;
340340- }, tok.span
341341-310310+ ( Event.Mapping_start
311311+ { anchor = None; tag = None; implicit = true; style = `Flow },
312312+ tok.span )
342313 | Token.Block_entry when indentless ->
343314 t.state <- Indentless_sequence_entry;
344344- Event.Sequence_start {
345345- anchor = None; tag = None;
346346- implicit = true;
347347- style = `Block;
348348- }, tok.span
349349-315315+ ( Event.Sequence_start
316316+ { anchor = None; tag = None; implicit = true; style = `Block },
317317+ tok.span )
350318 | Token.Scalar { style; value } ->
351319 skip_token t;
352320 t.state <- pop_state t;
353321 let plain_implicit = style = `Plain in
354322 let quoted_implicit = style <> `Plain in
355355- Event.Scalar {
356356- anchor = None; tag = None; value;
357357- plain_implicit; quoted_implicit; style;
358358- }, tok.span
359359-323323+ ( Event.Scalar
324324+ {
325325+ anchor = None;
326326+ tag = None;
327327+ value;
328328+ plain_implicit;
329329+ quoted_implicit;
330330+ style;
331331+ },
332332+ tok.span )
360333 | _ ->
361334 (* Empty node *)
362335 t.state <- pop_state t;
···368341 match tok.token with
369342 | Token.Block_entry ->
370343 skip_token t;
371371- if check t (function
372372- | Token.Block_entry | Token.Block_end -> true
373373- | _ -> false)
344344+ if
345345+ check t (function
346346+ | Token.Block_entry | Token.Block_end -> true
347347+ | _ -> false)
374348 then begin
375349 t.state <- Block_sequence_entry;
376350 empty_scalar_event ~anchor:None ~tag:None tok.span
377377- end else begin
351351+ end
352352+ else begin
378353 push_state t Block_sequence_entry;
379354 parse_node t ~block:true ~indentless:false
380355 end
381356 | Token.Block_end ->
382357 skip_token t;
383358 t.state <- pop_state t;
384384- Event.Sequence_end, tok.span
385385- | _ ->
386386- Error.raise_span tok.span Expected_block_entry
359359+ (Event.Sequence_end, tok.span)
360360+ | _ -> Error.raise_span tok.span Expected_block_entry
387361388362(** Parse block mapping key *)
389363let parse_block_mapping_key t =
···391365 match tok.token with
392366 | Token.Key ->
393367 skip_token t;
394394- if check t (function
395395- | Token.Key | Token.Value | Token.Block_end -> true
396396- | _ -> false)
368368+ if
369369+ check t (function
370370+ | Token.Key | Token.Value | Token.Block_end -> true
371371+ | _ -> false)
397372 then begin
398373 t.state <- Block_mapping_value;
399374 empty_scalar_event ~anchor:None ~tag:None tok.span
400400- end else begin
375375+ end
376376+ else begin
401377 push_state t Block_mapping_value;
402378 parse_node t ~block:true ~indentless:true
403379 end
···408384 | Token.Block_end ->
409385 skip_token t;
410386 t.state <- pop_state t;
411411- Event.Mapping_end, tok.span
412412- | _ ->
413413- Error.raise_span tok.span Expected_key
387387+ (Event.Mapping_end, tok.span)
388388+ | _ -> Error.raise_span tok.span Expected_key
414389415390(** Parse block mapping value *)
416391let parse_block_mapping_value t =
···418393 match tok.token with
419394 | Token.Value ->
420395 skip_token t;
421421- if check t (function
422422- | Token.Key | Token.Value | Token.Block_end -> true
423423- | _ -> false)
396396+ if
397397+ check t (function
398398+ | Token.Key | Token.Value | Token.Block_end -> true
399399+ | _ -> false)
424400 then begin
425401 t.state <- Block_mapping_key;
426402 empty_scalar_event ~anchor:None ~tag:None tok.span
427427- end else begin
403403+ end
404404+ else begin
428405 push_state t Block_mapping_key;
429406 parse_node t ~block:true ~indentless:true
430407 end
···439416 match tok.token with
440417 | Token.Block_entry ->
441418 skip_token t;
442442- if check t (function
443443- | Token.Block_entry | Token.Key | Token.Value | Token.Block_end -> true
444444- | _ -> false)
419419+ if
420420+ check t (function
421421+ | Token.Block_entry | Token.Key | Token.Value | Token.Block_end ->
422422+ true
423423+ | _ -> false)
445424 then begin
446425 t.state <- Indentless_sequence_entry;
447426 empty_scalar_event ~anchor:None ~tag:None tok.span
448448- end else begin
427427+ end
428428+ else begin
449429 push_state t Indentless_sequence_entry;
450430 parse_node t ~block:true ~indentless:false
451431 end
452432 | _ ->
453433 t.state <- pop_state t;
454454- Event.Sequence_end, tok.span
434434+ (Event.Sequence_end, tok.span)
455435456436(** Parse flow sequence *)
457437let rec parse_flow_sequence_entry t ~first =
···460440 | Token.Flow_sequence_end ->
461441 skip_token t;
462442 t.state <- pop_state t;
463463- Event.Sequence_end, tok.span
443443+ (Event.Sequence_end, tok.span)
464444 | Token.Flow_entry when not first ->
465445 skip_token t;
466446 parse_flow_sequence_entry_internal t
467467- | _ when first ->
468468- parse_flow_sequence_entry_internal t
469469- | _ ->
470470- Error.raise_span tok.span Expected_sequence_end
447447+ | _ when first -> parse_flow_sequence_entry_internal t
448448+ | _ -> Error.raise_span tok.span Expected_sequence_end
471449472450and parse_flow_sequence_entry_internal t =
473451 let tok = current_token t in
···476454 (* Trailing comma case - don't emit empty scalar, just go back to sequence entry state *)
477455 skip_token t;
478456 t.state <- pop_state t;
479479- Event.Sequence_end, tok.span
457457+ (Event.Sequence_end, tok.span)
480458 | Token.Flow_entry ->
481459 (* Double comma or comma after comma - invalid *)
482482- Error.raise_span tok.span (Unexpected_token "unexpected ',' in flow sequence")
460460+ Error.raise_span tok.span
461461+ (Unexpected_token "unexpected ',' in flow sequence")
483462 | Token.Key ->
484463 skip_token t;
485464 t.state <- Flow_sequence_entry_mapping_key;
486486- Event.Mapping_start {
487487- anchor = None; tag = None;
488488- implicit = true;
489489- style = `Flow;
490490- }, tok.span
465465+ ( Event.Mapping_start
466466+ { anchor = None; tag = None; implicit = true; style = `Flow },
467467+ tok.span )
491468 | Token.Value ->
492469 (* Implicit empty key mapping: [ : value ] *)
493470 t.state <- Flow_sequence_entry_mapping_key;
494494- Event.Mapping_start {
495495- anchor = None; tag = None;
496496- implicit = true;
497497- style = `Flow;
498498- }, tok.span
471471+ ( Event.Mapping_start
472472+ { anchor = None; tag = None; implicit = true; style = `Flow },
473473+ tok.span )
499474 | _ ->
500475 push_state t Flow_sequence_entry;
501476 parse_node t ~block:false ~indentless:false
···503478(** Parse flow sequence entry mapping *)
504479let parse_flow_sequence_entry_mapping_key t =
505480 let tok = current_token t in
506506- if check t (function
507507- | Token.Value | Token.Flow_entry | Token.Flow_sequence_end -> true
508508- | _ -> false)
481481+ if
482482+ check t (function
483483+ | Token.Value | Token.Flow_entry | Token.Flow_sequence_end -> true
484484+ | _ -> false)
509485 then begin
510486 t.state <- Flow_sequence_entry_mapping_value;
511487 empty_scalar_event ~anchor:None ~tag:None tok.span
512512- end else begin
488488+ end
489489+ else begin
513490 push_state t Flow_sequence_entry_mapping_value;
514491 parse_node t ~block:false ~indentless:false
515492 end
···519496 match tok.token with
520497 | Token.Value ->
521498 skip_token t;
522522- if check t (function
523523- | Token.Flow_entry | Token.Flow_sequence_end -> true
524524- | _ -> false)
499499+ if
500500+ check t (function
501501+ | Token.Flow_entry | Token.Flow_sequence_end -> true
502502+ | _ -> false)
525503 then begin
526504 t.state <- Flow_sequence_entry_mapping_end;
527505 empty_scalar_event ~anchor:None ~tag:None tok.span
528528- end else begin
506506+ end
507507+ else begin
529508 push_state t Flow_sequence_entry_mapping_end;
530509 parse_node t ~block:false ~indentless:false
531510 end
···536515let parse_flow_sequence_entry_mapping_end t =
537516 let tok = current_token t in
538517 t.state <- Flow_sequence_entry;
539539- Event.Mapping_end, tok.span
518518+ (Event.Mapping_end, tok.span)
540519541520(** Parse flow mapping *)
542521let rec parse_flow_mapping_key t ~first =
···545524 | Token.Flow_mapping_end ->
546525 skip_token t;
547526 t.state <- pop_state t;
548548- Event.Mapping_end, tok.span
527527+ (Event.Mapping_end, tok.span)
549528 | Token.Flow_entry when not first ->
550529 skip_token t;
551530 parse_flow_mapping_key_internal t
552552- | _ when first ->
553553- parse_flow_mapping_key_internal t
554554- | _ ->
555555- Error.raise_span tok.span Expected_mapping_end
531531+ | _ when first -> parse_flow_mapping_key_internal t
532532+ | _ -> Error.raise_span tok.span Expected_mapping_end
556533557534and parse_flow_mapping_key_internal t =
558535 let tok = current_token t in
···561538 (* Trailing comma case - don't emit empty scalar, just return to key state *)
562539 skip_token t;
563540 t.state <- pop_state t;
564564- Event.Mapping_end, tok.span
541541+ (Event.Mapping_end, tok.span)
565542 | Token.Flow_entry ->
566543 (* Double comma or comma after comma - invalid *)
567567- Error.raise_span tok.span (Unexpected_token "unexpected ',' in flow mapping")
544544+ Error.raise_span tok.span
545545+ (Unexpected_token "unexpected ',' in flow mapping")
568546 | Token.Key ->
569547 skip_token t;
570570- if check t (function
571571- | Token.Value | Token.Flow_entry | Token.Flow_mapping_end -> true
572572- | _ -> false)
548548+ if
549549+ check t (function
550550+ | Token.Value | Token.Flow_entry | Token.Flow_mapping_end -> true
551551+ | _ -> false)
573552 then begin
574553 t.state <- Flow_mapping_value;
575554 empty_scalar_event ~anchor:None ~tag:None tok.span
576576- end else begin
555555+ end
556556+ else begin
577557 push_state t Flow_mapping_value;
578558 parse_node t ~block:false ~indentless:false
579559 end
···586566 if empty then begin
587567 t.state <- Flow_mapping_key;
588568 empty_scalar_event ~anchor:None ~tag:None tok.span
589589- end else
569569+ end
570570+ else
590571 match tok.token with
591572 | Token.Value ->
592573 skip_token t;
593593- if check t (function
594594- | Token.Flow_entry | Token.Flow_mapping_end -> true
595595- | _ -> false)
574574+ if
575575+ check t (function
576576+ | Token.Flow_entry | Token.Flow_mapping_end -> true
577577+ | _ -> false)
596578 then begin
597579 t.state <- Flow_mapping_key;
598580 empty_scalar_event ~anchor:None ~tag:None tok.span
599599- end else begin
581581+ end
582582+ else begin
600583 push_state t Flow_mapping_key;
601584 parse_node t ~block:false ~indentless:false
602585 end
···607590(** Main state machine dispatcher *)
608591let rec parse t =
609592 match t.state with
610610- | Stream_start ->
611611- parse_stream_start t
612612-613613- | Implicit_document_start ->
593593+ | Stream_start -> parse_stream_start t
594594+ | Implicit_document_start -> (
614595 (* Skip any document end markers before checking what's next *)
615596 while check t (function Token.Document_end -> true | _ -> false) do
616616- t.explicit_doc_end <- true; (* Seeing ... counts as explicit end *)
597597+ t.explicit_doc_end <- true;
598598+ (* Seeing ... counts as explicit end *)
617599 skip_token t
618600 done;
619601620602 let tok = current_token t in
621621- (match tok.token with
622622- | Token.Stream_end ->
623623- skip_token t;
624624- t.state <- End;
625625- t.finished <- true;
626626- Event.Stream_end, tok.span
627627- | Token.Version_directive _ | Token.Tag_directive _ ->
628628- (* Directives are only allowed at stream start or after explicit ... (MUS6/01) *)
629629- if not t.stream_start && not t.explicit_doc_end then
630630- Error.raise_span tok.span (Invalid_directive "directives require explicit document end '...' before them");
631631- parse_document_start t ~implicit:false
632632- | Token.Document_start ->
633633- parse_document_start t ~implicit:false
634634- (* These tokens are invalid at document start - they indicate leftover junk *)
635635- | Token.Flow_sequence_end | Token.Flow_mapping_end | Token.Flow_entry
636636- | Token.Block_end | Token.Value ->
637637- Error.raise_span tok.span (Unexpected_token "unexpected token at document start")
638638- | _ ->
639639- parse_document_start t ~implicit:true)
603603+ match tok.token with
604604+ | Token.Stream_end ->
605605+ skip_token t;
606606+ t.state <- End;
607607+ t.finished <- true;
608608+ (Event.Stream_end, tok.span)
609609+ | Token.Version_directive _ | Token.Tag_directive _ ->
610610+ (* Directives are only allowed at stream start or after explicit ... (MUS6/01) *)
611611+ if (not t.stream_start) && not t.explicit_doc_end then
612612+ Error.raise_span tok.span
613613+ (Invalid_directive
614614+ "directives require explicit document end '...' before them");
615615+ parse_document_start t ~implicit:false
616616+ | Token.Document_start -> parse_document_start t ~implicit:false
617617+ (* These tokens are invalid at document start - they indicate leftover junk *)
618618+ | Token.Flow_sequence_end | Token.Flow_mapping_end | Token.Flow_entry
619619+ | Token.Block_end | Token.Value ->
620620+ Error.raise_span tok.span
621621+ (Unexpected_token "unexpected token at document start")
622622+ | _ -> parse_document_start t ~implicit:true)
640623641624 | Document_content ->
642642- if check t (function
643643- | Token.Version_directive _ | Token.Tag_directive _
644644- | Token.Document_start | Token.Document_end | Token.Stream_end -> true
645645- | _ -> false)
625625+ if
626626+ check t (function
627627+ | Token.Version_directive _ | Token.Tag_directive _
628628+ | Token.Document_start | Token.Document_end | Token.Stream_end ->
629629+ true
630630+ | _ -> false)
646631 then begin
647632 let tok = current_token t in
648633 t.state <- pop_state t;
649634 empty_scalar_event ~anchor:None ~tag:None tok.span
650650- end else begin
635635+ end
636636+ else begin
651637 (* Push Document_content_done so we return there after parsing the node.
652638 This allows us to check for unexpected content after the node. *)
653639 push_state t Document_content_done;
654640 parse_node t ~block:true ~indentless:false
655641 end
656656-657642 | Document_content_done ->
658643 (* After parsing a node in document content, check for unexpected content *)
659659- if check t (function
660660- | Token.Version_directive _ | Token.Tag_directive _
661661- | Token.Document_start | Token.Document_end | Token.Stream_end -> true
662662- | _ -> false)
644644+ if
645645+ check t (function
646646+ | Token.Version_directive _ | Token.Tag_directive _
647647+ | Token.Document_start | Token.Document_end | Token.Stream_end ->
648648+ true
649649+ | _ -> false)
663650 then begin
664651 (* Valid document boundary - continue to Document_end *)
665652 t.state <- pop_state t;
666666- parse t (* Continue to emit the next event *)
667667- end else begin
653653+ parse t (* Continue to emit the next event *)
654654+ end
655655+ else begin
668656 (* Unexpected content after document value - this is an error (KS4U, BS4K) *)
669657 let tok = current_token t in
670658 Error.raise_span tok.span
671659 (Unexpected_token "content not allowed after document value")
672660 end
673673-674674- | Document_end ->
675675- parse_document_end t
661661+ | Document_end -> parse_document_end t
676662677663 | Block_sequence_first_entry ->
678664 t.state <- Block_sequence_entry;
679665 parse_block_sequence_entry t
680680-681681- | Block_sequence_entry ->
682682- parse_block_sequence_entry t
683683-684684- | Indentless_sequence_entry ->
685685- parse_indentless_sequence_entry t
686686-666666+ | Block_sequence_entry -> parse_block_sequence_entry t
667667+ | Indentless_sequence_entry -> parse_indentless_sequence_entry t
687668 | Block_mapping_first_key ->
688669 t.state <- Block_mapping_key;
689670 parse_block_mapping_key t
690690-691691- | Block_mapping_key ->
692692- parse_block_mapping_key t
693693-694694- | Block_mapping_value ->
695695- parse_block_mapping_value t
696696-697697- | Flow_sequence_first_entry ->
698698- parse_flow_sequence_entry t ~first:true
699699-700700- | Flow_sequence_entry ->
701701- parse_flow_sequence_entry t ~first:false
702702-703703- | Flow_sequence_entry_mapping_key ->
704704- parse_flow_sequence_entry_mapping_key t
705705-671671+ | Block_mapping_key -> parse_block_mapping_key t
672672+ | Block_mapping_value -> parse_block_mapping_value t
673673+ | Flow_sequence_first_entry -> parse_flow_sequence_entry t ~first:true
674674+ | Flow_sequence_entry -> parse_flow_sequence_entry t ~first:false
675675+ | Flow_sequence_entry_mapping_key -> parse_flow_sequence_entry_mapping_key t
706676 | Flow_sequence_entry_mapping_value ->
707677 parse_flow_sequence_entry_mapping_value t
708708-709709- | Flow_sequence_entry_mapping_end ->
710710- parse_flow_sequence_entry_mapping_end t
711711-712712- | Flow_mapping_first_key ->
713713- parse_flow_mapping_key t ~first:true
714714-715715- | Flow_mapping_key ->
716716- parse_flow_mapping_key t ~first:false
717717-718718- | Flow_mapping_value ->
719719- parse_flow_mapping_value t ~empty:false
678678+ | Flow_sequence_entry_mapping_end -> parse_flow_sequence_entry_mapping_end t
679679+ | Flow_mapping_first_key -> parse_flow_mapping_key t ~first:true
680680+ | Flow_mapping_key -> parse_flow_mapping_key t ~first:false
681681+ | Flow_mapping_value -> parse_flow_mapping_value t ~empty:false
720682721683 | End ->
722684 let span = Span.point Position.initial in
723685 t.finished <- true;
724724- Event.Stream_end, span
686686+ (Event.Stream_end, span)
725687726688(** Get next event *)
727689let next t =
···735697 let rec loop () =
736698 match next t with
737699 | None -> ()
738738- | Some ev -> f ev; loop ()
700700+ | Some ev ->
701701+ f ev;
702702+ loop ()
739703 in
740704 loop ()
741705742706(** Fold over all events *)
743707let fold f init t =
744708 let rec loop acc =
745745- match next t with
746746- | None -> acc
747747- | Some ev -> loop (f acc ev)
709709+ match next t with None -> acc | Some ev -> loop (f acc ev)
748710 in
749711 loop init
750712751713(** Convert to list *)
752752-let to_list t =
753753- fold (fun acc ev -> ev :: acc) [] t |> List.rev
714714+let to_list t = fold (fun acc ev -> ev :: acc) [] t |> List.rev
+11-28
lib/position.ml
···66(** Position tracking for source locations *)
7788type t = {
99- index : int; (** Byte offset from start *)
1010- line : int; (** 1-indexed line number *)
99+ index : int; (** Byte offset from start *)
1010+ line : int; (** 1-indexed line number *)
1111 column : int; (** 1-indexed column number *)
1212}
13131414let initial = { index = 0; line = 1; column = 1 }
1515-1616-let advance_byte t =
1717- { t with index = t.index + 1; column = t.column + 1 }
1818-1919-let advance_line t =
2020- { index = t.index + 1; line = t.line + 1; column = 1 }
2121-2222-let advance_char c t =
2323- if c = '\n' then advance_line t
2424- else advance_byte t
1515+let advance_byte t = { t with index = t.index + 1; column = t.column + 1 }
1616+let advance_line t = { index = t.index + 1; line = t.line + 1; column = 1 }
1717+let advance_char c t = if c = '\n' then advance_line t else advance_byte t
25182619let advance_utf8 uchar t =
2720 let len = Uchar.utf_8_byte_length uchar in
2821 let code = Uchar.to_int uchar in
2922 if code = 0x0A (* LF *) then
3023 { index = t.index + len; line = t.line + 1; column = 1 }
3131- else
3232- { t with index = t.index + len; column = t.column + 1 }
2424+ else { t with index = t.index + len; column = t.column + 1 }
33253434-let advance_bytes n t =
3535- { t with index = t.index + n; column = t.column + n }
3636-3737-let pp fmt t =
3838- Format.fprintf fmt "line %d, column %d" t.line t.column
3939-4040-let to_string t =
4141- Format.asprintf "%a" pp t
4242-4343-let compare a b =
4444- Int.compare a.index b.index
4545-4646-let equal a b =
4747- a.index = b.index
2626+let advance_bytes n t = { t with index = t.index + n; column = t.column + n }
2727+let pp fmt t = Format.fprintf fmt "line %d, column %d" t.line t.column
2828+let to_string t = Format.asprintf "%a" pp t
2929+let compare a b = Int.compare a.index b.index
3030+let equal a b = a.index = b.index
+31-27
lib/quoting.ml
···5566(** YAML scalar quoting detection *)
7788-(** Check if a string value needs quoting in YAML output.
99- Returns true if the string:
88+(** Check if a string value needs quoting in YAML output. Returns true if the
99+ string:
1010 - Is empty
1111 - Starts with an indicator character
1212 - Is a reserved word (null, true, false, yes, no, etc.)
···1717 else
1818 let first = s.[0] in
1919 (* Check first character for indicators *)
2020- if first = '-' || first = '?' || first = ':' || first = ',' ||
2121- first = '[' || first = ']' || first = '{' || first = '}' ||
2222- first = '#' || first = '&' || first = '*' || first = '!' ||
2323- first = '|' || first = '>' || first = '\'' || first = '"' ||
2424- first = '%' || first = '@' || first = '`' || first = ' ' then
2525- true
2020+ if
2121+ first = '-' || first = '?' || first = ':' || first = ',' || first = '['
2222+ || first = ']' || first = '{' || first = '}' || first = '#' || first = '&'
2323+ || first = '*' || first = '!' || first = '|' || first = '>'
2424+ || first = '\'' || first = '"' || first = '%' || first = '@'
2525+ || first = '`' || first = ' '
2626+ then true
2627 else
2728 (* Check for reserved/special values *)
2829 let lower = String.lowercase_ascii s in
2929- if lower = "null" || lower = "true" || lower = "false" ||
3030- lower = "yes" || lower = "no" || lower = "on" || lower = "off" ||
3131- lower = "~" || lower = ".inf" || lower = "-.inf" || lower = ".nan" then
3232- true
3030+ if
3131+ lower = "null" || lower = "true" || lower = "false" || lower = "yes"
3232+ || lower = "no" || lower = "on" || lower = "off" || lower = "~"
3333+ || lower = ".inf" || lower = "-.inf" || lower = ".nan"
3434+ then true
3335 else
3436 (* Check for problematic characters *)
3537 try
3636- String.iter (fun c ->
3737- if c = ':' || c = '#' || c = '\n' || c = '\r' then
3838- raise Exit
3939- ) s;
3838+ String.iter
3939+ (fun c ->
4040+ if c = ':' || c = '#' || c = '\n' || c = '\r' then raise Exit)
4141+ s;
4042 (* Check if it looks like a number *)
4141- (try ignore (Float.of_string s); true with _ -> false)
4343+ try
4444+ ignore (Float.of_string s);
4545+ true
4646+ with _ -> false
4247 with Exit -> true
43484444-(** Check if a string requires double quotes (vs single quotes).
4545- Returns true if the string contains characters that need escape sequences. *)
4949+(** Check if a string requires double quotes (vs single quotes). Returns true if
5050+ the string contains characters that need escape sequences. *)
4651let needs_double_quotes s =
4752 try
4848- String.iter (fun c ->
4949- if c = '\n' || c = '\r' || c = '\t' || c = '\\' ||
5050- c < ' ' || c = '"' then
5151- raise Exit
5252- ) s;
5353+ String.iter
5454+ (fun c ->
5555+ if c = '\n' || c = '\r' || c = '\t' || c = '\\' || c < ' ' || c = '"'
5656+ then raise Exit)
5757+ s;
5358 false
5459 with Exit -> true
55605661(** Choose the appropriate quoting style for a string value *)
5762let choose_style s =
5863 match (needs_double_quotes s, needs_quoting s) with
5959- | (true, _) -> `Double_quoted
6060- | (_, true) -> `Single_quoted
6464+ | true, _ -> `Double_quoted
6565+ | _, true -> `Single_quoted
6166 | _ -> `Plain
6262-
+22-24
lib/scalar.ml
···1414 style : Scalar_style.t;
1515}
16161717-let make
1818- ?(anchor : string option)
1919- ?(tag : string option)
2020- ?(plain_implicit = true)
2121- ?(quoted_implicit = false)
2222- ?(style = `Plain)
2323- value =
1717+let make ?(anchor : string option) ?(tag : string option)
1818+ ?(plain_implicit = true) ?(quoted_implicit = false) ?(style = `Plain) value
1919+ =
2420 { anchor; tag; value; plain_implicit; quoted_implicit; style }
25212622let value t = t.value
···2925let style t = t.style
3026let plain_implicit t = t.plain_implicit
3127let quoted_implicit t = t.quoted_implicit
3232-3328let with_anchor anchor t = { t with anchor = Some anchor }
3429let with_tag tag t = { t with tag = Some tag }
3530let with_style style t = { t with style }
···4136 Format.fprintf fmt ", style=%a)" Scalar_style.pp t.style
42374338let equal a b =
4444- Option.equal String.equal a.anchor b.anchor &&
4545- Option.equal String.equal a.tag b.tag &&
4646- String.equal a.value b.value &&
4747- a.plain_implicit = b.plain_implicit &&
4848- a.quoted_implicit = b.quoted_implicit &&
4949- Scalar_style.equal a.style b.style
3939+ Option.equal String.equal a.anchor b.anchor
4040+ && Option.equal String.equal a.tag b.tag
4141+ && String.equal a.value b.value
4242+ && a.plain_implicit = b.plain_implicit
4343+ && a.quoted_implicit = b.quoted_implicit
4444+ && Scalar_style.equal a.style b.style
50455146let compare a b =
5247 let c = Option.compare String.compare a.anchor b.anchor in
5353- if c <> 0 then c else
5454- let c = Option.compare String.compare a.tag b.tag in
5555- if c <> 0 then c else
5656- let c = String.compare a.value b.value in
5757- if c <> 0 then c else
5858- let c = Bool.compare a.plain_implicit b.plain_implicit in
5959- if c <> 0 then c else
6060- let c = Bool.compare a.quoted_implicit b.quoted_implicit in
6161- if c <> 0 then c else
6262- Scalar_style.compare a.style b.style
4848+ if c <> 0 then c
4949+ else
5050+ let c = Option.compare String.compare a.tag b.tag in
5151+ if c <> 0 then c
5252+ else
5353+ let c = String.compare a.value b.value in
5454+ if c <> 0 then c
5555+ else
5656+ let c = Bool.compare a.plain_implicit b.plain_implicit in
5757+ if c <> 0 then c
5858+ else
5959+ let c = Bool.compare a.quoted_implicit b.quoted_implicit in
6060+ if c <> 0 then c else Scalar_style.compare a.style b.style
···5566(** YAML tokenizer/scanner with lookahead for ambiguity resolution *)
7788-(** Simple key tracking for mapping key disambiguation *)
98type simple_key = {
109 sk_possible : bool;
1110 sk_required : bool;
1211 sk_token_number : int;
1312 sk_position : Position.t;
1413}
1414+(** Simple key tracking for mapping key disambiguation *)
15151616-(** Indent level tracking *)
1716type indent = {
1817 indent : int;
1918 needs_block_end : bool;
2019}
2020+(** Indent level tracking *)
21212222type t = {
2323 input : Input.t;
···2727 mutable stream_started : bool;
2828 mutable stream_ended : bool;
2929 mutable indent_stack : indent list; (** Stack of indentation levels *)
3030- mutable flow_level : int; (** Nesting depth in \[\] or \{\} *)
3131- mutable flow_indent : int; (** Column where outermost flow collection started *)
3232- mutable simple_keys : simple_key option list; (** Per flow-level simple key tracking *)
3030+ mutable flow_level : int; (** Nesting depth in \[\] or \{\} *)
3131+ mutable flow_indent : int;
3232+ (** Column where outermost flow collection started *)
3333+ mutable simple_keys : simple_key option list;
3434+ (** Per flow-level simple key tracking *)
3335 mutable allow_simple_key : bool;
3434- mutable leading_whitespace : bool; (** True when at start of line (only whitespace seen) *)
3535- mutable document_has_content : bool; (** True if we've emitted content tokens in current document *)
3636- mutable adjacent_value_allowed_at : Position.t option; (** Position where adjacent : is allowed *)
3737- mutable flow_mapping_stack : bool list; (** Stack of whether each flow level is a mapping *)
3636+ mutable leading_whitespace : bool;
3737+ (** True when at start of line (only whitespace seen) *)
3838+ mutable document_has_content : bool;
3939+ (** True if we've emitted content tokens in current document *)
4040+ mutable adjacent_value_allowed_at : Position.t option;
4141+ (** Position where adjacent : is allowed *)
4242+ mutable flow_mapping_stack : bool list;
4343+ (** Stack of whether each flow level is a mapping *)
3844}
39454046let create input =
···4854 indent_stack = [];
4955 flow_level = 0;
5056 flow_indent = 0;
5151- simple_keys = [None]; (* One entry for the base level *)
5757+ simple_keys = [ None ];
5858+ (* One entry for the base level *)
5259 allow_simple_key = true;
5353- leading_whitespace = true; (* Start at beginning of stream *)
6060+ leading_whitespace = true;
6161+ (* Start at beginning of stream *)
5462 document_has_content = false;
5563 adjacent_value_allowed_at = None;
5664 flow_mapping_stack = [];
···5967let of_string s = create (Input.of_string s)
6068let of_input = create
6169let of_reader r = create (Input.of_reader r)
6262-6370let position t = Input.position t.input
64716572(** Add a token to the queue *)
···72797380(** Get current indent level *)
7481let current_indent t =
7575- match t.indent_stack with
7676- | [] -> -1
7777- | { indent; _ } :: _ -> indent
8282+ match t.indent_stack with [] -> -1 | { indent; _ } :: _ -> indent
78837979-(** Skip whitespace to end of line, checking for valid comments.
8080- Returns true if any whitespace (including tabs) was found before a comment. *)
8484+(** Skip whitespace to end of line, checking for valid comments. Returns true if
8585+ any whitespace (including tabs) was found before a comment. *)
8186let skip_whitespace_and_comment t =
8287 let has_whitespace = ref false in
8388 (* Skip blanks (spaces and tabs) *)
···98103 Error.raise_at (Input.mark t.input) Invalid_comment
99104 end;
100105 (* Skip to end of line *)
101101- while not (Input.is_eof t.input) && not (Input.next_is_break t.input) do
106106+ while (not (Input.is_eof t.input)) && not (Input.next_is_break t.input) do
102107 ignore (Input.next t.input)
103108 done
104109 end
···109114 let found_space = ref false in
110115 while Input.next_is_blank t.input do
111116 (match Input.peek t.input with
112112- | Some '\t' -> found_tab := true
113113- | Some ' ' -> found_space := true
114114- | _ -> ());
117117+ | Some '\t' -> found_tab := true
118118+ | Some ' ' -> found_space := true
119119+ | _ -> ());
115120 ignore (Input.next t.input)
116121 done;
117122 (!found_tab, !found_space)
···120125let rec skip_to_next_token t =
121126 (* Check for tabs used as indentation in block context *)
122127 (match Input.peek t.input with
123123- | Some '\t' when t.flow_level = 0 && t.leading_whitespace &&
124124- (column t - 1) < current_indent t ->
125125- (* Tab found in indentation zone - this is invalid *)
126126- (* Skip to end of line to check if line has content *)
127127- let start_pos = Input.mark t.input in
128128- while Input.next_is_blank t.input do
129129- ignore (Input.next t.input)
130130- done;
131131- (* If we have content on this line with a tab, raise error *)
132132- if not (Input.next_is_break t.input) && not (Input.is_eof t.input) then
133133- Error.raise_at start_pos Tab_in_indentation
134134- | _ -> ());
128128+ | Some '\t'
129129+ when t.flow_level = 0 && t.leading_whitespace
130130+ && column t - 1 < current_indent t ->
131131+ (* Tab found in indentation zone - this is invalid *)
132132+ (* Skip to end of line to check if line has content *)
133133+ let start_pos = Input.mark t.input in
134134+ while Input.next_is_blank t.input do
135135+ ignore (Input.next t.input)
136136+ done;
137137+ (* If we have content on this line with a tab, raise error *)
138138+ if (not (Input.next_is_break t.input)) && not (Input.is_eof t.input) then
139139+ Error.raise_at start_pos Tab_in_indentation
140140+ | _ -> ());
135141136142 (* Skip blanks and validate comments *)
137143 skip_whitespace_and_comment t;
···158164 ignore (Input.next t.input)
159165 done;
160166 (* If only tabs were used (no spaces) and column < flow_indent, error *)
161161- if not (Input.next_is_break t.input) && not (Input.is_eof t.input) &&
162162- column t < t.flow_indent then
163163- Error.raise_at start_mark Invalid_flow_indentation
167167+ if
168168+ (not (Input.next_is_break t.input))
169169+ && (not (Input.is_eof t.input))
170170+ && column t < t.flow_indent
171171+ then Error.raise_at start_mark Invalid_flow_indentation
164172 end;
165173 skip_to_next_token t
166166- end else begin
174174+ end
175175+ else begin
167176 ignore (Input.next t.input);
168177 skip_to_next_token t
169178 end
···174183 if t.flow_level = 0 && col > current_indent t then begin
175184 t.indent_stack <- { indent = col; needs_block_end = true } :: t.indent_stack;
176185 true
177177- end else
178178- false
186186+ end
187187+ else false
179188180189(** Unroll indentation to given column *)
181190let unroll_indent t col =
182182- while t.flow_level = 0 &&
183183- match t.indent_stack with
184184- | { indent; needs_block_end = true; _ } :: _ when indent > col -> true
185185- | _ -> false
191191+ while
192192+ t.flow_level = 0
193193+ &&
194194+ match t.indent_stack with
195195+ | { indent; needs_block_end = true; _ } :: _ when indent > col -> true
196196+ | _ -> false
186197 do
187198 match t.indent_stack with
188199 | { indent = _; needs_block_end = true; _ } :: rest ->
···199210 (* A simple key is required only if we're in a block context,
200211 at the current indentation level, AND the current indent needs a block end.
201212 This matches saphyr's logic and prevents false positives for values. *)
202202- let required = t.flow_level = 0 &&
203203- match t.indent_stack with
204204- | { indent; needs_block_end = true; _ } :: _ ->
205205- indent = column t
206206- | _ -> false
213213+ let required =
214214+ t.flow_level = 0
215215+ &&
216216+ match t.indent_stack with
217217+ | { indent; needs_block_end = true; _ } :: _ -> indent = column t
218218+ | _ -> false
207219 in
208208- let sk = {
209209- sk_possible = true;
210210- sk_required = required;
211211- sk_token_number = t.token_number;
212212- sk_position = Input.position t.input;
213213- } in
220220+ let sk =
221221+ {
222222+ sk_possible = true;
223223+ sk_required = required;
224224+ sk_token_number = t.token_number;
225225+ sk_position = Input.position t.input;
226226+ }
227227+ in
214228 (* Remove any existing simple key at current level *)
215215- t.simple_keys <- (
216216- match t.simple_keys with
229229+ t.simple_keys <-
230230+ (match t.simple_keys with
217231 | _ :: rest -> Some sk :: rest
218218- | [] -> [Some sk]
219219- )
232232+ | [] -> [ Some sk ])
220233 end
221234222235(** Remove simple key at current level *)
···229242230243(** Stale simple keys that span too many tokens *)
231244let stale_simple_keys t =
232232- t.simple_keys <- List.map (fun sk_opt ->
233233- match sk_opt with
234234- | Some sk when sk.sk_possible &&
235235- (Input.position t.input).line > sk.sk_position.line &&
236236- t.flow_level = 0 ->
237237- if sk.sk_required then
238238- Error.raise_at sk.sk_position Expected_key;
239239- None
240240- | _ -> sk_opt
241241- ) t.simple_keys
245245+ t.simple_keys <-
246246+ List.map
247247+ (fun sk_opt ->
248248+ match sk_opt with
249249+ | Some sk
250250+ when sk.sk_possible
251251+ && (Input.position t.input).line > sk.sk_position.line
252252+ && t.flow_level = 0 ->
253253+ if sk.sk_required then Error.raise_at sk.sk_position Expected_key;
254254+ None
255255+ | _ -> sk_opt)
256256+ t.simple_keys
242257243258(** Read anchor or alias name *)
244259let scan_anchor_alias t =
···251266 This matches the saphyr implementation: is_yaml_non_space && !is_flow *)
252267 while
253268 match Input.peek t.input with
254254- | Some c when not (Input.is_whitespace c) &&
255255- not (Input.is_flow_indicator c) &&
256256- c <> '\x00' ->
269269+ | Some c
270270+ when (not (Input.is_whitespace c))
271271+ && (not (Input.is_flow_indicator c))
272272+ && c <> '\x00' ->
257273 Buffer.add_char buf c;
258274 ignore (Input.next t.input);
259275 true
260276 | _ -> false
261261- do () done;
277277+ do
278278+ ()
279279+ done;
262280 let name = Buffer.contents buf in
263281 if String.length name = 0 then
264282 Error.raise_at start (Invalid_anchor "empty anchor name");
···270288 let buf = Buffer.create 16 in
271289 (* Expect ! *)
272290 (match Input.peek t.input with
273273- | Some '!' ->
274274- Buffer.add_char buf '!';
275275- ignore (Input.next t.input)
276276- | _ -> Error.raise_at start (Invalid_tag "expected '!'"));
291291+ | Some '!' ->
292292+ Buffer.add_char buf '!';
293293+ ignore (Input.next t.input)
294294+ | _ -> Error.raise_at start (Invalid_tag "expected '!'"));
277295 (* Read word chars *)
278296 while
279297 match Input.peek t.input with
···282300 ignore (Input.next t.input);
283301 true
284302 | _ -> false
285285- do () done;
303303+ do
304304+ ()
305305+ done;
286306 (* Check for secondary ! *)
287307 (match Input.peek t.input with
288288- | Some '!' ->
289289- Buffer.add_char buf '!';
290290- ignore (Input.next t.input)
291291- | _ -> ());
308308+ | Some '!' ->
309309+ Buffer.add_char buf '!';
310310+ ignore (Input.next t.input)
311311+ | _ -> ());
292312 Buffer.contents buf
293313294314(** Scan tag suffix (after handle) *)
···298318 in
299319 let hex_val c =
300320 match c with
301301- | '0'..'9' -> Char.code c - Char.code '0'
302302- | 'A'..'F' -> Char.code c - Char.code 'A' + 10
303303- | 'a'..'f' -> Char.code c - Char.code 'a' + 10
321321+ | '0' .. '9' -> Char.code c - Char.code '0'
322322+ | 'A' .. 'F' -> Char.code c - Char.code 'A' + 10
323323+ | 'a' .. 'f' -> Char.code c - Char.code 'a' + 10
304324 | _ -> 0
305325 in
306326 let buf = Buffer.create 32 in
307327 while
308328 match Input.peek t.input with
309309- | Some '%' ->
329329+ | Some '%' -> (
310330 (* Percent-encoded character *)
311331 ignore (Input.next t.input);
312312- (match Input.peek t.input, Input.peek_nth t.input 1 with
313313- | Some c1, Some c2 when is_hex_digit c1 && is_hex_digit c2 ->
314314- ignore (Input.next t.input);
315315- ignore (Input.next t.input);
316316- let code = (hex_val c1) * 16 + (hex_val c2) in
317317- Buffer.add_char buf (Char.chr code);
318318- true
319319- | _ ->
320320- (* Invalid percent encoding - keep the % *)
321321- Buffer.add_char buf '%';
322322- true)
323323- | Some c when not (Input.is_whitespace c) &&
324324- not (Input.is_flow_indicator c) ->
332332+ match (Input.peek t.input, Input.peek_nth t.input 1) with
333333+ | Some c1, Some c2 when is_hex_digit c1 && is_hex_digit c2 ->
334334+ ignore (Input.next t.input);
335335+ ignore (Input.next t.input);
336336+ let code = (hex_val c1 * 16) + hex_val c2 in
337337+ Buffer.add_char buf (Char.chr code);
338338+ true
339339+ | _ ->
340340+ (* Invalid percent encoding - keep the % *)
341341+ Buffer.add_char buf '%';
342342+ true)
343343+ | Some c
344344+ when (not (Input.is_whitespace c)) && not (Input.is_flow_indicator c) ->
325345 Buffer.add_char buf c;
326346 ignore (Input.next t.input);
327347 true
328348 | _ -> false
329329- do () done;
349349+ do
350350+ ()
351351+ done;
330352 Buffer.contents buf
331353332354(** Scan a tag *)
333355let scan_tag t =
334356 let start = Input.mark t.input in
335335- ignore (Input.next t.input); (* consume ! *)
357357+ ignore (Input.next t.input);
358358+ (* consume ! *)
336359 let handle, suffix =
337360 match Input.peek t.input with
338361 | Some '<' ->
···346369 Buffer.add_char buf c;
347370 ignore (Input.next t.input);
348371 true
349349- | None -> Error.raise_at (Input.mark t.input) (Invalid_tag "unclosed verbatim tag")
350350- do () done;
351351- ignore (Input.next t.input); (* consume > *)
372372+ | None ->
373373+ Error.raise_at (Input.mark t.input)
374374+ (Invalid_tag "unclosed verbatim tag")
375375+ do
376376+ ()
377377+ done;
378378+ ignore (Input.next t.input);
379379+ (* consume > *)
352380 ("", Buffer.contents buf)
353381 | Some c when Input.is_whitespace c || Input.is_flow_indicator c ->
354382 (* Non-specific tag: ! *)
355383 ("!", "")
356384 | Some '!' ->
357385 (* Secondary handle: !! *)
358358- ignore (Input.next t.input); (* consume second ! *)
386386+ ignore (Input.next t.input);
387387+ (* consume second ! *)
359388 let suffix = scan_tag_suffix t in
360389 ("!!", suffix)
361361- | _ ->
390390+ | _ -> (
362391 (* Primary handle or just suffix: !foo or !e!foo *)
363392 (* Read alphanumeric characters *)
364393 let buf = Buffer.create 16 in
···369398 ignore (Input.next t.input);
370399 true
371400 | _ -> false
372372- do () done;
401401+ do
402402+ ()
403403+ done;
373404 (* Check if next character is ! - if so, this is a named handle *)
374374- (match Input.peek t.input with
375375- | Some '!' ->
376376- (* Named handle like !e! *)
377377- ignore (Input.next t.input);
378378- let handle_name = Buffer.contents buf in
379379- let suffix = scan_tag_suffix t in
380380- ("!" ^ handle_name ^ "!", suffix)
381381- | _ ->
382382- (* Just ! followed by suffix *)
383383- ("!", Buffer.contents buf ^ scan_tag_suffix t))
405405+ match Input.peek t.input with
406406+ | Some '!' ->
407407+ (* Named handle like !e! *)
408408+ ignore (Input.next t.input);
409409+ let handle_name = Buffer.contents buf in
410410+ let suffix = scan_tag_suffix t in
411411+ ("!" ^ handle_name ^ "!", suffix)
412412+ | _ ->
413413+ (* Just ! followed by suffix *)
414414+ ("!", Buffer.contents buf ^ scan_tag_suffix t))
384415 in
385416 (* Validate that tag is followed by whitespace, break, or (in flow) flow indicator *)
386417 (match Input.peek t.input with
387387- | None -> () (* EOF is ok *)
388388- | Some c when Input.is_whitespace c || Input.is_break c -> ()
389389- | Some c when t.flow_level > 0 && Input.is_flow_indicator c -> ()
390390- | _ -> Error.raise_at start (Invalid_tag "expected whitespace or line break after tag"));
418418+ | None -> () (* EOF is ok *)
419419+ | Some c when Input.is_whitespace c || Input.is_break c -> ()
420420+ | Some c when t.flow_level > 0 && Input.is_flow_indicator c -> ()
421421+ | _ ->
422422+ Error.raise_at start
423423+ (Invalid_tag "expected whitespace or line break after tag"));
391424 let span = Span.make ~start ~stop:(Input.mark t.input) in
392425 (handle, suffix, span)
393426394427(** Scan single-quoted scalar *)
395428let scan_single_quoted t =
396429 let start = Input.mark t.input in
397397- ignore (Input.next t.input); (* consume opening single-quote *)
430430+ ignore (Input.next t.input);
431431+ (* consume opening single-quote *)
398432 let buf = Buffer.create 64 in
399399- let whitespace = Buffer.create 16 in (* Track trailing whitespace *)
433433+ let whitespace = Buffer.create 16 in
434434+ (* Track trailing whitespace *)
400435401436 let flush_whitespace () =
402437 if Buffer.length whitespace > 0 then begin
···408443 let rec loop () =
409444 match Input.peek t.input with
410445 | None -> Error.raise_at start Unclosed_single_quote
411411- | Some '\'' ->
446446+ | Some '\'' -> (
412447 ignore (Input.next t.input);
413448 (* Check for escaped quote ('') *)
414414- (match Input.peek t.input with
415415- | Some '\'' ->
416416- flush_whitespace ();
417417- Buffer.add_char buf '\'';
418418- ignore (Input.next t.input);
419419- loop ()
420420- | _ ->
421421- (* End of string - flush any trailing whitespace *)
422422- flush_whitespace ())
449449+ match Input.peek t.input with
450450+ | Some '\'' ->
451451+ flush_whitespace ();
452452+ Buffer.add_char buf '\'';
453453+ ignore (Input.next t.input);
454454+ loop ()
455455+ | _ ->
456456+ (* End of string - flush any trailing whitespace *)
457457+ flush_whitespace ())
423458 | Some ' ' | Some '\t' ->
424459 (* Track whitespace - don't add to buf yet *)
425460 Buffer.add_char whitespace (Option.get (Input.peek t.input));
···439474 (* Check indentation: continuation must be > block indent (QB6E, DK95) *)
440475 let col = column t in
441476 let indent = current_indent t in
442442- if not (Input.is_eof t.input) && not (Input.next_is_break t.input) && col <= indent && indent >= 0 then
443443- Error.raise_at (Input.mark t.input) (Invalid_quoted_scalar_indentation "invalid indentation in quoted scalar");
477477+ if
478478+ (not (Input.is_eof t.input))
479479+ && (not (Input.next_is_break t.input))
480480+ && col <= indent && indent >= 0
481481+ then
482482+ Error.raise_at (Input.mark t.input)
483483+ (Invalid_quoted_scalar_indentation
484484+ "invalid indentation in quoted scalar");
444485 (* Count empty lines (consecutive line breaks) *)
445486 let empty_lines = ref 0 in
446487 while Input.next_is_break t.input do
···454495 (* Check indentation after each empty line too *)
455496 let col = column t in
456497 let indent = current_indent t in
457457- if not (Input.is_eof t.input) && not (Input.next_is_break t.input) && col <= indent && indent >= 0 then
458458- Error.raise_at (Input.mark t.input) (Invalid_quoted_scalar_indentation "invalid indentation in quoted scalar")
498498+ if
499499+ (not (Input.is_eof t.input))
500500+ && (not (Input.next_is_break t.input))
501501+ && col <= indent && indent >= 0
502502+ then
503503+ Error.raise_at (Input.mark t.input)
504504+ (Invalid_quoted_scalar_indentation
505505+ "invalid indentation in quoted scalar")
459506 done;
460507 (* Apply folding rules *)
461508 if !empty_lines > 0 then begin
···463510 for _ = 1 to !empty_lines do
464511 Buffer.add_char buf '\n'
465512 done
466466- end else
513513+ end
514514+ else
467515 (* Single break: fold to space (even at start of string) *)
468516 Buffer.add_char buf ' ';
469517 loop ()
···486534 | Some c when Input.is_hex c ->
487535 Buffer.add_char buf c;
488536 ignore (Input.next t.input)
489489- | _ ->
490490- Error.raise_at start (Invalid_hex_escape (Buffer.contents buf))
537537+ | _ -> Error.raise_at start (Invalid_hex_escape (Buffer.contents buf))
491538 done;
492539 let code = int_of_string ("0x" ^ Buffer.contents buf) in
493493- if code <= 0x7F then
494494- String.make 1 (Char.chr code)
540540+ if code <= 0x7F then String.make 1 (Char.chr code)
495541 else if code <= 0x7FF then
496542 let b1 = 0xC0 lor (code lsr 6) in
497543 let b2 = 0x80 lor (code land 0x3F) in
···500546 let b1 = 0xE0 lor (code lsr 12) in
501547 let b2 = 0x80 lor ((code lsr 6) land 0x3F) in
502548 let b3 = 0x80 lor (code land 0x3F) in
503503- String.init 3 (fun i -> Char.chr (match i with 0 -> b1 | 1 -> b2 | _ -> b3))
549549+ String.init 3 (fun i ->
550550+ Char.chr (match i with 0 -> b1 | 1 -> b2 | _ -> b3))
504551 else
505552 let b1 = 0xF0 lor (code lsr 18) in
506553 let b2 = 0x80 lor ((code lsr 12) land 0x3F) in
507554 let b3 = 0x80 lor ((code lsr 6) land 0x3F) in
508555 let b4 = 0x80 lor (code land 0x3F) in
509509- String.init 4 (fun i -> Char.chr (match i with 0 -> b1 | 1 -> b2 | 2 -> b3 | _ -> b4))
556556+ String.init 4 (fun i ->
557557+ Char.chr (match i with 0 -> b1 | 1 -> b2 | 2 -> b3 | _ -> b4))
510558511559(** Scan double-quoted scalar *)
512560let scan_double_quoted t =
513561 let start = Input.mark t.input in
514514- ignore (Input.next t.input); (* consume opening double-quote *)
562562+ ignore (Input.next t.input);
563563+ (* consume opening double-quote *)
515564 let buf = Buffer.create 64 in
516516- let whitespace = Buffer.create 16 in (* Track pending whitespace *)
565565+ let whitespace = Buffer.create 16 in
566566+ (* Track pending whitespace *)
517567518568 let flush_whitespace () =
519569 if Buffer.length whitespace > 0 then begin
···529579 (* Flush trailing whitespace before closing quote to preserve it *)
530580 flush_whitespace ();
531581 ignore (Input.next t.input)
532532- | Some ' ' | Some '\t' as c_opt ->
582582+ | (Some ' ' | Some '\t') as c_opt ->
533583 (* Track whitespace - don't add to buf yet *)
534584 let c = match c_opt with Some c -> c | None -> assert false in
535585 Buffer.add_char whitespace c;
···537587 loop ()
538588 | Some '\\' ->
539589 (* Escape sequence - this is non-whitespace content *)
540540- flush_whitespace (); (* Commit any pending whitespace *)
590590+ flush_whitespace ();
591591+ (* Commit any pending whitespace *)
541592 ignore (Input.next t.input);
542593 (match Input.peek t.input with
543543- | None -> Error.raise_at start (Invalid_escape_sequence "\\<EOF>")
544544- | Some '0' -> Buffer.add_char buf '\x00'; ignore (Input.next t.input)
545545- | Some 'a' -> Buffer.add_char buf '\x07'; ignore (Input.next t.input)
546546- | Some 'b' -> Buffer.add_char buf '\x08'; ignore (Input.next t.input)
547547- | Some 't' | Some '\t' -> Buffer.add_char buf '\t'; ignore (Input.next t.input)
548548- | Some 'n' -> Buffer.add_char buf '\n'; ignore (Input.next t.input)
549549- | Some 'v' -> Buffer.add_char buf '\x0B'; ignore (Input.next t.input)
550550- | Some 'f' -> Buffer.add_char buf '\x0C'; ignore (Input.next t.input)
551551- | Some 'r' -> Buffer.add_char buf '\r'; ignore (Input.next t.input)
552552- | Some 'e' -> Buffer.add_char buf '\x1B'; ignore (Input.next t.input)
553553- | Some ' ' -> Buffer.add_char buf ' '; ignore (Input.next t.input)
554554- | Some '"' -> Buffer.add_char buf '"'; ignore (Input.next t.input)
555555- | Some '/' -> Buffer.add_char buf '/'; ignore (Input.next t.input)
556556- | Some '\\' -> Buffer.add_char buf '\\'; ignore (Input.next t.input)
557557- | Some 'N' -> Buffer.add_string buf "\xC2\x85"; ignore (Input.next t.input) (* NEL *)
558558- | Some '_' -> Buffer.add_string buf "\xC2\xA0"; ignore (Input.next t.input) (* NBSP *)
559559- | Some 'L' -> Buffer.add_string buf "\xE2\x80\xA8"; ignore (Input.next t.input) (* LS *)
560560- | Some 'P' -> Buffer.add_string buf "\xE2\x80\xA9"; ignore (Input.next t.input) (* PS *)
561561- | Some 'x' ->
562562- ignore (Input.next t.input);
563563- Buffer.add_string buf (decode_hex t 2)
564564- | Some 'u' ->
565565- ignore (Input.next t.input);
566566- Buffer.add_string buf (decode_hex t 4)
567567- | Some 'U' ->
568568- ignore (Input.next t.input);
569569- Buffer.add_string buf (decode_hex t 8)
570570- | Some '\n' | Some '\r' ->
571571- (* Line continuation escape *)
572572- Input.consume_break t.input;
573573- while Input.next_is_blank t.input do
574574- ignore (Input.next t.input)
575575- done
576576- | Some c ->
577577- Error.raise_at (Input.mark t.input)
578578- (Invalid_escape_sequence (Printf.sprintf "\\%c" c)));
594594+ | None -> Error.raise_at start (Invalid_escape_sequence "\\<EOF>")
595595+ | Some '0' ->
596596+ Buffer.add_char buf '\x00';
597597+ ignore (Input.next t.input)
598598+ | Some 'a' ->
599599+ Buffer.add_char buf '\x07';
600600+ ignore (Input.next t.input)
601601+ | Some 'b' ->
602602+ Buffer.add_char buf '\x08';
603603+ ignore (Input.next t.input)
604604+ | Some 't' | Some '\t' ->
605605+ Buffer.add_char buf '\t';
606606+ ignore (Input.next t.input)
607607+ | Some 'n' ->
608608+ Buffer.add_char buf '\n';
609609+ ignore (Input.next t.input)
610610+ | Some 'v' ->
611611+ Buffer.add_char buf '\x0B';
612612+ ignore (Input.next t.input)
613613+ | Some 'f' ->
614614+ Buffer.add_char buf '\x0C';
615615+ ignore (Input.next t.input)
616616+ | Some 'r' ->
617617+ Buffer.add_char buf '\r';
618618+ ignore (Input.next t.input)
619619+ | Some 'e' ->
620620+ Buffer.add_char buf '\x1B';
621621+ ignore (Input.next t.input)
622622+ | Some ' ' ->
623623+ Buffer.add_char buf ' ';
624624+ ignore (Input.next t.input)
625625+ | Some '"' ->
626626+ Buffer.add_char buf '"';
627627+ ignore (Input.next t.input)
628628+ | Some '/' ->
629629+ Buffer.add_char buf '/';
630630+ ignore (Input.next t.input)
631631+ | Some '\\' ->
632632+ Buffer.add_char buf '\\';
633633+ ignore (Input.next t.input)
634634+ | Some 'N' ->
635635+ Buffer.add_string buf "\xC2\x85";
636636+ ignore (Input.next t.input) (* NEL *)
637637+ | Some '_' ->
638638+ Buffer.add_string buf "\xC2\xA0";
639639+ ignore (Input.next t.input) (* NBSP *)
640640+ | Some 'L' ->
641641+ Buffer.add_string buf "\xE2\x80\xA8";
642642+ ignore (Input.next t.input) (* LS *)
643643+ | Some 'P' ->
644644+ Buffer.add_string buf "\xE2\x80\xA9";
645645+ ignore (Input.next t.input) (* PS *)
646646+ | Some 'x' ->
647647+ ignore (Input.next t.input);
648648+ Buffer.add_string buf (decode_hex t 2)
649649+ | Some 'u' ->
650650+ ignore (Input.next t.input);
651651+ Buffer.add_string buf (decode_hex t 4)
652652+ | Some 'U' ->
653653+ ignore (Input.next t.input);
654654+ Buffer.add_string buf (decode_hex t 8)
655655+ | Some '\n' | Some '\r' ->
656656+ (* Line continuation escape *)
657657+ Input.consume_break t.input;
658658+ while Input.next_is_blank t.input do
659659+ ignore (Input.next t.input)
660660+ done
661661+ | Some c ->
662662+ Error.raise_at (Input.mark t.input)
663663+ (Invalid_escape_sequence (Printf.sprintf "\\%c" c)));
579664 loop ()
580665 | Some '\n' | Some '\r' ->
581666 (* Line break: discard any pending trailing whitespace *)
···596681 if Input.next_is_break t.input then begin
597682 Input.consume_break t.input;
598683 incr empty_lines;
599599- started_with_tab := false (* Reset for next line *)
600600- end else
601601- continue := false
684684+ started_with_tab := false (* Reset for next line *)
685685+ end
686686+ else continue := false
602687 done;
603688 (* Check for document boundary - this terminates the quoted string *)
604689 if Input.at_document_boundary t.input then
···609694 let indent = current_indent t in
610695 let start_col = start.column in
611696 (* DK95/01: if continuation started with tabs and column < start column, error *)
612612- if not (Input.is_eof t.input) && !started_with_tab && col < start_col then
613613- Error.raise_at (Input.mark t.input) (Invalid_quoted_scalar_indentation "invalid indentation in quoted scalar");
614614- if not (Input.is_eof t.input) && col <= indent && indent >= 0 then
615615- Error.raise_at (Input.mark t.input) (Invalid_quoted_scalar_indentation "invalid indentation in quoted scalar");
697697+ if (not (Input.is_eof t.input)) && !started_with_tab && col < start_col
698698+ then
699699+ Error.raise_at (Input.mark t.input)
700700+ (Invalid_quoted_scalar_indentation
701701+ "invalid indentation in quoted scalar");
702702+ if (not (Input.is_eof t.input)) && col <= indent && indent >= 0 then
703703+ Error.raise_at (Input.mark t.input)
704704+ (Invalid_quoted_scalar_indentation
705705+ "invalid indentation in quoted scalar");
616706 (* Per YAML spec: single break = space, break + empty lines = newlines *)
617707 if !empty_lines > 0 then begin
618708 (* Empty lines: output N newlines where N = number of empty lines *)
619709 for _ = 1 to !empty_lines do
620710 Buffer.add_char buf '\n'
621711 done
622622- end else
712712+ end
713713+ else
623714 (* Single break folds to space *)
624715 Buffer.add_char buf ' ';
625716 loop ()
626717 | Some c ->
627718 (* Non-whitespace character *)
628628- flush_whitespace (); (* Commit any pending whitespace *)
719719+ flush_whitespace ();
720720+ (* Commit any pending whitespace *)
629721 Buffer.add_char buf c;
630722 ignore (Input.next t.input);
631723 loop ()
···637729(** Check if character can appear in plain scalar at this position *)
638730let can_continue_plain t c ~in_flow =
639731 match c with
640640- | ':' ->
732732+ | ':' -> (
641733 (* : is OK if not followed by whitespace or flow indicator *)
642642- (match Input.peek_nth t.input 1 with
643643- | None -> true
644644- | Some c2 when Input.is_whitespace c2 -> false
645645- | Some c2 when in_flow && Input.is_flow_indicator c2 -> false
646646- | _ -> true)
647647- | '#' ->
734734+ match Input.peek_nth t.input 1 with
735735+ | None -> true
736736+ | Some c2 when Input.is_whitespace c2 -> false
737737+ | Some c2 when in_flow && Input.is_flow_indicator c2 -> false
738738+ | _ -> true)
739739+ | '#' -> (
648740 (* # is a comment indicator only if preceded by whitespace *)
649741 (* Check the previous character to determine if this is a comment *)
650650- (match Input.peek_back t.input with
651651- | None -> true (* At start - can't be comment indicator, allow it *)
652652- | Some c when Input.is_whitespace c -> false (* Preceded by whitespace - comment *)
653653- | Some c when Input.is_break c -> false (* At start of line - comment *)
654654- | _ -> true) (* Not preceded by whitespace - part of scalar *)
742742+ match Input.peek_back t.input with
743743+ | None -> true (* At start - can't be comment indicator, allow it *)
744744+ | Some c when Input.is_whitespace c ->
745745+ false (* Preceded by whitespace - comment *)
746746+ | Some c when Input.is_break c -> false (* At start of line - comment *)
747747+ | _ -> true (* Not preceded by whitespace - part of scalar *))
655748 | c when in_flow && Input.is_flow_indicator c -> false
656749 | _ when Input.is_break c -> false
657750 | _ -> true
···663756 let indent = current_indent t in
664757 (* In flow context, scalars must be indented more than the current block indent.
665758 This ensures that content at block indent or less ends the flow context. *)
666666- if in_flow && (column t - 1) < indent then
759759+ if in_flow && column t - 1 < indent then
667760 Error.raise_at start Invalid_flow_indentation;
668761 let buf = Buffer.create 64 in
669762 let spaces = Buffer.create 16 in
670670- let whitespace = Buffer.create 16 in (* Track whitespace within a line *)
763763+ let whitespace = Buffer.create 16 in
764764+ (* Track whitespace within a line *)
671765 let leading_blanks = ref false in
672766673767 let rec scan_line () =
···684778 if Buffer.length spaces > 0 then begin
685779 if !leading_blanks then begin
686780 (* Fold line break *)
687687- if Buffer.contents spaces = "\n" then
688688- Buffer.add_char buf ' '
781781+ if Buffer.contents spaces = "\n" then Buffer.add_char buf ' '
689782 else begin
690783 (* Multiple breaks - preserve all but first *)
691784 let s = Buffer.contents spaces in
692785 Buffer.add_substring buf s 1 (String.length s - 1)
693786 end
694694- end else
695695- Buffer.add_buffer buf spaces;
787787+ end
788788+ else Buffer.add_buffer buf spaces;
696789 Buffer.clear spaces
697790 end;
698791 (* Add any pending whitespace from within the line *)
···719812 if !leading_blanks then begin
720813 (* We already had a break - this is an additional break (empty line) *)
721814 Buffer.add_char spaces '\n'
722722- end else begin
815815+ end
816816+ else begin
723817 (* First line break *)
724818 Buffer.clear spaces;
725819 Buffer.add_char spaces '\n';
···739833 (* However, allow empty lines (line breaks) to continue even if dedented *)
740834 if Input.next_is_break t.input then
741835 scan_lines () (* Empty line - continue *)
742742- else if not in_flow && col <= indent then
743743- () (* Stop - dedented or at parent level in block context *)
744744- else if Input.at_document_boundary t.input then
745745- () (* Stop - document boundary *)
746746- else
747747- scan_lines ()
836836+ else if (not in_flow) && col <= indent then ()
837837+ (* Stop - dedented or at parent level in block context *)
838838+ else if Input.at_document_boundary t.input then ()
839839+ (* Stop - document boundary *)
840840+ else scan_lines ()
748841 end
749842 in
750843···755848 let len = String.length value in
756849 let rec find_end i =
757850 if i < 0 then 0
758758- else match value.[i] with
759759- | ' ' | '\t' -> find_end (i - 1)
760760- | _ -> i + 1
851851+ else match value.[i] with ' ' | '\t' -> find_end (i - 1) | _ -> i + 1
761852 in
762853 let end_pos = find_end (len - 1) in
763854 String.sub value 0 end_pos
···769860(** Scan block scalar (literal | or folded >) *)
770861let scan_block_scalar t literal =
771862 let start = Input.mark t.input in
772772- ignore (Input.next t.input); (* consume | or > *)
863863+ ignore (Input.next t.input);
864864+865865+ (* consume | or > *)
773866774867 (* Parse header: optional indentation indicator and chomping *)
775868 let explicit_indent = ref None in
···777870778871 (* First character of header *)
779872 (match Input.peek t.input with
780780- | Some c when Input.is_digit c && c <> '0' ->
781781- explicit_indent := Some (Char.code c - Char.code '0');
782782- ignore (Input.next t.input)
783783- | Some '-' -> chomping := Chomping.Strip; ignore (Input.next t.input)
784784- | Some '+' -> chomping := Chomping.Keep; ignore (Input.next t.input)
785785- | _ -> ());
873873+ | Some c when Input.is_digit c && c <> '0' ->
874874+ explicit_indent := Some (Char.code c - Char.code '0');
875875+ ignore (Input.next t.input)
876876+ | Some '-' ->
877877+ chomping := Chomping.Strip;
878878+ ignore (Input.next t.input)
879879+ | Some '+' ->
880880+ chomping := Chomping.Keep;
881881+ ignore (Input.next t.input)
882882+ | _ -> ());
786883787884 (* Second character of header *)
788885 (match Input.peek t.input with
789789- | Some c when Input.is_digit c && c <> '0' && !explicit_indent = None ->
790790- explicit_indent := Some (Char.code c - Char.code '0');
791791- ignore (Input.next t.input)
792792- | Some '-' when !chomping = Chomping.Clip ->
793793- chomping := Chomping.Strip; ignore (Input.next t.input)
794794- | Some '+' when !chomping = Chomping.Clip ->
795795- chomping := Chomping.Keep; ignore (Input.next t.input)
796796- | _ -> ());
886886+ | Some c when Input.is_digit c && c <> '0' && !explicit_indent = None ->
887887+ explicit_indent := Some (Char.code c - Char.code '0');
888888+ ignore (Input.next t.input)
889889+ | Some '-' when !chomping = Chomping.Clip ->
890890+ chomping := Chomping.Strip;
891891+ ignore (Input.next t.input)
892892+ | Some '+' when !chomping = Chomping.Clip ->
893893+ chomping := Chomping.Keep;
894894+ ignore (Input.next t.input)
895895+ | _ -> ());
797896798897 (* Skip whitespace and optional comment *)
799898 skip_whitespace_and_comment t;
800899801900 (* Consume line break *)
802802- if Input.next_is_break t.input then
803803- Input.consume_break t.input
901901+ if Input.next_is_break t.input then Input.consume_break t.input
804902 else if not (Input.is_eof t.input) then
805903 Error.raise_at (Input.mark t.input)
806904 (Invalid_block_scalar_header "expected newline after header");
···808906 let base_indent = current_indent t in
809907 (* base_indent is the indent level from the stack, -1 if empty.
810908 It's used directly for comparisons in implicit indent case. *)
811811- let content_indent = ref (
812812- match !explicit_indent with
813813- | Some n ->
814814- (* Explicit indent: base_indent is 1-indexed column, convert to 0-indexed.
909909+ let content_indent =
910910+ ref
911911+ (match !explicit_indent with
912912+ | Some n ->
913913+ (* Explicit indent: base_indent is 1-indexed column, convert to 0-indexed.
815914 content_indent = (base_indent - 1) + n, but at least n for document level. *)
816816- let base_level = max 0 (base_indent - 1) in
817817- base_level + n
818818- | None -> 0 (* Will be determined by first non-empty line *)
819819- ) in
915915+ let base_level = max 0 (base_indent - 1) in
916916+ base_level + n
917917+ | None -> 0 (* Will be determined by first non-empty line *))
918918+ in
820919821920 let buf = Buffer.create 256 in
822921 let trailing_breaks = Buffer.create 16 in
823823- let leading_blank = ref false in (* Was the previous line "more indented"? *)
824824- let max_empty_line_indent = ref 0 in (* Track max indent of empty lines before first content *)
922922+ let leading_blank = ref false in
923923+ (* Was the previous line "more indented"? *)
924924+ let max_empty_line_indent = ref 0 in
925925+ (* Track max indent of empty lines before first content *)
825926826927 (* Skip to content indentation, skipping empty lines.
827928 Returns the number of spaces actually skipped (important for detecting dedentation). *)
···829930 if !content_indent > 0 then begin
830931 (* Explicit indent - skip up to content_indent spaces *)
831932 let spaces_skipped = ref 0 in
832832- while !spaces_skipped < !content_indent && Input.next_is (( = ) ' ') t.input do
933933+ while
934934+ !spaces_skipped < !content_indent && Input.next_is (( = ) ' ') t.input
935935+ do
833936 incr spaces_skipped;
834937 ignore (Input.next t.input)
835938 done;
···840943 Buffer.add_char trailing_breaks '\n';
841944 Input.consume_break t.input;
842945 skip_to_content_indent ()
843843- end else if !spaces_skipped < !content_indent then begin
946946+ end
947947+ else if !spaces_skipped < !content_indent then begin
844948 (* Line starts with fewer spaces than content_indent - dedented *)
845949 !spaces_skipped
846846- end else if Input.next_is_blank t.input then begin
950950+ end
951951+ else if Input.next_is_blank t.input then begin
847952 (* Line has spaces/tabs beyond content_indent - could be whitespace content or empty line.
848953 For literal scalars, whitespace-only lines ARE content (not empty).
849954 For folded scalars, whitespace-only lines that are "more indented" are preserved. *)
···853958 else begin
854959 (* Folded: check if rest is only blanks *)
855960 let idx = ref 0 in
856856- while match Input.peek_nth t.input !idx with
857857- | Some c when Input.is_blank c -> incr idx; true
858858- | _ -> false
859859- do () done;
860860- match Input.peek_nth t.input (!idx) with
961961+ while
962962+ match Input.peek_nth t.input !idx with
963963+ | Some c when Input.is_blank c ->
964964+ incr idx;
965965+ true
966966+ | _ -> false
967967+ do
968968+ ()
969969+ done;
970970+ match Input.peek_nth t.input !idx with
861971 | None | Some '\n' | Some '\r' ->
862972 (* Empty/whitespace-only line in folded - skip spaces *)
863973 while Input.next_is_blank t.input do
···870980 (* Has non-whitespace content *)
871981 !content_indent
872982 end
873873- end else
874874- !content_indent
875875- end else begin
983983+ end
984984+ else !content_indent
985985+ end
986986+ else begin
876987 (* Implicit indent - skip empty lines without consuming spaces.
877988 Note: Only SPACES count as indentation. Tabs are content, not indentation.
878989 So we only check for spaces when determining if a line is "empty". *)
···880991 Buffer.add_char trailing_breaks '\n';
881992 Input.consume_break t.input;
882993 skip_to_content_indent ()
883883- end else if Input.next_is (( = ) ' ') t.input then begin
994994+ end
995995+ else if Input.next_is (( = ) ' ') t.input then begin
884996 (* Check if line is empty (only spaces before break) *)
885997 let idx = ref 0 in
886886- while match Input.peek_nth t.input !idx with
887887- | Some ' ' -> incr idx; true
888888- | _ -> false
889889- do () done;
890890- match Input.peek_nth t.input (!idx) with
998998+ while
999999+ match Input.peek_nth t.input !idx with
10001000+ | Some ' ' ->
10011001+ incr idx;
10021002+ true
10031003+ | _ -> false
10041004+ do
10051005+ ()
10061006+ done;
10071007+ match Input.peek_nth t.input !idx with
8911008 | None | Some '\n' | Some '\r' ->
8921009 (* Line has only spaces - empty line *)
8931010 (* Track max indent of empty lines for later validation *)
894894- if !idx > !max_empty_line_indent then
895895- max_empty_line_indent := !idx;
10111011+ if !idx > !max_empty_line_indent then max_empty_line_indent := !idx;
8961012 while Input.next_is (( = ) ' ') t.input do
8971013 ignore (Input.next t.input)
8981014 done;
···9021018 | _ ->
9031019 (* Has content (including tabs which are content, not indentation) *)
9041020 0
905905- end else if Input.next_is (( = ) '\t') t.input then begin
10211021+ end
10221022+ else if Input.next_is (( = ) '\t') t.input then begin
9061023 (* Tab at start of line in implicit indent mode - this is an error (Y79Y)
9071024 because tabs cannot be used as indentation in YAML *)
9081025 Error.raise_at (Input.mark t.input) Tab_in_indentation
909909- end else
10261026+ end
10271027+ else
9101028 (* Not at break or space - other content character *)
9111029 0
9121030 end
···9381056 let should_process =
9391057 if !content_indent = 0 then begin
9401058 (* For implicit indent, content must be more indented than base_level. *)
941941- if line_indent <= base_level then
942942- false (* No content - first line not indented enough *)
10591059+ if line_indent <= base_level then false
10601060+ (* No content - first line not indented enough *)
9431061 else begin
9441062 (* Validate: first content line must be indented at least as much as
9451063 the maximum indent seen on empty lines before it (5LLU, S98Z, W9L4) *)
946946- if line_indent < !max_empty_line_indent && line_indent > base_level then
10641064+ if line_indent < !max_empty_line_indent && line_indent > base_level
10651065+ then
9471066 Error.raise_at (Input.mark t.input)
948948- (Invalid_block_scalar_header "wrongly indented line in block scalar");
10671067+ (Invalid_block_scalar_header
10681068+ "wrongly indented line in block scalar");
9491069 content_indent := line_indent;
9501070 true
9511071 end
952952- end else if line_indent < !content_indent then
953953- false (* Dedented - done with content *)
954954- else
955955- true
10721072+ end
10731073+ else if line_indent < !content_indent then false
10741074+ (* Dedented - done with content *)
10751075+ else true
9561076 in
95710779581078 if should_process then begin
···9601080 For folded scalars, lines that start with any whitespace (space or tab) after the
9611081 content indentation are "more indented" and preserve breaks.
9621082 Note: we check Input.next_is_blank BEFORE reading content to see if content starts with whitespace. *)
963963- let trailing_blank = line_indent > !content_indent || Input.next_is_blank t.input in
10831083+ let trailing_blank =
10841084+ line_indent > !content_indent || Input.next_is_blank t.input
10851085+ in
96410869651087 (* Add trailing breaks to buffer *)
9661088 if Buffer.length buf > 0 then begin
9671089 if Buffer.length trailing_breaks > 0 then begin
968968- if literal then
969969- Buffer.add_buffer buf trailing_breaks
10901090+ if literal then Buffer.add_buffer buf trailing_breaks
9701091 else begin
9711092 (* Folded scalar: fold only if both previous and current lines are not more-indented *)
972972- if not !leading_blank && not trailing_blank then begin
10931093+ if (not !leading_blank) && not trailing_blank then begin
9731094 let breaks = Buffer.contents trailing_breaks in
974974- if String.length breaks = 1 then
975975- Buffer.add_char buf ' '
976976- else
977977- Buffer.add_substring buf breaks 1 (String.length breaks - 1)
978978- end else begin
10951095+ if String.length breaks = 1 then Buffer.add_char buf ' '
10961096+ else Buffer.add_substring buf breaks 1 (String.length breaks - 1)
10971097+ end
10981098+ else begin
9791099 (* Preserve breaks for more-indented lines *)
9801100 Buffer.add_buffer buf trailing_breaks
9811101 end
9821102 end
983983- end else if not literal then
984984- Buffer.add_char buf ' '
985985- end else
986986- Buffer.add_buffer buf trailing_breaks;
11031103+ end
11041104+ else if not literal then Buffer.add_char buf ' '
11051105+ end
11061106+ else Buffer.add_buffer buf trailing_breaks;
9871107 Buffer.clear trailing_breaks;
98811089891109 (* Add extra indentation for literal or more-indented folded lines *)
9901110 (* On the first line (when determining content_indent), we've already consumed all spaces,
9911111 so we should NOT add any back. On subsequent lines, we add only the spaces beyond content_indent. *)
992992- if not first_line && (literal || (!extra_spaces > 0 && not literal)) then begin
11121112+ if (not first_line) && (literal || (!extra_spaces > 0 && not literal))
11131113+ then begin
9931114 for _ = 1 to !extra_spaces do
9941115 Buffer.add_char buf ' '
9951116 done
9961117 end;
99711189981119 (* Read line content *)
999999- while not (Input.is_eof t.input) && not (Input.next_is_break t.input) do
11201120+ while
11211121+ (not (Input.is_eof t.input)) && not (Input.next_is_break t.input)
11221122+ do
10001123 Buffer.add_char buf (Input.next_exn t.input)
10011124 done;
10021125···10231146 | Chomping.Strip -> content
10241147 | Chomping.Clip ->
10251148 if String.length content > 0 then content ^ "\n" else content
10261026- | Chomping.Keep ->
10271027- content ^ Buffer.contents trailing_breaks
11491149+ | Chomping.Keep -> content ^ Buffer.contents trailing_breaks
10281150 in
1029115110301152 let span = Span.make ~start ~stop:(Input.mark t.input) in
···10341156(** Scan directive (after %) *)
10351157let scan_directive t =
10361158 let start = Input.mark t.input in
10371037- ignore (Input.next t.input); (* consume % *)
11591159+ ignore (Input.next t.input);
11601160+11611161+ (* consume % *)
1038116210391163 (* Read directive name *)
10401164 let name_buf = Buffer.create 16 in
···10451169 ignore (Input.next t.input);
10461170 true
10471171 | _ -> false
10481048- do () done;
11721172+ do
11731173+ ()
11741174+ done;
10491175 let name = Buffer.contents name_buf in
1050117610511177 (* Skip blanks *)
···10601186 let minor = ref 0 in
10611187 (* Read major version *)
10621188 while Input.next_is_digit t.input do
10631063- major := !major * 10 + (Char.code (Input.next_exn t.input) - Char.code '0')
11891189+ major :=
11901190+ (!major * 10) + (Char.code (Input.next_exn t.input) - Char.code '0')
10641191 done;
10651192 (* Expect . *)
10661193 (match Input.peek t.input with
10671067- | Some '.' -> ignore (Input.next t.input)
10681068- | _ -> Error.raise_at (Input.mark t.input) (Invalid_yaml_version "expected '.'"));
11941194+ | Some '.' -> ignore (Input.next t.input)
11951195+ | _ ->
11961196+ Error.raise_at (Input.mark t.input)
11971197+ (Invalid_yaml_version "expected '.'"));
10691198 (* Read minor version *)
10701199 while Input.next_is_digit t.input do
10711071- minor := !minor * 10 + (Char.code (Input.next_exn t.input) - Char.code '0')
12001200+ minor :=
12011201+ (!minor * 10) + (Char.code (Input.next_exn t.input) - Char.code '0')
10721202 done;
10731203 (* Validate: only whitespace and comments allowed before line break (MUS6) *)
10741204 skip_whitespace_and_comment t;
10751075- if not (Input.next_is_break t.input) && not (Input.is_eof t.input) then
10761076- Error.raise_at (Input.mark t.input) (Invalid_directive "expected comment or line break after version");
12051205+ if (not (Input.next_is_break t.input)) && not (Input.is_eof t.input) then
12061206+ Error.raise_at (Input.mark t.input)
12071207+ (Invalid_directive "expected comment or line break after version");
10771208 let span = Span.make ~start ~stop:(Input.mark t.input) in
10781078- Token.Version_directive { major = !major; minor = !minor }, span
10791079-12091209+ (Token.Version_directive { major = !major; minor = !minor }, span)
10801210 | "TAG" ->
10811211 (* Tag directive: %TAG !foo! tag:example.com,2000: *)
10821212 let handle = scan_tag_handle t in
···10931223 ignore (Input.next t.input);
10941224 true
10951225 | _ -> false
10961096- do () done;
12261226+ do
12271227+ ()
12281228+ done;
10971229 let prefix = Buffer.contents prefix_buf in
10981230 let span = Span.make ~start ~stop:(Input.mark t.input) in
10991099- Token.Tag_directive { handle; prefix }, span
11001100-12311231+ (Token.Tag_directive { handle; prefix }, span)
11011232 | _ ->
11021233 (* Reserved/Unknown directive - skip to end of line and ignore *)
11031234 (* Per YAML spec, reserved directives should be ignored with a warning *)
11041104- while not (Input.is_eof t.input) && not (Input.next_is_break t.input) do
12351235+ while (not (Input.is_eof t.input)) && not (Input.next_is_break t.input) do
11051236 ignore (Input.next t.input)
11061237 done;
11071238 let span = Span.make ~start ~stop:(Input.mark t.input) in
11081239 (* Return an empty tag directive token to indicate directive was processed but ignored *)
11091109- Token.Tag_directive { handle = ""; prefix = "" }, span
12401240+ (Token.Tag_directive { handle = ""; prefix = "" }, span)
1110124111111242(** Fetch the next token(s) into the queue *)
11121243let rec fetch_next_token t =
···11201251 (* We're about to process actual content, not leading whitespace *)
11211252 t.leading_whitespace <- false;
1122125311231123- if Input.is_eof t.input then
11241124- fetch_stream_end t
11251125- else if Input.at_document_boundary t.input then
11261126- fetch_document_indicator t
12541254+ if Input.is_eof t.input then fetch_stream_end t
12551255+ else if Input.at_document_boundary t.input then fetch_document_indicator t
11271256 else begin
11281257 match Input.peek t.input with
11291258 | None -> fetch_stream_end t
11301130- | Some '%' when (Input.position t.input).column = 1 ->
11311131- fetch_directive t
12591259+ | Some '%' when (Input.position t.input).column = 1 -> fetch_directive t
11321260 | Some '[' -> fetch_flow_collection_start t Token.Flow_sequence_start
11331261 | Some '{' -> fetch_flow_collection_start t Token.Flow_mapping_start
11341262 | Some ']' -> fetch_flow_collection_end t Token.Flow_sequence_end
···11361264 | Some ',' -> fetch_flow_entry t
11371265 | Some '-' when t.flow_level = 0 && check_block_entry t ->
11381266 fetch_block_entry t
11391139- | Some '?' when check_key t ->
11401140- fetch_key t
11411141- | Some ':' when check_value t ->
11421142- fetch_value t
12671267+ | Some '?' when check_key t -> fetch_key t
12681268+ | Some ':' when check_value t -> fetch_value t
11431269 | Some '*' -> fetch_alias t
11441270 | Some '&' -> fetch_anchor t
11451271 | Some '!' -> fetch_tag t
···11471273 | Some '>' when t.flow_level = 0 -> fetch_block_scalar t false
11481274 | Some '\'' -> fetch_single_quoted t
11491275 | Some '"' -> fetch_double_quoted t
11501150- | Some '-' when can_start_plain t ->
11511151- fetch_plain_scalar t
11521152- | Some '?' when can_start_plain t ->
11531153- fetch_plain_scalar t
11541154- | Some ':' when can_start_plain t ->
11551155- fetch_plain_scalar t
11561156- | Some c when can_start_plain_char c t ->
11571157- fetch_plain_scalar t
11581158- | Some c ->
11591159- Error.raise_at (Input.mark t.input) (Unexpected_character c)
12761276+ | Some '-' when can_start_plain t -> fetch_plain_scalar t
12771277+ | Some '?' when can_start_plain t -> fetch_plain_scalar t
12781278+ | Some ':' when can_start_plain t -> fetch_plain_scalar t
12791279+ | Some c when can_start_plain_char c t -> fetch_plain_scalar t
12801280+ | Some c -> Error.raise_at (Input.mark t.input) (Unexpected_character c)
11601281 end
1161128211621283and fetch_stream_end t =
···11771298 let indicator = Input.peek_string t.input 3 in
11781299 Input.skip t.input 3;
11791300 let span = Span.make ~start ~stop:(Input.mark t.input) in
11801180- let token = if indicator = "---" then Token.Document_start else Token.Document_end in
13011301+ let token =
13021302+ if indicator = "---" then Token.Document_start else Token.Document_end
13031303+ in
11811304 (* Reset document content flag after document end marker *)
11821305 if indicator = "..." then begin
11831306 t.document_has_content <- false;
11841307 (* After document end marker, skip whitespace and check for end of line or comment *)
11851185- while Input.next_is_blank t.input do ignore (Input.next t.input) done;
11861186- (match Input.peek t.input with
11871187- | None -> () (* EOF is ok *)
11881188- | Some c when Input.is_break c -> ()
11891189- | Some '#' -> () (* Comment is ok *)
11901190- | _ -> Error.raise_at start (Invalid_directive "content not allowed after document end marker on same line"))
13081308+ while Input.next_is_blank t.input do
13091309+ ignore (Input.next t.input)
13101310+ done;
13111311+ match Input.peek t.input with
13121312+ | None -> () (* EOF is ok *)
13131313+ | Some c when Input.is_break c -> ()
13141314+ | Some '#' -> () (* Comment is ok *)
13151315+ | _ ->
13161316+ Error.raise_at start
13171317+ (Invalid_directive
13181318+ "content not allowed after document end marker on same line")
11911319 end;
11921320 emit t span token
11931321···11981326 If we've emitted content in the current document, we need a document end marker first *)
11991327 if t.document_has_content then
12001328 Error.raise_at (Input.mark t.input)
12011201- (Unexpected_token "directives must be separated from document content by document end marker (...)");
13291329+ (Unexpected_token
13301330+ "directives must be separated from document content by document end \
13311331+ marker (...)");
12021332 unroll_indent t (-1);
12031333 remove_simple_key t;
12041334 t.allow_simple_key <- false;
···12081338and fetch_flow_collection_start t token_type =
12091339 save_simple_key t;
12101340 (* Record indent of outermost flow collection *)
12111211- if t.flow_level = 0 then
12121212- t.flow_indent <- column t;
13411341+ if t.flow_level = 0 then t.flow_indent <- column t;
12131342 t.flow_level <- t.flow_level + 1;
12141343 (* Track whether this is a mapping or sequence *)
12151215- let is_mapping = (token_type = Token.Flow_mapping_start) in
13441344+ let is_mapping = token_type = Token.Flow_mapping_start in
12161345 t.flow_mapping_stack <- is_mapping :: t.flow_mapping_stack;
12171346 t.allow_simple_key <- true;
12181347 t.simple_keys <- None :: t.simple_keys;
···12251354and fetch_flow_collection_end t token_type =
12261355 remove_simple_key t;
12271356 t.flow_level <- t.flow_level - 1;
12281228- t.flow_mapping_stack <- (match t.flow_mapping_stack with _ :: rest -> rest | [] -> []);
13571357+ t.flow_mapping_stack <-
13581358+ (match t.flow_mapping_stack with _ :: rest -> rest | [] -> []);
12291359 t.simple_keys <- (match t.simple_keys with _ :: rest -> rest | [] -> []);
12301360 t.allow_simple_key <- false;
12311361 let start = Input.mark t.input in
···12701400 ignore (Input.next t.input);
1271140112721402 (* Check for tabs after - : pattern like -\t- is invalid *)
12731273- let (found_tabs, _found_spaces) = skip_blanks_check_tabs t in
14031403+ let found_tabs, _found_spaces = skip_blanks_check_tabs t in
12741404 if found_tabs then begin
12751405 (* If we found tabs and next char is - followed by whitespace, error *)
12761406 match Input.peek t.input with
12771277- | Some '-' ->
12781278- (match Input.peek_nth t.input 1 with
12791279- | None -> Error.raise_at start Tab_in_indentation
12801280- | Some c when Input.is_whitespace c ->
12811281- Error.raise_at start Tab_in_indentation
12821282- | Some _ -> ())
14071407+ | Some '-' -> (
14081408+ match Input.peek_nth t.input 1 with
14091409+ | None -> Error.raise_at start Tab_in_indentation
14101410+ | Some c when Input.is_whitespace c ->
14111411+ Error.raise_at start Tab_in_indentation
14121412+ | Some _ -> ())
12831413 | _ -> ()
12841414 end;
12851415···12891419and check_key t =
12901420 (* ? followed by whitespace or flow indicator in both block and flow *)
12911421 match Input.peek_nth t.input 1 with
12921292- | None -> true
12931293- | Some c ->
12941294- Input.is_whitespace c ||
12951295- (t.flow_level > 0 && Input.is_flow_indicator c)
14221422+ | None -> true
14231423+ | Some c ->
14241424+ Input.is_whitespace c || (t.flow_level > 0 && Input.is_flow_indicator c)
1296142512971426and fetch_key t =
12981427 if t.flow_level = 0 then begin
···13111440 ignore (Input.next t.input);
1312144113131442 (* Check for tabs after ? : pattern like ?\t- or ?\tkey is invalid *)
13141314- let (found_tabs, _found_spaces) = skip_blanks_check_tabs t in
14431443+ let found_tabs, _found_spaces = skip_blanks_check_tabs t in
13151444 if found_tabs && t.flow_level = 0 then begin
13161445 (* In block context, tabs after ? are not allowed *)
13171446 Error.raise_at start Tab_in_indentation
···13241453 (* : followed by whitespace in block, or whitespace/flow indicator in flow, or adjacent value *)
13251454 match Input.peek_nth t.input 1 with
13261455 | None -> true
13271327- | Some c ->
13281328- Input.is_whitespace c ||
13291329- (t.flow_level > 0 && Input.is_flow_indicator c) ||
14561456+ | Some c -> (
14571457+ Input.is_whitespace c
14581458+ || (t.flow_level > 0 && Input.is_flow_indicator c)
14591459+ ||
13301460 (* Allow adjacent values in flow context at designated positions *)
13311331- (t.flow_level > 0 &&
13321332- match t.adjacent_value_allowed_at with
13331333- | Some pos -> pos.Position.line = (Input.position t.input).Position.line &&
13341334- pos.Position.column = (Input.position t.input).Position.column
13351335- | None -> false)
14611461+ t.flow_level > 0
14621462+ &&
14631463+ match t.adjacent_value_allowed_at with
14641464+ | Some pos ->
14651465+ pos.Position.line = (Input.position t.input).Position.line
14661466+ && pos.Position.column = (Input.position t.input).Position.column
14671467+ | None -> false)
1336146813371469and fetch_value t =
13381470 let start = Input.mark t.input in
···13421474 | Some sk :: _ when sk.sk_possible ->
13431475 (* In implicit flow mapping (inside a flow sequence), key and : must be on the same line.
13441476 In explicit flow mapping { }, key and : can span lines. *)
13451345- let is_implicit_flow_mapping = match t.flow_mapping_stack with
13461346- | false :: _ -> true (* false = we're in a sequence, so any mapping is implicit *)
14771477+ let is_implicit_flow_mapping =
14781478+ match t.flow_mapping_stack with
14791479+ | false :: _ ->
14801480+ true (* false = we're in a sequence, so any mapping is implicit *)
13471481 | _ -> false
13481482 in
13491349- if is_implicit_flow_mapping && sk.sk_position.line < (Input.position t.input).line then
13501350- Error.raise_at start Illegal_flow_key_line;
14831483+ if
14841484+ is_implicit_flow_mapping
14851485+ && sk.sk_position.line < (Input.position t.input).line
14861486+ then Error.raise_at start Illegal_flow_key_line;
13511487 (* Insert KEY token before the simple key value *)
13521488 let key_span = Span.point sk.sk_position in
13531489 let key_token = { Token.token = Token.Key; span = key_span } in
···13551491 let tokens = Queue.to_seq t.tokens |> Array.of_seq in
13561492 Queue.clear t.tokens;
13571493 let insert_pos = sk.sk_token_number - t.tokens_taken in
13581358- Array.iteri (fun i tok ->
13591359- if i = insert_pos then Queue.add key_token t.tokens;
13601360- Queue.add tok t.tokens
13611361- ) tokens;
13621362- if insert_pos >= Array.length tokens then
13631363- Queue.add key_token t.tokens;
14941494+ Array.iteri
14951495+ (fun i tok ->
14961496+ if i = insert_pos then Queue.add key_token t.tokens;
14971497+ Queue.add tok t.tokens)
14981498+ tokens;
14991499+ if insert_pos >= Array.length tokens then Queue.add key_token t.tokens;
13641500 t.token_number <- t.token_number + 1;
13651501 (* Roll indent for implicit block mapping *)
13661502 if t.flow_level = 0 then begin
···13711507 let bm_token = { Token.token = Token.Block_mapping_start; span } in
13721508 let tokens = Queue.to_seq t.tokens |> Array.of_seq in
13731509 Queue.clear t.tokens;
13741374- Array.iteri (fun i tok ->
13751375- if i = insert_pos then Queue.add bm_token t.tokens;
13761376- Queue.add tok t.tokens
13771377- ) tokens;
15101510+ Array.iteri
15111511+ (fun i tok ->
15121512+ if i = insert_pos then Queue.add bm_token t.tokens;
15131513+ Queue.add tok t.tokens)
15141514+ tokens;
13781515 if insert_pos >= Array.length tokens then
13791516 Queue.add bm_token t.tokens;
13801517 t.token_number <- t.token_number + 1
13811518 end
13821519 end;
13831383- t.simple_keys <- None :: (List.tl t.simple_keys);
15201520+ t.simple_keys <- None :: List.tl t.simple_keys;
13841521 true
13851522 | _ ->
13861523 (* No simple key - this is a complex value (or empty key) *)
···14001537 remove_simple_key t;
14011538 (* In block context without simple key, allow simple keys for compact mappings like ": moon: white"
14021539 In flow context or after using a simple key, disallow simple keys *)
14031403- t.allow_simple_key <- (not used_simple_key) && (t.flow_level = 0);
15401540+ t.allow_simple_key <- (not used_simple_key) && t.flow_level = 0;
14041541 t.document_has_content <- true;
14051542 let start = Input.mark t.input in
14061543 ignore (Input.next t.input);
1407154414081545 (* Check for tabs after : : patterns like :\t- or :\tkey: are invalid in block context (Y79Y/09)
14091546 However, :\t bar (tab followed by space then content) is valid (6BCT) *)
14101410- let (found_tabs, found_spaces) = skip_blanks_check_tabs t in
14111411- if found_tabs && not found_spaces && t.flow_level = 0 then begin
15471547+ let found_tabs, found_spaces = skip_blanks_check_tabs t in
15481548+ if found_tabs && (not found_spaces) && t.flow_level = 0 then begin
14121549 (* In block context, tabs-only after : followed by indicator or alphanumeric are not allowed *)
14131550 match Input.peek t.input with
14141414- | Some ('-' | '?') ->
14151415- Error.raise_at start Tab_in_indentation
14161416- | Some c when (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ->
15511551+ | Some ('-' | '?') -> Error.raise_at start Tab_in_indentation
15521552+ | Some c
15531553+ when (c >= 'a' && c <= 'z')
15541554+ || (c >= 'A' && c <= 'Z')
15551555+ || (c >= '0' && c <= '9') ->
14171556 (* Tab-only followed by alphanumeric - likely a key, which is invalid *)
14181557 Error.raise_at start Tab_in_indentation
14191558 | _ -> ()
···14301569 t.allow_simple_key <- false;
14311570 t.document_has_content <- true;
14321571 let start = Input.mark t.input in
14331433- ignore (Input.next t.input); (* consume * or & *)
15721572+ ignore (Input.next t.input);
15731573+ (* consume * or & *)
14341574 let name, span = scan_anchor_alias t in
14351575 let span = Span.make ~start ~stop:span.stop in
14361576 let token = if is_alias then Token.Alias name else Token.Anchor name in
···14751615 match Input.peek_nth t.input 1 with
14761616 | None -> false
14771617 | Some c ->
14781478- not (Input.is_whitespace c) &&
14791479- (t.flow_level = 0 || not (Input.is_flow_indicator c))
16181618+ (not (Input.is_whitespace c))
16191619+ && (t.flow_level = 0 || not (Input.is_flow_indicator c))
1480162014811621and can_start_plain_char c _t =
14821622 (* Characters that can start a plain scalar *)
···14921632 (* If the plain scalar ended after crossing a line break (leading_blanks = true),
14931633 allow simple keys. This is important because the scanner already consumed the
14941634 line break and leading whitespace when checking for continuation. *)
14951495- if ended_with_linebreak then
14961496- t.allow_simple_key <- true;
16351635+ if ended_with_linebreak then t.allow_simple_key <- true;
14971636 emit t span (Token.Scalar { style = `Plain; value })
1498163714991638(** Check if we need more tokens to resolve simple keys *)
···15021641 else if Queue.is_empty t.tokens then true
15031642 else
15041643 (* Check if any simple key could affect the first queued token *)
15051505- List.exists (function
15061506- | Some sk when sk.sk_possible ->
15071507- sk.sk_token_number >= t.tokens_taken
15081508- | _ -> false
15091509- ) t.simple_keys
16441644+ List.exists
16451645+ (function
16461646+ | Some sk when sk.sk_possible -> sk.sk_token_number >= t.tokens_taken
16471647+ | _ -> false)
16481648+ t.simple_keys
1510164915111650(** Ensure we have enough tokens to return one safely *)
15121651let ensure_tokens t =
···15231662(** Get next token *)
15241663let next t =
15251664 ensure_tokens t;
15261526- if Queue.is_empty t.tokens then
15271527- None
16651665+ if Queue.is_empty t.tokens then None
15281666 else begin
15291667 t.tokens_taken <- t.tokens_taken + 1;
15301668 Some (Queue.pop t.tokens)
···15401678 let rec loop () =
15411679 match next t with
15421680 | None -> ()
15431543- | Some tok -> f tok; loop ()
16811681+ | Some tok ->
16821682+ f tok;
16831683+ loop ()
15441684 in
15451685 loop ()
1546168615471687(** Fold over all tokens *)
15481688let fold f init t =
15491689 let rec loop acc =
15501550- match next t with
15511551- | None -> acc
15521552- | Some tok -> loop (f acc tok)
16901690+ match next t with None -> acc | Some tok -> loop (f acc tok)
15531691 in
15541692 loop init
1555169315561694(** Convert to list *)
15571557-let to_list t =
15581558- fold (fun acc tok -> tok :: acc) [] t |> List.rev
16951695+let to_list t = fold (fun acc tok -> tok :: acc) [] t |> List.rev
+20-28
lib/sequence.ml
···1313 members : 'a list;
1414}
15151616-let make
1717- ?(anchor : string option)
1818- ?(tag : string option)
1919- ?(implicit = true)
2020- ?(style = `Any)
2121- members =
1616+let make ?(anchor : string option) ?(tag : string option) ?(implicit = true)
1717+ ?(style = `Any) members =
2218 { anchor; tag; implicit; style; members }
23192420let members t = t.members
···2622let tag t = t.tag
2723let implicit t = t.implicit
2824let style t = t.style
2929-3025let with_anchor anchor t = { t with anchor = Some anchor }
3126let with_tag tag t = { t with tag = Some tag }
3227let with_style style t = { t with style }
3333-3428let map f t = { t with members = List.map f t.members }
3535-3629let length t = List.length t.members
3737-3830let is_empty t = t.members = []
3939-4031let nth t n = List.nth t.members n
4141-4232let nth_opt t n = List.nth_opt t.members n
4343-4433let iter f t = List.iter f t.members
4545-4634let fold f init t = List.fold_left f init t.members
47354836let pp pp_elem fmt t =
···5139 Option.iter (Format.fprintf fmt "tag=%s,@ ") t.tag;
5240 Format.fprintf fmt "style=%a,@ " Layout_style.pp t.style;
5341 Format.fprintf fmt "members=[@,%a@]@,)"
5454- (Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt ",@ ") pp_elem)
4242+ (Format.pp_print_list
4343+ ~pp_sep:(fun fmt () -> Format.fprintf fmt ",@ ")
4444+ pp_elem)
5545 t.members
56465747let equal eq a b =
5858- Option.equal String.equal a.anchor b.anchor &&
5959- Option.equal String.equal a.tag b.tag &&
6060- a.implicit = b.implicit &&
6161- Layout_style.equal a.style b.style &&
6262- List.equal eq a.members b.members
4848+ Option.equal String.equal a.anchor b.anchor
4949+ && Option.equal String.equal a.tag b.tag
5050+ && a.implicit = b.implicit
5151+ && Layout_style.equal a.style b.style
5252+ && List.equal eq a.members b.members
63536454let compare cmp a b =
6555 let c = Option.compare String.compare a.anchor b.anchor in
6666- if c <> 0 then c else
6767- let c = Option.compare String.compare a.tag b.tag in
6868- if c <> 0 then c else
6969- let c = Bool.compare a.implicit b.implicit in
7070- if c <> 0 then c else
7171- let c = Layout_style.compare a.style b.style in
7272- if c <> 0 then c else
7373- List.compare cmp a.members b.members
5656+ if c <> 0 then c
5757+ else
5858+ let c = Option.compare String.compare a.tag b.tag in
5959+ if c <> 0 then c
6060+ else
6161+ let c = Bool.compare a.implicit b.implicit in
6262+ if c <> 0 then c
6363+ else
6464+ let c = Layout_style.compare a.style b.style in
6565+ if c <> 0 then c else List.compare cmp a.members b.members
+166-139
lib/serialize.ml
···10101111(** {1 Internal Helpers} *)
12121313-(** Emit a YAML node using an emit function.
1414- This is the core implementation used by both Emitter.t and function-based APIs. *)
1313+(** Emit a YAML node using an emit function. This is the core implementation
1414+ used by both Emitter.t and function-based APIs. *)
1515let rec emit_yaml_node_impl ~emit (yaml : Yaml.t) =
1616 match yaml with
1717 | `Scalar s ->
1818- emit (Event.Scalar {
1919- anchor = Scalar.anchor s;
2020- tag = Scalar.tag s;
2121- value = Scalar.value s;
2222- plain_implicit = Scalar.plain_implicit s;
2323- quoted_implicit = Scalar.quoted_implicit s;
2424- style = Scalar.style s;
2525- })
2626-2727- | `Alias name ->
2828- emit (Event.Alias { anchor = name })
2929-1818+ emit
1919+ (Event.Scalar
2020+ {
2121+ anchor = Scalar.anchor s;
2222+ tag = Scalar.tag s;
2323+ value = Scalar.value s;
2424+ plain_implicit = Scalar.plain_implicit s;
2525+ quoted_implicit = Scalar.quoted_implicit s;
2626+ style = Scalar.style s;
2727+ })
2828+ | `Alias name -> emit (Event.Alias { anchor = name })
3029 | `A seq ->
3130 let members = Sequence.members seq in
3231 (* Force flow style for empty sequences *)
3332 let style = if members = [] then `Flow else Sequence.style seq in
3434- emit (Event.Sequence_start {
3535- anchor = Sequence.anchor seq;
3636- tag = Sequence.tag seq;
3737- implicit = Sequence.implicit seq;
3838- style;
3939- });
3333+ emit
3434+ (Event.Sequence_start
3535+ {
3636+ anchor = Sequence.anchor seq;
3737+ tag = Sequence.tag seq;
3838+ implicit = Sequence.implicit seq;
3939+ style;
4040+ });
4041 List.iter (emit_yaml_node_impl ~emit) members;
4142 emit Event.Sequence_end
4242-4343 | `O map ->
4444 let members = Mapping.members map in
4545 (* Force flow style for empty mappings *)
4646 let style = if members = [] then `Flow else Mapping.style map in
4747- emit (Event.Mapping_start {
4848- anchor = Mapping.anchor map;
4949- tag = Mapping.tag map;
5050- implicit = Mapping.implicit map;
5151- style;
5252- });
5353- List.iter (fun (k, v) ->
5454- emit_yaml_node_impl ~emit k;
5555- emit_yaml_node_impl ~emit v
5656- ) members;
4747+ emit
4848+ (Event.Mapping_start
4949+ {
5050+ anchor = Mapping.anchor map;
5151+ tag = Mapping.tag map;
5252+ implicit = Mapping.implicit map;
5353+ style;
5454+ });
5555+ List.iter
5656+ (fun (k, v) ->
5757+ emit_yaml_node_impl ~emit k;
5858+ emit_yaml_node_impl ~emit v)
5959+ members;
5760 emit Event.Mapping_end
58615959-(** Emit a Value node using an emit function.
6060- This is the core implementation used by both Emitter.t and function-based APIs. *)
6262+(** Emit a Value node using an emit function. This is the core implementation
6363+ used by both Emitter.t and function-based APIs. *)
6164let rec emit_value_node_impl ~emit ~config (value : Value.t) =
6265 match value with
6366 | `Null ->
6464- emit (Event.Scalar {
6565- anchor = None; tag = None;
6666- value = "null";
6767- plain_implicit = true; quoted_implicit = false;
6868- style = `Plain;
6969- })
7070-6767+ emit
6868+ (Event.Scalar
6969+ {
7070+ anchor = None;
7171+ tag = None;
7272+ value = "null";
7373+ plain_implicit = true;
7474+ quoted_implicit = false;
7575+ style = `Plain;
7676+ })
7177 | `Bool b ->
7272- emit (Event.Scalar {
7373- anchor = None; tag = None;
7474- value = if b then "true" else "false";
7575- plain_implicit = true; quoted_implicit = false;
7676- style = `Plain;
7777- })
7878-7878+ emit
7979+ (Event.Scalar
8080+ {
8181+ anchor = None;
8282+ tag = None;
8383+ value = (if b then "true" else "false");
8484+ plain_implicit = true;
8585+ quoted_implicit = false;
8686+ style = `Plain;
8787+ })
7988 | `Float f ->
8089 let value =
8190 match Float.classify_float f with
···8493 | _ ->
8594 if Float.is_integer f && Float.abs f < 1e15 then
8695 Printf.sprintf "%.0f" f
8787- else
8888- Printf.sprintf "%g" f
9696+ else Printf.sprintf "%g" f
8997 in
9090- emit (Event.Scalar {
9191- anchor = None; tag = None;
9292- value;
9393- plain_implicit = true; quoted_implicit = false;
9494- style = `Plain;
9595- })
9696-9898+ emit
9999+ (Event.Scalar
100100+ {
101101+ anchor = None;
102102+ tag = None;
103103+ value;
104104+ plain_implicit = true;
105105+ quoted_implicit = false;
106106+ style = `Plain;
107107+ })
97108 | `String s ->
98109 let style = Quoting.choose_style s in
9999- emit (Event.Scalar {
100100- anchor = None; tag = None;
101101- value = s;
102102- plain_implicit = style = `Plain;
103103- quoted_implicit = style <> `Plain;
104104- style;
105105- })
106106-110110+ emit
111111+ (Event.Scalar
112112+ {
113113+ anchor = None;
114114+ tag = None;
115115+ value = s;
116116+ plain_implicit = style = `Plain;
117117+ quoted_implicit = style <> `Plain;
118118+ style;
119119+ })
107120 | `A items ->
108121 (* Force flow style for empty sequences, otherwise use config *)
109122 let style =
110123 if items = [] || config.Emitter.layout_style = `Flow then `Flow else `Block
111124 in
112112- emit (Event.Sequence_start {
113113- anchor = None; tag = None;
114114- implicit = true;
115115- style;
116116- });
125125+ emit
126126+ (Event.Sequence_start
127127+ { anchor = None; tag = None; implicit = true; style });
117128 List.iter (emit_value_node_impl ~emit ~config) items;
118129 emit Event.Sequence_end
119119-120130 | `O pairs ->
121131 (* Force flow style for empty mappings, otherwise use config *)
122132 let style =
123133 if pairs = [] || config.Emitter.layout_style = `Flow then `Flow else `Block
124134 in
125125- emit (Event.Mapping_start {
126126- anchor = None; tag = None;
127127- implicit = true;
128128- style;
129129- });
130130- List.iter (fun (k, v) ->
131131- let style = Quoting.choose_style k in
132132- emit (Event.Scalar {
133133- anchor = None; tag = None;
134134- value = k;
135135- plain_implicit = style = `Plain;
136136- quoted_implicit = style <> `Plain;
137137- style;
138138- });
139139- emit_value_node_impl ~emit ~config v
140140- ) pairs;
135135+ emit
136136+ (Event.Mapping_start
137137+ { anchor = None; tag = None; implicit = true; style });
138138+ List.iter
139139+ (fun (k, v) ->
140140+ let style = Quoting.choose_style k in
141141+ emit
142142+ (Event.Scalar
143143+ {
144144+ anchor = None;
145145+ tag = None;
146146+ value = k;
147147+ plain_implicit = style = `Plain;
148148+ quoted_implicit = style <> `Plain;
149149+ style;
150150+ });
151151+ emit_value_node_impl ~emit ~config v)
152152+ pairs;
141153 emit Event.Mapping_end
142154143155(** Strip anchors from a YAML tree (used when resolving aliases for output) *)
···146158 | `Scalar s ->
147159 if Option.is_none (Scalar.anchor s) then yaml
148160 else
149149- `Scalar (Scalar.make
150150- ?tag:(Scalar.tag s)
151151- ~plain_implicit:(Scalar.plain_implicit s)
152152- ~quoted_implicit:(Scalar.quoted_implicit s)
153153- ~style:(Scalar.style s)
154154- (Scalar.value s))
161161+ `Scalar
162162+ (Scalar.make ?tag:(Scalar.tag s)
163163+ ~plain_implicit:(Scalar.plain_implicit s)
164164+ ~quoted_implicit:(Scalar.quoted_implicit s) ~style:(Scalar.style s)
165165+ (Scalar.value s))
155166 | `Alias _ -> yaml
156167 | `A seq ->
157157- `A (Sequence.make
158158- ?tag:(Sequence.tag seq)
159159- ~implicit:(Sequence.implicit seq)
160160- ~style:(Sequence.style seq)
161161- (List.map strip_anchors (Sequence.members seq)))
168168+ `A
169169+ (Sequence.make ?tag:(Sequence.tag seq) ~implicit:(Sequence.implicit seq)
170170+ ~style:(Sequence.style seq)
171171+ (List.map strip_anchors (Sequence.members seq)))
162172 | `O map ->
163163- `O (Mapping.make
164164- ?tag:(Mapping.tag map)
165165- ~implicit:(Mapping.implicit map)
166166- ~style:(Mapping.style map)
167167- (List.map (fun (k, v) -> (strip_anchors k, strip_anchors v)) (Mapping.members map)))
173173+ `O
174174+ (Mapping.make ?tag:(Mapping.tag map) ~implicit:(Mapping.implicit map)
175175+ ~style:(Mapping.style map)
176176+ (List.map
177177+ (fun (k, v) -> (strip_anchors k, strip_anchors v))
178178+ (Mapping.members map)))
168179169180(** Emit a document using an emit function *)
170181let emit_document_impl ?(resolve_aliases = true) ~emit doc =
171171- emit (Event.Document_start {
172172- version = Document.version doc;
173173- implicit = Document.implicit_start doc;
174174- });
182182+ emit
183183+ (Event.Document_start
184184+ {
185185+ version = Document.version doc;
186186+ implicit = Document.implicit_start doc;
187187+ });
175188 (match Document.root doc with
176176- | Some yaml ->
177177- let yaml = if resolve_aliases then
178178- yaml |> Yaml.resolve_aliases |> strip_anchors
179179- else yaml in
180180- emit_yaml_node_impl ~emit yaml
181181- | None ->
182182- emit (Event.Scalar {
183183- anchor = None; tag = None;
184184- value = "";
185185- plain_implicit = true; quoted_implicit = false;
186186- style = `Plain;
187187- }));
189189+ | Some yaml ->
190190+ let yaml =
191191+ if resolve_aliases then yaml |> Yaml.resolve_aliases |> strip_anchors
192192+ else yaml
193193+ in
194194+ emit_yaml_node_impl ~emit yaml
195195+ | None ->
196196+ emit
197197+ (Event.Scalar
198198+ {
199199+ anchor = None;
200200+ tag = None;
201201+ value = "";
202202+ plain_implicit = true;
203203+ quoted_implicit = false;
204204+ style = `Plain;
205205+ }));
188206 emit (Event.Document_end { implicit = Document.implicit_end doc })
189207190208(** {1 Emitter.t-based API} *)
191209192210(** Emit a YAML node to an emitter *)
193193-let emit_yaml_node t yaml =
194194- emit_yaml_node_impl ~emit:(Emitter.emit t) yaml
211211+let emit_yaml_node t yaml = emit_yaml_node_impl ~emit:(Emitter.emit t) yaml
195212196213(** Emit a complete YAML document to an emitter *)
197214let emit_yaml t yaml =
···249266(** Serialize documents to a buffer.
250267251268 @param config Emitter configuration (default: {!Emitter.default_config})
252252- @param resolve_aliases Whether to resolve aliases before emission (default: true)
269269+ @param resolve_aliases
270270+ Whether to resolve aliases before emission (default: true)
253271 @param buffer Optional buffer to append to; creates new one if not provided
254272 @return The buffer containing serialized YAML *)
255255-let documents_to_buffer ?(config = Emitter.default_config) ?(resolve_aliases = true) ?buffer documents =
273273+let documents_to_buffer ?(config = Emitter.default_config)
274274+ ?(resolve_aliases = true) ?buffer documents =
256275 let buf = Option.value buffer ~default:(Buffer.create 1024) in
257276 let t = Emitter.create ~config () in
258277 Emitter.emit t (Event.Stream_start { encoding = config.encoding });
···278297(** Serialize documents to a string.
279298280299 @param config Emitter configuration (default: {!Emitter.default_config})
281281- @param resolve_aliases Whether to resolve aliases before emission (default: true) *)
282282-let documents_to_string ?(config = Emitter.default_config) ?(resolve_aliases = true) documents =
300300+ @param resolve_aliases
301301+ Whether to resolve aliases before emission (default: true) *)
302302+let documents_to_string ?(config = Emitter.default_config)
303303+ ?(resolve_aliases = true) documents =
283304 Buffer.contents (documents_to_buffer ~config ~resolve_aliases documents)
284305285306(** {1 Writer-based API}
286307287287- These functions write directly to a bytesrw [Bytes.Writer.t],
288288- enabling true streaming output without intermediate string allocation.
289289- Uses the emitter's native Writer support for efficiency. *)
308308+ These functions write directly to a bytesrw [Bytes.Writer.t], enabling true
309309+ streaming output without intermediate string allocation. Uses the emitter's
310310+ native Writer support for efficiency. *)
290311291312(** Serialize a Value directly to a Bytes.Writer.
292313293314 @param config Emitter configuration (default: {!Emitter.default_config})
294294- @param eod Whether to write end-of-data after serialization (default: true) *)
295295-let value_to_writer ?(config = Emitter.default_config) ?(eod = true) writer value =
315315+ @param eod Whether to write end-of-data after serialization (default: true)
316316+*)
317317+let value_to_writer ?(config = Emitter.default_config) ?(eod = true) writer
318318+ value =
296319 let t = Emitter.of_writer ~config writer in
297320 emit_value t value;
298321 if eod then Emitter.flush t
···300323(** Serialize a Yaml.t directly to a Bytes.Writer.
301324302325 @param config Emitter configuration (default: {!Emitter.default_config})
303303- @param eod Whether to write end-of-data after serialization (default: true) *)
304304-let yaml_to_writer ?(config = Emitter.default_config) ?(eod = true) writer yaml =
326326+ @param eod Whether to write end-of-data after serialization (default: true)
327327+*)
328328+let yaml_to_writer ?(config = Emitter.default_config) ?(eod = true) writer yaml
329329+ =
305330 let t = Emitter.of_writer ~config writer in
306331 emit_yaml t yaml;
307332 if eod then Emitter.flush t
···309334(** Serialize documents directly to a Bytes.Writer.
310335311336 @param config Emitter configuration (default: {!Emitter.default_config})
312312- @param resolve_aliases Whether to resolve aliases before emission (default: true)
313313- @param eod Whether to write end-of-data after serialization (default: true) *)
314314-let documents_to_writer ?(config = Emitter.default_config) ?(resolve_aliases = true) ?(eod = true) writer documents =
337337+ @param resolve_aliases
338338+ Whether to resolve aliases before emission (default: true)
339339+ @param eod Whether to write end-of-data after serialization (default: true)
340340+*)
341341+let documents_to_writer ?(config = Emitter.default_config)
342342+ ?(resolve_aliases = true) ?(eod = true) writer documents =
315343 let t = Emitter.of_writer ~config writer in
316344 Emitter.emit t (Event.Stream_start { encoding = config.encoding });
317345 List.iter (emit_document ~resolve_aliases t) documents;
···320348321349(** {1 Function-based API}
322350323323- These functions accept an emit function [Event.t -> unit] instead of
324324- an {!Emitter.t}, allowing them to work with any event sink
325325- (e.g., streaming writers, custom processors). *)
351351+ These functions accept an emit function [Event.t -> unit] instead of an
352352+ {!Emitter.t}, allowing them to work with any event sink (e.g., streaming
353353+ writers, custom processors). *)
326354327355(** Emit a YAML node using an emitter function *)
328328-let emit_yaml_node_fn ~emitter yaml =
329329- emit_yaml_node_impl ~emit:emitter yaml
356356+let emit_yaml_node_fn ~emitter yaml = emit_yaml_node_impl ~emit:emitter yaml
330357331358(** Emit a complete YAML stream using an emitter function *)
332359let emit_yaml_fn ~emitter ~config yaml =
+10-16
lib/span.ml
···5566(** Source spans representing ranges in input *)
7788-type t = {
99- start : Position.t;
1010- stop : Position.t;
1111-}
88+type t = { start : Position.t; stop : Position.t }
1291310let make ~start ~stop = { start; stop }
1414-1511let point pos = { start = pos; stop = pos }
16121713let merge a b =
1818- let start = if Position.compare a.start b.start <= 0 then a.start else b.start in
1414+ let start =
1515+ if Position.compare a.start b.start <= 0 then a.start else b.start
1616+ in
1917 let stop = if Position.compare a.stop b.stop >= 0 then a.stop else b.stop in
2018 { start; stop }
21192222-let extend span pos =
2323- { span with stop = pos }
2020+let extend span pos = { span with stop = pos }
24212522let pp fmt t =
2623 if t.start.line = t.stop.line then
2727- Format.fprintf fmt "line %d, columns %d-%d"
2828- t.start.line t.start.column t.stop.column
2929- else
3030- Format.fprintf fmt "lines %d-%d" t.start.line t.stop.line
2424+ Format.fprintf fmt "line %d, columns %d-%d" t.start.line t.start.column
2525+ t.stop.column
2626+ else Format.fprintf fmt "lines %d-%d" t.start.line t.stop.line
31273232-let to_string t =
3333- Format.asprintf "%a" pp t
2828+let to_string t = Format.asprintf "%a" pp t
34293530let compare a b =
3631 let c = Position.compare a.start b.start in
3732 if c <> 0 then c else Position.compare a.stop b.stop
38333939-let equal a b =
4040- Position.equal a.start b.start && Position.equal a.stop b.stop
3434+let equal a b = Position.equal a.start b.start && Position.equal a.stop b.stop
+12-14
lib/tag.ml
···1818 | 0 -> None
1919 | _ when s.[0] <> '!' -> None
2020 | 1 -> Some { handle = "!"; suffix = "" }
2121- | _ ->
2121+ | _ -> (
2222 match s.[1] with
2323- | '!' -> (* !! handle *)
2323+ | '!' ->
2424+ (* !! handle *)
2425 Some { handle = "!!"; suffix = String.sub s 2 (len - 2) }
2525- | '<' -> (* Verbatim tag !<...> *)
2626+ | '<' ->
2727+ (* Verbatim tag !<...> *)
2628 if len > 2 && s.[len - 1] = '>' then
2729 Some { handle = "!"; suffix = String.sub s 2 (len - 3) }
2828- else
2929- None
3030- | _ -> (* Primary handle or local tag *)
3131- Some { handle = "!"; suffix = String.sub s 1 (len - 1) }
3030+ else None
3131+ | _ ->
3232+ (* Primary handle or local tag *)
3333+ Some { handle = "!"; suffix = String.sub s 1 (len - 1) })
32343335let to_string t =
3434- if t.handle = "!" && t.suffix = "" then "!"
3535- else t.handle ^ t.suffix
3636+ if t.handle = "!" && t.suffix = "" then "!" else t.handle ^ t.suffix
36373738let to_uri t =
3839 match t.handle with
···4041 | "!" -> "!" ^ t.suffix
4142 | h -> h ^ t.suffix
42434343-let pp fmt t =
4444- Format.pp_print_string fmt (to_string t)
4545-4646-let equal a b =
4747- String.equal a.handle b.handle && String.equal a.suffix b.suffix
4444+let pp fmt t = Format.pp_print_string fmt (to_string t)
4545+let equal a b = String.equal a.handle b.handle && String.equal a.suffix b.suffix
48464947let compare a b =
5048 let c = String.compare a.handle b.handle in
···5566(** Yamlrw Unix - Channel and file I/O for YAML
7788- This module provides channel and file operations for parsing
99- and emitting YAML using bytesrw for efficient streaming I/O. *)
88+ This module provides channel and file operations for parsing and emitting
99+ YAML using bytesrw for efficient streaming I/O. *)
10101111(** {1 Types} *)
1212···7676(** {1 File Input} *)
77777878val value_of_file :
7979- ?resolve_aliases:bool ->
8080- ?max_nodes:int ->
8181- ?max_depth:int ->
8282- string ->
8383- value
7979+ ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> string -> value
8480(** Parse a JSON-compatible value from a file. *)
85818682val yaml_of_file :
8787- ?resolve_aliases:bool ->
8888- ?max_nodes:int ->
8989- ?max_depth:int ->
9090- string ->
9191- yaml
8383+ ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> string -> yaml
9284(** Parse a full YAML value from a file. *)
93859486val documents_of_file : string -> document list
+16-20
lib/value.ml
···5566(** JSON-compatible YAML value representation *)
7788-type t = [
99- | `Null
88+type t =
99+ [ `Null
1010 | `Bool of bool
1111 | `Float of float
1212 | `String of string
1313 | `A of t list
1414- | `O of (string * t) list
1515-]
1414+ | `O of (string * t) list ]
16151716(* Type equality is ensured by structural compatibility with Yamlrw.value *)
1817···2322let int n : t = `Float (Float.of_int n)
2423let float f : t = `Float f
2524let string s : t = `String s
2626-2725let list f xs : t = `A (List.map f xs)
2826let obj pairs : t = `O pairs
2927···7270 | `O pairs -> List.exists (fun (k, _) -> k = key) pairs
7371 | _ -> false
74727575-let find key = function
7676- | `O pairs -> List.assoc_opt key pairs
7777- | _ -> None
7373+let find key = function `O pairs -> List.assoc_opt key pairs | _ -> None
78747975let get key v =
8080- match find key v with
8181- | Some v -> v
8282- | None -> Error.raise (Key_not_found key)
7676+ match find key v with Some v -> v | None -> Error.raise (Key_not_found key)
83778478let keys = function
8579 | `O pairs -> List.map fst pairs
···9286(** Combinators *)
93879488let combine v1 v2 =
9595- match v1, v2 with
8989+ match (v1, v2) with
9690 | `O o1, `O o2 -> `O (o1 @ o2)
9791 | v1, _ -> Error.raise (Type_mismatch ("object", type_name v1))
9892···113107 | `Float f ->
114108 if Float.is_integer f && Float.abs f < 1e15 then
115109 Format.fprintf fmt "%.0f" f
116116- else
117117- Format.fprintf fmt "%g" f
110110+ else Format.fprintf fmt "%g" f
118111 | `String s -> Format.fprintf fmt "%S" s
119112 | `A [] -> Format.pp_print_string fmt "[]"
120113 | `A items ->
121114 Format.fprintf fmt "@[<hv 2>[@,%a@]@,]"
122122- (Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt ",@ ") pp)
115115+ (Format.pp_print_list
116116+ ~pp_sep:(fun fmt () -> Format.fprintf fmt ",@ ")
117117+ pp)
123118 items
124119 | `O [] -> Format.pp_print_string fmt "{}"
125120 | `O pairs ->
126121 Format.fprintf fmt "@[<hv 2>{@,%a@]@,}"
127127- (Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt ",@ ")
122122+ (Format.pp_print_list
123123+ ~pp_sep:(fun fmt () -> Format.fprintf fmt ",@ ")
128124 (fun fmt (k, v) -> Format.fprintf fmt "@[<hv 2>%S:@ %a@]" k pp v))
129125 pairs
130126131127(** Equality and comparison *)
132128133129let rec equal (a : t) (b : t) =
134134- match a, b with
130130+ match (a, b) with
135131 | `Null, `Null -> true
136132 | `Bool a, `Bool b -> a = b
137133 | `Float a, `Float b -> Float.equal a b
138134 | `String a, `String b -> String.equal a b
139135 | `A a, `A b -> List.equal equal a b
140136 | `O a, `O b ->
141141- List.length a = List.length b &&
142142- List.for_all2 (fun (k1, v1) (k2, v2) -> k1 = k2 && equal v1 v2) a b
137137+ List.length a = List.length b
138138+ && List.for_all2 (fun (k1, v1) (k2, v2) -> k1 = k2 && equal v1 v2) a b
143139 | _ -> false
144140145141let rec compare (a : t) (b : t) =
146146- match a, b with
142142+ match (a, b) with
147143 | `Null, `Null -> 0
148144 | `Null, _ -> -1
149145 | _, `Null -> 1
+115-102
lib/yaml.ml
···5566(** Full YAML representation with anchors, tags, and aliases *)
7788-type t = [
99- | `Scalar of Scalar.t
88+type t =
99+ [ `Scalar of Scalar.t
1010 | `Alias of string
1111 | `A of t Sequence.t
1212- | `O of (t, t) Mapping.t
1313-]
1212+ | `O of (t, t) Mapping.t ]
14131514(** Pretty printing *)
1615···2423(** Equality *)
25242625let rec equal (a : t) (b : t) =
2727- match a, b with
2626+ match (a, b) with
2827 | `Scalar a, `Scalar b -> Scalar.equal a b
2928 | `Alias a, `Alias b -> String.equal a b
3029 | `A a, `A b -> Sequence.equal equal a b
···4039 | `Bool false -> `Scalar (Scalar.make "false")
4140 | `Float f ->
4241 let s =
4343- if Float.is_integer f && Float.abs f < 1e15 then
4444- Printf.sprintf "%.0f" f
4545- else
4646- Printf.sprintf "%g" f
4242+ if Float.is_integer f && Float.abs f < 1e15 then Printf.sprintf "%.0f" f
4343+ else Printf.sprintf "%g" f
4744 in
4845 `Scalar (Scalar.make s)
4949- | `String s ->
5050- `Scalar (Scalar.make s ~style:`Double_quoted)
5151- | `A items ->
5252- `A (Sequence.make (List.map of_value items))
4646+ | `String s -> `Scalar (Scalar.make s ~style:`Double_quoted)
4747+ | `A items -> `A (Sequence.make (List.map of_value items))
5348 | `O pairs ->
5454- `O (Mapping.make (List.map (fun (k, v) ->
5555- (`Scalar (Scalar.make k), of_value v)
5656- ) pairs))
4949+ `O
5050+ (Mapping.make
5151+ (List.map
5252+ (fun (k, v) -> (`Scalar (Scalar.make k), of_value v))
5353+ pairs))
57545858-(** Default limits for alias expansion (protection against billion laughs attack) *)
5555+(** Default limits for alias expansion (protection against billion laughs
5656+ attack) *)
5957let default_max_alias_nodes = 10_000_000
5858+6059let default_max_alias_depth = 100
61606261(** Resolve aliases by replacing them with referenced nodes.
63626464- Processes the tree in document order so that aliases resolve to the
6565- anchor value that was defined at the point the alias was encountered.
6363+ Processes the tree in document order so that aliases resolve to the anchor
6464+ value that was defined at the point the alias was encountered.
66656767- See {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section 3.2.2.2
6868- (Anchors and Aliases)} of the YAML 1.2.2 specification for details on
6969- how anchors and aliases work in YAML.
6666+ See
6767+ {{:https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases}Section 3.2.2.2
6868+ (Anchors and Aliases)} of the YAML 1.2.2 specification for details on how
6969+ anchors and aliases work in YAML.
70707171- This implements protection against the "billion laughs attack"
7272- (see {{:https://yaml.org/spec/1.2.2/#321-processes}Section 3.2.1 (Processes)})
7373- by limiting both the total number of nodes and the nesting depth during expansion.
7171+ This implements protection against the "billion laughs attack" (see
7272+ {{:https://yaml.org/spec/1.2.2/#321-processes}Section 3.2.1 (Processes)}) by
7373+ limiting both the total number of nodes and the nesting depth during
7474+ expansion.
74757575- @param max_nodes Maximum number of nodes to create during expansion (default 10M)
7676- @param max_depth Maximum depth of alias-within-alias resolution (default 100)
7777- @raise Error.Yamlrw_error with {!type:Error.kind} [Alias_expansion_node_limit] if max_nodes is exceeded
7878- @raise Error.Yamlrw_error with {!type:Error.kind} [Alias_expansion_depth_limit] if max_depth is exceeded
7979-*)
8080-let resolve_aliases ?(max_nodes = default_max_alias_nodes) ?(max_depth = default_max_alias_depth) (root : t) : t =
7676+ @param max_nodes
7777+ Maximum number of nodes to create during expansion (default 10M)
7878+ @param max_depth
7979+ Maximum depth of alias-within-alias resolution (default 100)
8080+ @raise Error.Yamlrw_error
8181+ with {!type:Error.kind} [Alias_expansion_node_limit] if max_nodes is
8282+ exceeded
8383+ @raise Error.Yamlrw_error
8484+ with {!type:Error.kind} [Alias_expansion_depth_limit] if max_depth is
8585+ exceeded *)
8686+let resolve_aliases ?(max_nodes = default_max_alias_nodes)
8787+ ?(max_depth = default_max_alias_depth) (root : t) : t =
8188 let anchors = Hashtbl.create 16 in
8289 let node_count = ref 0 in
8390···103110 need expansion if it was registered before those anchors existed *)
104111 resolve ~depth:(depth + 1) target
105112 | None -> Error.raise (Undefined_alias name)
106106-107113 (* Single pass: process in document order, registering anchors and resolving aliases *)
108114 and resolve ~depth (v : t) : t =
109115 check_node_limit ();
···112118 (* Register anchor after we have the resolved node *)
113119 Option.iter (fun name -> register_anchor name v) (Scalar.anchor s);
114120 v
115115- | `Alias name ->
116116- expand_alias ~depth name
121121+ | `Alias name -> expand_alias ~depth name
117122 | `A seq ->
118123 (* First resolve all members in order *)
119119- let resolved_members = List.map (resolve ~depth) (Sequence.members seq) in
120120- let resolved = `A (Sequence.make
121121- ?anchor:(Sequence.anchor seq)
122122- ?tag:(Sequence.tag seq)
123123- ~implicit:(Sequence.implicit seq)
124124- ~style:(Sequence.style seq)
125125- resolved_members) in
124124+ let resolved_members =
125125+ List.map (resolve ~depth) (Sequence.members seq)
126126+ in
127127+ let resolved =
128128+ `A
129129+ (Sequence.make ?anchor:(Sequence.anchor seq) ?tag:(Sequence.tag seq)
130130+ ~implicit:(Sequence.implicit seq) ~style:(Sequence.style seq)
131131+ resolved_members)
132132+ in
126133 (* Register anchor with resolved node *)
127134 Option.iter (fun name -> register_anchor name resolved) (Sequence.anchor seq);
128135 resolved
129136 | `O map ->
130137 (* Process key-value pairs in document order *)
131131- let resolved_pairs = List.map (fun (k, v) ->
132132- let resolved_k = resolve ~depth k in
133133- let resolved_v = resolve ~depth v in
134134- (resolved_k, resolved_v)
135135- ) (Mapping.members map) in
136136- let resolved = `O (Mapping.make
137137- ?anchor:(Mapping.anchor map)
138138- ?tag:(Mapping.tag map)
139139- ~implicit:(Mapping.implicit map)
140140- ~style:(Mapping.style map)
141141- resolved_pairs) in
138138+ let resolved_pairs =
139139+ List.map
140140+ (fun (k, v) ->
141141+ let resolved_k = resolve ~depth k in
142142+ let resolved_v = resolve ~depth v in
143143+ (resolved_k, resolved_v))
144144+ (Mapping.members map)
145145+ in
146146+ let resolved =
147147+ `O
148148+ (Mapping.make ?anchor:(Mapping.anchor map) ?tag:(Mapping.tag map)
149149+ ~implicit:(Mapping.implicit map) ~style:(Mapping.style map)
150150+ resolved_pairs)
151151+ in
142152 (* Register anchor with resolved node *)
143153 Option.iter (fun name -> register_anchor name resolved) (Mapping.anchor map);
144154 resolved
···153163154164 (* If explicitly tagged, respect the tag *)
155165 match tag with
156156- | Some "tag:yaml.org,2002:null" | Some "!!null" ->
157157- `Null
158158- | Some "tag:yaml.org,2002:bool" | Some "!!bool" ->
159159- (match String.lowercase_ascii value with
160160- | "true" | "yes" | "on" -> `Bool true
161161- | "false" | "no" | "off" -> `Bool false
162162- | _ -> Error.raise (Invalid_scalar_conversion (value, "bool")))
163163- | Some "tag:yaml.org,2002:int" | Some "!!int" ->
164164- (try `Float (Float.of_string value)
165165- with _ -> Error.raise (Invalid_scalar_conversion (value, "int")))
166166- | Some "tag:yaml.org,2002:float" | Some "!!float" ->
167167- (try `Float (Float.of_string value)
168168- with _ -> Error.raise (Invalid_scalar_conversion (value, "float")))
169169- | Some "tag:yaml.org,2002:str" | Some "!!str" ->
170170- `String value
166166+ | Some "tag:yaml.org,2002:null" | Some "!!null" -> `Null
167167+ | Some "tag:yaml.org,2002:bool" | Some "!!bool" -> (
168168+ match String.lowercase_ascii value with
169169+ | "true" | "yes" | "on" -> `Bool true
170170+ | "false" | "no" | "off" -> `Bool false
171171+ | _ -> Error.raise (Invalid_scalar_conversion (value, "bool")))
172172+ | Some "tag:yaml.org,2002:int" | Some "!!int" -> (
173173+ try `Float (Float.of_string value)
174174+ with _ -> Error.raise (Invalid_scalar_conversion (value, "int")))
175175+ | Some "tag:yaml.org,2002:float" | Some "!!float" -> (
176176+ try `Float (Float.of_string value)
177177+ with _ -> Error.raise (Invalid_scalar_conversion (value, "float")))
178178+ | Some "tag:yaml.org,2002:str" | Some "!!str" -> `String value
171179 | Some _ ->
172180 (* Unknown tag - treat as string *)
173181 `String value
174182 | None ->
175183 (* Implicit type resolution for plain scalars *)
176176- if style <> `Plain then
177177- `String value
178178- else
179179- infer_scalar_type value
184184+ if style <> `Plain then `String value else infer_scalar_type value
180185181186(** Infer type from plain scalar value *)
182187and infer_scalar_type value =
···208213 else if (first = '-' || first = '+') && len >= 2 then
209214 let second = value.[1] in
210215 (* After sign, must be digit or dot-digit (for +.5, -.5) *)
211211- second >= '0' && second <= '9' ||
212212- (second = '.' && len >= 3 && value.[2] >= '0' && value.[2] <= '9')
216216+ (second >= '0' && second <= '9')
217217+ || (second = '.' && len >= 3 && value.[2] >= '0' && value.[2] <= '9')
213218 else false
214219 in
215220 (* Try integer/float *)
···231236 | _ ->
232237 (* Decimal with leading zero or octal in YAML 1.1 *)
233238 Some (`Float (Float.of_string value))
234234- else
235235- Some (`Float (Float.of_string value))
239239+ else Some (`Float (Float.of_string value))
236240 with _ -> None
237241 else None
238242 in
···244248 OCaml accepts "nan", "inf", "infinity" which are NOT valid YAML floats.
245249 YAML requires the leading dot: .nan, .inf, -.inf
246250 See: https://github.com/avsm/ocaml-yaml/issues/82 *)
247247- if String.length value >= 2 && value.[0] = '.' &&
248248- value.[1] >= '0' && value.[1] <= '9' then
249249- try `Float (Float.of_string value)
250250- with _ -> `String value
251251- else
252252- `String value
251251+ if
252252+ String.length value >= 2
253253+ && value.[0] = '.'
254254+ && value.[1] >= '0'
255255+ && value.[1] <= '9'
256256+ then try `Float (Float.of_string value) with _ -> `String value
257257+ else `String value
253258254259(** Convert to JSON-compatible Value.
255260256256- Converts a full YAML representation to a simplified JSON-compatible value type.
257257- This process implements the representation graph to serialization tree conversion
258258- described in {{:https://yaml.org/spec/1.2.2/#32-processes}Section 3.2 (Processes)}
259259- of the YAML 1.2.2 specification.
261261+ Converts a full YAML representation to a simplified JSON-compatible value
262262+ type. This process implements the representation graph to serialization tree
263263+ conversion described in
264264+ {{:https://yaml.org/spec/1.2.2/#32-processes}Section 3.2 (Processes)} of the
265265+ YAML 1.2.2 specification.
260266261261- See also {{:https://yaml.org/spec/1.2.2/#10212-json-schema}Section 10.2.1.2
262262- (JSON Schema)} for the tag resolution used during conversion.
267267+ See also
268268+ {{:https://yaml.org/spec/1.2.2/#10212-json-schema}Section 10.2.1.2 (JSON
269269+ Schema)} for the tag resolution used during conversion.
263270264264- @param resolve_aliases_first Whether to resolve aliases before conversion (default true)
271271+ @param resolve_aliases_first
272272+ Whether to resolve aliases before conversion (default true)
265273 @param max_nodes Maximum nodes during alias expansion (default 10M)
266274 @param max_depth Maximum alias nesting depth (default 100)
267267- @raise Error.Yamlrw_error with {!type:Error.kind} [Unresolved_alias] if resolve_aliases_first is false and an alias is encountered
268268-*)
269269-let to_value
270270- ?(resolve_aliases_first = true)
275275+ @raise Error.Yamlrw_error
276276+ with {!type:Error.kind} [Unresolved_alias] if resolve_aliases_first is
277277+ false and an alias is encountered *)
278278+let to_value ?(resolve_aliases_first = true)
271279 ?(max_nodes = default_max_alias_nodes)
272272- ?(max_depth = default_max_alias_depth)
273273- (v : t) : Value.t =
274274- let v = if resolve_aliases_first then resolve_aliases ~max_nodes ~max_depth v else v in
280280+ ?(max_depth = default_max_alias_depth) (v : t) : Value.t =
281281+ let v =
282282+ if resolve_aliases_first then resolve_aliases ~max_nodes ~max_depth v else v
283283+ in
275284 let rec convert (v : t) : Value.t =
276285 match v with
277286 | `Scalar s -> scalar_to_value s
278287 | `Alias name -> Error.raise (Unresolved_alias name)
279288 | `A seq -> `A (List.map convert (Sequence.members seq))
280289 | `O map ->
281281- `O (List.map (fun (k, v) ->
282282- let key = match k with
283283- | `Scalar s -> Scalar.value s
284284- | _ -> Error.raise (Type_mismatch ("string key", "complex key"))
285285- in
286286- (key, convert v)
287287- ) (Mapping.members map))
290290+ `O
291291+ (List.map
292292+ (fun (k, v) ->
293293+ let key =
294294+ match k with
295295+ | `Scalar s -> Scalar.value s
296296+ | _ ->
297297+ Error.raise (Type_mismatch ("string key", "complex key"))
298298+ in
299299+ (key, convert v))
300300+ (Mapping.members map))
288301 in
289302 convert v
290303
+183-253
lib/yamlrw.ml
···11111212exception Yamlrw_error = Error.Yamlrw_error
13131414-1514(** {2 Core Types} *)
16151616+type value =
1717+ [ `Null (** YAML null, ~, or empty values *)
1818+ | `Bool of bool (** YAML booleans (true, false, yes, no, on, off) *)
1919+ | `Float of float (** All YAML numbers (integers stored as floats) *)
2020+ | `String of string (** YAML strings *)
2121+ | `A of value list (** YAML sequences/arrays *)
2222+ | `O of (string * value) list (** YAML mappings/objects with string keys *)
2323+ ]
1724(** JSON-compatible YAML representation. Use this for simple data interchange.
18251926 This type is structurally equivalent to {!Value.t} and compatible with the
2020- ezjsonm representation. For additional operations, see {!Value} and {!Util}. *)
2121-type value = [
2222- | `Null (** YAML null, ~, or empty values *)
2323- | `Bool of bool (** YAML booleans (true, false, yes, no, on, off) *)
2424- | `Float of float (** All YAML numbers (integers stored as floats) *)
2525- | `String of string (** YAML strings *)
2626- | `A of value list (** YAML sequences/arrays *)
2727- | `O of (string * value) list (** YAML mappings/objects with string keys *)
2828-]
2727+ ezjsonm representation. For additional operations, see {!Value} and {!Util}.
2828+*)
29293030+type yaml =
3131+ [ `Scalar of Scalar.t (** YAML scalar value with style and metadata *)
3232+ | `Alias of string (** Alias reference to an anchored node *)
3333+ | `A of yaml Sequence.t (** YAML sequence with style and metadata *)
3434+ | `O of (yaml, yaml) Mapping.t (** YAML mapping with style and metadata *)
3535+ ]
3036(** Full YAML representation preserving anchors, tags, and aliases.
31373238 This type is structurally equivalent to {!Yaml.t}. Use this when you need
···3440 type tags for custom types, scalar styles (plain, quoted, literal, folded),
3541 and collection styles (block vs flow).
36423737- For additional operations, see {!Yaml}, {!Scalar}, {!Sequence}, and {!Mapping}. *)
3838-type yaml = [
3939- | `Scalar of Scalar.t (** YAML scalar value with style and metadata *)
4040- | `Alias of string (** Alias reference to an anchored node *)
4141- | `A of yaml Sequence.t (** YAML sequence with style and metadata *)
4242- | `O of (yaml, yaml) Mapping.t (** YAML mapping with style and metadata *)
4343-]
4343+ For additional operations, see {!Yaml}, {!Scalar}, {!Sequence}, and
4444+ {!Mapping}. *)
44454646+type document = {
4747+ version : (int * int) option;
4848+ (** Optional YAML version directive (e.g., (1, 2) for YAML 1.2) *)
4949+ tags : (string * string) list;
5050+ (** TAG directives mapping handles to prefixes *)
5151+ root : yaml option; (** Root content of the document *)
5252+ implicit_start : bool;
5353+ (** Whether the document start marker (---) is implicit *)
5454+ implicit_end : bool; (** Whether the document end marker (...) is implicit *)
5555+}
4556(** A YAML document with directives and metadata.
46574758 This type is structurally equivalent to {!Document.t}. A YAML stream can
4859 contain multiple documents, each separated by document markers.
49605061 For additional operations, see {!Document}. *)
5151-type document = {
5252- version : (int * int) option; (** Optional YAML version directive (e.g., (1, 2) for YAML 1.2) *)
5353- tags : (string * string) list; (** TAG directives mapping handles to prefixes *)
5454- root : yaml option; (** Root content of the document *)
5555- implicit_start : bool; (** Whether the document start marker (---) is implicit *)
5656- implicit_end : bool; (** Whether the document end marker (...) is implicit *)
5757-}
5858-59626063(** {2 Character Encoding} *)
61646265module Encoding = Encoding
6363-64666567(** {2 Parsing} *)
6668···7274(** Default maximum alias nesting depth (100). *)
7375let default_max_alias_depth = Yaml.default_max_alias_depth
74767575-let of_string
7676- ?(resolve_aliases = true)
7777- ?(max_nodes = default_max_alias_nodes)
7878- ?(max_depth = default_max_alias_depth)
7979- s : value =
8080- (Loader.value_of_string ~resolve_aliases ~max_nodes ~max_depth s :> value)
8177(** Parse a YAML string into a JSON-compatible value.
82788379 @param resolve_aliases Whether to expand aliases (default: true)
8480 @param max_nodes Maximum nodes during alias expansion (default: 10M)
8581 @param max_depth Maximum alias nesting depth (default: 100)
8682 @raise Yamlrw_error on parse error or if multiple documents found *)
8383+let of_string ?(resolve_aliases = true) ?(max_nodes = default_max_alias_nodes)
8484+ ?(max_depth = default_max_alias_depth) s : value =
8585+ (Loader.value_of_string ~resolve_aliases ~max_nodes ~max_depth s :> value)
87868888-let yaml_of_string
8989- ?(resolve_aliases = false)
9090- ?(max_nodes = default_max_alias_nodes)
9191- ?(max_depth = default_max_alias_depth)
9292- s : yaml =
9393- (Loader.yaml_of_string ~resolve_aliases ~max_nodes ~max_depth s :> yaml)
9487(** Parse a YAML string preserving full YAML metadata (anchors, tags, etc).
95889689 By default, aliases are NOT resolved, preserving the document structure.
···9992 @param max_nodes Maximum nodes during alias expansion (default: 10M)
10093 @param max_depth Maximum alias nesting depth (default: 100)
10194 @raise Yamlrw_error on parse error or if multiple documents found *)
9595+let yaml_of_string ?(resolve_aliases = false)
9696+ ?(max_nodes = default_max_alias_nodes)
9797+ ?(max_depth = default_max_alias_depth) s : yaml =
9898+ (Loader.yaml_of_string ~resolve_aliases ~max_nodes ~max_depth s :> yaml)
10299103103-let documents_of_string s : document list =
104104- let docs = Loader.documents_of_string s in
105105- List.map (fun (d : Document.t) : document -> {
106106- version = d.version;
107107- tags = d.tags;
108108- root = (d.root :> yaml option);
109109- implicit_start = d.implicit_start;
110110- implicit_end = d.implicit_end;
111111- }) docs
112100(** Parse a multi-document YAML stream.
113101114114- Use this when your YAML input contains multiple documents separated
115115- by document markers (---).
102102+ Use this when your YAML input contains multiple documents separated by
103103+ document markers (---).
116104117105 @raise Yamlrw_error on parse error *)
118118-106106+let documents_of_string s : document list =
107107+ let docs = Loader.documents_of_string s in
108108+ List.map
109109+ (fun (d : Document.t) : document ->
110110+ {
111111+ version = d.version;
112112+ tags = d.tags;
113113+ root = (d.root :> yaml option);
114114+ implicit_start = d.implicit_start;
115115+ implicit_end = d.implicit_end;
116116+ })
117117+ docs
119118120119(** {2 Formatting Styles} *)
121120122121module Scalar_style = Scalar_style
123123-124122module Layout_style = Layout_style
125123126126-127124(** {2 Serialization} *)
128125129126let make_config ~encoding ~scalar_style ~layout_style =
130127 { Emitter.default_config with encoding; scalar_style; layout_style }
131128132132-let to_buffer
133133- ?(encoding = `Utf8)
134134- ?(scalar_style = `Any)
135135- ?(layout_style = `Any)
136136- ?buffer
137137- (value : value) =
138138- let config = make_config ~encoding ~scalar_style ~layout_style in
139139- Serialize.value_to_buffer ~config ?buffer (value :> Value.t)
140129(** Serialize a value to a buffer.
141130142131 @param encoding Output encoding (default: UTF-8)
143132 @param scalar_style Preferred scalar style (default: Any)
144133 @param layout_style Preferred layout style (default: Any)
145145- @param buffer Optional buffer to append to (allocates new one if not provided)
134134+ @param buffer
135135+ Optional buffer to append to (allocates new one if not provided)
146136 @return The buffer containing the serialized YAML *)
137137+let to_buffer ?(encoding = `Utf8) ?(scalar_style = `Any) ?(layout_style = `Any)
138138+ ?buffer (value : value) =
139139+ let config = make_config ~encoding ~scalar_style ~layout_style in
140140+ Serialize.value_to_buffer ~config ?buffer (value :> Value.t)
147141148148-let to_string
149149- ?(encoding = `Utf8)
150150- ?(scalar_style = `Any)
151151- ?(layout_style = `Any)
152152- (value : value) =
153153- Buffer.contents (to_buffer ~encoding ~scalar_style ~layout_style value)
154142(** Serialize a value to a YAML string.
155143156144 @param encoding Output encoding (default: UTF-8)
157145 @param scalar_style Preferred scalar style (default: Any)
158146 @param layout_style Preferred layout style (default: Any) *)
147147+let to_string ?(encoding = `Utf8) ?(scalar_style = `Any) ?(layout_style = `Any)
148148+ (value : value) =
149149+ Buffer.contents (to_buffer ~encoding ~scalar_style ~layout_style value)
159150160160-let yaml_to_buffer
161161- ?(encoding = `Utf8)
162162- ?(scalar_style = `Any)
163163- ?(layout_style = `Any)
164164- ?buffer
165165- (yaml : yaml) =
166166- let config = make_config ~encoding ~scalar_style ~layout_style in
167167- Serialize.yaml_to_buffer ~config ?buffer (yaml :> Yaml.t)
168151(** Serialize a full YAML value to a buffer.
169152170153 @param encoding Output encoding (default: UTF-8)
171154 @param scalar_style Preferred scalar style (default: Any)
172155 @param layout_style Preferred layout style (default: Any)
173173- @param buffer Optional buffer to append to (allocates new one if not provided)
156156+ @param buffer
157157+ Optional buffer to append to (allocates new one if not provided)
174158 @return The buffer containing the serialized YAML *)
159159+let yaml_to_buffer ?(encoding = `Utf8) ?(scalar_style = `Any)
160160+ ?(layout_style = `Any) ?buffer (yaml : yaml) =
161161+ let config = make_config ~encoding ~scalar_style ~layout_style in
162162+ Serialize.yaml_to_buffer ~config ?buffer (yaml :> Yaml.t)
175163176176-let yaml_to_string
177177- ?(encoding = `Utf8)
178178- ?(scalar_style = `Any)
179179- ?(layout_style = `Any)
180180- (yaml : yaml) =
181181- Buffer.contents (yaml_to_buffer ~encoding ~scalar_style ~layout_style yaml)
182164(** Serialize a full YAML value to a string.
183165184166 @param encoding Output encoding (default: UTF-8)
185167 @param scalar_style Preferred scalar style (default: Any)
186168 @param layout_style Preferred layout style (default: Any) *)
169169+let yaml_to_string ?(encoding = `Utf8) ?(scalar_style = `Any)
170170+ ?(layout_style = `Any) (yaml : yaml) =
171171+ Buffer.contents (yaml_to_buffer ~encoding ~scalar_style ~layout_style yaml)
187172188188-let documents_to_buffer
189189- ?(encoding = `Utf8)
190190- ?(scalar_style = `Any)
191191- ?(layout_style = `Any)
192192- ?(resolve_aliases = true)
193193- ?buffer
194194- (documents : document list) =
195195- let config = make_config ~encoding ~scalar_style ~layout_style in
196196- let docs' = List.map (fun (d : document) : Document.t -> {
197197- Document.version = d.version;
198198- Document.tags = d.tags;
199199- Document.root = (d.root :> Yaml.t option);
200200- Document.implicit_start = d.implicit_start;
201201- Document.implicit_end = d.implicit_end;
202202- }) documents in
203203- Serialize.documents_to_buffer ~config ~resolve_aliases ?buffer docs'
204173(** Serialize multiple documents to a buffer.
205174206175 @param encoding Output encoding (default: UTF-8)
207176 @param scalar_style Preferred scalar style (default: Any)
208177 @param layout_style Preferred layout style (default: Any)
209178 @param resolve_aliases Whether to expand aliases (default: true)
210210- @param buffer Optional buffer to append to (allocates new one if not provided)
179179+ @param buffer
180180+ Optional buffer to append to (allocates new one if not provided)
211181 @return The buffer containing the serialized YAML *)
212212-213213-let documents_to_string
214214- ?(encoding = `Utf8)
215215- ?(scalar_style = `Any)
216216- ?(layout_style = `Any)
217217- ?(resolve_aliases = true)
182182+let documents_to_buffer ?(encoding = `Utf8) ?(scalar_style = `Any)
183183+ ?(layout_style = `Any) ?(resolve_aliases = true) ?buffer
218184 (documents : document list) =
219219- Buffer.contents (documents_to_buffer ~encoding ~scalar_style ~layout_style ~resolve_aliases documents)
185185+ let config = make_config ~encoding ~scalar_style ~layout_style in
186186+ let docs' =
187187+ List.map
188188+ (fun (d : document) : Document.t ->
189189+ {
190190+ Document.version = d.version;
191191+ Document.tags = d.tags;
192192+ Document.root = (d.root :> Yaml.t option);
193193+ Document.implicit_start = d.implicit_start;
194194+ Document.implicit_end = d.implicit_end;
195195+ })
196196+ documents
197197+ in
198198+ Serialize.documents_to_buffer ~config ~resolve_aliases ?buffer docs'
199199+220200(** Serialize multiple documents to a YAML stream.
221201222202 @param encoding Output encoding (default: UTF-8)
223203 @param scalar_style Preferred scalar style (default: Any)
224204 @param layout_style Preferred layout style (default: Any)
225205 @param resolve_aliases Whether to expand aliases (default: true) *)
206206+let documents_to_string ?(encoding = `Utf8) ?(scalar_style = `Any)
207207+ ?(layout_style = `Any) ?(resolve_aliases = true) (documents : document list)
208208+ =
209209+ Buffer.contents
210210+ (documents_to_buffer ~encoding ~scalar_style ~layout_style ~resolve_aliases
211211+ documents)
226212227213(** {2 Buffer Parsing} *)
228214229229-let of_buffer
230230- ?(resolve_aliases = true)
231231- ?(max_nodes = default_max_alias_nodes)
232232- ?(max_depth = default_max_alias_depth)
233233- buffer : value =
234234- of_string ~resolve_aliases ~max_nodes ~max_depth (Buffer.contents buffer)
235215(** Parse YAML from a buffer into a JSON-compatible value.
236216237217 @param resolve_aliases Whether to expand aliases (default: true)
238218 @param max_nodes Maximum nodes during alias expansion (default: 10M)
239219 @param max_depth Maximum alias nesting depth (default: 100)
240220 @raise Yamlrw_error on parse error or if multiple documents found *)
221221+let of_buffer ?(resolve_aliases = true) ?(max_nodes = default_max_alias_nodes)
222222+ ?(max_depth = default_max_alias_depth) buffer : value =
223223+ of_string ~resolve_aliases ~max_nodes ~max_depth (Buffer.contents buffer)
241224242242-let yaml_of_buffer
243243- ?(resolve_aliases = false)
244244- ?(max_nodes = default_max_alias_nodes)
245245- ?(max_depth = default_max_alias_depth)
246246- buffer : yaml =
247247- yaml_of_string ~resolve_aliases ~max_nodes ~max_depth (Buffer.contents buffer)
248225(** Parse YAML from a buffer preserving full YAML metadata.
249226250227 @param resolve_aliases Whether to expand aliases (default: false)
251228 @param max_nodes Maximum nodes during alias expansion (default: 10M)
252229 @param max_depth Maximum alias nesting depth (default: 100)
253230 @raise Yamlrw_error on parse error or if multiple documents found *)
231231+let yaml_of_buffer ?(resolve_aliases = false)
232232+ ?(max_nodes = default_max_alias_nodes)
233233+ ?(max_depth = default_max_alias_depth) buffer : yaml =
234234+ yaml_of_string ~resolve_aliases ~max_nodes ~max_depth (Buffer.contents buffer)
254235255255-let documents_of_buffer buffer : document list =
256256- documents_of_string (Buffer.contents buffer)
257236(** Parse a multi-document YAML stream from a buffer.
258237259238 @raise Yamlrw_error on parse error *)
260260-239239+let documents_of_buffer buffer : document list =
240240+ documents_of_string (Buffer.contents buffer)
261241262242(** {2 Conversion} *)
263243264264-let to_json
265265- ?(resolve_aliases = true)
266266- ?(max_nodes = default_max_alias_nodes)
267267- ?(max_depth = default_max_alias_depth)
268268- (yaml : yaml) : value =
269269- (Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes ~max_depth (yaml :> Yaml.t) :> value)
270244(** Convert full YAML to JSON-compatible value.
271245272246 @param resolve_aliases Whether to expand aliases (default: true)
273247 @param max_nodes Maximum nodes during alias expansion (default: 10M)
274248 @param max_depth Maximum alias nesting depth (default: 100)
275249 @raise Yamlrw_error if alias limits exceeded or complex keys found *)
250250+let to_json ?(resolve_aliases = true) ?(max_nodes = default_max_alias_nodes)
251251+ ?(max_depth = default_max_alias_depth) (yaml : yaml) : value =
252252+ (Yaml.to_value ~resolve_aliases_first:resolve_aliases ~max_nodes ~max_depth
253253+ (yaml :> Yaml.t)
254254+ :> value)
276255277277-let of_json (value : value) : yaml =
278278- (Yaml.of_value (value :> Value.t) :> yaml)
279256(** Convert JSON-compatible value to full YAML representation. *)
280280-257257+let of_json (value : value) : yaml = (Yaml.of_value (value :> Value.t) :> yaml)
281258282259(** {2 Pretty Printing & Equality} *)
283260261261+(** Pretty-print a value. *)
284262let pp = Value.pp
285285-(** Pretty-print a value. *)
286263287287-let equal = Value.equal
288264(** Test equality of two values. *)
289289-265265+let equal = Value.equal
290266291267(** {2 Util - Value Combinators} *)
292268293269module Util = struct
294270 (** Combinators for working with {!type:value} values.
295271296296- This module provides constructors, accessors, and transformations
297297- for JSON-compatible YAML values. *)
272272+ This module provides constructors, accessors, and transformations for
273273+ JSON-compatible YAML values. *)
298274299275 type t = Value.t
300276···349325 let get_string v = match v with `String s -> s | _ -> type_error "string" v
350326 let get_list v = match v with `A l -> l | _ -> type_error "list" v
351327 let get_obj v = match v with `O o -> o | _ -> type_error "object" v
352352-353353- let get_int v =
354354- match as_int v with
355355- | Some i -> i
356356- | None -> type_error "int" v
328328+ let get_int v = match as_int v with Some i -> i | None -> type_error "int" v
357329358330 (** {3 Object Operations} *)
359331···361333 | `O pairs -> List.exists (fun (k, _) -> k = key) pairs
362334 | _ -> false
363335364364- let find key = function
365365- | `O pairs -> List.assoc_opt key pairs
366366- | _ -> None
336336+ let find key = function `O pairs -> List.assoc_opt key pairs | _ -> None
337337+ let get key v = match find key v with Some v -> v | None -> raise Not_found
367338368368- let get key v =
369369- match find key v with
370370- | Some v -> v
371371- | None -> raise Not_found
339339+ let keys v =
340340+ match v with `O pairs -> List.map fst pairs | _ -> type_error "object" v
372341373373- let keys v = match v with
374374- | `O pairs -> List.map fst pairs
375375- | _ -> type_error "object" v
376376-377377- let values v = match v with
378378- | `O pairs -> List.map snd pairs
379379- | _ -> type_error "object" v
342342+ let values v =
343343+ match v with `O pairs -> List.map snd pairs | _ -> type_error "object" v
380344381345 let update key value = function
382346 | `O pairs ->
383347 let rec go = function
384384- | [] -> [(key, value)]
348348+ | [] -> [ (key, value) ]
385349 | (k, _) :: rest when k = key -> (key, value) :: rest
386350 | kv :: rest -> kv :: go rest
387351 in
···393357 | v -> type_error "object" v
394358395359 let combine v1 v2 =
396396- match v1, v2 with
360360+ match (v1, v2) with
397361 | `O o1, `O o2 -> `O (o1 @ o2)
398362 | `O _, _ -> type_error "object" v2
399363 | _, _ -> type_error "object" v1
400364401365 (** {3 List Operations} *)
402366403403- let map f = function
404404- | `A l -> `A (List.map f l)
405405- | v -> type_error "list" v
406406-407407- let mapi f = function
408408- | `A l -> `A (List.mapi f l)
409409- | v -> type_error "list" v
367367+ let map f = function `A l -> `A (List.map f l) | v -> type_error "list" v
368368+ let mapi f = function `A l -> `A (List.mapi f l) | v -> type_error "list" v
410369411370 let filter pred = function
412371 | `A l -> `A (List.filter pred l)
···416375 | `A l -> List.fold_left f init l
417376 | v -> type_error "list" v
418377419419- let nth n = function
420420- | `A l -> List.nth_opt l n
421421- | _ -> None
422422-423423- let length = function
424424- | `A l -> List.length l
425425- | `O o -> List.length o
426426- | _ -> 0
378378+ let nth n = function `A l -> List.nth_opt l n | _ -> None
379379+ let length = function `A l -> List.length l | `O o -> List.length o | _ -> 0
427380428381 let flatten = function
429429- | `A l ->
430430- `A (List.concat_map (function `A inner -> inner | v -> [v]) l)
382382+ | `A l -> `A (List.concat_map (function `A inner -> inner | v -> [ v ]) l)
431383 | v -> type_error "list" v
432384433385 (** {3 Path Operations} *)
···435387 let rec get_path path v =
436388 match path with
437389 | [] -> Some v
438438- | key :: rest ->
439439- match find key v with
440440- | Some child -> get_path rest child
441441- | None -> None
390390+ | key :: rest -> (
391391+ match find key v with Some child -> get_path rest child | None -> None)
442392443393 let get_path_exn path v =
444444- match get_path path v with
445445- | Some v -> v
446446- | None -> raise Not_found
394394+ match get_path path v with Some v -> v | None -> raise Not_found
447395448396 (** {3 Iteration} *)
449397···451399 | `O pairs -> List.iter (fun (k, v) -> f k v) pairs
452400 | v -> type_error "object" v
453401454454- let iter_list f = function
455455- | `A l -> List.iter f l
456456- | v -> type_error "list" v
402402+ let iter_list f = function `A l -> List.iter f l | v -> type_error "list" v
457403458404 let fold_obj f init = function
459405 | `O pairs -> List.fold_left (fun acc (k, v) -> f acc k v) init pairs
···472418 (** {3 Conversion Helpers} *)
473419474420 let to_bool ?default v =
475475- match as_bool v, default with
421421+ match (as_bool v, default) with
476422 | Some b, _ -> b
477423 | None, Some d -> d
478424 | None, None -> type_error "bool" v
479425480426 let to_int ?default v =
481481- match as_int v, default with
427427+ match (as_int v, default) with
482428 | Some i, _ -> i
483429 | None, Some d -> d
484430 | None, None -> type_error "int" v
485431486432 let to_float ?default v =
487487- match as_float v, default with
433433+ match (as_float v, default) with
488434 | Some f, _ -> f
489435 | None, Some d -> d
490436 | None, None -> type_error "float" v
491437492438 let to_string ?default v =
493493- match as_string v, default with
439439+ match (as_string v, default) with
494440 | Some s, _ -> s
495441 | None, Some d -> d
496442 | None, None -> type_error "string" v
497443498444 let to_list ?default v =
499499- match as_list v, default with
445445+ match (as_list v, default) with
500446 | Some l, _ -> l
501447 | None, Some d -> d
502448 | None, None -> type_error "list" v
503449end
504504-505450506451(** {2 Stream - Low-Level Event API} *)
507452···521466 type position = Position.t
522467 (** A position in the source (line, column, byte offset). *)
523468524524- (** Result of parsing an event. *)
525469 type event_result = {
526470 event : event;
527471 start_pos : position;
528472 end_pos : position;
529473 }
474474+ (** Result of parsing an event. *)
530475531476 (** {3 Parsing} *)
532477533478 type parser = Parser.t
534479 (** A streaming YAML parser. *)
535480536536- let parser s = Parser.of_string s
537481 (** Create a parser from a string. *)
482482+ let parser s = Parser.of_string s
538483484484+ (** Get the next event from the parser. Returns [None] when parsing is
485485+ complete. *)
539486 let next p =
540487 match Parser.next p with
541488 | Some { event; span } ->
542542- Some {
543543- event;
544544- start_pos = span.start;
545545- end_pos = span.stop;
546546- }
489489+ Some { event; start_pos = span.start; end_pos = span.stop }
547490 | None -> None
548548- (** Get the next event from the parser.
549549- Returns [None] when parsing is complete. *)
550491492492+ (** Iterate over all events from the parser. *)
551493 let iter f p =
552494 let rec go () =
553495 match next p with
···557499 | None -> ()
558500 in
559501 go ()
560560- (** Iterate over all events from the parser. *)
561502503503+ (** Fold over all events from the parser. *)
562504 let fold f init p =
563505 let rec go acc =
564506 match Parser.next p with
···566508 | None -> acc
567509 in
568510 go init
569569- (** Fold over all events from the parser. *)
570511571512 (** {3 Emitting} *)
572513573514 type emitter = Emitter.t
574515 (** A streaming YAML emitter. *)
575516576576- let emitter ?len:_ () = Emitter.create ()
577517 (** Create a new emitter. *)
518518+ let emitter ?len:_ () = Emitter.create ()
578519579579- let contents e = Emitter.contents e
580520 (** Get the emitted YAML string. *)
521521+ let contents e = Emitter.contents e
581522582582- let emit e ev = Emitter.emit e ev
583523 (** Emit an event. *)
524524+ let emit e ev = Emitter.emit e ev
584525585526 (** {3 Event Emission Helpers} *)
586527587528 let stream_start e enc =
588529 Emitter.emit e (Event.Stream_start { encoding = enc })
589530590590- let stream_end e =
591591- Emitter.emit e Event.Stream_end
531531+ let stream_end e = Emitter.emit e Event.Stream_end
592532593533 let document_start e ?version ?(implicit = true) () =
594594- let version = match version with
534534+ let version =
535535+ match version with
595536 | Some `V1_1 -> Some (1, 1)
596537 | Some `V1_2 -> Some (1, 2)
597538 | None -> None
···602543 Emitter.emit e (Event.Document_end { implicit })
603544604545 let scalar e ?anchor ?tag ?(style = `Any) value =
605605- Emitter.emit e (Event.Scalar {
606606- anchor;
607607- tag;
608608- value;
609609- plain_implicit = true;
610610- quoted_implicit = true;
611611- style;
612612- })
546546+ Emitter.emit e
547547+ (Event.Scalar
548548+ {
549549+ anchor;
550550+ tag;
551551+ value;
552552+ plain_implicit = true;
553553+ quoted_implicit = true;
554554+ style;
555555+ })
613556614614- let alias e name =
615615- Emitter.emit e (Event.Alias { anchor = name })
557557+ let alias e name = Emitter.emit e (Event.Alias { anchor = name })
616558617559 let sequence_start e ?anchor ?tag ?(style = `Any) () =
618618- Emitter.emit e (Event.Sequence_start {
619619- anchor;
620620- tag;
621621- implicit = true;
622622- style;
623623- })
560560+ Emitter.emit e
561561+ (Event.Sequence_start { anchor; tag; implicit = true; style })
624562625625- let sequence_end e =
626626- Emitter.emit e Event.Sequence_end
563563+ let sequence_end e = Emitter.emit e Event.Sequence_end
627564628565 let mapping_start e ?anchor ?tag ?(style = `Any) () =
629629- Emitter.emit e (Event.Mapping_start {
630630- anchor;
631631- tag;
632632- implicit = true;
633633- style;
634634- })
566566+ Emitter.emit e (Event.Mapping_start { anchor; tag; implicit = true; style })
635567636636- let mapping_end e =
637637- Emitter.emit e Event.Mapping_end
568568+ let mapping_end e = Emitter.emit e Event.Mapping_end
638569end
639639-640570641571(** {2 Internal Modules} *)
642572643643-(** These modules are exposed for advanced use cases requiring
644644- fine-grained control over parsing, emission, or data structures.
573573+(** These modules are exposed for advanced use cases requiring fine-grained
574574+ control over parsing, emission, or data structures.
645575646576 For typical usage, prefer the top-level functions and {!Util}. *)
647577648648-(** Source position tracking. *)
649578module Position = Position
579579+(** Source position tracking. *)
650580581581+module Span = Span
651582(** Source span (range of positions). *)
652652-module Span = Span
653583584584+module Chomping = Chomping
654585(** Block scalar chomping modes. *)
655655-module Chomping = Chomping
656586657657-(** YAML type tags. *)
658587module Tag = Tag
588588+(** YAML type tags. *)
659589660660-(** JSON-compatible value type and operations. *)
661590module Value = Value
591591+(** JSON-compatible value type and operations. *)
662592663663-(** YAML scalar with metadata. *)
664593module Scalar = Scalar
594594+(** YAML scalar with metadata. *)
665595596596+module Sequence = Sequence
666597(** YAML sequence with metadata. *)
667667-module Sequence = Sequence
668598599599+module Mapping = Mapping
669600(** YAML mapping with metadata. *)
670670-module Mapping = Mapping
671601672672-(** Full YAML value type. *)
673602module Yaml = Yaml
603603+(** Full YAML value type. *)
674604675675-(** YAML document with directives. *)
676605module Document = Document
606606+(** YAML document with directives. *)
677607678678-(** Lexical tokens. *)
679608module Token = Token
609609+(** Lexical tokens. *)
680610611611+module Scanner = Scanner
681612(** Lexical scanner. *)
682682-module Scanner = Scanner
683613684684-(** Parser events. *)
685614module Event = Event
615615+(** Parser events. *)
686616617617+module Parser = Parser
687618(** Event-based parser. *)
688688-module Parser = Parser
689619620620+module Loader = Loader
690621(** Document loader. *)
691691-module Loader = Loader
692622693693-(** Event-based emitter. *)
694623module Emitter = Emitter
624624+(** Event-based emitter. *)
695625696696-(** Input stream utilities. *)
697626module Input = Input
627627+(** Input stream utilities. *)
698628629629+module Serialize = Serialize
699630(** Buffer serialization utilities. *)
700700-module Serialize = Serialize
+96-90
lib/yamlrw.mli
···3232 let age = Yamlrw.Util.(get_int (get "age" value)) in
3333 ]} *)
34343535-3635(** {2 Error Handling} *)
37363837module Error = Error
···4039exception Yamlrw_error of Error.t
4140(** Raised on parse or emit errors. *)
42414343-4442(** {2 Core Types} *)
45434646-type value = [
4747- | `Null (** YAML null, ~, or empty values *)
4848- | `Bool of bool (** YAML booleans (true, false, yes, no, on, off) *)
4949- | `Float of float (** All YAML numbers (integers stored as floats) *)
5050- | `String of string (** YAML strings *)
5151- | `A of value list (** YAML sequences/arrays *)
5252- | `O of (string * value) list (** YAML mappings/objects with string keys *)
5353-]
4444+type value =
4545+ [ `Null (** YAML null, ~, or empty values *)
4646+ | `Bool of bool (** YAML booleans (true, false, yes, no, on, off) *)
4747+ | `Float of float (** All YAML numbers (integers stored as floats) *)
4848+ | `String of string (** YAML strings *)
4949+ | `A of value list (** YAML sequences/arrays *)
5050+ | `O of (string * value) list (** YAML mappings/objects with string keys *)
5151+ ]
5452(** JSON-compatible YAML representation. Use this for simple data interchange.
55535654 This type is structurally equivalent to {!Value.t} and compatible with the
5757- ezjsonm representation. For additional operations, see {!Value} and {!Util}. *)
5555+ ezjsonm representation. For additional operations, see {!Value} and {!Util}.
5656+*)
58575959-type yaml = [
6060- | `Scalar of Scalar.t (** YAML scalar value with style and metadata *)
6161- | `Alias of string (** Alias reference to an anchored node *)
6262- | `A of yaml Sequence.t (** YAML sequence with style and metadata *)
6363- | `O of (yaml, yaml) Mapping.t (** YAML mapping with style and metadata *)
6464-]
5858+type yaml =
5959+ [ `Scalar of Scalar.t (** YAML scalar value with style and metadata *)
6060+ | `Alias of string (** Alias reference to an anchored node *)
6161+ | `A of yaml Sequence.t (** YAML sequence with style and metadata *)
6262+ | `O of (yaml, yaml) Mapping.t (** YAML mapping with style and metadata *)
6363+ ]
6564(** Full YAML representation preserving anchors, tags, and aliases.
66656766 This type is structurally equivalent to {!Yaml.t}. Use this when you need
···6968 type tags for custom types, scalar styles (plain, quoted, literal, folded),
7069 and collection styles (block vs flow).
71707272- For additional operations, see {!Yaml}, {!Scalar}, {!Sequence}, and {!Mapping}. *)
7171+ For additional operations, see {!Yaml}, {!Scalar}, {!Sequence}, and
7272+ {!Mapping}. *)
73737474type document = {
7575- version : (int * int) option; (** Optional YAML version directive (e.g., (1, 2) for YAML 1.2) *)
7676- tags : (string * string) list; (** TAG directives mapping handles to prefixes *)
7777- root : yaml option; (** Root content of the document *)
7878- implicit_start : bool; (** Whether the document start marker (---) is implicit *)
7979- implicit_end : bool; (** Whether the document end marker (...) is implicit *)
7575+ version : (int * int) option;
7676+ (** Optional YAML version directive (e.g., (1, 2) for YAML 1.2) *)
7777+ tags : (string * string) list;
7878+ (** TAG directives mapping handles to prefixes *)
7979+ root : yaml option; (** Root content of the document *)
8080+ implicit_start : bool;
8181+ (** Whether the document start marker (---) is implicit *)
8282+ implicit_end : bool; (** Whether the document end marker (...) is implicit *)
8083}
8184(** A YAML document with directives and metadata.
8285···8487 contain multiple documents, each separated by document markers.
85888689 For additional operations, see {!Document}. *)
8787-88908991(** {2 Character Encoding} *)
90929193module Encoding = Encoding
92949393-9495(** {2 Parsing} *)
95969697type version = [ `V1_1 | `V1_2 ]
···103104(** Default maximum alias nesting depth (100). *)
104105105106val of_string :
106106- ?resolve_aliases:bool ->
107107- ?max_nodes:int ->
108108- ?max_depth:int ->
109109- string -> value
107107+ ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> string -> value
110108(** Parse a YAML string into a JSON-compatible value.
111109112110 @param resolve_aliases Whether to expand aliases (default: true)
···115113 @raise Yamlrw_error on parse error or if multiple documents found *)
116114117115val yaml_of_string :
118118- ?resolve_aliases:bool ->
119119- ?max_nodes:int ->
120120- ?max_depth:int ->
121121- string -> yaml
116116+ ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> string -> yaml
122117(** Parse a YAML string preserving full YAML metadata (anchors, tags, etc).
123118124119 By default, aliases are NOT resolved, preserving the document structure.
···131126val documents_of_string : string -> document list
132127(** Parse a multi-document YAML stream.
133128134134- Use this when your YAML input contains multiple documents separated
135135- by document markers (---).
129129+ Use this when your YAML input contains multiple documents separated by
130130+ document markers (---).
136131137132 @raise Yamlrw_error on parse error *)
138133139139-140134(** {2 Formatting Styles} *)
141135142136module Scalar_style = Scalar_style
143143-144137module Layout_style = Layout_style
145145-146138147139(** {2 Serialization} *)
148140···151143 ?scalar_style:Scalar_style.t ->
152144 ?layout_style:Layout_style.t ->
153145 ?buffer:Buffer.t ->
154154- value -> Buffer.t
146146+ value ->
147147+ Buffer.t
155148(** Serialize a value to a buffer.
156149157150 @param encoding Output encoding (default: UTF-8)
158151 @param scalar_style Preferred scalar style (default: Any)
159152 @param layout_style Preferred layout style (default: Any)
160160- @param buffer Optional buffer to append to (allocates new one if not provided)
153153+ @param buffer
154154+ Optional buffer to append to (allocates new one if not provided)
161155 @return The buffer containing the serialized YAML *)
162156163157val to_string :
164158 ?encoding:Encoding.t ->
165159 ?scalar_style:Scalar_style.t ->
166160 ?layout_style:Layout_style.t ->
167167- value -> string
161161+ value ->
162162+ string
168163(** Serialize a value to a YAML string.
169164170165 @param encoding Output encoding (default: UTF-8)
···176171 ?scalar_style:Scalar_style.t ->
177172 ?layout_style:Layout_style.t ->
178173 ?buffer:Buffer.t ->
179179- yaml -> Buffer.t
174174+ yaml ->
175175+ Buffer.t
180176(** Serialize a full YAML value to a buffer.
181177182178 @param encoding Output encoding (default: UTF-8)
183179 @param scalar_style Preferred scalar style (default: Any)
184180 @param layout_style Preferred layout style (default: Any)
185185- @param buffer Optional buffer to append to (allocates new one if not provided)
181181+ @param buffer
182182+ Optional buffer to append to (allocates new one if not provided)
186183 @return The buffer containing the serialized YAML *)
187184188185val yaml_to_string :
189186 ?encoding:Encoding.t ->
190187 ?scalar_style:Scalar_style.t ->
191188 ?layout_style:Layout_style.t ->
192192- yaml -> string
189189+ yaml ->
190190+ string
193191(** Serialize a full YAML value to a string.
194192195193 @param encoding Output encoding (default: UTF-8)
···202200 ?layout_style:Layout_style.t ->
203201 ?resolve_aliases:bool ->
204202 ?buffer:Buffer.t ->
205205- document list -> Buffer.t
203203+ document list ->
204204+ Buffer.t
206205(** Serialize multiple documents to a buffer.
207206208207 @param encoding Output encoding (default: UTF-8)
209208 @param scalar_style Preferred scalar style (default: Any)
210209 @param layout_style Preferred layout style (default: Any)
211210 @param resolve_aliases Whether to expand aliases (default: true)
212212- @param buffer Optional buffer to append to (allocates new one if not provided)
211211+ @param buffer
212212+ Optional buffer to append to (allocates new one if not provided)
213213 @return The buffer containing the serialized YAML *)
214214215215val documents_to_string :
···217217 ?scalar_style:Scalar_style.t ->
218218 ?layout_style:Layout_style.t ->
219219 ?resolve_aliases:bool ->
220220- document list -> string
220220+ document list ->
221221+ string
221222(** Serialize multiple documents to a YAML stream.
222223223224 @param encoding Output encoding (default: UTF-8)
···228229(** {2 Buffer Parsing} *)
229230230231val of_buffer :
231231- ?resolve_aliases:bool ->
232232- ?max_nodes:int ->
233233- ?max_depth:int ->
234234- Buffer.t -> value
232232+ ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> Buffer.t -> value
235233(** Parse YAML from a buffer into a JSON-compatible value.
236234237235 @param resolve_aliases Whether to expand aliases (default: true)
···240238 @raise Yamlrw_error on parse error or if multiple documents found *)
241239242240val yaml_of_buffer :
243243- ?resolve_aliases:bool ->
244244- ?max_nodes:int ->
245245- ?max_depth:int ->
246246- Buffer.t -> yaml
241241+ ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> Buffer.t -> yaml
247242(** Parse YAML from a buffer preserving full YAML metadata.
248243249244 @param resolve_aliases Whether to expand aliases (default: false)
···256251257252 @raise Yamlrw_error on parse error *)
258253259259-260254(** {2 Conversion} *)
261255262256val to_json :
263263- ?resolve_aliases:bool ->
264264- ?max_nodes:int ->
265265- ?max_depth:int ->
266266- yaml -> value
257257+ ?resolve_aliases:bool -> ?max_nodes:int -> ?max_depth:int -> yaml -> value
267258(** Convert full YAML to JSON-compatible value.
268259269260 @param resolve_aliases Whether to expand aliases (default: true)
···274265val of_json : value -> yaml
275266(** Convert JSON-compatible value to full YAML representation. *)
276267277277-278268(** {2 Pretty Printing & Equality} *)
279269280270val pp : Format.formatter -> value -> unit
···283273val equal : value -> value -> bool
284274(** Test equality of two values. *)
285275286286-287276(** {2 Util - Value Combinators}
288277289278 Combinators for working with {!type:value} values.
290279291291- This module provides constructors, accessors, and transformations
292292- for JSON-compatible YAML values. *)
280280+ This module provides constructors, accessors, and transformations for
281281+ JSON-compatible YAML values. *)
293282294283module Util : sig
295284 type t = Value.t
···400389 (** {3 Object Operations} *)
401390402391 val mem : string -> t -> bool
403403- (** [mem key obj] checks if [key] exists in object [obj].
404404- Returns [false] if [obj] is not an object. *)
392392+ (** [mem key obj] checks if [key] exists in object [obj]. Returns [false] if
393393+ [obj] is not an object. *)
405394406395 val find : string -> t -> t option
407407- (** [find key obj] looks up [key] in object [obj].
408408- Returns [None] if key not found or if [obj] is not an object. *)
396396+ (** [find key obj] looks up [key] in object [obj]. Returns [None] if key not
397397+ found or if [obj] is not an object. *)
409398410399 val get : string -> t -> t
411411- (** [get key obj] looks up [key] in object [obj].
412412- Raises [Not_found] if key not found. *)
400400+ (** [get key obj] looks up [key] in object [obj]. Raises [Not_found] if key
401401+ not found. *)
413402414403 val keys : t -> string list
415404 (** Get all keys from an object.
···420409 @raise Type_error if not an object *)
421410422411 val update : string -> t -> t -> t
423423- (** [update key value obj] sets [key] to [value] in [obj].
424424- Adds the key if it doesn't exist.
412412+ (** [update key value obj] sets [key] to [value] in [obj]. Adds the key if it
413413+ doesn't exist.
425414 @raise Type_error if [obj] is not an object *)
426415427416 val remove : string -> t -> t
···429418 @raise Type_error if [obj] is not an object *)
430419431420 val combine : t -> t -> t
432432- (** [combine obj1 obj2] merges two objects, with [obj2] values taking precedence.
421421+ (** [combine obj1 obj2] merges two objects, with [obj2] values taking
422422+ precedence.
433423 @raise Type_error if either argument is not an object *)
434424435425 (** {3 List Operations} *)
···451441 @raise Type_error if [lst] is not a list *)
452442453443 val nth : int -> t -> t option
454454- (** [nth n lst] gets element at index [n].
455455- Returns [None] if [lst] is not a list or index out of bounds. *)
444444+ (** [nth n lst] gets element at index [n]. Returns [None] if [lst] is not a
445445+ list or index out of bounds. *)
456446457447 val length : t -> int
458448 (** Get the length of a list or object. Returns 0 for other types. *)
459449460450 val flatten : t -> t
461461- (** Flatten a list of lists into a single list.
462462- Non-list elements are kept as-is.
451451+ (** Flatten a list of lists into a single list. Non-list elements are kept
452452+ as-is.
463453 @raise Type_error if not a list *)
464454465455 (** {3 Path Operations} *)
466456467457 val get_path : string list -> t -> t option
468468- (** [get_path ["a"; "b"; "c"] obj] looks up nested path [obj.a.b.c].
469469- Returns [None] if any key is not found. *)
458458+ (** [get_path ["a"; "b"; "c"] obj] looks up nested path [obj.a.b.c]. Returns
459459+ [None] if any key is not found. *)
470460471461 val get_path_exn : string list -> t -> t
472462 (** Like {!get_path} but raises [Not_found] if path not found. *)
···521511 @raise Type_error if type doesn't match and no default provided *)
522512end
523513524524-525514(** {2 Stream - Low-Level Event API}
526515527516 Low-level streaming API for event-based YAML processing.
···532521 - Fine-grained control over YAML emission *)
533522534523module Stream : sig
535535-536524 (** {3 Event Types} *)
537525538526 type event = Event.t
···557545 (** Create a parser from a string. *)
558546559547 val next : parser -> event_result option
560560- (** Get the next event from the parser.
561561- Returns [None] when parsing is complete. *)
548548+ (** Get the next event from the parser. Returns [None] when parsing is
549549+ complete. *)
562550563551 val iter : (event -> position -> position -> unit) -> parser -> unit
564552 (** [iter f parser] calls [f event start_pos end_pos] for each event. *)
···589577 val stream_end : emitter -> unit
590578 (** Emit a stream end event. *)
591579592592- val document_start : emitter -> ?version:version -> ?implicit:bool -> unit -> unit
580580+ val document_start :
581581+ emitter -> ?version:version -> ?implicit:bool -> unit -> unit
593582 (** Emit a document start event.
594583 @param version YAML version directive
595584 @param implicit Whether start marker is implicit (default: true) *)
···598587 (** Emit a document end event.
599588 @param implicit Whether end marker is implicit (default: true) *)
600589601601- val scalar : emitter -> ?anchor:string -> ?tag:string -> ?style:Scalar_style.t -> string -> unit
590590+ val scalar :
591591+ emitter ->
592592+ ?anchor:string ->
593593+ ?tag:string ->
594594+ ?style:Scalar_style.t ->
595595+ string ->
596596+ unit
602597 (** Emit a scalar value.
603598 @param anchor Optional anchor name
604599 @param tag Optional type tag
···607602 val alias : emitter -> string -> unit
608603 (** Emit an alias reference. *)
609604610610- val sequence_start : emitter -> ?anchor:string -> ?tag:string -> ?style:Layout_style.t -> unit -> unit
605605+ val sequence_start :
606606+ emitter ->
607607+ ?anchor:string ->
608608+ ?tag:string ->
609609+ ?style:Layout_style.t ->
610610+ unit ->
611611+ unit
611612 (** Emit a sequence start event.
612613 @param anchor Optional anchor name
613614 @param tag Optional type tag
···616617 val sequence_end : emitter -> unit
617618 (** Emit a sequence end event. *)
618619619619- val mapping_start : emitter -> ?anchor:string -> ?tag:string -> ?style:Layout_style.t -> unit -> unit
620620+ val mapping_start :
621621+ emitter ->
622622+ ?anchor:string ->
623623+ ?tag:string ->
624624+ ?style:Layout_style.t ->
625625+ unit ->
626626+ unit
620627 (** Emit a mapping start event.
621628 @param anchor Optional anchor name
622629 @param tag Optional type tag
···626633 (** Emit a mapping end event. *)
627634end
628635629629-630636(** {2 Internal Modules}
631637632632- These modules are exposed for advanced use cases requiring
633633- fine-grained control over parsing, emission, or data structures.
638638+ These modules are exposed for advanced use cases requiring fine-grained
639639+ control over parsing, emission, or data structures.
634640635641 For typical usage, prefer the top-level functions and {!Util}. *)
636642
+17-8
tests/dune
···12121313; Alias to run the full YAML test suite and generate HTML report
1414; Requires yaml-test-suite to be cloned to tests/yaml-test-suite
1515+1516(rule
1617 (alias yaml-test-suite)
1717- (deps (source_tree yaml-test-suite))
1818+ (deps
1919+ (source_tree yaml-test-suite))
1820 (targets yaml-test-results.html)
1921 (action
2020- (run %{exe:run_all_tests.exe}
2121- --test-suite-path %{workspace_root}/tests/yaml-test-suite
2222- --html yaml-test-results.html)))
2222+ (run
2323+ %{exe:run_all_tests.exe}
2424+ --test-suite-path
2525+ %{workspace_root}/tests/yaml-test-suite
2626+ --html
2727+ yaml-test-results.html)))
23282429(rule
2530 (alias yaml-test-suite-eio)
2626- (deps (source_tree yaml-test-suite))
3131+ (deps
3232+ (source_tree yaml-test-suite))
2733 (targets yaml-test-results-eio.html)
2834 (action
2929- (run %{exe:run_all_tests_eio.exe}
3030- --test-suite-path %{workspace_root}/tests/yaml-test-suite
3131- --html yaml-test-results-eio.html)))
3535+ (run
3636+ %{exe:run_all_tests_eio.exe}
3737+ --test-suite-path
3838+ %{workspace_root}/tests/yaml-test-suite
3939+ --html
4040+ yaml-test-results-eio.html)))
+209-144
tests/run_all_tests.ml
···1414(* HTML escape function *)
1515let html_escape s =
1616 let buf = Buffer.create (String.length s) in
1717- String.iter (function
1818- | '<' -> Buffer.add_string buf "<"
1919- | '>' -> Buffer.add_string buf ">"
2020- | '&' -> Buffer.add_string buf "&"
2121- | '"' -> Buffer.add_string buf """
2222- | c -> Buffer.add_char buf c
2323- ) s;
1717+ String.iter
1818+ (function
1919+ | '<' -> Buffer.add_string buf "<"
2020+ | '>' -> Buffer.add_string buf ">"
2121+ | '&' -> Buffer.add_string buf "&"
2222+ | '"' -> Buffer.add_string buf """
2323+ | c -> Buffer.add_char buf c)
2424+ s;
2425 Buffer.contents buf
25262627let normalize_tree s =
···3334 name : string;
3435 yaml : string;
3536 is_error_test : bool;
3636- status : [`Pass | `Fail of string | `Skip];
3737+ status : [ `Pass | `Fail of string | `Skip ];
3738 output : string;
3838- json_status : [`Pass | `Fail of string | `Skip];
3939+ json_status : [ `Pass | `Fail of string | `Skip ];
3940 json_expected : string;
4041 json_actual : string;
4142}
···4546 This handles formatting differences and object key ordering. *)
4647 JC.compare_json_strings expected actual
47484848-let run_json_test (test : TL.test_case) : [`Pass | `Fail of string | `Skip] * string =
4949+let run_json_test (test : TL.test_case) :
5050+ [ `Pass | `Fail of string | `Skip ] * string =
4951 match test.json with
5052 | None -> (`Skip, "")
5151- | Some expected_json ->
5353+ | Some expected_json -> (
5254 if test.fail then
5355 (* Error tests shouldn't have JSON comparison *)
5456 (`Skip, "")
···5658 try
5759 (* Handle multi-document YAML by using documents_of_string *)
5860 let docs = Loader.documents_of_string test.yaml in
5959- let values = List.filter_map (fun doc ->
6060- match Document.root doc with
6161- | None -> None
6262- | Some yaml -> Some (Yaml.to_value ~resolve_aliases_first:true yaml)
6363- ) docs in
6464- let actual_json = match values with
6565- | [] -> "" (* Empty document produces empty JSON *)
6666- | [v] -> JF.to_json v
6161+ let values =
6262+ List.filter_map
6363+ (fun doc ->
6464+ match Document.root doc with
6565+ | None -> None
6666+ | Some yaml ->
6767+ Some (Yaml.to_value ~resolve_aliases_first:true yaml))
6868+ docs
6969+ in
7070+ let actual_json =
7171+ match values with
7272+ | [] -> "" (* Empty document produces empty JSON *)
7373+ | [ v ] -> JF.to_json v
6774 | vs -> JF.documents_to_json vs
6875 in
6969- if compare_json expected_json actual_json then
7070- (`Pass, actual_json)
7171- else
7272- (`Fail "JSON mismatch", actual_json)
7676+ if compare_json expected_json actual_json then (`Pass, actual_json)
7777+ else (`Fail "JSON mismatch", actual_json)
7378 with
7479 | Yamlrw_error e ->
7580 (`Fail (Format.asprintf "Parse error: %a" Error.pp e), "")
7681 | exn ->
7782 (`Fail (Printf.sprintf "Exception: %s" (Printexc.to_string exn)), "")
8383+ )
78847985let run_test (test : TL.test_case) : test_result =
8086 let json_status, json_actual = run_json_test test in
8181- let base = {
8282- id = test.id;
8383- name = test.name;
8484- yaml = test.yaml;
8585- is_error_test = test.fail;
8686- status = `Skip;
8787- output = "";
8888- json_status;
8989- json_expected = Option.value ~default:"" test.json;
9090- json_actual;
9191- } in
8787+ let base =
8888+ {
8989+ id = test.id;
9090+ name = test.name;
9191+ yaml = test.yaml;
9292+ is_error_test = test.fail;
9393+ status = `Skip;
9494+ output = "";
9595+ json_status;
9696+ json_expected = Option.value ~default:"" test.json;
9797+ json_actual;
9898+ }
9999+ in
92100 if test.fail then begin
93101 try
94102 let parser = Parser.of_string test.yaml in
95103 let events = Parser.to_list parser in
96104 let tree = TF.of_spanned_events events in
9797- { base with
9898- status = `Fail "Expected parsing to fail";
9999- output = tree;
100100- }
105105+ { base with status = `Fail "Expected parsing to fail"; output = tree }
101106 with
102107 | Yamlrw_error e ->
103103- { base with
104104- status = `Pass;
105105- output = Format.asprintf "%a" Error.pp e;
106106- }
107107- | exn ->
108108- { base with
109109- status = `Pass;
110110- output = Printexc.to_string exn;
111111- }
108108+ { base with status = `Pass; output = Format.asprintf "%a" Error.pp e }
109109+ | exn -> { base with status = `Pass; output = Printexc.to_string exn }
112110 end
113111 else begin
114112 match test.tree with
115115- | None ->
113113+ | None -> (
116114 (* No expected tree - check if json indicates expected success *)
117117- (match test.json with
118118- | Some _ ->
119119- (* Has json output, so should parse successfully *)
120120- (try
121121- let parser = Parser.of_string test.yaml in
122122- let events = Parser.to_list parser in
123123- let tree = TF.of_spanned_events events in
124124- { base with status = `Pass; output = tree }
125125- with exn ->
126126- { base with
127127- status = `Fail (Printf.sprintf "Should parse but got: %s" (Printexc.to_string exn));
128128- output = Printexc.to_string exn;
129129- })
130130- | None ->
131131- (* No tree, no json, no fail - ambiguous edge case, skip *)
132132- { base with status = `Skip; output = "(no expected tree or json)" })
133133- | Some expected ->
115115+ match test.json with
116116+ | Some _ -> (
117117+ (* Has json output, so should parse successfully *)
118118+ try
119119+ let parser = Parser.of_string test.yaml in
120120+ let events = Parser.to_list parser in
121121+ let tree = TF.of_spanned_events events in
122122+ { base with status = `Pass; output = tree }
123123+ with exn ->
124124+ {
125125+ base with
126126+ status =
127127+ `Fail
128128+ (Printf.sprintf "Should parse but got: %s"
129129+ (Printexc.to_string exn));
130130+ output = Printexc.to_string exn;
131131+ })
132132+ | None ->
133133+ (* No tree, no json, no fail - ambiguous edge case, skip *)
134134+ { base with status = `Skip; output = "(no expected tree or json)" })
135135+ | Some expected -> (
134136 try
135137 let parser = Parser.of_string test.yaml in
136138 let events = Parser.to_list parser in
···140142 if expected_norm = actual_norm then
141143 { base with status = `Pass; output = actual }
142144 else
143143- { base with
145145+ {
146146+ base with
144147 status = `Fail (Printf.sprintf "Tree mismatch");
145145- output = Printf.sprintf "Expected:\n%s\n\nActual:\n%s" expected_norm actual_norm;
148148+ output =
149149+ Printf.sprintf "Expected:\n%s\n\nActual:\n%s" expected_norm
150150+ actual_norm;
146151 }
147152 with exn ->
148148- { base with
149149- status = `Fail (Printf.sprintf "Exception: %s" (Printexc.to_string exn));
153153+ {
154154+ base with
155155+ status =
156156+ `Fail (Printf.sprintf "Exception: %s" (Printexc.to_string exn));
150157 output = Printexc.to_string exn;
151151- }
158158+ })
152159 end
153160154161let status_class = function
···163170164171let generate_html results output_file =
165172 let oc = open_out output_file in
166166- let pass_count = List.length (List.filter (fun r -> r.status = `Pass) results) in
167167- let fail_count = List.length (List.filter (fun r -> match r.status with `Fail _ -> true | _ -> false) results) in
168168- let skip_count = List.length (List.filter (fun r -> r.status = `Skip) results) in
173173+ let pass_count =
174174+ List.length (List.filter (fun r -> r.status = `Pass) results)
175175+ in
176176+ let fail_count =
177177+ List.length
178178+ (List.filter
179179+ (fun r -> match r.status with `Fail _ -> true | _ -> false)
180180+ results)
181181+ in
182182+ let skip_count =
183183+ List.length (List.filter (fun r -> r.status = `Skip) results)
184184+ in
169185 let total = List.length results in
170170- let json_pass_count = List.length (List.filter (fun r -> r.json_status = `Pass) results) in
171171- let json_fail_count = List.length (List.filter (fun r -> match r.json_status with `Fail _ -> true | _ -> false) results) in
172172- let json_skip_count = List.length (List.filter (fun r -> r.json_status = `Skip) results) in
186186+ let json_pass_count =
187187+ List.length (List.filter (fun r -> r.json_status = `Pass) results)
188188+ in
189189+ let json_fail_count =
190190+ List.length
191191+ (List.filter
192192+ (fun r -> match r.json_status with `Fail _ -> true | _ -> false)
193193+ results)
194194+ in
195195+ let json_skip_count =
196196+ List.length (List.filter (fun r -> r.json_status = `Skip) results)
197197+ in
173198174174- Printf.fprintf oc {|<!DOCTYPE html>
199199+ Printf.fprintf oc
200200+ {|<!DOCTYPE html>
175201<html lang="en">
176202<head>
177203 <meta charset="UTF-8">
···335361 <input type="text" class="search" placeholder="Search by ID or name...">
336362 </div>
337363 <div class="tests">
338338-|} pass_count fail_count skip_count total json_pass_count json_fail_count json_skip_count;
364364+|}
365365+ pass_count fail_count skip_count total json_pass_count json_fail_count
366366+ json_skip_count;
339367340340- List.iter (fun result ->
341341- let error_badge = if result.is_error_test then
342342- {|<span class="badge error-test">Error Test</span>|}
343343- else "" in
344344- let json_badge = Printf.sprintf {|<span class="badge %s" style="margin-left: 4px;">JSON: %s</span>|}
345345- (status_class result.json_status) (status_text result.json_status) in
346346- let json_section = if result.json_expected <> "" || result.json_actual <> "" then
347347- Printf.sprintf {|
368368+ List.iter
369369+ (fun result ->
370370+ let error_badge =
371371+ if result.is_error_test then
372372+ {|<span class="badge error-test">Error Test</span>|}
373373+ else ""
374374+ in
375375+ let json_badge =
376376+ Printf.sprintf
377377+ {|<span class="badge %s" style="margin-left: 4px;">JSON: %s</span>|}
378378+ (status_class result.json_status)
379379+ (status_text result.json_status)
380380+ in
381381+ let json_section =
382382+ if result.json_expected <> "" || result.json_actual <> "" then
383383+ Printf.sprintf
384384+ {|
348385 <div class="section">
349386 <div class="section-title">Expected JSON</div>
350387 <pre>%s</pre>
···353390 <div class="section-title">Actual JSON</div>
354391 <pre>%s</pre>
355392 </div>|}
356356- (html_escape result.json_expected)
357357- (html_escape result.json_actual)
358358- else "" in
359359- Printf.fprintf oc {| <div class="test" data-status="%s" data-json-status="%s" data-id="%s" data-name="%s">
393393+ (html_escape result.json_expected)
394394+ (html_escape result.json_actual)
395395+ else ""
396396+ in
397397+ Printf.fprintf oc
398398+ {| <div class="test" data-status="%s" data-json-status="%s" data-id="%s" data-name="%s">
360399 <div class="test-header" onclick="this.parentElement.classList.toggle('expanded')">
361400 <span class="expand-icon">▶</span>
362401 <span class="badge %s">%s</span>
···377416 </div>
378417 </div>
379418|}
380380- (status_class result.status)
381381- (status_class result.json_status)
382382- (html_escape result.id)
383383- (html_escape (String.lowercase_ascii result.name))
384384- (status_class result.status)
385385- (status_text result.status)
386386- json_badge
387387- (html_escape result.id)
388388- (html_escape result.name)
389389- error_badge
390390- (html_escape result.yaml)
391391- (html_escape result.output)
392392- json_section
393393- ) results;
419419+ (status_class result.status)
420420+ (status_class result.json_status)
421421+ (html_escape result.id)
422422+ (html_escape (String.lowercase_ascii result.name))
423423+ (status_class result.status)
424424+ (status_text result.status)
425425+ json_badge (html_escape result.id) (html_escape result.name) error_badge
426426+ (html_escape result.yaml)
427427+ (html_escape result.output)
428428+ json_section)
429429+ results;
394430395395- Printf.fprintf oc {| </div>
431431+ Printf.fprintf oc
432432+ {| </div>
396433 </div>
397434 <script>
398435 document.querySelectorAll('.filter-btn').forEach(btn => {
···425462 let html_output = ref None in
426463 let show_skipped = ref false in
427464 let test_suite_path_ref = ref test_suite_path in
428428- let args = [
429429- "--html", Arg.String (fun s -> html_output := Some s),
430430- "<file> Generate HTML report to file";
431431- "--show-skipped", Arg.Set show_skipped,
432432- " Show details of skipped tests";
433433- "--test-suite-path", Arg.Set_string test_suite_path_ref,
434434- "<path> Path to yaml-test-suite directory";
435435- ] in
436436- Arg.parse args (fun _ -> ()) "Usage: run_all_tests [--html <file>] [--show-skipped] [--test-suite-path <path>]";
465465+ let args =
466466+ [
467467+ ( "--html",
468468+ Arg.String (fun s -> html_output := Some s),
469469+ "<file> Generate HTML report to file" );
470470+ ("--show-skipped", Arg.Set show_skipped, " Show details of skipped tests");
471471+ ( "--test-suite-path",
472472+ Arg.Set_string test_suite_path_ref,
473473+ "<path> Path to yaml-test-suite directory" );
474474+ ]
475475+ in
476476+ Arg.parse args
477477+ (fun _ -> ())
478478+ "Usage: run_all_tests [--html <file>] [--show-skipped] [--test-suite-path \
479479+ <path>]";
437480438481 let all_tests = TL.load_directory !test_suite_path_ref in
439482 Printf.printf "Total tests loaded: %d\n%!" (List.length all_tests);
440483441484 let results = List.map run_test all_tests in
442485443443- let pass_count = List.length (List.filter (fun r -> r.status = `Pass) results) in
444444- let fail_count = List.length (List.filter (fun r -> match r.status with `Fail _ -> true | _ -> false) results) in
445445- let skip_count = List.length (List.filter (fun r -> r.status = `Skip) results) in
486486+ let pass_count =
487487+ List.length (List.filter (fun r -> r.status = `Pass) results)
488488+ in
489489+ let fail_count =
490490+ List.length
491491+ (List.filter
492492+ (fun r -> match r.status with `Fail _ -> true | _ -> false)
493493+ results)
494494+ in
495495+ let skip_count =
496496+ List.length (List.filter (fun r -> r.status = `Skip) results)
497497+ in
446498447447- let json_pass_count = List.length (List.filter (fun r -> r.json_status = `Pass) results) in
448448- let json_fail_count = List.length (List.filter (fun r -> match r.json_status with `Fail _ -> true | _ -> false) results) in
449449- let json_skip_count = List.length (List.filter (fun r -> r.json_status = `Skip) results) in
499499+ let json_pass_count =
500500+ List.length (List.filter (fun r -> r.json_status = `Pass) results)
501501+ in
502502+ let json_fail_count =
503503+ List.length
504504+ (List.filter
505505+ (fun r -> match r.json_status with `Fail _ -> true | _ -> false)
506506+ results)
507507+ in
508508+ let json_skip_count =
509509+ List.length (List.filter (fun r -> r.json_status = `Skip) results)
510510+ in
450511451451- Printf.printf "\nEvent Tree Results: %d pass, %d fail, %d skip (total: %d)\n%!"
452452- pass_count fail_count skip_count (pass_count + fail_count + skip_count);
512512+ Printf.printf
513513+ "\nEvent Tree Results: %d pass, %d fail, %d skip (total: %d)\n%!" pass_count
514514+ fail_count skip_count
515515+ (pass_count + fail_count + skip_count);
453516454454- Printf.printf "JSON Results: %d pass, %d fail, %d skip\n%!"
455455- json_pass_count json_fail_count json_skip_count;
517517+ Printf.printf "JSON Results: %d pass, %d fail, %d skip\n%!" json_pass_count
518518+ json_fail_count json_skip_count;
456519457520 if fail_count > 0 then begin
458521 Printf.printf "\nFailing event tree tests:\n";
459459- List.iter (fun r ->
460460- match r.status with
461461- | `Fail msg -> Printf.printf " %s: %s - %s\n" r.id r.name msg
462462- | _ -> ()
463463- ) results
522522+ List.iter
523523+ (fun r ->
524524+ match r.status with
525525+ | `Fail msg -> Printf.printf " %s: %s - %s\n" r.id r.name msg
526526+ | _ -> ())
527527+ results
464528 end;
465529466530 if json_fail_count > 0 then begin
467531 Printf.printf "\nFailing JSON tests:\n";
468468- List.iter (fun r ->
469469- match r.json_status with
470470- | `Fail msg -> Printf.printf " %s: %s - %s\n" r.id r.name msg
471471- | _ -> ()
472472- ) results
532532+ List.iter
533533+ (fun r ->
534534+ match r.json_status with
535535+ | `Fail msg -> Printf.printf " %s: %s - %s\n" r.id r.name msg
536536+ | _ -> ())
537537+ results
473538 end;
474539475540 if !show_skipped && skip_count > 0 then begin
476541 Printf.printf "\nSkipped tests (no expected tree):\n";
477477- List.iter (fun r ->
478478- if r.status = `Skip then begin
479479- Printf.printf " %s: %s\n" r.id r.name;
480480- Printf.printf " YAML (%d chars): %S\n" (String.length r.yaml)
481481- (if String.length r.yaml <= 60 then r.yaml
482482- else String.sub r.yaml 0 60 ^ "...")
483483- end
484484- ) results
542542+ List.iter
543543+ (fun r ->
544544+ if r.status = `Skip then begin
545545+ Printf.printf " %s: %s\n" r.id r.name;
546546+ Printf.printf " YAML (%d chars): %S\n" (String.length r.yaml)
547547+ (if String.length r.yaml <= 60 then r.yaml
548548+ else String.sub r.yaml 0 60 ^ "...")
549549+ end)
550550+ results
485551 end;
486552487553 (match !html_output with
···491557 | None -> ());
492558493559 (* Exit with non-zero code if any tests failed *)
494494- if fail_count > 0 || json_fail_count > 0 then
495495- exit 1
560560+ if fail_count > 0 || json_fail_count > 0 then exit 1
+224-162
tests/run_all_tests_eio.ml
···1616(* HTML escape function *)
1717let html_escape s =
1818 let buf = Buffer.create (String.length s) in
1919- String.iter (function
2020- | '<' -> Buffer.add_string buf "<"
2121- | '>' -> Buffer.add_string buf ">"
2222- | '&' -> Buffer.add_string buf "&"
2323- | '"' -> Buffer.add_string buf """
2424- | c -> Buffer.add_char buf c
2525- ) s;
1919+ String.iter
2020+ (function
2121+ | '<' -> Buffer.add_string buf "<"
2222+ | '>' -> Buffer.add_string buf ">"
2323+ | '&' -> Buffer.add_string buf "&"
2424+ | '"' -> Buffer.add_string buf """
2525+ | c -> Buffer.add_char buf c)
2626+ s;
2627 Buffer.contents buf
27282829let normalize_tree s =
···3536 name : string;
3637 yaml : string;
3738 is_error_test : bool;
3838- status : [`Pass | `Fail of string | `Skip];
3939+ status : [ `Pass | `Fail of string | `Skip ];
3940 output : string;
4040- json_status : [`Pass | `Fail of string | `Skip];
4141+ json_status : [ `Pass | `Fail of string | `Skip ];
4142 json_expected : string;
4243 json_actual : string;
4344}
44454545-let compare_json expected actual =
4646- JC.compare_json_strings expected actual
4646+let compare_json expected actual = JC.compare_json_strings expected actual
47474848-let run_json_test (test : TL.test_case) : [`Pass | `Fail of string | `Skip] * string =
4848+let run_json_test (test : TL.test_case) :
4949+ [ `Pass | `Fail of string | `Skip ] * string =
4950 match test.json with
5051 | None -> (`Skip, "")
5151- | Some expected_json ->
5252- if test.fail then
5353- (`Skip, "")
5252+ | Some expected_json -> (
5353+ if test.fail then (`Skip, "")
5454 else
5555 try
5656 let docs = Loader.documents_of_string test.yaml in
5757- let values = List.filter_map (fun doc ->
5858- match Document.root doc with
5959- | None -> None
6060- | Some yaml -> Some (Yaml.to_value ~resolve_aliases_first:true yaml)
6161- ) docs in
6262- let actual_json = match values with
5757+ let values =
5858+ List.filter_map
5959+ (fun doc ->
6060+ match Document.root doc with
6161+ | None -> None
6262+ | Some yaml ->
6363+ Some (Yaml.to_value ~resolve_aliases_first:true yaml))
6464+ docs
6565+ in
6666+ let actual_json =
6767+ match values with
6368 | [] -> ""
6464- | [v] -> JF.to_json v
6969+ | [ v ] -> JF.to_json v
6570 | vs -> JF.documents_to_json vs
6671 in
6767- if compare_json expected_json actual_json then
6868- (`Pass, actual_json)
6969- else
7070- (`Fail "JSON mismatch", actual_json)
7272+ if compare_json expected_json actual_json then (`Pass, actual_json)
7373+ else (`Fail "JSON mismatch", actual_json)
7174 with
7275 | Yamlrw_error e ->
7376 (`Fail (Format.asprintf "Parse error: %a" Error.pp e), "")
7477 | exn ->
7578 (`Fail (Printf.sprintf "Exception: %s" (Printexc.to_string exn)), "")
7979+ )
76807781let run_test (test : TL.test_case) : test_result =
7882 let json_status, json_actual = run_json_test test in
7979- let base = {
8080- id = test.id;
8181- name = test.name;
8282- yaml = test.yaml;
8383- is_error_test = test.fail;
8484- status = `Skip;
8585- output = "";
8686- json_status;
8787- json_expected = Option.value ~default:"" test.json;
8888- json_actual;
8989- } in
8383+ let base =
8484+ {
8585+ id = test.id;
8686+ name = test.name;
8787+ yaml = test.yaml;
8888+ is_error_test = test.fail;
8989+ status = `Skip;
9090+ output = "";
9191+ json_status;
9292+ json_expected = Option.value ~default:"" test.json;
9393+ json_actual;
9494+ }
9595+ in
9096 if test.fail then begin
9197 try
9298 let parser = Parser.of_string test.yaml in
9399 let events = Parser.to_list parser in
94100 let tree = TF.of_spanned_events events in
9595- { base with
9696- status = `Fail "Expected parsing to fail";
9797- output = tree;
9898- }
101101+ { base with status = `Fail "Expected parsing to fail"; output = tree }
99102 with
100103 | Yamlrw_error e ->
101101- { base with
102102- status = `Pass;
103103- output = Format.asprintf "%a" Error.pp e;
104104- }
105105- | exn ->
106106- { base with
107107- status = `Pass;
108108- output = Printexc.to_string exn;
109109- }
104104+ { base with status = `Pass; output = Format.asprintf "%a" Error.pp e }
105105+ | exn -> { base with status = `Pass; output = Printexc.to_string exn }
110106 end
111107 else begin
112108 match test.tree with
113113- | None ->
114114- (match test.json with
115115- | Some _ ->
116116- (try
117117- let parser = Parser.of_string test.yaml in
118118- let events = Parser.to_list parser in
119119- let tree = TF.of_spanned_events events in
120120- { base with status = `Pass; output = tree }
121121- with exn ->
122122- { base with
123123- status = `Fail (Printf.sprintf "Should parse but got: %s" (Printexc.to_string exn));
124124- output = Printexc.to_string exn;
125125- })
126126- | None ->
127127- { base with status = `Skip; output = "(no expected tree or json)" })
128128- | Some expected ->
109109+ | None -> (
110110+ match test.json with
111111+ | Some _ -> (
112112+ try
113113+ let parser = Parser.of_string test.yaml in
114114+ let events = Parser.to_list parser in
115115+ let tree = TF.of_spanned_events events in
116116+ { base with status = `Pass; output = tree }
117117+ with exn ->
118118+ {
119119+ base with
120120+ status =
121121+ `Fail
122122+ (Printf.sprintf "Should parse but got: %s"
123123+ (Printexc.to_string exn));
124124+ output = Printexc.to_string exn;
125125+ })
126126+ | None ->
127127+ { base with status = `Skip; output = "(no expected tree or json)" })
128128+ | Some expected -> (
129129 try
130130 let parser = Parser.of_string test.yaml in
131131 let events = Parser.to_list parser in
···135135 if expected_norm = actual_norm then
136136 { base with status = `Pass; output = actual }
137137 else
138138- { base with
138138+ {
139139+ base with
139140 status = `Fail (Printf.sprintf "Tree mismatch");
140140- output = Printf.sprintf "Expected:\n%s\n\nActual:\n%s" expected_norm actual_norm;
141141+ output =
142142+ Printf.sprintf "Expected:\n%s\n\nActual:\n%s" expected_norm
143143+ actual_norm;
141144 }
142145 with exn ->
143143- { base with
144144- status = `Fail (Printf.sprintf "Exception: %s" (Printexc.to_string exn));
146146+ {
147147+ base with
148148+ status =
149149+ `Fail (Printf.sprintf "Exception: %s" (Printexc.to_string exn));
145150 output = Printexc.to_string exn;
146146- }
151151+ })
147152 end
148153149154(* Run tests in parallel using Eio fibers *)
150150-let run_tests_parallel tests =
151151- Eio.Fiber.List.map run_test tests
155155+let run_tests_parallel tests = Eio.Fiber.List.map run_test tests
152156153157let status_class = function
154158 | `Pass -> "pass"
···161165 | `Skip -> "SKIP"
162166163167let generate_html ~fs results output_file =
164164- let pass_count = List.length (List.filter (fun r -> r.status = `Pass) results) in
165165- let fail_count = List.length (List.filter (fun r -> match r.status with `Fail _ -> true | _ -> false) results) in
166166- let skip_count = List.length (List.filter (fun r -> r.status = `Skip) results) in
168168+ let pass_count =
169169+ List.length (List.filter (fun r -> r.status = `Pass) results)
170170+ in
171171+ let fail_count =
172172+ List.length
173173+ (List.filter
174174+ (fun r -> match r.status with `Fail _ -> true | _ -> false)
175175+ results)
176176+ in
177177+ let skip_count =
178178+ List.length (List.filter (fun r -> r.status = `Skip) results)
179179+ in
167180 let total = List.length results in
168168- let json_pass_count = List.length (List.filter (fun r -> r.json_status = `Pass) results) in
169169- let json_fail_count = List.length (List.filter (fun r -> match r.json_status with `Fail _ -> true | _ -> false) results) in
170170- let json_skip_count = List.length (List.filter (fun r -> r.json_status = `Skip) results) in
181181+ let json_pass_count =
182182+ List.length (List.filter (fun r -> r.json_status = `Pass) results)
183183+ in
184184+ let json_fail_count =
185185+ List.length
186186+ (List.filter
187187+ (fun r -> match r.json_status with `Fail _ -> true | _ -> false)
188188+ results)
189189+ in
190190+ let json_skip_count =
191191+ List.length (List.filter (fun r -> r.json_status = `Skip) results)
192192+ in
171193172194 let buf = Buffer.create 65536 in
173173- Printf.bprintf buf {|<!DOCTYPE html>
195195+ Printf.bprintf buf
196196+ {|<!DOCTYPE html>
174197<html lang="en">
175198<head>
176199 <meta charset="UTF-8">
···344367 <input type="text" class="search" placeholder="Search by ID or name...">
345368 </div>
346369 <div class="tests">
347347-|} pass_count fail_count skip_count total json_pass_count json_fail_count json_skip_count;
370370+|}
371371+ pass_count fail_count skip_count total json_pass_count json_fail_count
372372+ json_skip_count;
348373349349- List.iter (fun result ->
350350- let error_badge = if result.is_error_test then
351351- {|<span class="badge error-test">Error Test</span>|}
352352- else "" in
353353- let json_badge = Printf.sprintf {|<span class="badge %s" style="margin-left: 4px;">JSON: %s</span>|}
354354- (status_class result.json_status) (status_text result.json_status) in
355355- let json_section = if result.json_expected <> "" || result.json_actual <> "" then
356356- Printf.sprintf {|
374374+ List.iter
375375+ (fun result ->
376376+ let error_badge =
377377+ if result.is_error_test then
378378+ {|<span class="badge error-test">Error Test</span>|}
379379+ else ""
380380+ in
381381+ let json_badge =
382382+ Printf.sprintf
383383+ {|<span class="badge %s" style="margin-left: 4px;">JSON: %s</span>|}
384384+ (status_class result.json_status)
385385+ (status_text result.json_status)
386386+ in
387387+ let json_section =
388388+ if result.json_expected <> "" || result.json_actual <> "" then
389389+ Printf.sprintf
390390+ {|
357391 <div class="section">
358392 <div class="section-title">Expected JSON</div>
359393 <pre>%s</pre>
···362396 <div class="section-title">Actual JSON</div>
363397 <pre>%s</pre>
364398 </div>|}
365365- (html_escape result.json_expected)
366366- (html_escape result.json_actual)
367367- else "" in
368368- Printf.bprintf buf {| <div class="test" data-status="%s" data-json-status="%s" data-id="%s" data-name="%s">
399399+ (html_escape result.json_expected)
400400+ (html_escape result.json_actual)
401401+ else ""
402402+ in
403403+ Printf.bprintf buf
404404+ {| <div class="test" data-status="%s" data-json-status="%s" data-id="%s" data-name="%s">
369405 <div class="test-header" onclick="this.parentElement.classList.toggle('expanded')">
370406 <span class="expand-icon">▶</span>
371407 <span class="badge %s">%s</span>
···386422 </div>
387423 </div>
388424|}
389389- (status_class result.status)
390390- (status_class result.json_status)
391391- (html_escape result.id)
392392- (html_escape (String.lowercase_ascii result.name))
393393- (status_class result.status)
394394- (status_text result.status)
395395- json_badge
396396- (html_escape result.id)
397397- (html_escape result.name)
398398- error_badge
399399- (html_escape result.yaml)
400400- (html_escape result.output)
401401- json_section
402402- ) results;
425425+ (status_class result.status)
426426+ (status_class result.json_status)
427427+ (html_escape result.id)
428428+ (html_escape (String.lowercase_ascii result.name))
429429+ (status_class result.status)
430430+ (status_text result.status)
431431+ json_badge (html_escape result.id) (html_escape result.name) error_badge
432432+ (html_escape result.yaml)
433433+ (html_escape result.output)
434434+ json_section)
435435+ results;
403436404404- Printf.bprintf buf {| </div>
437437+ Printf.bprintf buf
438438+ {| </div>
405439 </div>
406440 <script>
407441 document.querySelectorAll('.filter-btn').forEach(btn => {
···438472 let show_skipped = ref false in
439473 let sequential = ref false in
440474 let test_suite_path_ref = ref test_suite_path in
441441- let args = [
442442- "--html", Arg.String (fun s -> html_output := Some s),
443443- "<file> Generate HTML report to file";
444444- "--show-skipped", Arg.Set show_skipped,
445445- " Show details of skipped tests";
446446- "--sequential", Arg.Set sequential,
447447- " Run tests sequentially instead of in parallel";
448448- "--test-suite-path", Arg.Set_string test_suite_path_ref,
449449- "<path> Path to yaml-test-suite directory";
450450- ] in
451451- Arg.parse args (fun _ -> ()) "Usage: run_all_tests_eio [--html <file>] [--show-skipped] [--sequential] [--test-suite-path <path>]";
475475+ let args =
476476+ [
477477+ ( "--html",
478478+ Arg.String (fun s -> html_output := Some s),
479479+ "<file> Generate HTML report to file" );
480480+ ("--show-skipped", Arg.Set show_skipped, " Show details of skipped tests");
481481+ ( "--sequential",
482482+ Arg.Set sequential,
483483+ " Run tests sequentially instead of in parallel" );
484484+ ( "--test-suite-path",
485485+ Arg.Set_string test_suite_path_ref,
486486+ "<path> Path to yaml-test-suite directory" );
487487+ ]
488488+ in
489489+ Arg.parse args
490490+ (fun _ -> ())
491491+ "Usage: run_all_tests_eio [--html <file>] [--show-skipped] [--sequential] \
492492+ [--test-suite-path <path>]";
452493453494 Eio_main.run @@ fun env ->
454495 (* Use fs (full filesystem) rather than cwd (sandboxed) to allow ".." navigation *)
455496 let fs = Eio.Stdenv.fs env in
456497 (* Get the absolute path to the test suite *)
457457- let test_suite_abs = if Filename.is_relative !test_suite_path_ref then
458458- Filename.concat (Sys.getcwd ()) !test_suite_path_ref
459459- else
460460- !test_suite_path_ref
498498+ let test_suite_abs =
499499+ if Filename.is_relative !test_suite_path_ref then
500500+ Filename.concat (Sys.getcwd ()) !test_suite_path_ref
501501+ else !test_suite_path_ref
461502 in
462503463504 let start_time = Unix.gettimeofday () in
464505465506 (* Load tests using Eio (parallel by default) *)
466466- let all_tests = if !sequential then
467467- TL.load_directory ~fs test_suite_abs
468468- else
469469- TL.load_directory_parallel ~fs test_suite_abs
507507+ let all_tests =
508508+ if !sequential then TL.load_directory ~fs test_suite_abs
509509+ else TL.load_directory_parallel ~fs test_suite_abs
470510 in
471511 let load_time = Unix.gettimeofday () in
472472- Printf.printf "Loaded %d tests in %.3fs\n%!" (List.length all_tests) (load_time -. start_time);
512512+ Printf.printf "Loaded %d tests in %.3fs\n%!" (List.length all_tests)
513513+ (load_time -. start_time);
473514474515 (* Run tests (parallel or sequential based on flag) *)
475475- let results = if !sequential then
476476- List.map run_test all_tests
477477- else
478478- run_tests_parallel all_tests
516516+ let results =
517517+ if !sequential then List.map run_test all_tests
518518+ else run_tests_parallel all_tests
479519 in
480520 let run_time = Unix.gettimeofday () in
481521 Printf.printf "Ran tests in %.3fs\n%!" (run_time -. load_time);
482522483483- let pass_count = List.length (List.filter (fun r -> r.status = `Pass) results) in
484484- let fail_count = List.length (List.filter (fun r -> match r.status with `Fail _ -> true | _ -> false) results) in
485485- let skip_count = List.length (List.filter (fun r -> r.status = `Skip) results) in
523523+ let pass_count =
524524+ List.length (List.filter (fun r -> r.status = `Pass) results)
525525+ in
526526+ let fail_count =
527527+ List.length
528528+ (List.filter
529529+ (fun r -> match r.status with `Fail _ -> true | _ -> false)
530530+ results)
531531+ in
532532+ let skip_count =
533533+ List.length (List.filter (fun r -> r.status = `Skip) results)
534534+ in
486535487487- let json_pass_count = List.length (List.filter (fun r -> r.json_status = `Pass) results) in
488488- let json_fail_count = List.length (List.filter (fun r -> match r.json_status with `Fail _ -> true | _ -> false) results) in
489489- let json_skip_count = List.length (List.filter (fun r -> r.json_status = `Skip) results) in
536536+ let json_pass_count =
537537+ List.length (List.filter (fun r -> r.json_status = `Pass) results)
538538+ in
539539+ let json_fail_count =
540540+ List.length
541541+ (List.filter
542542+ (fun r -> match r.json_status with `Fail _ -> true | _ -> false)
543543+ results)
544544+ in
545545+ let json_skip_count =
546546+ List.length (List.filter (fun r -> r.json_status = `Skip) results)
547547+ in
490548491491- Printf.printf "\nEvent Tree Results: %d pass, %d fail, %d skip (total: %d)\n%!"
492492- pass_count fail_count skip_count (pass_count + fail_count + skip_count);
549549+ Printf.printf
550550+ "\nEvent Tree Results: %d pass, %d fail, %d skip (total: %d)\n%!" pass_count
551551+ fail_count skip_count
552552+ (pass_count + fail_count + skip_count);
493553494494- Printf.printf "JSON Results: %d pass, %d fail, %d skip\n%!"
495495- json_pass_count json_fail_count json_skip_count;
554554+ Printf.printf "JSON Results: %d pass, %d fail, %d skip\n%!" json_pass_count
555555+ json_fail_count json_skip_count;
496556497557 if fail_count > 0 then begin
498558 Printf.printf "\nFailing event tree tests:\n";
499499- List.iter (fun r ->
500500- match r.status with
501501- | `Fail msg -> Printf.printf " %s: %s - %s\n" r.id r.name msg
502502- | _ -> ()
503503- ) results
559559+ List.iter
560560+ (fun r ->
561561+ match r.status with
562562+ | `Fail msg -> Printf.printf " %s: %s - %s\n" r.id r.name msg
563563+ | _ -> ())
564564+ results
504565 end;
505566506567 if json_fail_count > 0 then begin
507568 Printf.printf "\nFailing JSON tests:\n";
508508- List.iter (fun r ->
509509- match r.json_status with
510510- | `Fail msg -> Printf.printf " %s: %s - %s\n" r.id r.name msg
511511- | _ -> ()
512512- ) results
569569+ List.iter
570570+ (fun r ->
571571+ match r.json_status with
572572+ | `Fail msg -> Printf.printf " %s: %s - %s\n" r.id r.name msg
573573+ | _ -> ())
574574+ results
513575 end;
514576515577 if !show_skipped && skip_count > 0 then begin
516578 Printf.printf "\nSkipped tests (no expected tree):\n";
517517- List.iter (fun r ->
518518- if r.status = `Skip then begin
519519- Printf.printf " %s: %s\n" r.id r.name;
520520- Printf.printf " YAML (%d chars): %S\n" (String.length r.yaml)
521521- (if String.length r.yaml <= 60 then r.yaml
522522- else String.sub r.yaml 0 60 ^ "...")
523523- end
524524- ) results
579579+ List.iter
580580+ (fun r ->
581581+ if r.status = `Skip then begin
582582+ Printf.printf " %s: %s\n" r.id r.name;
583583+ Printf.printf " YAML (%d chars): %S\n" (String.length r.yaml)
584584+ (if String.length r.yaml <= 60 then r.yaml
585585+ else String.sub r.yaml 0 60 ^ "...")
586586+ end)
587587+ results
525588 end;
526589527590 let total_time = Unix.gettimeofday () in
···534597 | None -> ());
535598536599 (* Exit with non-zero code if any tests failed *)
537537- if fail_count > 0 || json_fail_count > 0 then
538538- exit 1
600600+ if fail_count > 0 || json_fail_count > 0 then exit 1
···1414 | Object of (string * json) list
15151616let rec equal a b =
1717- match a, b with
1717+ match (a, b) with
1818 | Null, Null -> true
1919 | Bool a, Bool b -> a = b
2020 | Float a, Float b -> Float.equal a b
···2222 | Array a, Array b -> List.equal equal a b
2323 | Object a, Object b ->
2424 (* Compare objects as sets of key-value pairs (order independent) *)
2525- let sorted_a = List.sort (fun (k1, _) (k2, _) -> String.compare k1 k2) a in
2626- let sorted_b = List.sort (fun (k1, _) (k2, _) -> String.compare k1 k2) b in
2727- List.length sorted_a = List.length sorted_b &&
2828- List.for_all2 (fun (k1, v1) (k2, v2) -> k1 = k2 && equal v1 v2) sorted_a sorted_b
2525+ let sorted_a =
2626+ List.sort (fun (k1, _) (k2, _) -> String.compare k1 k2) a
2727+ in
2828+ let sorted_b =
2929+ List.sort (fun (k1, _) (k2, _) -> String.compare k1 k2) b
3030+ in
3131+ List.length sorted_a = List.length sorted_b
3232+ && List.for_all2
3333+ (fun (k1, v1) (k2, v2) -> k1 = k2 && equal v1 v2)
3434+ sorted_a sorted_b
2935 | _ -> false
30363137(* Parse JSON string using jsonm *)
···4652 and parse_array acc =
4753 match Jsonm.decode decoder with
4854 | `Lexeme `Ae -> Ok (Array (List.rev acc))
4949- | `Lexeme _ as lex ->
5555+ | `Lexeme _ as lex -> (
5056 (* Push back and parse value *)
5157 let result = parse_value_with_lex lex in
5252- (match result with
5353- | Ok v -> parse_array (v :: acc)
5454- | Error _ as e -> e)
5858+ match result with Ok v -> parse_array (v :: acc) | Error _ as e -> e)
5559 | `Error e -> Error (Format.asprintf "%a" Jsonm.pp_error e)
5660 | `End -> Error "unexpected end in array"
5761 | `Await -> Error "unexpected await"
5862 and parse_object acc =
5963 match Jsonm.decode decoder with
6064 | `Lexeme `Oe -> Ok (Object (List.rev acc))
6161- | `Lexeme (`Name key) ->
6262- (match parse_value () with
6363- | Ok v -> parse_object ((key, v) :: acc)
6464- | Error _ as e -> e)
6565+ | `Lexeme (`Name key) -> (
6666+ match parse_value () with
6767+ | Ok v -> parse_object ((key, v) :: acc)
6868+ | Error _ as e -> e)
6569 | `Lexeme _ -> Error "expected object key"
6670 | `Error e -> Error (Format.asprintf "%a" Jsonm.pp_error e)
6771 | `End -> Error "unexpected end in object"
···99103 and parse_array acc =
100104 match Jsonm.decode decoder with
101105 | `Lexeme `Ae -> Some (Array (List.rev acc))
102102- | `Lexeme _ as lex ->
103103- (match parse_value_with_lex lex with
104104- | Some v -> parse_array (v :: acc)
105105- | None -> None)
106106+ | `Lexeme _ as lex -> (
107107+ match parse_value_with_lex lex with
108108+ | Some v -> parse_array (v :: acc)
109109+ | None -> None)
106110 | _ -> None
107111 and parse_object acc =
108112 match Jsonm.decode decoder with
109113 | `Lexeme `Oe -> Some (Object (List.rev acc))
110110- | `Lexeme (`Name key) ->
111111- (match parse_value () with
112112- | Some v -> parse_object ((key, v) :: acc)
113113- | None -> None)
114114+ | `Lexeme (`Name key) -> (
115115+ match parse_value () with
116116+ | Some v -> parse_object ((key, v) :: acc)
117117+ | None -> None)
114118 | _ -> None
115119 and parse_value_with_lex lex =
116120 match lex with
···134138 (* Handle empty strings *)
135139 let expected_trimmed = String.trim expected in
136140 let actual_trimmed = String.trim actual in
137137- if expected_trimmed = "" && actual_trimmed = "" then
138138- true
139139- else if expected_trimmed = "" || actual_trimmed = "" then
140140- false
141141+ if expected_trimmed = "" && actual_trimmed = "" then true
142142+ else if expected_trimmed = "" || actual_trimmed = "" then false
141143 else
142144 (* Parse as potentially multiple JSON values *)
143145 let expected_values = parse_json_multi expected in
144146 let actual_values = parse_json_multi actual in
145145- List.length expected_values = List.length actual_values &&
146146- List.for_all2 equal expected_values actual_values
147147+ List.length expected_values = List.length actual_values
148148+ && List.for_all2 equal expected_values actual_values
+37-27
tests/test_suite_lib/json_format.ml
···1010let escape_string s =
1111 let buf = Buffer.create (String.length s * 2) in
1212 Buffer.add_char buf '"';
1313- String.iter (fun c ->
1414- match c with
1515- | '"' -> Buffer.add_string buf "\\\""
1616- | '\\' -> Buffer.add_string buf "\\\\"
1717- | '\n' -> Buffer.add_string buf "\\n"
1818- | '\r' -> Buffer.add_string buf "\\r"
1919- | '\t' -> Buffer.add_string buf "\\t"
2020- | '\x08' -> Buffer.add_string buf "\\b"
2121- | '\x0c' -> Buffer.add_string buf "\\f"
2222- | c when Char.code c < 32 ->
2323- Buffer.add_string buf (Printf.sprintf "\\u%04x" (Char.code c))
2424- | c -> Buffer.add_char buf c
2525- ) s;
1313+ String.iter
1414+ (fun c ->
1515+ match c with
1616+ | '"' -> Buffer.add_string buf "\\\""
1717+ | '\\' -> Buffer.add_string buf "\\\\"
1818+ | '\n' -> Buffer.add_string buf "\\n"
1919+ | '\r' -> Buffer.add_string buf "\\r"
2020+ | '\t' -> Buffer.add_string buf "\\t"
2121+ | '\x08' -> Buffer.add_string buf "\\b"
2222+ | '\x0c' -> Buffer.add_string buf "\\f"
2323+ | c when Char.code c < 32 ->
2424+ Buffer.add_string buf (Printf.sprintf "\\u%04x" (Char.code c))
2525+ | c -> Buffer.add_char buf c)
2626+ s;
2627 Buffer.add_char buf '"';
2728 Buffer.contents buf
28292929-let rec format_value ?(indent=0) (v : Value.t) =
3030+let rec format_value ?(indent = 0) (v : Value.t) =
3031 let spaces n = String.make n ' ' in
3132 match v with
3233 | `Null -> "null"
3334 | `Bool true -> "true"
3435 | `Bool false -> "false"
3536 | `Float f ->
3636- if Float.is_nan f then "null" (* JSON doesn't support NaN *)
3737- else if f = Float.infinity || f = Float.neg_infinity then "null" (* JSON doesn't support Inf *)
3737+ if Float.is_nan f then "null" (* JSON doesn't support NaN *)
3838+ else if f = Float.infinity || f = Float.neg_infinity then "null"
3939+ (* JSON doesn't support Inf *)
3840 else if Float.is_integer f && Float.abs f < 1e15 then
3941 Printf.sprintf "%.0f" f
4042 else
4143 (* Try to match yaml-test-suite's number formatting *)
4244 let s = Printf.sprintf "%g" f in
4345 (* Ensure we have a decimal point for floats *)
4444- if String.contains s '.' || String.contains s 'e' || String.contains s 'E' then s
4646+ if
4747+ String.contains s '.' || String.contains s 'e'
4848+ || String.contains s 'E'
4949+ then s
4550 else s ^ ".0"
4651 | `String s -> escape_string s
4752 | `A [] -> "[]"
4853 | `A items ->
4954 let inner_indent = indent + 2 in
5050- let formatted_items = List.map (fun item ->
5151- spaces inner_indent ^ format_value ~indent:inner_indent item
5252- ) items in
5555+ let formatted_items =
5656+ List.map
5757+ (fun item ->
5858+ spaces inner_indent ^ format_value ~indent:inner_indent item)
5959+ items
6060+ in
5361 "[\n" ^ String.concat ",\n" formatted_items ^ "\n" ^ spaces indent ^ "]"
5462 | `O [] -> "{}"
5563 | `O pairs ->
5664 let inner_indent = indent + 2 in
5757- let formatted_pairs = List.map (fun (k, v) ->
5858- let key = escape_string k in
5959- let value = format_value ~indent:inner_indent v in
6060- spaces inner_indent ^ key ^ ": " ^ value
6161- ) pairs in
6565+ let formatted_pairs =
6666+ List.map
6767+ (fun (k, v) ->
6868+ let key = escape_string k in
6969+ let value = format_value ~indent:inner_indent v in
7070+ spaces inner_indent ^ key ^ ": " ^ value)
7171+ pairs
7272+ in
6273 "{\n" ^ String.concat ",\n" formatted_pairs ^ "\n" ^ spaces indent ^ "}"
63746464-let to_json (v : Value.t) : string =
6565- format_value v
7575+let to_json (v : Value.t) : string = format_value v
66766777(* Format multiple documents (for multi-doc YAML) *)
6878let documents_to_json (docs : Value.t list) : string =
+5-10
tests/test_suite_lib/test_suite_loader.ml
···1818 Some s
1919 with _ -> None
20202121- let file_exists () path =
2222- Sys.file_exists path
2323-2424- let is_directory () path =
2525- Sys.file_exists path && Sys.is_directory path
2626-2727- let read_dir () path =
2828- Array.to_list (Sys.readdir path)
2121+ let file_exists () path = Sys.file_exists path
2222+ let is_directory () path = Sys.file_exists path && Sys.is_directory path
2323+ let read_dir () path = Array.to_list (Sys.readdir path)
2924end
30252626+module Loader = Test_suite_loader_generic.Make (Sync_io)
3127(** Internal loader module *)
3232-module Loader = Test_suite_loader_generic.Make(Sync_io)
33283434-(** Re-export test_case type from loader *)
3529type test_case = Loader.test_case = {
3630 id : string;
3731 name : string;
···4034 json : string option;
4135 fail : bool;
4236}
3737+(** Re-export test_case type from loader *)
43384439(** Load tests without needing to pass a context *)
4540let load_directory path : test_case list = Loader.load_directory () path
+15-15
tests/test_suite_lib/test_suite_loader_eio.ml
···88module Generic = Test_suite_lib.Test_suite_loader_generic
991010(** Eio file I/O implementation *)
1111-module Eio_io : Generic.FILE_IO with type ctx = Eio.Fs.dir_ty Eio.Path.t = struct
1111+module Eio_io : Generic.FILE_IO with type ctx = Eio.Fs.dir_ty Eio.Path.t =
1212+struct
1213 type ctx = Eio.Fs.dir_ty Eio.Path.t
13141415 let read_file fs path =
1515- try
1616- Some (Eio.Path.load Eio.Path.(fs / path))
1717- with _ -> None
1616+ try Some (Eio.Path.load Eio.Path.(fs / path)) with _ -> None
18171918 let file_exists fs path =
2019 match Eio.Path.kind ~follow:true Eio.Path.(fs / path) with
···2827 | _ -> false
2928 | exception _ -> false
30293131- let read_dir fs path =
3232- Eio.Path.read_dir Eio.Path.(fs / path)
3030+ let read_dir fs path = Eio.Path.read_dir Eio.Path.(fs / path)
3331end
34323333+module Loader = Generic.Make (Eio_io)
3534(** Internal loader module *)
3636-module Loader = Generic.Make(Eio_io)
37353838-(** Re-export test_case type from loader *)
3936type test_case = Loader.test_case = {
4037 id : string;
4138 name : string;
···4441 json : string option;
4542 fail : bool;
4643}
4444+(** Re-export test_case type from loader *)
47454846(** Load tests with Eio filesystem context *)
4947let load_directory ~fs path : test_case list = Loader.load_directory fs path
···5351 if not (Eio_io.is_directory fs test_suite_path) then []
5452 else
5553 let entries = Eio_io.read_dir fs test_suite_path in
5656- let test_ids = entries
5454+ let test_ids =
5555+ entries
5756 |> List.filter (fun e ->
5858- Eio_io.is_directory fs (Filename.concat test_suite_path e) &&
5959- String.length e >= 4 &&
6060- e.[0] >= '0' && e.[0] <= 'Z')
5757+ Eio_io.is_directory fs (Filename.concat test_suite_path e)
5858+ && String.length e >= 4
5959+ && e.[0] >= '0'
6060+ && e.[0] <= 'Z')
6161 |> List.sort String.compare
6262 in
6363 (* Load each test ID in parallel using fibers *)
6464- Eio.Fiber.List.map (fun test_id ->
6565- Loader.load_test_id fs test_suite_path test_id
6666- ) test_ids
6464+ Eio.Fiber.List.map
6565+ (fun test_id -> Loader.load_test_id fs test_suite_path test_id)
6666+ test_ids
6767 |> List.concat
+40-31
tests/test_suite_lib/test_suite_loader_generic.ml
···5566(** Generic test suite loader - parameterized by file I/O operations *)
7788-(** Test case representation *)
98type test_case = {
109 id : string;
1110 name : string;
···1413 json : string option;
1514 fail : bool;
1615}
1616+(** Test case representation *)
17171818(** Module type for file I/O operations *)
1919module type FILE_IO = sig
2020- (** Context type for file operations (unit for sync, ~fs for Eio) *)
2120 type ctx
2121+ (** Context type for file operations (unit for sync, ~fs for Eio) *)
22222323+ val read_file : ctx -> string -> string option
2324 (** Read a file, returning None if it doesn't exist or can't be read *)
2424- val read_file : ctx -> string -> string option
25252626- (** Check if a path exists and is a regular file *)
2726 val file_exists : ctx -> string -> bool
2727+ (** Check if a path exists and is a regular file *)
28282929- (** Check if a path exists and is a directory *)
3029 val is_directory : ctx -> string -> bool
3030+ (** Check if a path exists and is a directory *)
31313232+ val read_dir : ctx -> string -> string list
3233 (** List directory entries *)
3333- val read_dir : ctx -> string -> string list
3434end
35353636(** Create a test loader from file I/O operations *)
···4545 }
46464747 let read_file_required ctx path =
4848- match IO.read_file ctx path with
4949- | Some s -> s
5050- | None -> ""
4848+ match IO.read_file ctx path with Some s -> s | None -> ""
51495250 (** Load a single test from a directory *)
5351 let load_test_dir ctx base_id dir_path =
···6058 (* Must have in.yaml to be a valid test *)
6159 if not (IO.file_exists ctx yaml_file) then None
6260 else
6363- let name = match IO.read_file ctx name_file with
6161+ let name =
6262+ match IO.read_file ctx name_file with
6463 | Some s -> String.trim s
6564 | None -> base_id
6665 in
···7069 let fail = IO.file_exists ctx error_file in
7170 Some { id = base_id; name; yaml; tree; json; fail }
72717373- (** Load tests from a test ID directory (may have subdirectories for variants) *)
7272+ (** Load tests from a test ID directory (may have subdirectories for variants)
7373+ *)
7474 let load_test_id ctx test_suite_path test_id =
7575 let dir_path = Filename.concat test_suite_path test_id in
7676 if not (IO.is_directory ctx dir_path) then []
7777 else
7878 let entries = IO.read_dir ctx dir_path in
7979 (* Check if this directory has variant subdirectories (00, 01, etc.) *)
8080- let has_variants = List.exists (fun e ->
8181- let subdir = Filename.concat dir_path e in
8282- IO.is_directory ctx subdir &&
8383- String.length e >= 2 &&
8484- e.[0] >= '0' && e.[0] <= '9'
8585- ) entries in
8080+ let has_variants =
8181+ List.exists
8282+ (fun e ->
8383+ let subdir = Filename.concat dir_path e in
8484+ IO.is_directory ctx subdir
8585+ && String.length e >= 2
8686+ && e.[0] >= '0'
8787+ && e.[0] <= '9')
8888+ entries
8989+ in
86908791 if has_variants then
8892 (* Load each variant subdirectory *)
8989- let variants = entries
9393+ let variants =
9494+ entries
9095 |> List.filter (fun e ->
9196 let subdir = Filename.concat dir_path e in
9292- IO.is_directory ctx subdir &&
9393- String.length e >= 2 &&
9494- e.[0] >= '0' && e.[0] <= '9')
9797+ IO.is_directory ctx subdir
9898+ && String.length e >= 2
9999+ && e.[0] >= '0'
100100+ && e.[0] <= '9')
95101 |> List.sort String.compare
96102 in
9797- List.filter_map (fun variant ->
9898- let variant_path = Filename.concat dir_path variant in
9999- let variant_id = Printf.sprintf "%s:%s" test_id variant in
100100- load_test_dir ctx variant_id variant_path
101101- ) variants
103103+ List.filter_map
104104+ (fun variant ->
105105+ let variant_path = Filename.concat dir_path variant in
106106+ let variant_id = Printf.sprintf "%s:%s" test_id variant in
107107+ load_test_dir ctx variant_id variant_path)
108108+ variants
102109 else
103110 (* Single test in this directory *)
104111 match load_test_dir ctx test_id dir_path with
105105- | Some t -> [t]
112112+ | Some t -> [ t ]
106113 | None -> []
107114108115 (** Load all tests from a test suite directory *)
···110117 if not (IO.is_directory ctx test_suite_path) then []
111118 else
112119 let entries = IO.read_dir ctx test_suite_path in
113113- let test_ids = entries
120120+ let test_ids =
121121+ entries
114122 |> List.filter (fun e ->
115115- IO.is_directory ctx (Filename.concat test_suite_path e) &&
116116- String.length e >= 4 &&
117117- e.[0] >= '0' && e.[0] <= 'Z')
123123+ IO.is_directory ctx (Filename.concat test_suite_path e)
124124+ && String.length e >= 4
125125+ && e.[0] >= '0'
126126+ && e.[0] <= 'Z')
118127 |> List.sort String.compare
119128 in
120129 List.concat_map (load_test_id ctx test_suite_path) test_ids
+27-28
tests/test_suite_lib/tree_format.ml
···991010let escape_string s =
1111 let buf = Buffer.create (String.length s * 2) in
1212- String.iter (fun c ->
1313- match c with
1414- | '\n' -> Buffer.add_string buf "\\n"
1515- | '\t' -> Buffer.add_string buf "\\t"
1616- | '\r' -> Buffer.add_string buf "\\r"
1717- | '\\' -> Buffer.add_string buf "\\\\"
1818- | '\x00' -> Buffer.add_string buf "\\0"
1919- | '\x07' -> Buffer.add_string buf "\\a"
2020- | '\x08' -> Buffer.add_string buf "\\b"
2121- | '\x0b' -> Buffer.add_string buf "\\v"
2222- | '\x0c' -> Buffer.add_string buf "\\f"
2323- | '\x1b' -> Buffer.add_string buf "\\e"
2424- | '\xa0' -> Buffer.add_string buf "\\_"
2525- | c -> Buffer.add_char buf c
2626- ) s;
1212+ String.iter
1313+ (fun c ->
1414+ match c with
1515+ | '\n' -> Buffer.add_string buf "\\n"
1616+ | '\t' -> Buffer.add_string buf "\\t"
1717+ | '\r' -> Buffer.add_string buf "\\r"
1818+ | '\\' -> Buffer.add_string buf "\\\\"
1919+ | '\x00' -> Buffer.add_string buf "\\0"
2020+ | '\x07' -> Buffer.add_string buf "\\a"
2121+ | '\x08' -> Buffer.add_string buf "\\b"
2222+ | '\x0b' -> Buffer.add_string buf "\\v"
2323+ | '\x0c' -> Buffer.add_string buf "\\f"
2424+ | '\x1b' -> Buffer.add_string buf "\\e"
2525+ | '\xa0' -> Buffer.add_string buf "\\_"
2626+ | c -> Buffer.add_char buf c)
2727+ s;
2728 Buffer.contents buf
28292930let style_char = function
···3940 | Event.Stream_start _ -> "+STR"
4041 | Event.Stream_end -> "-STR"
4142 | Event.Document_start { implicit; _ } ->
4242- if implicit then "+DOC"
4343- else "+DOC ---"
4444- | Event.Document_end { implicit } ->
4545- if implicit then "-DOC"
4646- else "-DOC ..."
4343+ if implicit then "+DOC" else "+DOC ---"
4444+ | Event.Document_end { implicit } -> if implicit then "-DOC" else "-DOC ..."
4745 | Event.Mapping_start { anchor; tag; style; _ } ->
4846 let anchor_str = match anchor with Some a -> " &" ^ a | None -> "" in
4947 let tag_str = match tag with Some t -> " <" ^ t ^ ">" | None -> "" in
···6058 let anchor_str = match anchor with Some a -> " &" ^ a | None -> "" in
6159 let tag_str = match tag with Some t -> " <" ^ t ^ ">" | None -> "" in
6260 let style_c = style_char style in
6363- Printf.sprintf "=VAL%s%s %c%s" anchor_str tag_str style_c (escape_string value)
6464- | Event.Alias { anchor } ->
6565- Printf.sprintf "=ALI *%s" anchor
6161+ Printf.sprintf "=VAL%s%s %c%s" anchor_str tag_str style_c
6262+ (escape_string value)
6363+ | Event.Alias { anchor } -> Printf.sprintf "=ALI *%s" anchor
66646765let of_spanned_events events =
6866 let buf = Buffer.create 256 in
6969- List.iter (fun (e : Event.spanned) ->
7070- let line = format_event e in
7171- Buffer.add_string buf line;
7272- Buffer.add_char buf '\n'
7373- ) events;
6767+ List.iter
6868+ (fun (e : Event.spanned) ->
6969+ let line = format_event e in
7070+ Buffer.add_string buf line;
7171+ Buffer.add_char buf '\n')
7272+ events;
7473 Buffer.contents buf
+147-112
tests/test_yamlrw.ml
···2424 Alcotest.(check int) "token count" 8 (List.length token_types);
2525 (* Stream_start, Block_mapping_start, Key, Scalar, Value, Scalar, Block_end, Stream_end *)
2626 match token_types with
2727- | Token.Stream_start _ :: Token.Block_mapping_start :: Token.Key ::
2828- Token.Scalar { value = "hello"; _ } :: Token.Value ::
2929- Token.Scalar { value = "world"; _ } :: Token.Block_end :: Token.Stream_end :: [] ->
2727+ | [
2828+ Token.Stream_start _;
2929+ Token.Block_mapping_start;
3030+ Token.Key;
3131+ Token.Scalar { value = "hello"; _ };
3232+ Token.Value;
3333+ Token.Scalar { value = "world"; _ };
3434+ Token.Block_end;
3535+ Token.Stream_end;
3636+ ] ->
3037 ()
3131- | _ ->
3232- Alcotest.fail "unexpected token sequence"
3838+ | _ -> Alcotest.fail "unexpected token sequence"
33393440let test_scanner_sequence () =
3541 let scanner = Scanner.of_string "- one\n- two\n- three" in
···3945let test_scanner_flow () =
4046 let scanner = Scanner.of_string "[1, 2, 3]" in
4147 let tokens = Scanner.to_list scanner in
4242- let has_flow_start = List.exists (fun (t : Token.spanned) ->
4343- match t.token with Token.Flow_sequence_start -> true | _ -> false
4444- ) tokens in
4848+ let has_flow_start =
4949+ List.exists
5050+ (fun (t : Token.spanned) ->
5151+ match t.token with Token.Flow_sequence_start -> true | _ -> false)
5252+ tokens
5353+ in
4554 Alcotest.(check bool) "has flow sequence start" true has_flow_start
46554747-let scanner_tests = [
4848- "simple mapping", `Quick, test_scanner_simple;
4949- "sequence", `Quick, test_scanner_sequence;
5050- "flow sequence", `Quick, test_scanner_flow;
5151-]
5656+let scanner_tests =
5757+ [
5858+ ("simple mapping", `Quick, test_scanner_simple);
5959+ ("sequence", `Quick, test_scanner_sequence);
6060+ ("flow sequence", `Quick, test_scanner_flow);
6161+ ]
52625363(** Parser tests *)
5464···5666 let parser = Parser.of_string "key: value" in
5767 let events = Parser.to_list parser in
5868 Alcotest.(check bool) "has events" true (List.length events > 0);
5959- let has_stream_start = List.exists (fun (e : Event.spanned) ->
6060- match e.event with Event.Stream_start _ -> true | _ -> false
6161- ) events in
6969+ let has_stream_start =
7070+ List.exists
7171+ (fun (e : Event.spanned) ->
7272+ match e.event with Event.Stream_start _ -> true | _ -> false)
7373+ events
7474+ in
6275 Alcotest.(check bool) "has stream start" true has_stream_start
63766477let test_parser_sequence_events () =
6578 let parser = Parser.of_string "- a\n- b" in
6679 let events = Parser.to_list parser in
6767- let has_seq_start = List.exists (fun (e : Event.spanned) ->
6868- match e.event with Event.Sequence_start _ -> true | _ -> false
6969- ) events in
8080+ let has_seq_start =
8181+ List.exists
8282+ (fun (e : Event.spanned) ->
8383+ match e.event with Event.Sequence_start _ -> true | _ -> false)
8484+ events
8585+ in
7086 Alcotest.(check bool) "has sequence start" true has_seq_start
71877272-let parser_tests = [
7373- "parse events", `Quick, test_parser_events;
7474- "sequence events", `Quick, test_parser_sequence_events;
7575-]
8888+let parser_tests =
8989+ [
9090+ ("parse events", `Quick, test_parser_events);
9191+ ("sequence events", `Quick, test_parser_sequence_events);
9292+ ]
76937794(** Value parsing tests *)
7895···93110 check_value "float" (`Float 3.14) (of_string "3.14")
9411195112let test_parse_string () =
9696- check_value "plain" (`String "hello") (of_string "hello world" |> function `String s -> `String (String.sub s 0 5) | v -> v);
113113+ check_value "plain" (`String "hello")
114114+ ( of_string "hello world" |> function
115115+ | `String s -> `String (String.sub s 0 5)
116116+ | v -> v );
97117 check_value "quoted" (`String "hello") (of_string {|"hello"|})
9811899119let test_parse_sequence () =
100120 let result = of_string "- one\n- two\n- three" in
101121 match result with
102102- | `A [_; _; _] -> ()
122122+ | `A [ _; _; _ ] -> ()
103123 | _ -> Alcotest.fail "expected sequence with 3 elements"
104124105125let test_parse_mapping () =
···118138|} in
119139 let result = of_string yaml in
120140 match result with
121121- | `O [("person", `O _)] -> ()
141141+ | `O [ ("person", `O _) ] -> ()
122142 | _ -> Alcotest.fail "expected nested structure"
123143124144let test_parse_flow_sequence () =
125145 let result = of_string "[1, 2, 3]" in
126146 match result with
127127- | `A [`Float 1.0; `Float 2.0; `Float 3.0] -> ()
147147+ | `A [ `Float 1.0; `Float 2.0; `Float 3.0 ] -> ()
128148 | _ -> Alcotest.fail "expected flow sequence [1, 2, 3]"
129149130150let test_parse_flow_mapping () =
131151 let result = of_string "{a: 1, b: 2}" in
132152 match result with
133133- | `O [("a", `Float 1.0); ("b", `Float 2.0)] -> ()
153153+ | `O [ ("a", `Float 1.0); ("b", `Float 2.0) ] -> ()
134154 | _ -> Alcotest.fail "expected flow mapping {a: 1, b: 2}"
135155136156let test_parse_flow_mapping_trailing_comma () =
137157 let result = of_string "{ a: 1, }" in
138158 match result with
139139- | `O [("a", `Float 1.0)] -> ()
159159+ | `O [ ("a", `Float 1.0) ] -> ()
140160 | `O pairs ->
141141- Alcotest.failf "expected 1 pair but got %d pairs (trailing comma should not create empty entry)"
161161+ Alcotest.failf
162162+ "expected 1 pair but got %d pairs (trailing comma should not create \
163163+ empty entry)"
142164 (List.length pairs)
143165 | _ -> Alcotest.fail "expected flow mapping with 1 pair"
144166145145-let value_tests = [
146146- "parse null", `Quick, test_parse_null;
147147- "parse bool", `Quick, test_parse_bool;
148148- "parse number", `Quick, test_parse_number;
149149- "parse string", `Quick, test_parse_string;
150150- "parse sequence", `Quick, test_parse_sequence;
151151- "parse mapping", `Quick, test_parse_mapping;
152152- "parse nested", `Quick, test_parse_nested;
153153- "parse flow sequence", `Quick, test_parse_flow_sequence;
154154- "parse flow mapping", `Quick, test_parse_flow_mapping;
155155- "flow mapping trailing comma", `Quick, test_parse_flow_mapping_trailing_comma;
156156-]
167167+let value_tests =
168168+ [
169169+ ("parse null", `Quick, test_parse_null);
170170+ ("parse bool", `Quick, test_parse_bool);
171171+ ("parse number", `Quick, test_parse_number);
172172+ ("parse string", `Quick, test_parse_string);
173173+ ("parse sequence", `Quick, test_parse_sequence);
174174+ ("parse mapping", `Quick, test_parse_mapping);
175175+ ("parse nested", `Quick, test_parse_nested);
176176+ ("parse flow sequence", `Quick, test_parse_flow_sequence);
177177+ ("parse flow mapping", `Quick, test_parse_flow_mapping);
178178+ ( "flow mapping trailing comma",
179179+ `Quick,
180180+ test_parse_flow_mapping_trailing_comma );
181181+ ]
157182158183(** Emitter tests *)
159184···162187 Alcotest.(check bool) "contains null" true (String.length result > 0)
163188164189let starts_with prefix s =
165165- String.length s >= String.length prefix &&
166166- String.sub s 0 (String.length prefix) = prefix
190190+ String.length s >= String.length prefix
191191+ && String.sub s 0 (String.length prefix) = prefix
167192168193let test_emit_mapping () =
169169- let value = `O [("name", `String "Alice"); ("age", `Float 30.0)] in
194194+ let value = `O [ ("name", `String "Alice"); ("age", `Float 30.0) ] in
170195 let result = to_string value in
171196 let trimmed = String.trim result in
172172- Alcotest.(check bool) "contains name" true (starts_with "name" trimmed || starts_with "\"name\"" trimmed)
197197+ Alcotest.(check bool)
198198+ "contains name" true
199199+ (starts_with "name" trimmed || starts_with "\"name\"" trimmed)
173200174201let test_roundtrip_simple () =
175202 let yaml = "name: Alice" in
···187214 ()
188215 | _ -> Alcotest.fail "roundtrip failed"
189216190190-let emitter_tests = [
191191- "emit null", `Quick, test_emit_null;
192192- "emit mapping", `Quick, test_emit_mapping;
193193- "roundtrip simple", `Quick, test_roundtrip_simple;
194194- "roundtrip sequence", `Quick, test_roundtrip_sequence;
195195-]
217217+let emitter_tests =
218218+ [
219219+ ("emit null", `Quick, test_emit_null);
220220+ ("emit mapping", `Quick, test_emit_mapping);
221221+ ("roundtrip simple", `Quick, test_roundtrip_simple);
222222+ ("roundtrip sequence", `Quick, test_roundtrip_sequence);
223223+ ]
196224197225(** YAML-specific tests *)
198226···204232 | _ -> Alcotest.fail "expected scalar with anchor"
205233206234let test_yaml_alias () =
207207- let yaml = {|
235235+ let yaml =
236236+ {|
208237defaults: &defaults
209238 timeout: 30
210239production:
211240 <<: *defaults
212241 port: 8080
213213-|} in
242242+|}
243243+ in
214244 (* Just check it parses without error *)
215245 let _ = yaml_of_string yaml in
216246 ()
217247218218-let yaml_tests = [
219219- "yaml anchor", `Quick, test_yaml_anchor;
220220- "yaml alias", `Quick, test_yaml_alias;
221221-]
248248+let yaml_tests =
249249+ [
250250+ ("yaml anchor", `Quick, test_yaml_anchor);
251251+ ("yaml alias", `Quick, test_yaml_alias);
252252+ ]
222253223254(** Multiline scalar tests *)
224255···230261|} in
231262 let result = of_string yaml in
232263 match result with
233233- | `O [("description", `String _)] -> ()
264264+ | `O [ ("description", `String _) ] -> ()
234265 | _ -> Alcotest.fail "expected mapping with literal block"
235266236267let test_folded_block () =
···241272|} in
242273 let result = of_string yaml in
243274 match result with
244244- | `O [("description", `String _)] -> ()
275275+ | `O [ ("description", `String _) ] -> ()
245276 | _ -> Alcotest.fail "expected mapping with folded block"
246277247247-let multiline_tests = [
248248- "literal block", `Quick, test_literal_block;
249249- "folded block", `Quick, test_folded_block;
250250-]
278278+let multiline_tests =
279279+ [
280280+ ("literal block", `Quick, test_literal_block);
281281+ ("folded block", `Quick, test_folded_block);
282282+ ]
251283252284(** Error handling tests *)
253285···255287 try
256288 let _ = of_string "key: [unclosed" in
257289 Alcotest.fail "expected error"
258258- with
259259- | Yamlrw_error e ->
260260- Alcotest.(check bool) "has span" true (e.span <> None)
290290+ with Yamlrw_error e -> Alcotest.(check bool) "has span" true (e.span <> None)
261291262262-let error_tests = [
263263- "error position", `Quick, test_error_position;
264264-]
292292+let error_tests = [ ("error position", `Quick, test_error_position) ]
265293266294(** Alias expansion limit tests (billion laughs protection) *)
267295268296let test_node_limit () =
269297 (* Small bomb that would expand to 9^4 = 6561 nodes *)
270270- let yaml = {|
298298+ let yaml =
299299+ {|
271300a: &a [1,2,3,4,5,6,7,8,9]
272301b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
273302c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]
274303d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]
275275-|} in
304304+|}
305305+ in
276306 (* Should fail with a small node limit *)
277307 try
278308 let _ = of_string ~max_nodes:100 yaml in
279309 Alcotest.fail "expected node limit error"
280280- with
281281- | Yamlrw_error e ->
282282- (match e.Error.kind with
283283- | Error.Alias_expansion_node_limit _ -> ()
284284- | _ -> Alcotest.fail "expected Alias_expansion_node_limit error")
310310+ with Yamlrw_error e -> (
311311+ match e.Error.kind with
312312+ | Error.Alias_expansion_node_limit _ -> ()
313313+ | _ -> Alcotest.fail "expected Alias_expansion_node_limit error")
285314286315let test_depth_limit () =
287316 (* Create deeply nested alias chain:
288317 *e -> [*d,*d] -> [*c,*c] -> [*b,*b] -> [*a,*a] -> [x,y,z]
289318 Each alias resolution increases depth by 1 *)
290290- let yaml = {|
319319+ let yaml =
320320+ {|
291321a: &a [x, y, z]
292322b: &b [*a, *a]
293323c: &c [*b, *b]
294324d: &d [*c, *c]
295325e: &e [*d, *d]
296326result: *e
297297-|} in
327327+|}
328328+ in
298329 (* Should fail with a small depth limit (depth 3 means max 3 alias hops) *)
299330 try
300331 let _ = of_string ~max_depth:3 yaml in
301332 Alcotest.fail "expected depth limit error"
302302- with
303303- | Yamlrw_error e ->
304304- (match e.Error.kind with
305305- | Error.Alias_expansion_depth_limit _ -> ()
306306- | _ -> Alcotest.fail ("expected Alias_expansion_depth_limit error, got: " ^ Error.kind_to_string e.Error.kind))
333333+ with Yamlrw_error e -> (
334334+ match e.Error.kind with
335335+ | Error.Alias_expansion_depth_limit _ -> ()
336336+ | _ ->
337337+ Alcotest.fail
338338+ ("expected Alias_expansion_depth_limit error, got: "
339339+ ^ Error.kind_to_string e.Error.kind))
307340308341let test_normal_aliases_work () =
309342 (* Normal alias usage should work fine *)
310310- let yaml = {|
343343+ let yaml =
344344+ {|
311345defaults: &defaults
312346 timeout: 30
313347 retries: 3
314348production:
315349 <<: *defaults
316350 port: 8080
317317-|} in
351351+|}
352352+ in
318353 let result = of_string yaml in
319319- match result with
320320- | `O _ -> ()
321321- | _ -> Alcotest.fail "expected mapping"
354354+ match result with `O _ -> () | _ -> Alcotest.fail "expected mapping"
322355323356let test_resolve_aliases_false () =
324357 (* With resolve_aliases=false, aliases should remain unresolved *)
···329362 let result = yaml_of_string ~resolve_aliases:false yaml in
330363 (* Check that alias is preserved *)
331364 match result with
332332- | `O map ->
365365+ | `O map -> (
333366 let pairs = Mapping.members map in
334334- (match List.assoc_opt (`Scalar (Scalar.make "b")) pairs with
335335- | Some (`Alias "anchor") -> ()
336336- | _ -> Alcotest.fail "expected alias to be preserved")
367367+ match List.assoc_opt (`Scalar (Scalar.make "b")) pairs with
368368+ | Some (`Alias "anchor") -> ()
369369+ | _ -> Alcotest.fail "expected alias to be preserved")
337370 | _ -> Alcotest.fail "expected mapping"
338371339339-let alias_limit_tests = [
340340- "node limit", `Quick, test_node_limit;
341341- "depth limit", `Quick, test_depth_limit;
342342- "normal aliases work", `Quick, test_normal_aliases_work;
343343- "resolve_aliases false", `Quick, test_resolve_aliases_false;
344344-]
372372+let alias_limit_tests =
373373+ [
374374+ ("node limit", `Quick, test_node_limit);
375375+ ("depth limit", `Quick, test_depth_limit);
376376+ ("normal aliases work", `Quick, test_normal_aliases_work);
377377+ ("resolve_aliases false", `Quick, test_resolve_aliases_false);
378378+ ]
345379346380(** Bug fix regression tests
347381 These tests verify that issues fixed in ocaml-yaml don't occur in ocaml-yamlrw *)
···411445let test_parse_special_floats () =
412446 let inf_result = of_string ".inf" in
413447 (match inf_result with
414414- | `Float f when Float.is_inf f && f > 0.0 -> ()
448448+ | `Float f when Float.is_infinite f && f > 0.0 -> ()
415449 | _ -> Alcotest.fail "expected positive infinity");
416450 let neg_inf_result = of_string "-.inf" in
417451 (match neg_inf_result with
418418- | `Float f when Float.is_inf f && f < 0.0 -> ()
452452+ | `Float f when Float.is_infinite f && f < 0.0 -> ()
419453 | _ -> Alcotest.fail "expected negative infinity");
420454 let nan_result = of_string ".nan" in
421455 (match nan_result with
···485519(** Run all tests *)
486520487521let () =
488488- Alcotest.run "yamlrw" [
489489- "scanner", scanner_tests;
490490- "parser", parser_tests;
491491- "value", value_tests;
492492- "emitter", emitter_tests;
493493- "yaml", yaml_tests;
494494- "multiline", multiline_tests;
495495- "errors", error_tests;
496496- "alias_limits", alias_limit_tests;
497497- "bugfix_regression", bugfix_regression_tests;
498498- ]
522522+ Alcotest.run "yamlrw"
523523+ [
524524+ ("scanner", scanner_tests);
525525+ ("parser", parser_tests);
526526+ ("value", value_tests);
527527+ ("emitter", emitter_tests);
528528+ ("yaml", yaml_tests);
529529+ ("multiline", multiline_tests);
530530+ ("errors", error_tests);
531531+ ("alias_limits", alias_limit_tests);
532532+ ("bugfix_regression", bugfix_regression_tests);
533533+ ]