Yaml encoder/decoder for OCaml jsont codecs
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2024 The yamlrw programmers. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6open Bytesrw 7open Jsont.Repr 8open Yamlrw 9 10(* YAML format *) 11 12type yaml_format = Block | Flow | Layout 13 14(* Decoder *) 15 16type decoder = { 17 parser : Parser.t; 18 file : string; 19 locs : bool; 20 _layout : bool; (* For future layout preservation *) 21 max_depth : int; 22 max_nodes : int; 23 mutable node_count : int; 24 mutable current : Event.spanned option; 25 _anchors : (string, Jsont.json) Hashtbl.t; (* For future anchor resolution *) 26 meta_none : Jsont.Meta.t; 27} 28 29let make_decoder 30 ?(locs = false) ?(layout = false) ?(file = "-") 31 ?(max_depth = 100) ?(max_nodes = 10_000_000) parser = 32 let meta_none = Jsont.Meta.make (Jsont.Textloc.(set_file none) file) in 33 { parser; file; locs; _layout = layout; max_depth; max_nodes; 34 node_count = 0; current = None; 35 _anchors = Hashtbl.create 16; meta_none } 36 37(* Decoder helpers *) 38 39let check_depth d ~nest = 40 if nest > d.max_depth then 41 Jsont.Error.msgf Jsont.Meta.none "Maximum nesting depth %d exceeded" d.max_depth 42 43let check_nodes d = 44 d.node_count <- d.node_count + 1; 45 if d.node_count > d.max_nodes then 46 Jsont.Error.msgf Jsont.Meta.none "Maximum node count %d exceeded" d.max_nodes 47 48let meta_of_span d span = 49 if not d.locs then d.meta_none else 50 let start = span.Span.start and stop = span.Span.stop in 51 let first_byte = start.Position.index in 52 let last_byte = max first_byte (stop.Position.index - 1) in 53 (* line_pos is (line_number, byte_position_of_line_start) *) 54 let first_line = (start.Position.line, start.Position.index - start.Position.column + 1) in 55 let last_line = (stop.Position.line, stop.Position.index - stop.Position.column + 1) in 56 let textloc = Jsont.Textloc.make ~file:d.file 57 ~first_byte ~last_byte ~first_line ~last_line in 58 Jsont.Meta.make textloc 59 60let next_event d = 61 d.current <- Parser.next d.parser; 62 d.current 63 64let peek_event d = 65 match d.current with 66 | Some _ -> d.current 67 | None -> next_event d 68 69let skip_event d = 70 d.current <- None 71 72let _expect_event d pred name = 73 match peek_event d with 74 | Some ev when pred ev.Event.event -> skip_event d; ev 75 | Some ev -> 76 let span = ev.Event.span in 77 let meta = meta_of_span d span in 78 Jsont.Error.msgf meta "Expected %s but found %a" name Event.pp ev.Event.event 79 | None -> 80 Jsont.Error.msgf Jsont.Meta.none "Expected %s but reached end of stream" name 81 82(* Error helpers *) 83 84let _err_expected_scalar d ev = 85 let meta = meta_of_span d ev.Event.span in 86 Jsont.Error.msgf meta "Expected scalar but found %a" Event.pp ev.Event.event 87 88let err_type_mismatch d span t ~fnd = 89 let meta = meta_of_span d span in 90 Jsont.Error.msgf meta "Expected %s but found %s" 91 (Jsont.Repr.kinded_sort t) fnd 92 93(* YAML scalar resolution *) 94 95let is_null_scalar s = 96 s = "" || s = "~" || 97 s = "null" || s = "Null" || s = "NULL" 98 99let bool_of_scalar_opt s = 100 match s with 101 | "true" | "True" | "TRUE" 102 | "yes" | "Yes" | "YES" 103 | "on" | "On" | "ON" -> Some true 104 | "false" | "False" | "FALSE" 105 | "no" | "No" | "NO" 106 | "off" | "Off" | "OFF" -> Some false 107 | _ -> None 108 109let float_of_scalar_opt s = 110 (* Handle YAML special floats *) 111 match s with 112 | ".inf" | ".Inf" | ".INF" -> Some Float.infinity 113 | "+.inf" | "+.Inf" | "+.INF" -> Some Float.infinity 114 | "-.inf" | "-.Inf" | "-.INF" -> Some Float.neg_infinity 115 | ".nan" | ".NaN" | ".NAN" -> Some Float.nan 116 | _ -> 117 (* Try parsing as number, allowing underscores *) 118 let s' = String.concat "" (String.split_on_char '_' s) in 119 (* Try int first (supports 0o, 0x, 0b) then float *) 120 match int_of_string_opt s' with 121 | Some i -> Some (float_of_int i) 122 | None -> float_of_string_opt s' 123 124let _int_of_scalar_opt s = 125 (* Handle hex, octal, and regular integers with underscores *) 126 let s' = String.concat "" (String.split_on_char '_' s) in 127 int_of_string_opt s' 128 129(* Decode a scalar value according to expected type *) 130let rec decode_scalar_as : 131 type a. decoder -> Event.spanned -> string -> Scalar_style.t -> a t -> a = 132 fun d ev value style t -> 133 check_nodes d; 134 let meta = meta_of_span d ev.Event.span in 135 match t with 136 | Null map -> 137 if is_null_scalar value then map.dec meta () 138 else err_type_mismatch d ev.span t ~fnd:("scalar " ^ value) 139 | Bool map -> 140 (match bool_of_scalar_opt value with 141 | Some b -> map.dec meta b 142 | None -> 143 (* For explicitly quoted strings, fail *) 144 if style <> `Plain then 145 err_type_mismatch d ev.span t ~fnd:("string " ^ value) 146 else 147 err_type_mismatch d ev.span t ~fnd:("scalar " ^ value)) 148 | Number map -> 149 (* Handle null -> nan mapping like jsont *) 150 if is_null_scalar value then map.dec meta Float.nan 151 else 152 (match float_of_scalar_opt value with 153 | Some f -> map.dec meta f 154 | None -> err_type_mismatch d ev.span t ~fnd:("scalar " ^ value)) 155 | String map -> 156 (* Don't decode null values as strings - they should fail so outer combinators 157 like 'option' or 'any' can handle them properly. 158 BUT: quoted strings should always be treated as strings, even if they 159 look like null (e.g., "" or "null") *) 160 if style = `Plain && is_null_scalar value then 161 err_type_mismatch d ev.span t ~fnd:"null" 162 else 163 (* Strings accept quoted scalars or non-null plain scalars *) 164 map.dec meta value 165 | Map m -> 166 (* Handle Map combinators (e.g., from Jsont.option) *) 167 m.dec (decode_scalar_as d ev value style m.dom) 168 | Rec lazy_t -> 169 (* Handle recursive types *) 170 decode_scalar_as d ev value style (Lazy.force lazy_t) 171 | _ -> 172 err_type_mismatch d ev.span t ~fnd:"scalar" 173 174(* Forward declaration for mutual recursion *) 175let rec decode : type a. decoder -> nest:int -> a t -> a = 176 fun d ~nest t -> 177 check_depth d ~nest; 178 match peek_event d with 179 | None -> Jsont.Error.msgf Jsont.Meta.none "Unexpected end of YAML stream" 180 | Some ev -> 181 match ev.Event.event, t with 182 (* Scalar events *) 183 | Event.Scalar { value; style; anchor; _ }, _ -> 184 skip_event d; 185 let result = decode_scalar d ~nest ev value style t in 186 (* Store anchor if present - TODO: implement anchor storage *) 187 (match anchor with 188 | Some _name -> 189 (* We need generic JSON for anchors - decode as json and convert back *) 190 () 191 | None -> ()); 192 result 193 194 (* Alias *) 195 | Event.Alias { anchor }, _ -> 196 skip_event d; 197 decode_alias d ev anchor t 198 199 (* Map combinator - must come before specific event matches *) 200 | _, Map m -> 201 m.dec (decode d ~nest m.dom) 202 203 (* Recursive types - must come before specific event matches *) 204 | _, Rec lazy_t -> 205 decode d ~nest (Lazy.force lazy_t) 206 207 (* Sequence -> Array *) 208 | Event.Sequence_start _, Array map -> 209 decode_array d ~nest ev map 210 211 | Event.Sequence_start _, Any map -> 212 decode_any_sequence d ~nest ev t map 213 214 | Event.Sequence_start _, _ -> 215 err_type_mismatch d ev.span t ~fnd:"sequence" 216 217 (* Mapping -> Object *) 218 | Event.Mapping_start _, Object map -> 219 decode_object d ~nest ev map 220 221 | Event.Mapping_start _, Any map -> 222 decode_any_mapping d ~nest ev t map 223 224 | Event.Mapping_start _, _ -> 225 err_type_mismatch d ev.span t ~fnd:"mapping" 226 227 (* Unexpected events *) 228 | Event.Sequence_end, _ -> 229 Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected sequence end" 230 | Event.Mapping_end, _ -> 231 Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected mapping end" 232 | Event.Document_start _, _ -> 233 Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected document start" 234 | Event.Document_end _, _ -> 235 Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected document end" 236 | Event.Stream_start _, _ -> 237 Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected stream start" 238 | Event.Stream_end, _ -> 239 Jsont.Error.msgf (meta_of_span d ev.span) "Unexpected stream end" 240 241and decode_scalar : type a. decoder -> nest:int -> Event.spanned -> string -> Scalar_style.t -> a t -> a = 242 fun d ~nest ev value style t -> 243 match t with 244 | Any map -> decode_any_scalar d ev value style t map 245 | Map m -> m.dec (decode_scalar d ~nest ev value style m.dom) 246 | Rec lazy_t -> decode_scalar d ~nest ev value style (Lazy.force lazy_t) 247 | _ -> decode_scalar_as d ev value style t 248 249and decode_any_scalar : type a. decoder -> Event.spanned -> string -> Scalar_style.t -> a t -> a any_map -> a = 250 fun d ev value style t map -> 251 check_nodes d; 252 (* Determine which decoder to use based on scalar content *) 253 if is_null_scalar value then 254 match map.dec_null with 255 | Some t' -> decode_scalar_as d ev value style t' 256 | None -> Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.Null 257 else if style = `Plain then 258 (* Try bool, then number, then string *) 259 match bool_of_scalar_opt value with 260 | Some _ -> 261 (match map.dec_bool with 262 | Some t' -> decode_scalar_as d ev value style t' 263 | None -> 264 match map.dec_string with 265 | Some t' -> decode_scalar_as d ev value style t' 266 | None -> Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.Bool) 267 | None -> 268 match float_of_scalar_opt value with 269 | Some _ -> 270 (match map.dec_number with 271 | Some t' -> decode_scalar_as d ev value style t' 272 | None -> 273 match map.dec_string with 274 | Some t' -> decode_scalar_as d ev value style t' 275 | None -> Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.Number) 276 | None -> 277 (* Plain scalar that's not bool/number -> string *) 278 match map.dec_string with 279 | Some t' -> decode_scalar_as d ev value style t' 280 | None -> Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.String 281 else 282 (* Quoted scalars are strings *) 283 match map.dec_string with 284 | Some t' -> decode_scalar_as d ev value style t' 285 | None -> Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.String 286 287and decode_alias : type a. decoder -> Event.spanned -> string -> a t -> a = 288 fun d ev anchor t -> 289 check_nodes d; 290 match Hashtbl.find_opt d._anchors anchor with 291 | None -> 292 let meta = meta_of_span d ev.span in 293 Jsont.Error.msgf meta "Unknown anchor: %s" anchor 294 | Some json -> 295 (* Decode the stored JSON value through the type *) 296 let t' = Jsont.Repr.unsafe_to_t t in 297 match Jsont.Json.decode' t' json with 298 | Ok v -> v 299 | Error e -> raise (Jsont.Error e) 300 301and decode_array : type a elt b. decoder -> nest:int -> Event.spanned -> (a, elt, b) array_map -> a = 302 fun d ~nest start_ev map -> 303 skip_event d; (* consume Sequence_start *) 304 check_nodes d; 305 let meta = meta_of_span d start_ev.span in 306 let builder = ref (map.dec_empty ()) in 307 let idx = ref 0 in 308 let rec loop () = 309 match peek_event d with 310 | Some { Event.event = Event.Sequence_end; span } -> 311 skip_event d; 312 let end_meta = meta_of_span d span in 313 map.dec_finish end_meta !idx !builder 314 | Some _ -> 315 let i = !idx in 316 (try 317 if map.dec_skip i !builder then begin 318 (* Skip this element by decoding as ignore *) 319 let _ : unit = decode d ~nest:(nest + 1) (Jsont.Repr.of_t Jsont.ignore) in 320 () 321 end else begin 322 let elt = decode d ~nest:(nest + 1) map.elt in 323 builder := map.dec_add i elt !builder 324 end 325 with Jsont.Error e -> 326 let imeta = Jsont.Meta.none in 327 Jsont.Repr.error_push_array meta map (i, imeta) e); 328 incr idx; 329 loop () 330 | None -> 331 Jsont.Error.msgf meta "Unclosed sequence" 332 in 333 loop () 334 335and decode_any_sequence : type a. decoder -> nest:int -> Event.spanned -> a t -> a any_map -> a = 336 fun d ~nest ev t map -> 337 match map.dec_array with 338 | Some t' -> 339 (* The t' decoder might be wrapped (e.g., Map for option types) 340 Directly decode the array and let the wrapper handle it *) 341 (match t' with 342 | Array array_map -> 343 decode_array d ~nest ev array_map 344 | _ -> 345 (* For wrapped types like Map (Array ...), use full decode *) 346 decode d ~nest t') 347 | None -> Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.Array 348 349and decode_object : type o. decoder -> nest:int -> Event.spanned -> (o, o) object_map -> o = 350 fun d ~nest start_ev map -> 351 skip_event d; (* consume Mapping_start *) 352 check_nodes d; 353 let meta = meta_of_span d start_ev.span in 354 let dict = decode_object_members d ~nest meta map String_map.empty Dict.empty in 355 let dict = Dict.add object_meta_arg meta dict in 356 apply_dict map.dec dict 357 358and decode_object_members : type o. 359 decoder -> nest:int -> Jsont.Meta.t -> (o, o) object_map -> 360 mem_dec String_map.t -> Dict.t -> Dict.t = 361 fun d ~nest obj_meta map mem_miss dict -> 362 (* Merge expected member decoders *) 363 let u _ _ _ = assert false in 364 let mem_miss = String_map.union u mem_miss map.mem_decs in 365 match map.shape with 366 | Object_basic umems -> 367 decode_object_basic d ~nest obj_meta map umems mem_miss dict 368 | Object_cases (umems_opt, cases) -> 369 (* Wrap umems_opt to hide existential types *) 370 let umems = Unknown_mems umems_opt in 371 decode_object_cases d ~nest obj_meta map umems cases mem_miss [] dict 372 373and decode_object_basic : type o mems builder. 374 decoder -> nest:int -> Jsont.Meta.t -> (o, o) object_map -> 375 (o, mems, builder) unknown_mems -> 376 mem_dec String_map.t -> Dict.t -> Dict.t = 377 fun d ~nest obj_meta map umems mem_miss dict -> 378 let ubuilder = ref (match umems with 379 | Unknown_skip | Unknown_error -> Obj.magic () 380 | Unknown_keep (mmap, _) -> mmap.dec_empty ()) in 381 let mem_miss = ref mem_miss in 382 let dict = ref dict in 383 let rec loop () = 384 match peek_event d with 385 | Some { Event.event = Event.Mapping_end; _ } -> 386 skip_event d; 387 (* Finalize *) 388 finish_object obj_meta map umems !ubuilder !mem_miss !dict 389 | Some ev -> 390 (* Expect a scalar key *) 391 let name, name_meta = decode_mapping_key d ev in 392 (* Look up member decoder *) 393 (match String_map.find_opt name map.mem_decs with 394 | Some (Mem_dec mem) -> 395 mem_miss := String_map.remove name !mem_miss; 396 (try 397 let v = decode d ~nest:(nest + 1) mem.type' in 398 dict := Dict.add mem.id v !dict 399 with Jsont.Error e -> 400 Jsont.Repr.error_push_object obj_meta map (name, name_meta) e) 401 | None -> 402 (* Unknown member *) 403 match umems with 404 | Unknown_skip -> 405 let _ : unit = decode d ~nest:(nest + 1) (Jsont.Repr.of_t Jsont.ignore) in 406 () 407 | Unknown_error -> 408 Jsont.Repr.unexpected_mems_error obj_meta map ~fnd:[(name, name_meta)] 409 | Unknown_keep (mmap, _) -> 410 (try 411 let v = decode d ~nest:(nest + 1) mmap.mems_type in 412 ubuilder := mmap.dec_add name_meta name v !ubuilder 413 with Jsont.Error e -> 414 Jsont.Repr.error_push_object obj_meta map (name, name_meta) e)); 415 loop () 416 | None -> 417 Jsont.Error.msgf obj_meta "Unclosed mapping" 418 in 419 loop () 420 421and finish_object : type o mems builder. 422 Jsont.Meta.t -> (o, o) object_map -> (o, mems, builder) unknown_mems -> 423 builder -> mem_dec String_map.t -> Dict.t -> Dict.t = 424 fun meta map umems ubuilder mem_miss dict -> 425 let dict = Dict.add object_meta_arg meta dict in 426 let dict = match umems with 427 | Unknown_skip | Unknown_error -> dict 428 | Unknown_keep (mmap, _) -> Dict.add mmap.id (mmap.dec_finish meta ubuilder) dict 429 in 430 (* Check for missing required members *) 431 let add_default _ (Mem_dec mem_map) dict = 432 match mem_map.dec_absent with 433 | Some v -> Dict.add mem_map.id v dict 434 | None -> raise Exit 435 in 436 try String_map.fold add_default mem_miss dict 437 with Exit -> 438 let no_default _ (Mem_dec mm) = Option.is_none mm.dec_absent in 439 let exp = String_map.filter no_default mem_miss in 440 Jsont.Repr.missing_mems_error meta map ~exp ~fnd:[] 441 442and decode_object_cases : type o cases tag. 443 decoder -> nest:int -> Jsont.Meta.t -> (o, o) object_map -> 444 unknown_mems_option -> 445 (o, cases, tag) object_cases -> 446 mem_dec String_map.t -> (Jsont.name * Jsont.json) list -> Dict.t -> Dict.t = 447 fun d ~nest obj_meta map umems cases mem_miss delayed dict -> 448 match peek_event d with 449 | Some { Event.event = Event.Mapping_end; _ } -> 450 skip_event d; 451 (* No tag found - use dec_absent if available *) 452 (match cases.tag.dec_absent with 453 | Some tag -> 454 decode_with_case_tag d ~nest obj_meta map umems cases tag mem_miss delayed dict 455 | None -> 456 (* Missing required case tag *) 457 let exp = String_map.singleton cases.tag.name (Mem_dec cases.tag) in 458 let fnd = List.map (fun ((n, _), _) -> n) delayed in 459 Jsont.Repr.missing_mems_error obj_meta map ~exp ~fnd) 460 | Some ev -> 461 let name, name_meta = decode_mapping_key d ev in 462 if String.equal name cases.tag.name then begin 463 (* Found the case tag *) 464 let tag = decode d ~nest:(nest + 1) cases.tag.type' in 465 decode_with_case_tag d ~nest obj_meta map umems cases tag mem_miss delayed dict 466 end else begin 467 (* Not the case tag - check if known member or delay *) 468 match String_map.find_opt name map.mem_decs with 469 | Some (Mem_dec mem) -> 470 let mem_miss = String_map.remove name mem_miss in 471 (try 472 let v = decode d ~nest:(nest + 1) mem.type' in 473 let dict = Dict.add mem.id v dict in 474 decode_object_cases d ~nest obj_meta map umems cases mem_miss delayed dict 475 with Jsont.Error e -> 476 Jsont.Repr.error_push_object obj_meta map (name, name_meta) e) 477 | None -> 478 (* Unknown member - decode as generic JSON and delay *) 479 let v = decode d ~nest:(nest + 1) (Jsont.Repr.of_t Jsont.json) in 480 let delayed = ((name, name_meta), v) :: delayed in 481 decode_object_cases d ~nest obj_meta map umems cases mem_miss delayed dict 482 end 483 | None -> 484 Jsont.Error.msgf obj_meta "Unclosed mapping" 485 486and decode_with_case_tag : type o cases tag. 487 decoder -> nest:int -> Jsont.Meta.t -> (o, o) object_map -> 488 unknown_mems_option -> 489 (o, cases, tag) object_cases -> tag -> 490 mem_dec String_map.t -> (Jsont.name * Jsont.json) list -> Dict.t -> Dict.t = 491 fun d ~nest obj_meta map umems cases tag mem_miss delayed dict -> 492 let eq_tag (Case c) = cases.tag_compare c.tag tag = 0 in 493 match List.find_opt eq_tag cases.cases with 494 | None -> 495 Jsont.Repr.unexpected_case_tag_error obj_meta map cases tag 496 | Some (Case case) -> 497 (* Continue decoding with the case's object map *) 498 let case_dict = decode_case_remaining d ~nest obj_meta case.object_map 499 umems mem_miss delayed dict in 500 let case_value = apply_dict case.object_map.dec case_dict in 501 Dict.add cases.id (case.dec case_value) dict 502 503and decode_case_remaining : type o. 504 decoder -> nest:int -> Jsont.Meta.t -> (o, o) object_map -> 505 unknown_mems_option -> 506 mem_dec String_map.t -> (Jsont.name * Jsont.json) list -> Dict.t -> Dict.t = 507 fun d ~nest obj_meta case_map _umems mem_miss delayed dict -> 508 (* First, process delayed members against the case map *) 509 let u _ _ _ = assert false in 510 let mem_miss = String_map.union u mem_miss case_map.mem_decs in 511 let dict, mem_miss = List.fold_left (fun (dict, mem_miss) ((name, meta), json) -> 512 match String_map.find_opt name case_map.mem_decs with 513 | Some (Mem_dec mem) -> 514 let t' = Jsont.Repr.unsafe_to_t mem.type' in 515 (match Jsont.Json.decode' t' json with 516 | Ok v -> 517 let dict = Dict.add mem.id v dict in 518 let mem_miss = String_map.remove name mem_miss in 519 (dict, mem_miss) 520 | Error e -> 521 Jsont.Repr.error_push_object obj_meta case_map (name, meta) e) 522 | None -> 523 (* Unknown for case too - skip them *) 524 (dict, mem_miss) 525 ) (dict, mem_miss) delayed in 526 (* Then continue reading remaining members using case's own unknown handling *) 527 match case_map.shape with 528 | Object_basic case_umems -> 529 decode_object_basic d ~nest obj_meta case_map case_umems mem_miss dict 530 | Object_cases _ -> 531 (* Nested cases shouldn't happen - use skip for safety *) 532 decode_object_basic d ~nest obj_meta case_map Unknown_skip mem_miss dict 533 534and decode_any_mapping : type a. decoder -> nest:int -> Event.spanned -> a t -> a any_map -> a = 535 fun d ~nest ev t map -> 536 match map.dec_object with 537 | Some t' -> decode d ~nest t' 538 | None -> Jsont.Repr.type_error (meta_of_span d ev.span) t ~fnd:Jsont.Sort.Object 539 540and decode_mapping_key : decoder -> Event.spanned -> string * Jsont.Meta.t = 541 fun d ev -> 542 match ev.Event.event with 543 | Event.Scalar { value; _ } -> 544 skip_event d; 545 let meta = meta_of_span d ev.span in 546 (value, meta) 547 | _ -> 548 let meta = meta_of_span d ev.span in 549 Jsont.Error.msgf meta "Mapping keys must be scalars (strings), found %a" 550 Event.pp ev.event 551 552(* Skip stream/document wrappers *) 553let skip_to_content d = 554 let rec loop () = 555 match peek_event d with 556 | Some { Event.event = Event.Stream_start _; _ } -> skip_event d; loop () 557 | Some { Event.event = Event.Document_start _; _ } -> skip_event d; loop () 558 | _ -> () 559 in 560 loop () 561 562let skip_end_wrappers d = 563 let rec loop () = 564 match peek_event d with 565 | Some { Event.event = Event.Document_end _; _ } -> skip_event d; loop () 566 | Some { Event.event = Event.Stream_end; _ } -> skip_event d; loop () 567 | None -> () 568 | Some ev -> 569 let meta = meta_of_span d ev.span in 570 Jsont.Error.msgf meta "Expected end of document but found %a" Event.pp ev.event 571 in 572 loop () 573 574(* Public decode API *) 575 576let decode' ?layout ?locs ?file ?max_depth ?max_nodes t reader = 577 let parser = Parser.of_reader reader in 578 let d = make_decoder ?layout ?locs ?file ?max_depth ?max_nodes parser in 579 try 580 skip_to_content d; 581 let t' = Jsont.Repr.of_t t in 582 let v = decode d ~nest:0 t' in 583 skip_end_wrappers d; 584 Ok v 585 with 586 | Jsont.Error e -> Error e 587 | Error.Yamlrw_error err -> 588 let msg = Error.to_string err in 589 Error (Jsont.Error.make_msg Jsont.Error.Context.empty Jsont.Meta.none msg) 590 591let decode ?layout ?locs ?file ?max_depth ?max_nodes t reader = 592 Result.map_error Jsont.Error.to_string 593 (decode' ?layout ?locs ?file ?max_depth ?max_nodes t reader) 594 595let decode_string' ?layout ?locs ?file ?max_depth ?max_nodes t s = 596 decode' ?layout ?locs ?file ?max_depth ?max_nodes t (Bytes.Reader.of_string s) 597 598let decode_string ?layout ?locs ?file ?max_depth ?max_nodes t s = 599 decode ?layout ?locs ?file ?max_depth ?max_nodes t (Bytes.Reader.of_string s) 600 601(* Encoder *) 602 603type encoder = { 604 emitter : Emitter.t; 605 format : yaml_format; 606 _indent : int; (* Stored for future use in custom formatting *) 607 explicit_doc : bool; 608 scalar_style : Scalar_style.t; 609} 610 611let make_encoder 612 ?(format = Block) ?(indent = 2) ?(explicit_doc = false) 613 ?(scalar_style = `Any) emitter = 614 { emitter; format; _indent = indent; explicit_doc; scalar_style } 615 616let layout_style_of_format = function 617 | Block -> `Block 618 | Flow -> `Flow 619 | Layout -> `Any 620 621(* Choose appropriate scalar style for a string *) 622let choose_scalar_style ~preferred s = 623 if preferred <> `Any then preferred 624 else if String.contains s '\n' then `Literal 625 else if String.length s > 80 then `Folded 626 else `Plain 627 628(* Encode null *) 629let encode_null e _meta = 630 Emitter.emit e.emitter (Event.Scalar { 631 anchor = None; 632 tag = None; 633 value = "null"; 634 plain_implicit = true; 635 quoted_implicit = true; 636 style = `Plain; 637 }) 638 639(* Encode boolean *) 640let encode_bool e _meta b = 641 Emitter.emit e.emitter (Event.Scalar { 642 anchor = None; 643 tag = None; 644 value = if b then "true" else "false"; 645 plain_implicit = true; 646 quoted_implicit = true; 647 style = `Plain; 648 }) 649 650(* Encode number *) 651let encode_number e _meta f = 652 let value = 653 match Float.classify_float f with 654 | FP_nan -> ".nan" 655 | FP_infinite -> if f > 0.0 then ".inf" else "-.inf" 656 | _ -> 657 if Float.is_integer f && Float.abs f < 1e15 then 658 Printf.sprintf "%.0f" f 659 else 660 Printf.sprintf "%g" f 661 in 662 Emitter.emit e.emitter (Event.Scalar { 663 anchor = None; 664 tag = None; 665 value; 666 plain_implicit = true; 667 quoted_implicit = true; 668 style = `Plain; 669 }) 670 671(* Encode string *) 672let encode_string e _meta s = 673 let style = choose_scalar_style ~preferred:e.scalar_style s in 674 Emitter.emit e.emitter (Event.Scalar { 675 anchor = None; 676 tag = None; 677 value = s; 678 plain_implicit = true; 679 quoted_implicit = true; 680 style; 681 }) 682 683let rec encode : type a. encoder -> a t -> a -> unit = 684 fun e t v -> 685 match t with 686 | Null map -> 687 let meta = map.enc_meta v in 688 let () = map.enc v in 689 encode_null e meta 690 691 | Bool map -> 692 let meta = map.enc_meta v in 693 let b = map.enc v in 694 encode_bool e meta b 695 696 | Number map -> 697 let meta = map.enc_meta v in 698 let f = map.enc v in 699 encode_number e meta f 700 701 | String map -> 702 let meta = map.enc_meta v in 703 let s = map.enc v in 704 encode_string e meta s 705 706 | Array map -> 707 encode_array e map v 708 709 | Object map -> 710 encode_object e map v 711 712 | Any map -> 713 let t' = map.enc v in 714 encode e t' v 715 716 | Map m -> 717 encode e m.dom (m.enc v) 718 719 | Rec lazy_t -> 720 encode e (Lazy.force lazy_t) v 721 722and encode_array : type a elt b. encoder -> (a, elt, b) array_map -> a -> unit = 723 fun e map v -> 724 let style = layout_style_of_format e.format in 725 Emitter.emit e.emitter (Event.Sequence_start { 726 anchor = None; 727 tag = None; 728 implicit = true; 729 style; 730 }); 731 let _ = map.enc (fun () _idx elt -> 732 encode e map.elt elt; 733 () 734 ) () v in 735 Emitter.emit e.emitter Event.Sequence_end 736 737and encode_object : type o. encoder -> (o, o) object_map -> o -> unit = 738 fun e map v -> 739 let style = layout_style_of_format e.format in 740 Emitter.emit e.emitter (Event.Mapping_start { 741 anchor = None; 742 tag = None; 743 implicit = true; 744 style; 745 }); 746 (* Encode each member *) 747 List.iter (fun (Mem_enc mem) -> 748 let mem_v = mem.enc v in 749 if not (mem.enc_omit mem_v) then begin 750 (* Emit key *) 751 Emitter.emit e.emitter (Event.Scalar { 752 anchor = None; 753 tag = None; 754 value = mem.name; 755 plain_implicit = true; 756 quoted_implicit = true; 757 style = `Plain; 758 }); 759 (* Emit value *) 760 encode e mem.type' mem_v 761 end 762 ) map.mem_encs; 763 (* Handle case objects *) 764 (match map.shape with 765 | Object_basic _ -> () 766 | Object_cases (_, cases) -> 767 let Case_value (case_map, case_v) = cases.enc_case (cases.enc v) in 768 (* Emit case tag *) 769 if not (cases.tag.enc_omit (case_map.tag)) then begin 770 Emitter.emit e.emitter (Event.Scalar { 771 anchor = None; 772 tag = None; 773 value = cases.tag.name; 774 plain_implicit = true; 775 quoted_implicit = true; 776 style = `Plain; 777 }); 778 encode e cases.tag.type' case_map.tag 779 end; 780 (* Emit case members *) 781 List.iter (fun (Mem_enc mem) -> 782 let mem_v = mem.enc case_v in 783 if not (mem.enc_omit mem_v) then begin 784 Emitter.emit e.emitter (Event.Scalar { 785 anchor = None; 786 tag = None; 787 value = mem.name; 788 plain_implicit = true; 789 quoted_implicit = true; 790 style = `Plain; 791 }); 792 encode e mem.type' mem_v 793 end 794 ) case_map.object_map.mem_encs); 795 Emitter.emit e.emitter Event.Mapping_end 796 797(* Public encode API *) 798 799let encode' ?buf:_ ?format ?indent ?explicit_doc ?scalar_style t v ~eod writer = 800 let config = { 801 Emitter.default_config with 802 indent = Option.value ~default:2 indent; 803 layout_style = (match format with 804 | Some Flow -> `Flow 805 | _ -> `Block); 806 } in 807 let emitter = Emitter.of_writer ~config writer in 808 let e = make_encoder ?format ?indent ?explicit_doc ?scalar_style emitter in 809 try 810 Emitter.emit e.emitter (Event.Stream_start { encoding = `Utf8 }); 811 Emitter.emit e.emitter (Event.Document_start { 812 version = None; 813 implicit = not e.explicit_doc; 814 }); 815 let t' = Jsont.Repr.of_t t in 816 encode e t' v; 817 Emitter.emit e.emitter (Event.Document_end { implicit = not e.explicit_doc }); 818 Emitter.emit e.emitter Event.Stream_end; 819 if eod then Emitter.flush e.emitter; 820 Ok () 821 with 822 | Jsont.Error err -> Error err 823 | Error.Yamlrw_error err -> 824 let msg = Error.to_string err in 825 Error (Jsont.Error.make_msg Jsont.Error.Context.empty Jsont.Meta.none msg) 826 827let encode ?buf ?format ?indent ?explicit_doc ?scalar_style t v ~eod writer = 828 Result.map_error Jsont.Error.to_string 829 (encode' ?buf ?format ?indent ?explicit_doc ?scalar_style t v ~eod writer) 830 831let encode_string' ?buf ?format ?indent ?explicit_doc ?scalar_style t v = 832 let b = Buffer.create 256 in 833 let writer = Bytes.Writer.of_buffer b in 834 match encode' ?buf ?format ?indent ?explicit_doc ?scalar_style t v ~eod:true writer with 835 | Ok () -> Ok (Buffer.contents b) 836 | Error e -> Error e 837 838let encode_string ?buf ?format ?indent ?explicit_doc ?scalar_style t v = 839 Result.map_error Jsont.Error.to_string 840 (encode_string' ?buf ?format ?indent ?explicit_doc ?scalar_style t v) 841 842(* Recode *) 843 844let recode ?layout ?locs ?file ?max_depth ?max_nodes 845 ?buf ?format ?indent ?explicit_doc ?scalar_style t reader writer ~eod = 846 let format = match layout, format with 847 | Some true, None -> Some Layout 848 | _, f -> f 849 in 850 let layout = match layout, format with 851 | None, Some Layout -> Some true 852 | l, _ -> l 853 in 854 match decode' ?layout ?locs ?file ?max_depth ?max_nodes t reader with 855 | Ok v -> encode ?buf ?format ?indent ?explicit_doc ?scalar_style t v ~eod writer 856 | Error e -> Error (Jsont.Error.to_string e) 857 858let recode_string ?layout ?locs ?file ?max_depth ?max_nodes 859 ?buf ?format ?indent ?explicit_doc ?scalar_style t s = 860 let format = match layout, format with 861 | Some true, None -> Some Layout 862 | _, f -> f 863 in 864 let layout = match layout, format with 865 | None, Some Layout -> Some true 866 | l, _ -> l 867 in 868 match decode_string' ?layout ?locs ?file ?max_depth ?max_nodes t s with 869 | Ok v -> encode_string ?buf ?format ?indent ?explicit_doc ?scalar_style t v 870 | Error e -> Error (Jsont.Error.to_string e)