Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Types.ml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310(** Core types shared by all YAMLx modules. This module defines the data structures that flow through the parsing pipeline: positions and errors, then tokens (Scanner → Parser), events (Parser → Composer), and AST nodes (Composer → user). Pipeline overview: string/channel └─▶ Reader (UTF-8 decoding, line tracking) └─▶ Scanner (tokenization) → token list └─▶ Parser (grammar) → event list └─▶ Composer (node building) → node list └─▶ Resolver (type resolution) → value list *) (** {1 Source positions} *) type pos = { line : int; column : int; (** 0-based Unicode codepoint column *) column_bytes : int; (** 0-based UTF-8 byte column *) offset : int; (** codepoint index from the start of the input *) offset_bytes : int; (** UTF-8 byte offset from the start of the input *) } (** A location in the source input. [line] is 1-based; [column] and [column_bytes] are 0-based distances from the start of the current line, measured in Unicode codepoints and UTF-8 bytes respectively. [offset] and [offset_bytes] are absolute distances from the start of the input, measured the same way. *) let zero_pos = { line = 1; column = 0; column_bytes = 0; offset = 0; offset_bytes = 0 } type loc = { start_pos : pos; end_pos : pos } (** A source range from [start_pos] (inclusive) to [end_pos] (exclusive). *) (** {1 Errors} *) type yaml_error = { msg : string; loc : loc } type error = | Scan_error of yaml_error (** Invalid character sequence or encoding error detected by the scanner. Carries a position. *) | Parse_error of yaml_error (** Well-formed tokens in an invalid order detected by the parser. Carries a position. *) | Expansion_limit_exceeded of int (** Alias expansion visited more nodes than the configured limit. The payload is the limit that was exceeded. See {!default_expansion_limit}. *) | Depth_limit_exceeded of int (** YAML nesting depth exceeded the configured maximum during composition. The payload is the limit that was exceeded. See {!default_max_depth}. *) | Printer_error of string (** A feature unsupported by the plain-YAML printer was encountered (e.g. a tag, a complex mapping key). *) | Document_count_error of string (** The input contained the wrong number of documents for a single-document operation. *) | Schema_error of yaml_error (** A schema conflict was detected: either the document's [%YAML] directive disagrees with the requested schema (when [~strict_schema:true]), or a plain scalar is ambiguous between YAML 1.1 and 1.2 (when [~reject_ambiguous:true]). *) | Simplicity_error of yaml_error (** A YAML feature not allowed in plain mode was encountered: an anchor, alias, explicit tag, or (in YAML 1.1 mode) a merge key ([<<]). Raised when [~plain:true] is passed to resolver functions. *) | Duplicate_key_error of yaml_error (** A mapping contains a duplicate key. Raised when [~strict_keys:true] is passed to resolver functions. The location points to the second (duplicate) occurrence. *) | Cycle_error of yaml_error (** A cyclic alias was encountered during value resolution. The YAML structure is valid but cannot be represented as a finite [value] tree. The location points to the alias node that closes the cycle. *) exception Error of error (** The single exception raised by this library. Match on the payload to distinguish error kinds. *) (** Default maximum number of nodes that may be visited during alias expansion. Applies to both {!Resolver.resolve_documents} and {!Printer.to_plain_yaml}. *) let default_expansion_limit = 1_000_000 (** Default maximum nesting depth accepted during composition. Inputs deeper than this raise {!Error (Depth_limit_exceeded _)}. *) let default_max_depth = 512 (** YAML schema used to resolve plain scalars to typed values. *) type schema = | Yaml_1_2 (** YAML 1.2 JSON schema (the default). Booleans are only [true]/[false]; octal uses [0o…] prefix; sexagesimal notation is not recognised. *) | Yaml_1_1 (** YAML 1.1 schema. Adds extended booleans ([yes]/[no]/[on]/[off] etc.), [0…] octal, sexagesimal integers and floats, and merge-key ([<<]) expansion. Use this to read legacy YAML files. *) let scan_error pos fmt = Printf.ksprintf (fun msg -> raise (Error (Scan_error { msg; loc = { start_pos = pos; end_pos = pos } }))) fmt let parse_error pos fmt = Printf.ksprintf (fun msg -> raise (Error (Parse_error { msg; loc = { start_pos = pos; end_pos = pos } }))) fmt (** {1 Scalar styles} *) (** How a scalar was written in the YAML source. The style is preserved through the parsing pipeline because it affects tag resolution and round-trip fidelity. *) type scalar_style = | Plain (** unquoted, e.g. [foo] *) | Single_quoted (** e.g. ['foo'] *) | Double_quoted (** e.g. ['foo'] *) | Literal (** block scalar [|]: newlines preserved *) | Folded (** block scalar [>]: newlines folded to spaces *) (** {1 Tokens — Scanner output} *) (** A token represents one lexical element. The YAML grammar requires the scanner to emit synthetic structural tokens (BLOCK_SEQUENCE_START, BLOCK_MAPPING_START, BLOCK_END) in addition to the characters that appear in the source. *) type token_kind = | Stream_start | Stream_end | Directive of string * string (** [Directive (name, value)]: e.g. [%YAML 1.2] or [%TAG ! !foo/] *) | Document_start (** [---] *) | Document_end (** [...] *) | Block_sequence_start (** Synthetic: emitted when a block sequence begins (indentation increases to a new level). *) | Block_mapping_start (** Synthetic: emitted when a block mapping begins. *) | Block_end (** Synthetic: emitted when indentation decreases, closing one or more block collections. *) | Flow_sequence_start (** \[ *) | Flow_sequence_end (** \] *) | Flow_mapping_start (** \{ *) | Flow_mapping_end (** \} *) | Block_entry (** [-] followed by whitespace or newline *) | Flow_entry (** [,] *) | Key (** [?] (explicit) or synthetic (implicit key) *) | Value (** [:] followed by whitespace or end-of-line *) | Alias of string (** [*name] *) | Anchor of string (** [&name] *) | Tag of string * string (** (handle, suffix) *) | Scalar of string * scalar_style type token = { tok_kind : token_kind; tok_start_pos : pos; tok_end_pos : pos } (** {1 Events — Parser output} *) (** Events are the output of the Parser. They form a flat, sequential stream that can be processed without building a full tree in memory. The format mirrors the yaml-test-suite event notation. *) type event_kind = | Stream_start | Stream_end | Document_start of { explicit : bool; version : (int * int) option; tag_directives : (string * string) list; } | Document_end of { explicit : bool } | Mapping_start of { anchor : string option; tag : string option; implicit : bool; flow : bool; } | Mapping_end | Sequence_start of { anchor : string option; tag : string option; implicit : bool; flow : bool; } | Sequence_end | Scalar of { anchor : string option; tag : string option; value : string; style : scalar_style; } | Alias of string type event = { kind : event_kind; start_pos : pos; end_pos : pos } (** {1 AST nodes — Composer output} *) (** Nodes are the in-memory representation of a parsed YAML document. Anchors are resolved at compose time so [Alias_node] carries the actual target node. The [resolved] field is lazy to support cyclic YAML (e.g. a node that contains an alias to itself). *) type node = | Scalar_node of { anchor : string option; tag : string option; value : string; style : scalar_style; loc : loc; height : int; head_comments : string list; line_comment : string option; foot_comments : string list; } | Sequence_node of { anchor : string option; tag : string option; items : node list; flow : bool; loc : loc; height : int; head_comments : string list; line_comment : string option; foot_comments : string list; } | Mapping_node of { anchor : string option; tag : string option; pairs : (node * node) list; flow : bool; loc : loc; height : int; head_comments : string list; line_comment : string option; foot_comments : string list; } | Alias_node of { name : string; resolved : node Lazy.t; (** The node this alias refers to. [Lazy.t] to allow cyclic structures where an alias refers to an ancestor node. *) loc : loc; height : int; head_comments : string list; line_comment : string option; foot_comments : string list; } (** {1 Resolved values — Resolver output} *) (** Typed values produced by applying the YAML 1.2 JSON schema to a composed node tree. *) type value = | Null of loc | Bool of loc * bool | Int of loc * int64 | Float of loc * float | String of loc * string | Seq of loc * value list | Map of loc * (loc * value * value) list (** Structural equality that ignores source locations. *) let rec equal_value a b = match (a, b) with | Null _, Null _ -> true | Bool (_, x), Bool (_, y) -> x = y | Int (_, x), Int (_, y) -> Int64.equal x y | Float (_, x), Float (_, y) -> Float.equal x y | String (_, x), String (_, y) -> x = y | Seq (_, xs), Seq (_, ys) -> List.equal equal_value xs ys | Map (_, ps), Map (_, qs) -> List.equal (fun (_, k1, v1) (_, k2, v2) -> equal_value k1 k2 && equal_value v1 v2) ps qs | _ -> false (** Total order on values that ignores source locations. Constructor order: Null < Bool < Int < Float < String < Seq < Map. *) let rec compare_value a b = match (a, b) with | Null _, Null _ -> 0 | Null _, _ -> -1 | _, Null _ -> 1 | Bool (_, x), Bool (_, y) -> Bool.compare x y | Bool _, _ -> -1 | _, Bool _ -> 1 | Int (_, x), Int (_, y) -> Int64.compare x y | Int _, _ -> -1 | _, Int _ -> 1 | Float (_, x), Float (_, y) -> Float.compare x y | Float _, _ -> -1 | _, Float _ -> 1 | String (_, x), String (_, y) -> String.compare x y | String _, _ -> -1 | _, String _ -> 1 | Seq (_, xs), Seq (_, ys) -> List.compare compare_value xs ys | Seq _, _ -> -1 | _, Seq _ -> 1 | Map (_, ps), Map (_, qs) -> List.compare (fun (_, k1, v1) (_, k2, v2) -> let c = compare_value k1 k2 in if c <> 0 then c else compare_value v1 v2) ps qs module Value_set = Set.Make (struct type t = value let compare = compare_value end)