Repository: avsm/ocaml-yaml
Pull request #46 (2021-07-14 02:05:26)

Move sexp conversion functions to a separate package
This PR separates the sexp conversion functions into a different package as desired in https://github.com/avsm/ocaml-yaml/issues/44.

Notes:

- Various "todo" logging statements in the main Yaml package rely on the sexp conversion functions. I've removed these usages of the sexp conversion functions.
- The tests use the sexp conversion functions to print values. Because the tests must run for the main Yaml package, without the sexp package installed, I've removed these usages of the sexp conversion functions.
- As seen at http://anil-code.recoil.org/ocaml-yaml/yaml/Yaml/index.html, the PPX generated some odd functions surrounded with double underscores. I've removed these functions from the public API.
- How should the copyright work? Should I just copy the copyright header found in the other source files (where the copyright belongs to Anil Madhavapeddy)?


Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 7155fe0..c585ec4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,21 @@
+## v3.0.0
+
+* Support Yaml 1.2, and stop advertising Yaml 1.0 support.
+  This also updates the vendored libyaml to 0.2.5 (@favonia #37).
+
+* Add a `Yaml.Util` module with a number of useful combinators
+  to manipulate `Yaml.value` types, such as retrieving keys and
+  values, finding entries and converting to OCaml native
+  types (@patricoferris #43)
+
+* Bump the internal write buffer for stream emission to
+  256k from 64k, as people are writing ever-larger Yaml
+  files! In the future, this static buffer will be replaced
+  by a dynamically growing output buffer but for now needs
+  to be set manually.
+
+* The minimum supported OCaml version is now OCaml 4.05.
+
 ## v2.1.0 (07/02/2020)
 
 * Fix a memory unsoundness issue with larger files in the
diff --git a/README.md b/README.md
index 63e36f0..ccd068f 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-## ocaml-yaml -- parse and generate YAML 1.1 files
+## ocaml-yaml -- parse and generate YAML 1.1/1.2 files
 
 This is an OCaml library to parse and generate the YAML file
 format.  It is intended to interoperable with the [Ezjsonm](https://github.com/mirage/ezjsonm)
diff --git a/fuzz/dune b/fuzz/dune
index 8972ff1..d66e69f 100644
--- a/fuzz/dune
+++ b/fuzz/dune
@@ -1,6 +1,6 @@
-(executables
- (names fuzz) (libraries crowbar yaml))
+;(executables
+; (names fuzz) (libraries crowbar yaml))
 
-(alias
- (name    runfuzz)
- (action  (run ./fuzz.exe)))
+;(alias
+; (name    runfuzz)
+; (action  (run ./fuzz.exe)))
diff --git a/lib/dune b/lib/dune
index ceea843..1bcbb4f 100644
--- a/lib/dune
+++ b/lib/dune
@@ -1,5 +1,4 @@
 (library
  (name yaml)
  (public_name yaml)
- (libraries yaml.ffi rresult logs fmt sexplib)
- (preprocess (pps ppx_sexp_conv)))
+ (libraries yaml.ffi rresult))
diff --git a/lib/stream.ml b/lib/stream.ml
index d7e89a3..7d7feac 100644
--- a/lib/stream.ml
+++ b/lib/stream.ml
@@ -12,7 +12,6 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *)
 
-open Sexplib.Conv
 open Rresult
 open Types
 
@@ -83,10 +82,15 @@ let list_of_tag_directives tds =
 
 let version_of_directive ~major ~minor =
   match major, minor with
-  | 1,0 -> `V1_0
   | 1,1 -> `V1_1
+  | 1,2 -> `V1_2
   | _ -> raise (Invalid_argument (Printf.sprintf "Unsupported Yaml version %d.%d" major minor))
 
+let directive_of_version =
+  function
+  | `V1_1 -> 1,1
+  | `V1_2 -> 1,2
+
 module Mark = struct
   type t = {
     index: int;
@@ -107,7 +111,7 @@ module Event = struct
   type pos = {
     start_mark: Mark.t;
     end_mark: Mark.t;
-  } [@@deriving sexp]
+  }
 
   type t =
    | Stream_start of { encoding: encoding }
@@ -121,7 +125,6 @@ module Event = struct
    | Sequence_end
    | Alias of { anchor: string }
    | Nothing
-   [@@deriving sexp]
 
   let of_ffi e : t * pos =
     let open T.Event in
@@ -244,7 +247,7 @@ type emitter = {
 
 let emitter_written {written;_} = Ctypes.(!@ written) |> Unsigned.Size_t.to_int
 
-let emitter ?(len=65535) () =
+let emitter ?(len=(65535*4)) () =
   let e = Ctypes.(allocate_n T.Emitter.t ~count:1) in
   let event = Ctypes.(allocate_n T.Event.t ~count:1) in
   let written = Ctypes.allocate_n Ctypes.size_t ~count:1 in
@@ -277,9 +280,18 @@ let stream_start t encoding =
 let stream_end t =
   check_emit "stream_end" t @@ B.stream_end_event_init t.event
 
-let document_start ?(implicit=true) t  =
+let document_start ?version ?(implicit=true) t  =
   let open Ctypes in
-  let ver = from_voidp T.Version_directive.t null in
+  let ver =
+    match version with
+    | None -> from_voidp T.Version_directive.t null
+    | Some v ->
+      let major, minor = directive_of_version v in
+      let v = make T.Version_directive.t in
+      setf v T.Version_directive.major major;
+      setf v T.Version_directive.minor minor;
+      allocate T.Version_directive.t @@ v
+  in
   let tag = from_voidp T.Tag_directive.t null in
   check_emit "doc_start" t @@ B.document_start_event_init t.event ver tag tag implicit
 
@@ -308,7 +320,7 @@ let emit t =
   let open Event in
   function
   | Stream_start { encoding } -> stream_start t encoding
-  | Document_start { version; implicit } -> document_start ~implicit t
+  | Document_start { version; implicit } -> document_start ?version ~implicit t
   | Document_end { implicit } -> document_end ~implicit t
   | Mapping_start { anchor; tag; implicit; style } -> mapping_start ?anchor ?tag ~implicit ~style t
   | Mapping_end -> mapping_end t
diff --git a/lib/types.ml b/lib/types.ml
index 3884a9f..6c780ea 100644
--- a/lib/types.ml
+++ b/lib/types.ml
@@ -12,19 +12,16 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *)
 
-open Sexplib.Conv
-
 type version = [
- | `V1_0
  | `V1_1
-] [@@deriving sexp]
+ | `V1_2
+]
 
 type encoding = [
  | `Any
  | `Utf16be
  | `Utf16le
  | `Utf8  ]
-[@@deriving sexp]
 
 type scalar_style = [
  | `Any
@@ -33,13 +30,12 @@ type scalar_style = [
  | `Double_quoted
  | `Literal
  | `Folded ]
-[@@deriving sexp]
 
 type layout_style = [
   | `Any
   | `Block
   | `Flow
-] [@@deriving sexp]
+]
 
 type value =
   [ `Null
@@ -48,7 +44,7 @@ type value =
   | `String of string
   | `A of value list
   | `O of (string * value) list
-] [@@deriving sexp]
+]
 
 type scalar = {
   anchor: string option;
@@ -57,13 +53,13 @@ type scalar = {
   plain_implicit: bool;
   quoted_implicit: bool;
   style: scalar_style
-} [@@deriving sexp]
+}
 
 type yaml =
   [ `Scalar of scalar
   | `Alias of string
   | `A of yaml list
   | `O of (scalar * yaml) list
-] [@@deriving sexp]
+]
 
 type 'a res = ('a, Rresult.R.msg) Result.result
diff --git a/lib/util.ml b/lib/util.ml
new file mode 100644
index 0000000..baf6c3b
--- /dev/null
+++ b/lib/util.ml
@@ -0,0 +1,81 @@
+exception Value_error of string
+
+let keys = function
+  | `O assoc -> Ok (List.map fst assoc)
+  | _ -> Error (`Msg "Failed to get keys from non-object value")
+
+let keys_exn = function
+  | `O assoc -> List.map fst assoc
+  | _ -> raise (Value_error "Failed to get keys from non-object value")
+
+let values = function
+  | `O assoc -> Ok (List.map snd assoc)
+  | _ -> Error (`Msg "Failed to get values from non-object value")
+
+let values_exn = function
+  | `O assoc -> List.map snd assoc
+  | _ -> raise (Value_error "Failed to get values from non-object value")
+
+let combine a b =
+  match (a, b) with
+  | `O a, `O b -> Ok (`O (a @ b))
+  | _ -> Error (`Msg "Expected two objects")
+
+let combine_exn a b =
+  match (a, b) with
+  | `O a, `O b -> `O (a @ b)
+  | _ -> raise (Value_error "Expected two objects")
+
+let find s = function
+  | `O assoc -> Ok (List.assoc_opt s assoc)
+  | _ -> Error (`Msg "Expected an object")
+
+let find_exn s = function
+  | `O assoc -> List.assoc_opt s assoc
+  | _ -> raise (Value_error "Expected an object")
+
+let map f = function
+  | `A lst -> Ok (`A (List.map f lst))
+  | _ -> Error (`Msg "Expected a value array")
+
+let map_exn f = function
+  | `A lst -> `A (List.map f lst)
+  | _ -> raise (Value_error "Expected a value array")
+
+let filter p = function
+  | `A lst -> Ok (`A (List.filter p lst))
+  | _ -> Error (`Msg "Expected a value array")
+
+let filter_exn p = function
+  | `A lst -> `A (List.filter p lst)
+  | _ -> raise (Value_error "Expected a value array")
+
+let to_string = function
+  | `String s -> Ok s
+  | _ -> Error (`Msg "Expected a string value")
+
+let to_string_exn = function
+  | `String s -> s
+  | _ -> raise (Value_error "Expected a string value")
+
+let to_bool = function
+  | `Bool b -> Ok b
+  | _ -> Error (`Msg "Expected a bool value")
+
+let to_bool_exn = function
+  | `Bool b -> b
+  | _ -> raise (Value_error "Expected a bool value")
+
+let to_float = function
+  | `Float f -> Ok f
+  | _ -> Error (`Msg "Expected a float value")
+
+let to_float_exn = function
+  | `Float f -> f
+  | _ -> raise (Value_error "Expected a float value")
+
+let string s = `String s
+let bool b = `Bool b
+let float f = `Float f
+let list f lst = `A (List.map f lst)
+let obj assoc = `O assoc
diff --git a/lib/yaml.ml b/lib/yaml.ml
index d3b6aba..32b7583 100644
--- a/lib/yaml.ml
+++ b/lib/yaml.ml
@@ -16,6 +16,7 @@ include Types
 open Rresult
 open R.Infix
 
+module Util = Util
 module Stream = Stream
 open Stream
 
@@ -128,7 +129,6 @@ let yaml_of_string s =
   parser s >>= fun t ->
   let next () =
    do_parse t >>= fun (e, pos) ->
-   Logs.debug (fun l -> l "event %s\n%!" (sexp_of_t e |> Sexplib.Sexp.to_string_hum));
    Ok (e,pos) in
   next () >>= fun (e,pos) ->
   match e with
@@ -148,14 +148,14 @@ let yaml_of_string s =
             next () >>=
             parse_map [] >>= fun s ->
             Ok (`O s)
-         | e -> R.error_msg (Fmt.strf "todo %s (%s)" (sexp_of_t e |> Sexplib.Sexp.to_string_hum) (sexp_of_pos pos |> Sexplib.Sexp.to_string_hum))
+         | e -> R.error_msg "todo"
        and parse_seq acc (e,pos) =
-          match e with
-          | Sequence_end -> Ok (List.rev acc)
-          | e ->
-             parse_v (e,pos) >>= fun v ->
-             next () >>=
-             parse_seq (v :: acc)
+         match e with
+         | Sequence_end -> Ok (List.rev acc)
+         | e ->
+           parse_v (e,pos) >>= fun v ->
+           next () >>=
+           parse_seq (v :: acc)
        and parse_map acc (e,pos) =
          match e with
          | Mapping_end -> Ok (List.rev acc)
@@ -167,7 +167,7 @@ let yaml_of_string s =
                 parse_v >>= fun v ->
                 next () >>=
                 parse_map ((k,v)::acc)
-             | _ -> R.error_msg (Fmt.strf "only string keys are supported (%s)" (sexp_of_pos pos |> Sexplib.Sexp.to_string_hum))
+             | _ -> R.error_msg "only string keys are supported"
              end
          end
        in
@@ -175,7 +175,7 @@ let yaml_of_string s =
        parse_v
     end
     | Stream_end -> Ok (`Scalar (scalar ""))
-    | e -> R.error_msg (Fmt.strf "Not document start: %s" (sexp_of_t e |> Sexplib.Sexp.to_string_hum))
+    | e -> R.error_msg "Not document start"
   end
   | _ -> R.error_msg "Not stream start"
 
diff --git a/lib/yaml.mli b/lib/yaml.mli
index 93ea9b6..a76876b 100644
--- a/lib/yaml.mli
+++ b/lib/yaml.mli
@@ -37,7 +37,7 @@ type value =
   | `String of string
   | `A of value list
   | `O of (string * value) list
-] [@@deriving sexp]
+]
 (** [value] is the subset of a Yaml document that is compatible
   with JSON.  This type is the same as {!Ezjsonm.value}, and so
   most simple uses of Yaml can be interchanged with JSON. *)
@@ -62,7 +62,7 @@ and scalar = {
   plain_implicit: bool;
   quoted_implicit: bool;
   style: scalar_style
-} [@@deriving sexp]
+}
 (** [scalar] is the description of a Yaml scalar.
 
    Scalar values are represented as strings. A scalar can optionally have an
@@ -78,26 +78,25 @@ and scalar_style = [
   | `Double_quoted
   | `Literal
   | `Folded ]
-  [@@deriving sexp]
 (** YAML provides three flow scalar styles: double-quoted, single-quoted
   and plain (unquoted). Each provides a different trade-off between readability
   and expressive power.
   The {{:http://www.yaml.org/spec/1.2/spec.html#id2786942:}Yaml spec section 7.3}
   has more details. *)
 
-type version = [ `V1_0 | `V1_1 ] [@@deriving sexp]
+type version = [ `V1_1 | `V1_2 ]
 (** Version of the YAML spec of a document.
   Refer to the {{:http://www.yaml.org/spec/1.2/spec.html}Yaml specification}
   for details of the differences between versions. *)
 
-type encoding = [ `Any | `Utf16be | `Utf16le | `Utf8 ] [@@deriving sexp]
+type encoding = [ `Any | `Utf16be | `Utf16le | `Utf8 ]
 (** Document encoding. The recommended format is [Utf8]. *)
 
 type layout_style = [
   | `Any
   | `Block
   | `Flow
-] [@@deriving sexp]
+]
 (** Mappings and sequences can be rendered in two different ways:
   - [Flow] styles can be thought of as the natural extension of
     JSON to cover folding long content lines for readability, tagging nodes
@@ -188,13 +187,12 @@ module Stream : sig
       { index: int  (** position in characters *)
       ; line: int  (** line number *)
       ; column: int  (** column number *) }
-      [@@deriving sexp]
   end
 
   (** Definition of an individual event during a processing stream *)
   module Event : sig
     (** Delimited positioning information for an event in the document. *)
-    type pos = {start_mark: Mark.t; end_mark: Mark.t} [@@deriving sexp]
+    type pos = {start_mark: Mark.t; end_mark: Mark.t}
 
     (** [t] represents a single event in a YAML processing stream.
 
@@ -225,7 +223,6 @@ module Stream : sig
       | Sequence_end
       | Alias of { anchor: string}
       | Nothing
-      [@@deriving sexp]
   end
 
   (** {3 Parsing functions} *)
@@ -255,7 +252,7 @@ module Stream : sig
 
   val emit : emitter -> Event.t -> unit res
 
-  val document_start : ?implicit:bool -> emitter -> unit res
+  val document_start : ?version:version -> ?implicit:bool -> emitter -> unit res
 
   val document_end : ?implicit:bool -> emitter -> unit res
 
@@ -281,3 +278,95 @@ module Stream : sig
   (** [library_version ()] returns the major, minor and patch version of the underlying libYAML implementation. *)
 
 end
+
+(** {2 Utility functions for yaml}
+
+    The {!Util} modules provides combinators and useful functions for
+    manipulating yaml and OCaml values. *)
+module Util : sig
+  exception Value_error of string
+  (** An exception raised when the wrong type of yaml value is passed to a
+      utility function. *)
+
+  val keys : value -> string list res
+  (** [keys obj] returns the keys of the object value or an error if the wrong
+      type of value is given as an argument. *)
+
+  val keys_exn : value -> string list
+  (** [keys_exn obj] is the same as {!keys} except it raises an exception. *)
+
+  val values : value -> value list res
+  (** [values obj] returns the values of the object value or an error if the
+      wrong type of value is given as an argument. *)
+
+  val values_exn : value -> value list
+  (** [values_exn obj] is the same as {!values} except it raises an exception. *)
+
+  val combine : value -> value -> value res
+  (** [combine a b] combines the key-value pairs of the two objects or returns
+      an error if the wrong type of value is given as an argument. *)
+
+  val combine_exn : value -> value -> value
+  (** [combines_exn a b] is the same as {!combines} except it raises an
+      exception. *)
+
+  val find : string -> value -> value option res
+  (** [find key obj] returns the value associated with the [key] in the object,
+      or [None] if nothing is found. An error is returned if the wrong type of
+      value is given as an argument. *)
+
+  val find_exn : string -> value -> value option
+  (** [find_exn s obj] is the same as {!find} except it raises an exception. *)
+
+  val map : (value -> value) -> value -> value res
+  (** [map f arr] applies [f] to every value in [arr], an error is retured if
+      the wrong type of value is given. *)
+
+  val map_exn : (value -> value) -> value -> value
+  (** [map_exn f obj] is the same as {!map} except it raises an exception. *)
+
+  val filter : (value -> bool) -> value -> value res
+  (** [filter f arr] filters out values in [arr] using [f], an error is retured
+      if the wrong type of value is given. *)
+
+  val filter_exn : (value -> bool) -> value -> value
+  (** [filter_exn p obj] is the same as {!filter} except it raises an exception. *)
+
+  val to_string : value -> string res
+  (** [to_string v] tries to convert [v] to a string if the underlying value is
+      a string. *)
+
+  val to_string_exn : value -> string
+  (** [to_string_exn v] is the same as {!to_string} except it raises an
+      exception. *)
+
+  val to_bool : value -> bool res
+  (** [to_bool v] tries to convert [v] to a boolean if the underlying value is a
+      boolean. *)
+
+  val to_bool_exn : value -> bool
+  (** [to_bool_exn v] is the same as {!to_bool} except it raises an exception. *)
+
+  val to_float : value -> float res
+  (** [to_float v] tries to convert [v] to a float if the underlying value is a
+      float. *)
+
+  val to_float_exn : value -> float
+  (** [to_float_exn v] is the same as {!to_float} except it raises an exception. *)
+
+  val string : string -> value
+  (** Make a value from a string. *)
+
+  val bool : bool -> value
+  (** Make a value from a boolean. *)
+
+  val float : float -> value
+  (** Make a value from a float. *)
+
+  val list : ('a -> value) -> 'a list -> value
+  (** [list f lst] makes a value from a list and a function [f] to apply to
+      every element. *)
+
+  val obj : (string * value) list -> value
+  (** Make a value from an association list. *)
+end
diff --git a/lib_sexp/dune b/lib_sexp/dune
new file mode 100644
index 0000000..a58ed4c
--- /dev/null
+++ b/lib_sexp/dune
@@ -0,0 +1,5 @@
+(library
+ (name yaml_sexp)
+ (public_name yaml-sexp)
+ (libraries yaml sexplib)
+ (preprocess (pps ppx_sexp_conv)))
diff --git a/lib_sexp/yaml_sexp.ml b/lib_sexp/yaml_sexp.ml
new file mode 100644
index 0000000..247f30f
--- /dev/null
+++ b/lib_sexp/yaml_sexp.ml
@@ -0,0 +1,96 @@
+(* Copyright (c) 2017 Anil Madhavapeddy <anil@recoil.org>
+ * Copyright (c) 2021 Alan J Hu <alanh@ccs.neu.edu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *)
+
+open Sexplib0.Sexp_conv
+
+type value =
+  [ `Null
+  | `Bool of bool
+  | `Float of float
+  | `String of string
+  | `A of value list
+  | `O of (string * value) list
+] [@@deriving sexp]
+
+type yaml =
+  [ `Scalar of scalar
+  | `Alias of string
+  | `A of yaml list
+  | `O of (scalar * yaml) list
+] [@@deriving sexp]
+
+and scalar = Yaml.scalar = {
+  anchor: string option;
+  tag: string option;
+  value: string;
+  plain_implicit: bool;
+  quoted_implicit: bool;
+  style: scalar_style
+} [@@deriving sexp]
+
+and scalar_style = [
+  | `Any
+  | `Plain
+  | `Single_quoted
+  | `Double_quoted
+  | `Literal
+  | `Folded ]
+[@@deriving sexp]
+
+type version = [ `V1_1 | `V1_2 ] [@@deriving sexp]
+
+type encoding = [ `Any | `Utf16be | `Utf16le | `Utf8 ] [@@deriving sexp]
+
+type layout_style = [
+  | `Any
+  | `Block
+  | `Flow
+] [@@deriving sexp]
+
+module Stream = struct
+  module Mark = struct
+    type t = Yaml.Stream.Mark.t =
+      { index: int
+      ; line: int
+      ; column: int }
+      [@@deriving sexp]
+  end
+
+  module Event = struct
+    type pos = Yaml.Stream.Event.pos = {start_mark: Mark.t; end_mark: Mark.t} [@@deriving sexp]
+
+    type t = Yaml.Stream.Event.t =
+      | Stream_start of { encoding: encoding}
+      | Document_start of { version: version option; implicit: bool}
+      | Document_end of { implicit: bool}
+      | Mapping_start of
+          { anchor: string option
+          ; tag: string option
+          ; implicit: bool
+          ; style: layout_style }
+      | Mapping_end
+      | Stream_end
+      | Scalar of scalar
+      | Sequence_start of
+          { anchor: string option
+          ; tag: string option
+          ; implicit: bool
+          ; style: layout_style }
+      | Sequence_end
+      | Alias of { anchor: string}
+      | Nothing
+      [@@deriving sexp]
+  end
+end
diff --git a/types/bindings/dune b/types/bindings/dune
index daef4ce..01b7c6a 100644
--- a/types/bindings/dune
+++ b/types/bindings/dune
@@ -1,5 +1,4 @@
 (library
  (name yaml_bindings_types)
  (public_name yaml.bindings.types)
- (libraries ctypes.stubs ctypes sexplib)
- (preprocess (pps ppx_sexp_conv)))
+ (libraries ctypes.stubs ctypes))
diff --git a/types/bindings/yaml_bindings_types.ml b/types/bindings/yaml_bindings_types.ml
index aaf6e3e..d8d0b90 100644
--- a/types/bindings/yaml_bindings_types.ml
+++ b/types/bindings/yaml_bindings_types.ml
@@ -12,28 +12,26 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *)
 
-open Sexplib.Conv
-
 module Encoding = struct
-  type t = [ `Any | `E of int64 | `Utf16be | `Utf16le | `Utf8 ] [@@deriving sexp]
+  type t = [ `Any | `E of int64 | `Utf16be | `Utf16le | `Utf8 ]
 end
 
 module Error = struct
   type t = [ `None | `Memory | `Reader | `Scanner | `Parser 
-           | `Composer | `Writer | `Emitter | `E of int64 ] [@@deriving sexp]
+           | `Composer | `Writer | `Emitter | `E of int64 ]
 end
 
 module Scalar_style = struct
   type t = [ `Any | `Plain | `Single_quoted | `Double_quoted
-           | `Literal | `Folded | `E of int64 ] [@@deriving sexp]
+           | `Literal | `Folded | `E of int64 ]
 end
 
 module Sequence_style = struct
-  type t = [ `Any | `Block | `Flow | `E of int64 ] [@@deriving sexp]
+  type t = [ `Any | `Block | `Flow | `E of int64 ]
 end
 
 module Mapping_style = struct
-  type t = [ `Any | `Block | `Flow | `E of int64 ] [@@deriving sexp]
+  type t = [ `Any | `Block | `Flow | `E of int64 ]
 end
 
 module Token_type = struct
@@ -42,13 +40,13 @@ module Token_type = struct
            | `Block_sequence_start | `Block_mapping_start | `Block_end
            | `Flow_sequence_start | `Flow_sequence_end | `Flow_mapping_start
            | `Flow_mapping_end | `Block_entry | `Flow_entry | `Key
-           | `Value | `Alias | `Anchor | `Tag | `Scalar | `E of int64 ] [@@deriving sexp]
+           | `Value | `Alias | `Anchor | `Tag | `Scalar | `E of int64 ]
 end
 
 module Event_type = struct
   type t = [ `None | `Stream_start | `Stream_end | `Document_start
            | `Document_end | `Alias | `Scalar | `Sequence_start
-           | `Sequence_end | `Mapping_start | `Mapping_end | `E of int64 ] [@@deriving sexp]
+           | `Sequence_end | `Mapping_start | `Mapping_end | `E of int64 ]
 end
  
 module M(F : Ctypes.TYPE) =
diff --git a/types/bindings/yaml_bindings_types.mli b/types/bindings/yaml_bindings_types.mli
index c63e2bf..edd535f 100644
--- a/types/bindings/yaml_bindings_types.mli
+++ b/types/bindings/yaml_bindings_types.mli
@@ -13,25 +13,25 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *)
 
 module Encoding : sig
-  type t = [ `Any | `E of int64 | `Utf16be | `Utf16le | `Utf8 ] [@@deriving sexp]
+  type t = [ `Any | `E of int64 | `Utf16be | `Utf16le | `Utf8 ]
 end
 
 module Error : sig
   type t = [ `None | `Memory | `Reader | `Scanner | `Parser
-           | `Composer | `Writer | `Emitter | `E of int64 ] [@@deriving sexp]
+           | `Composer | `Writer | `Emitter | `E of int64 ]
 end
 
 module Scalar_style : sig
   type t = [ `Any | `Plain | `Single_quoted | `Double_quoted
-           | `Literal | `Folded | `E of int64 ] [@@deriving sexp]
+           | `Literal | `Folded | `E of int64 ]
 end
 
 module Sequence_style : sig
-  type t = [ `Any | `Block | `Flow | `E of int64 ] [@@deriving sexp]
+  type t = [ `Any | `Block | `Flow | `E of int64 ]
 end
 
 module Mapping_style : sig
-  type t = [ `Any | `Block | `Flow | `E of int64 ] [@@deriving sexp]
+  type t = [ `Any | `Block | `Flow | `E of int64 ]
 end
 
 module Token_type : sig
@@ -40,13 +40,13 @@ module Token_type : sig
            | `Block_sequence_start | `Block_mapping_start | `Block_end
            | `Flow_sequence_start | `Flow_sequence_end | `Flow_mapping_start
            | `Flow_mapping_end | `Block_entry | `Flow_entry | `Key
-           | `Value | `Alias | `Anchor | `Tag | `Scalar | `E of int64 ] [@@deriving sexp]
+           | `Value | `Alias | `Anchor | `Tag | `Scalar | `E of int64 ]
 end
 
 module Event_type : sig
   type t = [ `None | `Stream_start | `Stream_end | `Document_start
            | `Document_end | `Alias | `Scalar | `Sequence_start
-           | `Sequence_end | `Mapping_start | `Mapping_end | `E of int64 ] [@@deriving sexp]
+           | `Sequence_end | `Mapping_start | `Mapping_end | `E of int64 ]
 end
  
 module M(F : Ctypes.TYPE) : sig
diff --git a/vendor/LICENSE b/vendor/License
similarity index 96%
rename from vendor/LICENSE
rename to vendor/License
index e7b6102..3d82c28 100644
--- a/vendor/LICENSE
+++ b/vendor/License
@@ -1,3 +1,4 @@
+Copyright (c) 2017-2020 Ingy döt Net
 Copyright (c) 2006-2016 Kirill Simonov
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
diff --git a/vendor/README.md b/vendor/README.md
index b41249d..13612c9 100644
--- a/vendor/README.md
+++ b/vendor/README.md
@@ -1,5 +1,5 @@
 This is a copy of https://github.com/yaml/libyaml.git
-revision 660242d6a418f0348c61057ed3052450527b3abf.
+revision 2c891fc7a770e8ba2fec34fc6b545c672beb37e6 (tag v0.2.5).
 
-yaml.h has been modified to remove anonymous structs, since ocaml-ctypes stub
+yaml.h has been modified to remove anonymous structs and unions, since ocaml-ctypes stub
 generation needs to probe their sizes at compile-time.
diff --git a/vendor/api.c b/vendor/api.c
index e793b08..16f88bd 100644
--- a/vendor/api.c
+++ b/vendor/api.c
@@ -623,10 +623,10 @@ yaml_token_delete(yaml_token_t *token)
  */
 
 static int
-yaml_check_utf8(yaml_char_t *start, size_t length)
+yaml_check_utf8(const yaml_char_t *start, size_t length)
 {
-    yaml_char_t *end = start+length;
-    yaml_char_t *pointer = start;
+    const yaml_char_t *end = start+length;
+    const yaml_char_t *pointer = start;
 
     while (pointer < end) {
         unsigned char octet;
@@ -794,7 +794,7 @@ yaml_document_end_event_initialize(yaml_event_t *event, int implicit)
  */
 
 YAML_DECLARE(int)
-yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor)
+yaml_alias_event_initialize(yaml_event_t *event, const yaml_char_t *anchor)
 {
     yaml_mark_t mark = { 0, 0, 0 };
     yaml_char_t *anchor_copy = NULL;
@@ -819,8 +819,8 @@ yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor)
 
 YAML_DECLARE(int)
 yaml_scalar_event_initialize(yaml_event_t *event,
-        yaml_char_t *anchor, yaml_char_t *tag,
-        yaml_char_t *value, int length,
+        const yaml_char_t *anchor, const yaml_char_t *tag,
+        const yaml_char_t *value, int length,
         int plain_implicit, int quoted_implicit,
         yaml_scalar_style_t style)
 {
@@ -873,7 +873,7 @@ error:
 
 YAML_DECLARE(int)
 yaml_sequence_start_event_initialize(yaml_event_t *event,
-        yaml_char_t *anchor, yaml_char_t *tag, int implicit,
+        const yaml_char_t *anchor, const yaml_char_t *tag, int implicit,
         yaml_sequence_style_t style)
 {
     yaml_mark_t mark = { 0, 0, 0 };
@@ -928,7 +928,7 @@ yaml_sequence_end_event_initialize(yaml_event_t *event)
 
 YAML_DECLARE(int)
 yaml_mapping_start_event_initialize(yaml_event_t *event,
-        yaml_char_t *anchor, yaml_char_t *tag, int implicit,
+        const yaml_char_t *anchor, const yaml_char_t *tag, int implicit,
         yaml_mapping_style_t style)
 {
     yaml_mark_t mark = { 0, 0, 0 };
@@ -1193,7 +1193,7 @@ yaml_document_get_root_node(yaml_document_t *document)
 
 YAML_DECLARE(int)
 yaml_document_add_scalar(yaml_document_t *document,
-        yaml_char_t *tag, yaml_char_t *value, int length,
+        const yaml_char_t *tag, const yaml_char_t *value, int length,
         yaml_scalar_style_t style)
 {
     struct {
@@ -1243,7 +1243,7 @@ error:
 
 YAML_DECLARE(int)
 yaml_document_add_sequence(yaml_document_t *document,
-        yaml_char_t *tag, yaml_sequence_style_t style)
+        const yaml_char_t *tag, yaml_sequence_style_t style)
 {
     struct {
         yaml_error_type_t error;
@@ -1288,7 +1288,7 @@ error:
 
 YAML_DECLARE(int)
 yaml_document_add_mapping(yaml_document_t *document,
-        yaml_char_t *tag, yaml_mapping_style_t style)
+        const yaml_char_t *tag, yaml_mapping_style_t style)
 {
     struct {
         yaml_error_type_t error;
diff --git a/vendor/emitter.c b/vendor/emitter.c
index b9392e4..609b28a 100644
--- a/vendor/emitter.c
+++ b/vendor/emitter.c
@@ -495,6 +495,7 @@ static int
 yaml_emitter_emit_stream_start(yaml_emitter_t *emitter,
         yaml_event_t *event)
 {
+    emitter->open_ended = 0;
     if (event->type == YAML_STREAM_START_EVENT)
     {
         if (!emitter->encoding) {
@@ -597,13 +598,20 @@ yaml_emitter_emit_document_start(yaml_emitter_t *emitter,
             if (!yaml_emitter_write_indent(emitter))
                 return 0;
         }
+        emitter->open_ended = 0;
 
         if (event->data.document_start.version_directive) {
             implicit = 0;
             if (!yaml_emitter_write_indicator(emitter, "%YAML", 1, 0, 0))
                 return 0;
-            if (!yaml_emitter_write_indicator(emitter, "1.1", 1, 0, 0))
-                return 0;
+            if (event->data.document_start.version_directive->minor == 1) {
+                if (!yaml_emitter_write_indicator(emitter, "1.1", 1, 0, 0))
+                    return 0;
+            }
+            else {
+                if (!yaml_emitter_write_indicator(emitter, "1.2", 1, 0, 0))
+                    return 0;
+            }
             if (!yaml_emitter_write_indent(emitter))
                 return 0;
         }
@@ -644,12 +652,25 @@ yaml_emitter_emit_document_start(yaml_emitter_t *emitter,
 
         emitter->state = YAML_EMIT_DOCUMENT_CONTENT_STATE;
 
+        emitter->open_ended = 0;
         return 1;
     }
 
     else if (event->type == YAML_STREAM_END_EVENT)
     {
 
+        /**
+         * This can happen if a block scalar with trailing empty lines
+         * is at the end of the stream
+         */
+        if (emitter->open_ended == 2)
+        {
+            if (!yaml_emitter_write_indicator(emitter, "...", 1, 0, 0))
+                return 0;
+            emitter->open_ended = 0;
+            if (!yaml_emitter_write_indent(emitter))
+                return 0;
+        }
         if (!yaml_emitter_flush(emitter))
             return 0;
 
@@ -691,9 +712,12 @@ yaml_emitter_emit_document_end(yaml_emitter_t *emitter,
         if (!event->data.document_end.implicit) {
             if (!yaml_emitter_write_indicator(emitter, "...", 1, 0, 0))
                 return 0;
+            emitter->open_ended = 0;
             if (!yaml_emitter_write_indent(emitter))
                 return 0;
         }
+        else if (!emitter->open_ended)
+            emitter->open_ended = 1;
         if (!yaml_emitter_flush(emitter))
             return 0;
 
@@ -999,6 +1023,8 @@ yaml_emitter_emit_alias(yaml_emitter_t *emitter, SHIM(yaml_event_t *event))
 {
     if (!yaml_emitter_process_anchor(emitter))
         return 0;
+    if (emitter->simple_key_context)
+        if (!PUT(emitter, ' ')) return 0;
     emitter->state = POP(emitter, emitter->states);
 
     return 1;
@@ -1227,7 +1253,7 @@ yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event)
 }
 
 /*
- * Write an achor.
+ * Write an anchor.
  */
 
 static int
@@ -1326,7 +1352,10 @@ static int
 yaml_emitter_analyze_version_directive(yaml_emitter_t *emitter,
         yaml_version_directive_t version_directive)
 {
-    if (version_directive.major != 1 || version_directive.minor != 1) {
+    if (version_directive.major != 1 || (
+        version_directive.minor != 1
+        && version_directive.minor != 2
+        )) {
         return yaml_emitter_set_emitter_error(emitter,
                 "incompatible %YAML directive");
     }
@@ -1796,7 +1825,6 @@ yaml_emitter_write_indicator(yaml_emitter_t *emitter,
 
     emitter->whitespace = is_whitespace;
     emitter->indention = (emitter->indention && is_indention);
-    emitter->open_ended = 0;
 
     return 1;
 }
@@ -1897,7 +1925,17 @@ yaml_emitter_write_plain_scalar(yaml_emitter_t *emitter,
 
     STRING_ASSIGN(string, value, length);
 
-    if (!emitter->whitespace) {
+    /**
+     * Avoid trailing spaces for empty values in block mode.
+     * In flow mode, we still want the space to prevent ambiguous things
+     * like {a:}.
+     * Currently, the emitter forbids any plain empty scalar in flow mode
+     * (e.g. it outputs {a: ''} instead), so emitter->flow_level will
+     * never be true here.
+     * But if the emitter is ever changed to allow emitting empty values,
+     * the check for flow_level is already here.
+     */
+    if (!emitter->whitespace && (length || emitter->flow_level)) {
         if (!PUT(emitter, ' ')) return 0;
     }
 
@@ -1939,10 +1977,6 @@ yaml_emitter_write_plain_scalar(yaml_emitter_t *emitter,
 
     emitter->whitespace = 0;
     emitter->indention = 0;
-    if (emitter->root_context)
-    {
-        emitter->open_ended = 1;
-    }
 
     return 1;
 }
@@ -2203,7 +2237,7 @@ yaml_emitter_write_block_scalar_hints(yaml_emitter_t *emitter,
         else if (string.start == string.pointer)
         {
             chomp_hint = "+";
-            emitter->open_ended = 1;
+            emitter->open_ended = 2;
         }
         else
         {
@@ -2213,7 +2247,7 @@ yaml_emitter_write_block_scalar_hints(yaml_emitter_t *emitter,
             if (IS_BREAK(string))
             {
                 chomp_hint = "+";
-                emitter->open_ended = 1;
+                emitter->open_ended = 2;
             }
         }
     }
diff --git a/vendor/loader.c b/vendor/loader.c
index db8501a..dea8ac4 100644
--- a/vendor/loader.c
+++ b/vendor/loader.c
@@ -37,27 +37,47 @@ yaml_parser_register_anchor(yaml_parser_t *parser,
 static void
 yaml_parser_delete_aliases(yaml_parser_t *parser);
 
+/*
+ * Document loading context.
+ */
+struct loader_ctx {
+    int *start;
+    int *end;
+    int *top;
+};
+
 /*
  * Composer functions.
  */
+static int
+yaml_parser_load_nodes(yaml_parser_t *parser, struct loader_ctx *ctx);
+
+static int
+yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *event);
 
 static int
-yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *first_event);
+yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx);
 
 static int
-yaml_parser_load_node(yaml_parser_t *parser, yaml_event_t *first_event);
+yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx);
 
 static int
-yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event);
+yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx);
 
 static int
-yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event);
+yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx);
 
 static int
-yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event);
+yaml_parser_load_sequence_end(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx);
 
 static int
-yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event);
+yaml_parser_load_mapping_end(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx);
 
 /*
  * Load the next document of the stream.
@@ -162,59 +182,78 @@ yaml_parser_delete_aliases(yaml_parser_t *parser)
  */
 
 static int
-yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *first_event)
+yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *event)
 {
-    yaml_event_t event;
+    struct loader_ctx ctx = { NULL, NULL, NULL };
 
-    assert(first_event->type == YAML_DOCUMENT_START_EVENT);
+    assert(event->type == YAML_DOCUMENT_START_EVENT);
                         /* DOCUMENT-START is expected. */
 
     parser->document->version_directive
-        = first_event->data.document_start.version_directive;
+        = event->data.document_start.version_directive;
     parser->document->tag_directives.start
-        = first_event->data.document_start.tag_directives.start;
+        = event->data.document_start.tag_directives.start;
     parser->document->tag_directives.end
-        = first_event->data.document_start.tag_directives.end;
+        = event->data.document_start.tag_directives.end;
     parser->document->start_implicit
-        = first_event->data.document_start.implicit;
-    parser->document->start_mark = first_event->start_mark;
-
-    if (!yaml_parser_parse(parser, &event)) return 0;
-
-    if (!yaml_parser_load_node(parser, &event)) return 0;
-
-    if (!yaml_parser_parse(parser, &event)) return 0;
-    assert(event.type == YAML_DOCUMENT_END_EVENT);
-                        /* DOCUMENT-END is expected. */
+        = event->data.document_start.implicit;
+    parser->document->start_mark = event->start_mark;
 
-    parser->document->end_implicit = event.data.document_end.implicit;
-    parser->document->end_mark = event.end_mark;
+    if (!STACK_INIT(parser, ctx, int*)) return 0;
+    if (!yaml_parser_load_nodes(parser, &ctx)) {
+        STACK_DEL(parser, ctx);
+        return 0;
+    }
+    STACK_DEL(parser, ctx);
 
     return 1;
 }
 
 /*
- * Compose a node.
+ * Compose a node tree.
  */
 
 static int
-yaml_parser_load_node(yaml_parser_t *parser, yaml_event_t *first_event)
+yaml_parser_load_nodes(yaml_parser_t *parser, struct loader_ctx *ctx)
 {
-    switch (first_event->type) {
-        case YAML_ALIAS_EVENT:
-            return yaml_parser_load_alias(parser, first_event);
-        case YAML_SCALAR_EVENT:
-            return yaml_parser_load_scalar(parser, first_event);
-        case YAML_SEQUENCE_START_EVENT:
-            return yaml_parser_load_sequence(parser, first_event);
-        case YAML_MAPPING_START_EVENT:
-            return yaml_parser_load_mapping(parser, first_event);
-        default:
-            assert(0);  /* Could not happen. */
-            return 0;
-    }
+    yaml_event_t event;
 
-    return 0;
+    do {
+        if (!yaml_parser_parse(parser, &event)) return 0;
+
+        switch (event.type) {
+            case YAML_ALIAS_EVENT:
+                if (!yaml_parser_load_alias(parser, &event, ctx)) return 0;
+                break;
+            case YAML_SCALAR_EVENT:
+                if (!yaml_parser_load_scalar(parser, &event, ctx)) return 0;
+                break;
+            case YAML_SEQUENCE_START_EVENT:
+                if (!yaml_parser_load_sequence(parser, &event, ctx)) return 0;
+                break;
+            case YAML_SEQUENCE_END_EVENT:
+                if (!yaml_parser_load_sequence_end(parser, &event, ctx))
+                    return 0;
+                break;
+            case YAML_MAPPING_START_EVENT:
+                if (!yaml_parser_load_mapping(parser, &event, ctx)) return 0;
+                break;
+            case YAML_MAPPING_END_EVENT:
+                if (!yaml_parser_load_mapping_end(parser, &event, ctx))
+                    return 0;
+                break;
+            default:
+                assert(0);  /* Could not happen. */
+                return 0;
+            case YAML_DOCUMENT_END_EVENT:
+                break;
+        }
+    } while (event.type != YAML_DOCUMENT_END_EVENT);
+
+    parser->document->end_implicit = event.data.document_end.implicit;
+    parser->document->end_mark = event.end_mark;
+
+    return 1;
 }
 
 /*
@@ -252,27 +291,80 @@ yaml_parser_register_anchor(yaml_parser_t *parser,
     return 1;
 }
 
+/*
+ * Compose node into its parent in the stree.
+ */
+
+static int
+yaml_parser_load_node_add(yaml_parser_t *parser, struct loader_ctx *ctx,
+        int index)
+{
+    struct yaml_node_s *parent;
+    int parent_index;
+
+    if (STACK_EMPTY(parser, *ctx)) {
+        /* This is the root node, there's no tree to add it to. */
+        return 1;
+    }
+
+    parent_index = *((*ctx).top - 1);
+    parent = &parser->document->nodes.start[parent_index-1];
+
+    switch (parent->type) {
+        case YAML_SEQUENCE_NODE:
+            if (!STACK_LIMIT(parser, parent->data.sequence.items, INT_MAX-1))
+                return 0;
+            if (!PUSH(parser, parent->data.sequence.items, index))
+                return 0;
+            break;
+        case YAML_MAPPING_NODE: {
+            yaml_node_pair_t pair;
+            if (!STACK_EMPTY(parser, parent->data.mapping.pairs)) {
+                yaml_node_pair_t *p = parent->data.mapping.pairs.top - 1;
+                if (p->key != 0 && p->value == 0) {
+                    p->value = index;
+                    break;
+                }
+            }
+
+            pair.key = index;
+            pair.value = 0;
+            if (!STACK_LIMIT(parser, parent->data.mapping.pairs, INT_MAX-1))
+                return 0;
+            if (!PUSH(parser, parent->data.mapping.pairs, pair))
+                return 0;
+
+            break;
+        }
+        default:
+            assert(0); /* Could not happen. */
+            return 0;
+    }
+    return 1;
+}
+
 /*
  * Compose a node corresponding to an alias.
  */
 
 static int
-yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event)
+yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx)
 {
-    yaml_char_t *anchor = first_event->data.alias.anchor;
+    yaml_char_t *anchor = event->data.alias.anchor;
     yaml_alias_data_t *alias_data;
 
     for (alias_data = parser->aliases.start;
             alias_data != parser->aliases.top; alias_data ++) {
         if (strcmp((char *)alias_data->anchor, (char *)anchor) == 0) {
             yaml_free(anchor);
-            return alias_data->index;
+            return yaml_parser_load_node_add(parser, ctx, alias_data->index);
         }
     }
 
     yaml_free(anchor);
     return yaml_parser_set_composer_error(parser, "found undefined alias",
-            first_event->start_mark);
+            event->start_mark);
 }
 
 /*
@@ -280,11 +372,12 @@ yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event)
  */
 
 static int
-yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event)
+yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx)
 {
     yaml_node_t node;
     int index;
-    yaml_char_t *tag = first_event->data.scalar.tag;
+    yaml_char_t *tag = event->data.scalar.tag;
 
     if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error;
 
@@ -294,23 +387,23 @@ yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event)
         if (!tag) goto error;
     }
 
-    SCALAR_NODE_INIT(node, tag, first_event->data.scalar.value,
-            first_event->data.scalar.length, first_event->data.scalar.style,
-            first_event->start_mark, first_event->end_mark);
+    SCALAR_NODE_INIT(node, tag, event->data.scalar.value,
+            event->data.scalar.length, event->data.scalar.style,
+            event->start_mark, event->end_mark);
 
     if (!PUSH(parser, parser->document->nodes, node)) goto error;
 
     index = parser->document->nodes.top - parser->document->nodes.start;
 
     if (!yaml_parser_register_anchor(parser, index,
-                first_event->data.scalar.anchor)) return 0;
+                event->data.scalar.anchor)) return 0;
 
-    return index;
+    return yaml_parser_load_node_add(parser, ctx, index);
 
 error:
     yaml_free(tag);
-    yaml_free(first_event->data.scalar.anchor);
-    yaml_free(first_event->data.scalar.value);
+    yaml_free(event->data.scalar.anchor);
+    yaml_free(event->data.scalar.value);
     return 0;
 }
 
@@ -319,17 +412,17 @@ error:
  */
 
 static int
-yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event)
+yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx)
 {
-    yaml_event_t event;
     yaml_node_t node;
     struct {
         yaml_node_item_t *start;
         yaml_node_item_t *end;
         yaml_node_item_t *top;
     } items = { NULL, NULL, NULL };
-    int index, item_index;
-    yaml_char_t *tag = first_event->data.sequence_start.tag;
+    int index;
+    yaml_char_t *tag = event->data.sequence_start.tag;
 
     if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error;
 
@@ -342,48 +435,54 @@ yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event)
     if (!STACK_INIT(parser, items, yaml_node_item_t*)) goto error;
 
     SEQUENCE_NODE_INIT(node, tag, items.start, items.end,
-            first_event->data.sequence_start.style,
-            first_event->start_mark, first_event->end_mark);
+            event->data.sequence_start.style,
+            event->start_mark, event->end_mark);
 
     if (!PUSH(parser, parser->document->nodes, node)) goto error;
 
     index = parser->document->nodes.top - parser->document->nodes.start;
 
     if (!yaml_parser_register_anchor(parser, index,
-                first_event->data.sequence_start.anchor)) return 0;
-
-    if (!yaml_parser_parse(parser, &event)) return 0;
-
-    while (event.type != YAML_SEQUENCE_END_EVENT) {
-        if (!STACK_LIMIT(parser,
-                    parser->document->nodes.start[index-1].data.sequence.items,
-                    INT_MAX-1)) return 0;
-        item_index = yaml_parser_load_node(parser, &event);
-        if (!item_index) return 0;
-        if (!PUSH(parser,
-                    parser->document->nodes.start[index-1].data.sequence.items,
-                    item_index)) return 0;
-        if (!yaml_parser_parse(parser, &event)) return 0;
-    }
+                event->data.sequence_start.anchor)) return 0;
+
+    if (!yaml_parser_load_node_add(parser, ctx, index)) return 0;
 
-    parser->document->nodes.start[index-1].end_mark = event.end_mark;
+    if (!STACK_LIMIT(parser, *ctx, INT_MAX-1)) return 0;
+    if (!PUSH(parser, *ctx, index)) return 0;
 
-    return index;
+    return 1;
 
 error:
     yaml_free(tag);
-    yaml_free(first_event->data.sequence_start.anchor);
+    yaml_free(event->data.sequence_start.anchor);
     return 0;
 }
 
+static int
+yaml_parser_load_sequence_end(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx)
+{
+    int index;
+
+    assert(((*ctx).top - (*ctx).start) > 0);
+
+    index = *((*ctx).top - 1);
+    assert(parser->document->nodes.start[index-1].type == YAML_SEQUENCE_NODE);
+    parser->document->nodes.start[index-1].end_mark = event->end_mark;
+
+    (void)POP(parser, *ctx);
+
+    return 1;
+}
+
 /*
  * Compose a mapping node.
  */
 
 static int
-yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event)
+yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx)
 {
-    yaml_event_t event;
     yaml_node_t node;
     struct {
         yaml_node_pair_t *start;
@@ -391,8 +490,7 @@ yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event)
         yaml_node_pair_t *top;
     } pairs = { NULL, NULL, NULL };
     int index;
-    yaml_node_pair_t pair;
-    yaml_char_t *tag = first_event->data.mapping_start.tag;
+    yaml_char_t *tag = event->data.mapping_start.tag;
 
     if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error;
 
@@ -405,40 +503,42 @@ yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event)
     if (!STACK_INIT(parser, pairs, yaml_node_pair_t*)) goto error;
 
     MAPPING_NODE_INIT(node, tag, pairs.start, pairs.end,
-            first_event->data.mapping_start.style,
-            first_event->start_mark, first_event->end_mark);
+            event->data.mapping_start.style,
+            event->start_mark, event->end_mark);
 
     if (!PUSH(parser, parser->document->nodes, node)) goto error;
 
     index = parser->document->nodes.top - parser->document->nodes.start;
 
     if (!yaml_parser_register_anchor(parser, index,
-                first_event->data.mapping_start.anchor)) return 0;
+                event->data.mapping_start.anchor)) return 0;
 
-    if (!yaml_parser_parse(parser, &event)) return 0;
-
-    while (event.type != YAML_MAPPING_END_EVENT) {
-        if (!STACK_LIMIT(parser,
-                    parser->document->nodes.start[index-1].data.mapping.pairs,
-                    INT_MAX-1)) return 0;
-        pair.key = yaml_parser_load_node(parser, &event);
-        if (!pair.key) return 0;
-        if (!yaml_parser_parse(parser, &event)) return 0;
-        pair.value = yaml_parser_load_node(parser, &event);
-        if (!pair.value) return 0;
-        if (!PUSH(parser,
-                    parser->document->nodes.start[index-1].data.mapping.pairs,
-                    pair)) return 0;
-        if (!yaml_parser_parse(parser, &event)) return 0;
-    }
+    if (!yaml_parser_load_node_add(parser, ctx, index)) return 0;
 
-    parser->document->nodes.start[index-1].end_mark = event.end_mark;
+    if (!STACK_LIMIT(parser, *ctx, INT_MAX-1)) return 0;
+    if (!PUSH(parser, *ctx, index)) return 0;
 
-    return index;
+    return 1;
 
 error:
     yaml_free(tag);
-    yaml_free(first_event->data.mapping_start.anchor);
+    yaml_free(event->data.mapping_start.anchor);
     return 0;
 }
 
+static int
+yaml_parser_load_mapping_end(yaml_parser_t *parser, yaml_event_t *event,
+        struct loader_ctx *ctx)
+{
+    int index;
+
+    assert(((*ctx).top - (*ctx).start) > 0);
+
+    index = *((*ctx).top - 1);
+    assert(parser->document->nodes.start[index-1].type == YAML_MAPPING_NODE);
+    parser->document->nodes.start[index-1].end_mark = event->end_mark;
+
+    (void)POP(parser, *ctx);
+
+    return 1;
+}
\ No newline at end of file
diff --git a/vendor/parser.c b/vendor/parser.c
index 1198c73..ec2f8d3 100644
--- a/vendor/parser.c
+++ b/vendor/parser.c
@@ -1261,7 +1261,10 @@ yaml_parser_process_directives(yaml_parser_t *parser,
                 goto error;
             }
             if (token->data.version_directive.major != 1
-                    || token->data.version_directive.minor != 1) {
+                    || (
+                        token->data.version_directive.minor != 1
+                        && token->data.version_directive.minor != 2
+                    )) {
                 yaml_parser_set_parser_error(parser,
                         "found incompatible YAML document", token->start_mark);
                 goto error;
diff --git a/vendor/scanner.c b/vendor/scanner.c
index ceee749..c6b4987 100644
--- a/vendor/scanner.c
+++ b/vendor/scanner.c
@@ -348,6 +348,7 @@
  *          SCALAR("another value",plain)
  *          KEY
  *          SCALAR("a mapping",plain)
+ *          VALUE
  *          BLOCK-MAPPING-START
  *          KEY
  *          SCALAR("key 1",plain)
@@ -711,7 +712,7 @@ yaml_parser_scan_tag_handle(yaml_parser_t *parser, int directive,
         yaml_mark_t start_mark, yaml_char_t **handle);
 
 static int
-yaml_parser_scan_tag_uri(yaml_parser_t *parser, int directive,
+yaml_parser_scan_tag_uri(yaml_parser_t *parser, int uri_char, int directive,
         yaml_char_t *head, yaml_mark_t start_mark, yaml_char_t **uri);
 
 static int
@@ -2292,7 +2293,7 @@ yaml_parser_scan_tag_directive_value(yaml_parser_t *parser,
 
     /* Scan a prefix. */
 
-    if (!yaml_parser_scan_tag_uri(parser, 1, NULL, start_mark, &prefix_value))
+    if (!yaml_parser_scan_tag_uri(parser, 1, 1, NULL, start_mark, &prefix_value))
         goto error;
 
     /* Expect a whitespace or line break. */
@@ -2410,7 +2411,7 @@ yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token)
 
         /* Consume the tag value. */
 
-        if (!yaml_parser_scan_tag_uri(parser, 0, NULL, start_mark, &suffix))
+        if (!yaml_parser_scan_tag_uri(parser, 1, 0, NULL, start_mark, &suffix))
             goto error;
 
         /* Check for '>' and eat it. */
@@ -2438,14 +2439,14 @@ yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token)
         {
             /* Scan the suffix now. */
 
-            if (!yaml_parser_scan_tag_uri(parser, 0, NULL, start_mark, &suffix))
+            if (!yaml_parser_scan_tag_uri(parser, 0, 0, NULL, start_mark, &suffix))
                 goto error;
         }
         else
         {
             /* It wasn't a handle after all.  Scan the rest of the tag. */
 
-            if (!yaml_parser_scan_tag_uri(parser, 0, handle, start_mark, &suffix))
+            if (!yaml_parser_scan_tag_uri(parser, 0, 0, handle, start_mark, &suffix))
                 goto error;
 
             /* Set the handle to '!'. */
@@ -2474,9 +2475,11 @@ yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token)
     if (!CACHE(parser, 1)) goto error;
 
     if (!IS_BLANKZ(parser->buffer)) {
-        yaml_parser_set_scanner_error(parser, "while scanning a tag",
-                start_mark, "did not find expected whitespace or line break");
-        goto error;
+        if (!parser->flow_level || !CHECK(parser->buffer, ',') ) {
+            yaml_parser_set_scanner_error(parser, "while scanning a tag",
+                    start_mark, "did not find expected whitespace or line break");
+            goto error;
+        }
     }
 
     end_mark = parser->mark;
@@ -2565,7 +2568,7 @@ error:
  */
 
 static int
-yaml_parser_scan_tag_uri(yaml_parser_t *parser, int directive,
+yaml_parser_scan_tag_uri(yaml_parser_t *parser, int uri_char, int directive,
         yaml_char_t *head, yaml_mark_t start_mark, yaml_char_t **uri)
 {
     size_t length = head ? strlen((char *)head) : 0;
@@ -2601,8 +2604,11 @@ yaml_parser_scan_tag_uri(yaml_parser_t *parser, int directive,
      * The set of characters that may appear in URI is as follows:
      *
      *      '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',
-     *      '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',
-     *      '%'.
+     *      '=', '+', '$', '.', '!', '~', '*', '\'', '(', ')', '%'.
+     *
+     * If we are inside a verbatim tag <...> (parameter uri_char is true)
+     * then also the following flow indicators are allowed:
+     *      ',', '[', ']'
      */
 
     while (IS_ALPHA(parser->buffer) || CHECK(parser->buffer, ';')
@@ -2610,12 +2616,15 @@ yaml_parser_scan_tag_uri(yaml_parser_t *parser, int directive,
             || CHECK(parser->buffer, ':') || CHECK(parser->buffer, '@')
             || CHECK(parser->buffer, '&') || CHECK(parser->buffer, '=')
             || CHECK(parser->buffer, '+') || CHECK(parser->buffer, '$')
-            || CHECK(parser->buffer, ',') || CHECK(parser->buffer, '.')
+            || CHECK(parser->buffer, '.') || CHECK(parser->buffer, '%')
             || CHECK(parser->buffer, '!') || CHECK(parser->buffer, '~')
             || CHECK(parser->buffer, '*') || CHECK(parser->buffer, '\'')
             || CHECK(parser->buffer, '(') || CHECK(parser->buffer, ')')
-            || CHECK(parser->buffer, '[') || CHECK(parser->buffer, ']')
-            || CHECK(parser->buffer, '%'))
+            || (uri_char && (
+                CHECK(parser->buffer, ',')
+                || CHECK(parser->buffer, '[') || CHECK(parser->buffer, ']')
+                )
+            ))
     {
         /* Check if it is a URI-escape sequence. */
 
@@ -3456,7 +3465,7 @@ yaml_parser_scan_plain_scalar(yaml_parser_t *parser, yaml_token_t *token)
             if ((CHECK(parser->buffer, ':') && IS_BLANKZ_AT(parser->buffer, 1))
                     || (parser->flow_level &&
                         (CHECK(parser->buffer, ',')
-                         || CHECK(parser->buffer, '?') || CHECK(parser->buffer, '[')
+                         || CHECK(parser->buffer, '[')
                          || CHECK(parser->buffer, ']') || CHECK(parser->buffer, '{')
                          || CHECK(parser->buffer, '}'))))
                 break;
@@ -3518,12 +3527,12 @@ yaml_parser_scan_plain_scalar(yaml_parser_t *parser, yaml_token_t *token)
         {
             if (IS_BLANK(parser->buffer))
             {
-                /* Check for tab character that abuse indentation. */
+                /* Check for tab characters that abuse indentation. */
 
                 if (leading_blanks && (int)parser->mark.column < indent
                         && IS_TAB(parser->buffer)) {
                     yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",
-                            start_mark, "found a tab character that violate indentation");
+                            start_mark, "found a tab character that violates indentation");
                     goto error;
                 }
 
diff --git a/vendor/yaml.h b/vendor/yaml.h
index df8e027..12b33a3 100644
--- a/vendor/yaml.h
+++ b/vendor/yaml.h
@@ -26,7 +26,9 @@ extern "C" {
 
 /** The public API declaration. */
 
-#ifdef _WIN32
+#if defined(__MINGW32__)
+#   define  YAML_DECLARE(type)  type
+#elif defined(_WIN32)
 #   if defined(YAML_DECLARE_STATIC)
 #       define  YAML_DECLARE(type)  type
 #   elif defined(YAML_DECLARE_EXPORT)
@@ -230,7 +232,7 @@ typedef enum yaml_token_type_e {
 
     /** A BLOCK-SEQUENCE-START token. */
     YAML_BLOCK_SEQUENCE_START_TOKEN,
-    /** A BLOCK-SEQUENCE-END token. */
+    /** A BLOCK-MAPPING-START token. */
     YAML_BLOCK_MAPPING_START_TOKEN,
     /** A BLOCK-END token. */
     YAML_BLOCK_END_TOKEN,
@@ -550,7 +552,7 @@ yaml_document_end_event_initialize(yaml_event_t *event, int implicit);
  */
 
 YAML_DECLARE(int)
-yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor);
+yaml_alias_event_initialize(yaml_event_t *event, const yaml_char_t *anchor);
 
 /**
  * Create a SCALAR event.
@@ -576,8 +578,8 @@ yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor);
 
 YAML_DECLARE(int)
 yaml_scalar_event_initialize(yaml_event_t *event,
-        yaml_char_t *anchor, yaml_char_t *tag,
-        yaml_char_t *value, int length,
+        const yaml_char_t *anchor, const yaml_char_t *tag,
+        const yaml_char_t *value, int length,
         int plain_implicit, int quoted_implicit,
         yaml_scalar_style_t style);
 
@@ -599,7 +601,7 @@ yaml_scalar_event_initialize(yaml_event_t *event,
 
 YAML_DECLARE(int)
 yaml_sequence_start_event_initialize(yaml_event_t *event,
-        yaml_char_t *anchor, yaml_char_t *tag, int implicit,
+        const yaml_char_t *anchor, const yaml_char_t *tag, int implicit,
         yaml_sequence_style_t style);
 
 /**
@@ -631,7 +633,7 @@ yaml_sequence_end_event_initialize(yaml_event_t *event);
 
 YAML_DECLARE(int)
 yaml_mapping_start_event_initialize(yaml_event_t *event,
-        yaml_char_t *anchor, yaml_char_t *tag, int implicit,
+        const yaml_char_t *anchor, const yaml_char_t *tag, int implicit,
         yaml_mapping_style_t style);
 
 /**
@@ -663,7 +665,7 @@ yaml_event_delete(yaml_event_t *event);
 
 /** The tag @c !!null with the only possible value: @c null. */
 #define YAML_NULL_TAG       "tag:yaml.org,2002:null"
-/** The tag @c !!bool with the values: @c true and @c falce. */
+/** The tag @c !!bool with the values: @c true and @c false. */
 #define YAML_BOOL_TAG       "tag:yaml.org,2002:bool"
 /** The tag @c !!str for string values. */
 #define YAML_STR_TAG        "tag:yaml.org,2002:str"
@@ -894,7 +896,7 @@ yaml_document_get_root_node(yaml_document_t *document);
 
 YAML_DECLARE(int)
 yaml_document_add_scalar(yaml_document_t *document,
-        yaml_char_t *tag, yaml_char_t *value, int length,
+        const yaml_char_t *tag, const yaml_char_t *value, int length,
         yaml_scalar_style_t style);
 
 /**
@@ -911,7 +913,7 @@ yaml_document_add_scalar(yaml_document_t *document,
 
 YAML_DECLARE(int)
 yaml_document_add_sequence(yaml_document_t *document,
-        yaml_char_t *tag, yaml_sequence_style_t style);
+        const yaml_char_t *tag, yaml_sequence_style_t style);
 
 /**
  * Create a MAPPING node and attach it to the document.
@@ -927,7 +929,7 @@ yaml_document_add_sequence(yaml_document_t *document,
 
 YAML_DECLARE(int)
 yaml_document_add_mapping(yaml_document_t *document,
-        yaml_char_t *tag, yaml_mapping_style_t style);
+        const yaml_char_t *tag, yaml_mapping_style_t style);
 
 /**
  * Add an item to a SEQUENCE node.
@@ -935,7 +937,7 @@ yaml_document_add_mapping(yaml_document_t *document,
  * @param[in,out]   document    A document object.
  * @param[in]       sequence    The sequence node id.
  * @param[in]       item        The item node id.
-*
+ *
  * @returns @c 1 if the function succeeded, @c 0 on error.
  */
 
@@ -950,7 +952,7 @@ yaml_document_append_sequence_item(yaml_document_t *document,
  * @param[in]       mapping     The mapping node id.
  * @param[in]       key         The key node id.
  * @param[in]       value       The value node id.
-*
+ *
  * @returns @c 1 if the function succeeded, @c 0 on error.
  */
 
@@ -1018,6 +1020,7 @@ typedef enum yaml_parser_state_e {
     YAML_PARSE_DOCUMENT_CONTENT_STATE,
     /** Expect DOCUMENT-END. */
     YAML_PARSE_DOCUMENT_END_STATE,
+
     /** Expect a block node. */
     YAML_PARSE_BLOCK_NODE_STATE,
     /** Expect a block node or indentless sequence. */
@@ -1028,6 +1031,7 @@ typedef enum yaml_parser_state_e {
     YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE,
     /** Expect an entry of a block sequence. */
     YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE,
+
     /** Expect an entry of an indentless sequence. */
     YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE,
     /** Expect the first key of a block mapping. */
@@ -1038,6 +1042,7 @@ typedef enum yaml_parser_state_e {
     YAML_PARSE_BLOCK_MAPPING_VALUE_STATE,
     /** Expect the first entry of a flow sequence. */
     YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE,
+
     /** Expect an entry of a flow sequence. */
     YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE,
     /** Expect a key of an ordered mapping. */
@@ -1049,6 +1054,7 @@ typedef enum yaml_parser_state_e {
     /** Expect the first key of a flow mapping. */
     YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE,
     /** Expect a key of a flow mapping. */
+
     YAML_PARSE_FLOW_MAPPING_KEY_STATE,
     /** Expect a value of a flow mapping. */
     YAML_PARSE_FLOW_MAPPING_VALUE_STATE,
@@ -1203,7 +1209,7 @@ typedef struct yaml_parser_s {
     /** The number of tokens fetched from the queue. */
     size_t tokens_parsed;
 
-    /* Does the tokens queue contain a token ready for dequeueing. */
+    /** Does the tokens queue contain a token ready for dequeueing. */
     int token_available;
 
     /** The indentation levels stack. */
@@ -1444,7 +1450,7 @@ yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event);
  * @param[in,out]   parser      A parser object.
  * @param[out]      document    An empty document object.
  *
- * @return @c 1 if the function succeeded, @c 0 on error.
+ * @returns @c 1 if the function succeeded, @c 0 on error.
  */
 
 YAML_DECLARE(int)
@@ -1487,6 +1493,7 @@ typedef enum yaml_emitter_state_e {
     YAML_EMIT_DOCUMENT_CONTENT_STATE,
     /** Expect DOCUMENT-END. */
     YAML_EMIT_DOCUMENT_END_STATE,
+
     /** Expect the first item of a flow sequence. */
     YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE,
     /** Expect an item of a flow sequence. */
@@ -1497,6 +1504,7 @@ typedef enum yaml_emitter_state_e {
     YAML_EMIT_FLOW_MAPPING_KEY_STATE,
     /** Expect a value for a simple key of a flow mapping. */
     YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE,
+
     /** Expect a value of a flow mapping. */
     YAML_EMIT_FLOW_MAPPING_VALUE_STATE,
     /** Expect the first item of a block sequence. */
@@ -1507,6 +1515,7 @@ typedef enum yaml_emitter_state_e {
     YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE,
     /** Expect the key of a block mapping. */
     YAML_EMIT_BLOCK_MAPPING_KEY_STATE,
+
     /** Expect a value for a simple key of a block mapping. */
     YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE,
     /** Expect a value of a block mapping. */
@@ -1515,6 +1524,18 @@ typedef enum yaml_emitter_state_e {
     YAML_EMIT_END_STATE
 } yaml_emitter_state_t;
 
+
+/* This is needed for C++ */
+
+typedef struct yaml_anchors_s {
+    /** The number of references. */
+    int references;
+    /** The anchor id. */
+    int anchor;
+    /** If the node has been emitted? */
+    int serialized;
+} yaml_anchors_t;
+
 /**
  * The emitter structure.
  *
@@ -1546,7 +1567,7 @@ typedef struct yaml_emitter_s {
     /** Write handler. */
     yaml_write_handler_t *write_handler;
 
-    /** A pointer for passing to the white handler. */
+    /** A pointer for passing to the write handler. */
     void *write_handler_data;
 
     /** Standard (string or file) output data. */
@@ -1740,14 +1761,7 @@ typedef struct yaml_emitter_s {
     int closed;
 
     /** The information associated with the document nodes. */
-    struct {
-        /** The number of references. */
-        int references;
-        /** The anchor id. */
-        int anchor;
-        /** If the node has been emitted? */
-        int serialized;
-    } *anchors;
+    yaml_anchors_t *anchors;
 
     /** The last assigned anchor id. */
     int last_anchor_id;
@@ -1851,7 +1865,7 @@ YAML_DECLARE(void)
 yaml_emitter_set_canonical(yaml_emitter_t *emitter, int canonical);
 
 /**
- * Set the intendation increment.
+ * Set the indentation increment.
  *
  * @param[in,out]   emitter     An emitter object.
  * @param[in]       indent      The indentation increment (1 < . < 10).
@@ -1938,8 +1952,8 @@ yaml_emitter_close(yaml_emitter_t *emitter);
  *
  * The documen object may be generated using the yaml_parser_load() function
  * or the yaml_document_initialize() function.  The emitter takes the
- * responsibility for the document object and destoys its content after
- * it is emitted. The document object is destroyedeven if the function fails.
+ * responsibility for the document object and destroys its content after
+ * it is emitted. The document object is destroyed even if the function fails.
  *
  * @param[in,out]   emitter     An emitter object.
  * @param[in,out]   document    A document object.
diff --git a/yaml-sexp.opam b/yaml-sexp.opam
new file mode 100644
index 0000000..69c9aad
--- /dev/null
+++ b/yaml-sexp.opam
@@ -0,0 +1,34 @@
+opam-version: "2.0"
+synopsis: "Parse and generate YAML 1.1 files"
+description: "ocaml-yaml with sexp support"
+maintainer: "Anil Madhavapeddy <anil@recoil.org>"
+authors: [
+  "Anil Madhavapeddy <anil@recoil.org>"
+  "Rizo Isrof <rizo@odis.io>"
+  "Patrick Ferris"
+  "favonia@gmail.com"
+  "Alan J Hu <alanh@ccs.neu.edu>"
+]
+license: "ISC"
+tags: ["org:mirage" "org:ocamllabs"]
+homepage: "https://github.com/avsm/ocaml-yaml"
+doc: "https://avsm.github.io/ocaml-yaml/"
+bug-reports: "https://github.com/avsm/ocaml-yaml/issues"
+depends: [
+  "dune" {>= "1.3"}
+  "ppx_sexp_conv" {>= "v0.9.0"}
+  "sexplib"
+  "yaml" {= version}
+  "mdx" {with-test}
+  "alcotest" {with-test}
+  "crowbar" {with-test}
+  "junit_alcotest" {with-test}
+  "ezjsonm" {with-test}
+  "bos" {with-test}
+]
+build: [
+  ["dune" "subst"] {dev}
+  ["dune" "build" "-p" name "-j" jobs]
+  ["dune" "runtest" "-p" name "-j" jobs] {with-test}
+]
+dev-repo: "git+https://github.com/avsm/ocaml-yaml.git"
diff --git a/yaml.opam b/yaml.opam
index c089ec1..14a20c4 100644
--- a/yaml.opam
+++ b/yaml.opam
@@ -1,22 +1,35 @@
 opam-version: "2.0"
+synopsis: "Parse and generate YAML 1.1/1.2 files"
+description: """\
+This is an OCaml library to parse and generate the YAML file
+format.  It is intended to interoperable with the [Ezjsonm](https://github.com/mirage/ezjsonm)
+JSON handling library, if the simple common subset of Yaml
+is used.  Anchors and other advanced Yaml features are not
+implemented in the JSON compatibility layer.
+
+The [Yaml module docs](http://anil-code.recoil.org/ocaml-yaml/yaml/Yaml/index.html) are browseable online."""
 maintainer: "Anil Madhavapeddy <anil@recoil.org>"
-authors: ["Anil Madhavapeddy <anil@recoil.org>" "Rizo Isrof <rizo@odis.io>"]
+authors: [
+  "Anil Madhavapeddy <anil@recoil.org>"
+  "Rizo Isrof <rizo@odis.io>"
+  "Patrick Ferris"
+  "favonia@gmail.com"
+  "Alan J Hu <alanh@ccs.neu.edu>"
+]
 license: "ISC"
 tags: ["org:mirage" "org:ocamllabs"]
 homepage: "https://github.com/avsm/ocaml-yaml"
 doc: "https://avsm.github.io/ocaml-yaml/"
 bug-reports: "https://github.com/avsm/ocaml-yaml/issues"
 depends: [
-  "ocaml" {>= "4.03.0"}
-  "dune" {build & >="1.3"}
+  "ocaml" {>= "4.05.0"}
+  "dune" {>= "1.3"}
   "dune-configurator"
   "ctypes" {>= "0.12.0"}
-  "ppx_sexp_conv" {>= "v0.9.0"}
-  "sexplib"
   "rresult"
-  "fmt"
-  "logs"
   "bos"
+  "fmt" {with-test}
+  "logs" {with-test}
   "mdx" {with-test}
   "alcotest" {with-test}
   "crowbar" {with-test}
@@ -24,18 +37,8 @@ depends: [
   "ezjsonm" {with-test}
 ]
 build: [
-  ["dune" "subst"] {pinned}
+  ["dune" "subst"] {dev}
   ["dune" "build" "-p" name "-j" jobs]
   ["dune" "runtest" "-p" name "-j" jobs] {with-test}
 ]
 dev-repo: "git+https://github.com/avsm/ocaml-yaml.git"
-synopsis: "Parse and generate YAML 1.1 files"
-description: """
-This is an OCaml library to parse and generate the YAML file
-format.  It is intended to interoperable with the [Ezjsonm](https://github.com/mirage/ezjsonm)
-JSON handling library, if the simple common subset of Yaml
-is used.  Anchors and other advanced Yaml features are not
-implemented in the JSON compatibility layer.
-
-The [Yaml module docs](http://anil-code.recoil.org/ocaml-yaml/yaml/Yaml/index.html) are browseable online.
-"""


Test patch:
diff --git a/tests/dune b/tests/dune
index 002e050..5539c6d 100644
--- a/tests/dune
+++ b/tests/dune
@@ -1,4 +1,13 @@
 (test
  (name test)
+ (package yaml)
  (libraries yaml bos ezjsonm alcotest junit_alcotest)
+ (modules test test_parse test_reflect test_event_parse test_emit test_version test_util)
+ (deps (source_tree yaml)))
+
+(test
+ (name test_sexp)
+ (package yaml-sexp)
+ (libraries yaml-sexp bos alcotest junit_alcotest)
+ (modules test_sexp test_parse_sexp)
  (deps (source_tree yaml)))
diff --git a/tests/test.ml b/tests/test.ml
index 4e9eb7d..e23ddf2 100644
--- a/tests/test.ml
+++ b/tests/test.ml
@@ -12,7 +12,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *)
 
-let files = [ "anchor.yml"; "cohttp.yml"; "linuxkit.yml"; "seq.yml"; "too_large.yml" ]
+let files = [ "anchor.yml"; "cohttp.yml"; "linuxkit.yml"; "seq.yml"; "too_large.yml"; "yaml-1.2.yml" ]
 let dir f = Fpath.(v "yaml" / f)
 
 let all_files = List.map dir ("bomb.yml"::files)
@@ -95,6 +95,7 @@ let tests = [
     "version", version;
     "quoted_scalars", quoted_scalars;
     "yaml_equal", yaml_equal;
+    "yaml_util", Test_util.tests;
   ]
 
 (* Run it *)
diff --git a/tests/test_event_parse.ml b/tests/test_event_parse.ml
index afdbcb2..8f50a2e 100644
--- a/tests/test_event_parse.ml
+++ b/tests/test_event_parse.ml
@@ -15,16 +15,13 @@
 module T = Yaml_types.M
 open Rresult
 
-let pp_event e pos =
-  print_endline (Sexplib.Sexp.to_string_hum (Yaml.Stream.Event.sexp_of_t e)) 
-
 let v file =
   let open R.Infix in
   Bos.OS.File.read file >>= fun buf ->
   Yaml.Stream.parser buf >>= fun t ->
   let rec iter_until_done fn =
-    Yaml.Stream.do_parse t >>= fun (e, pos) ->
-    match e with 
+    Yaml.Stream.do_parse t >>= fun (e, _pos) ->
+    match e with
     | Yaml.Stream.Event.Nothing -> R.ok ()
-    | event -> fn event pos; iter_until_done fn in
-  iter_until_done pp_event
+    | _ -> iter_until_done fn in
+  iter_until_done ()
diff --git a/tests/test_parse.ml b/tests/test_parse.ml
index 7babbb5..35f1a56 100644
--- a/tests/test_parse.ml
+++ b/tests/test_parse.ml
@@ -4,7 +4,6 @@ let v file =
   let open R.Infix in
   Bos.OS.File.read file >>= fun buf ->
   Yaml.yaml_of_string buf >>= fun v ->
-  Printf.printf "%s\n%!" (Yaml.sexp_of_yaml v |> Sexplib.Sexp.to_string_hum);
   Yaml.to_json v >>= fun json ->
   Ezjsonm.to_string (Ezjsonm.wrap json) |> fun b ->
   Printf.printf "%s\n%!" b;
diff --git a/tests/test_parse_sexp.ml b/tests/test_parse_sexp.ml
new file mode 100644
index 0000000..ff26400
--- /dev/null
+++ b/tests/test_parse_sexp.ml
@@ -0,0 +1,12 @@
+open Rresult
+
+let v file =
+  let open R.Infix in
+  Bos.OS.File.read file >>= fun buf ->
+  Yaml.yaml_of_string buf >>= fun v ->
+  Yaml_sexp.sexp_of_yaml v |> fun s ->
+  Sexplib.Sexp.to_string_hum s |> fun b ->
+  Printf.printf "%s\n%!" b;
+  Sexplib.Sexp.of_string b |> Yaml_sexp.yaml_of_sexp |> fun v' ->
+  assert(v = v');
+  Ok ()
diff --git a/tests/test_sexp.ml b/tests/test_sexp.ml
new file mode 100644
index 0000000..26b953a
--- /dev/null
+++ b/tests/test_sexp.ml
@@ -0,0 +1,43 @@
+(* Copyright (c) 2017-2021 Anil Madhavapeddy <anil@recoil.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *)
+
+let files = [ "anchor.yml"; "cohttp.yml"; "linuxkit.yml"; "seq.yml"; "too_large.yml"; "yaml-1.2.yml" ]
+let dir f = Fpath.(v "yaml" / f)
+
+let all_files = List.map dir ("bomb.yml"::files)
+let all_simple_files = List.map dir files
+
+type error = [`Msg of string]
+let pp_error ppf (`Msg x) = Fmt.string ppf x
+let error = Alcotest.testable pp_error (=)
+let t = Alcotest.(result unit error)
+let value = Alcotest.testable Yaml.pp Yaml.equal
+let check_file f fn =
+  let name = Fpath.to_string f in
+  let test () = Alcotest.check t name (Ok ()) (fn f) in
+  name, `Quick, test
+
+let parse_of_string =
+  List.map (fun f -> check_file f Test_parse_sexp.v)
+
+let tests = [
+    "parse_of_string", parse_of_string all_simple_files;
+  ]
+
+(* Run it *)
+let () =
+  Junit_alcotest.run_and_report "Yaml" tests |>
+  fun (r,e) ->
+  Junit.(to_file (make [r]) "alcotest2-junit.xml");
+  e ()
diff --git a/tests/test_util.ml b/tests/test_util.ml
new file mode 100644
index 0000000..28475a6
--- /dev/null
+++ b/tests/test_util.ml
@@ -0,0 +1,49 @@
+open Yaml.Util
+
+let value = Alcotest.of_pp Yaml.pp
+
+let err : [ `Msg of string ] Alcotest.testable =
+  Alcotest.of_pp (fun ppf (`Msg m) -> Fmt.pf ppf "Error %s" m)
+
+let value_res = Alcotest.result value err
+
+let test_combinators () =
+  let strings = [ "yaml"; "ocaml"; "yaml" ] in
+  let expect = `A [ `String "yaml"; `String "ocaml"; `String "yaml" ] in
+  let actual = list string strings in
+  Alcotest.check value "same value" expect actual
+
+let test_member () =
+  let obj = `O [ ("alice", `String "ocaml"); ("bob", `String "yaml") ] in
+  let expect = Ok (Some (`String "ocaml")) in
+  let actual = find "alice" obj in
+  Alcotest.check Alcotest.(result (option value) err) "same value" expect actual
+
+let test_missing_member () =
+  let obj = `O [ ("alice", `String "ocaml"); ("bob", `String "yaml") ] in
+  let expect = Ok None in
+  let actual = find "charlie" obj in
+  Alcotest.check Alcotest.(result (option value) err) "same none" expect actual
+
+let test_map () =
+  let arr = `A [ `Float 1.; `Float 2.; `Float 3. ] in
+  let expect = `A [ `String "1."; `String "2."; `String "3." ] in
+  let actual =
+    map_exn (fun f -> to_float_exn f |> string_of_float |> string) arr
+  in
+  Alcotest.check value "same array" expect actual
+
+let test_filter () =
+  let arr = `A [ `Float 1.; `Float 2.; `Float 3. ] in
+  let expect = `A [ `Float 1.; `Float 2. ] in
+  let actual = filter_exn (fun f -> to_float_exn f < 3.) arr in
+  Alcotest.check value "same array" expect actual
+
+let tests =
+  [
+    ("same yaml values", `Quick, test_combinators);
+    ("find values", `Quick, test_member);
+    ("missing value", `Quick, test_missing_member);
+    ("same array with map", `Quick, test_map);
+    ("same filtered array", `Quick, test_filter);
+  ]
diff --git a/tests/yaml/yaml-1.2.yml b/tests/yaml/yaml-1.2.yml
new file mode 100644
index 0000000..d590de3
--- /dev/null
+++ b/tests/yaml/yaml-1.2.yml
@@ -0,0 +1,3 @@
+- {"when the key is quoted":"space after colon can be omitted."}
+- "quoted slashes \/ are allowed."
+- {?"a key can be looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooger": "than 1024 when parsing is unambiguous before seeing the colon."}