Pure OCaml Yaml 1.2 reader and writer using Bytesrw
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** YAML scalar quoting detection *)
7
8(** Check if a string value needs quoting in YAML output. Returns true if the
9 string:
10 - Is empty
11 - Starts with an indicator character
12 - Is a reserved word (null, true, false, yes, no, etc.)
13 - Contains characters that would be ambiguous
14 - Looks like a number *)
15let needs_quoting s =
16 if String.length s = 0 then true
17 else
18 let first = s.[0] in
19 (* Check first character for indicators *)
20 if
21 first = '-' || first = '?' || first = ':' || first = ',' || first = '['
22 || first = ']' || first = '{' || first = '}' || first = '#' || first = '&'
23 || first = '*' || first = '!' || first = '|' || first = '>'
24 || first = '\'' || first = '"' || first = '%' || first = '@'
25 || first = '`' || first = ' '
26 then true
27 else
28 (* Check for reserved/special values *)
29 let lower = String.lowercase_ascii s in
30 if
31 lower = "null" || lower = "true" || lower = "false" || lower = "yes"
32 || lower = "no" || lower = "on" || lower = "off" || lower = "~"
33 || lower = ".inf" || lower = "-.inf" || lower = ".nan"
34 then true
35 else
36 (* Check for problematic characters *)
37 try
38 String.iter
39 (fun c ->
40 if c = ':' || c = '#' || c = '\n' || c = '\r' then raise Exit)
41 s;
42 (* Check if it looks like a number *)
43 try
44 ignore (Float.of_string s);
45 true
46 with _ -> false
47 with Exit -> true
48
49(** Check if a string requires double quotes (vs single quotes). Returns true if
50 the string contains characters that need escape sequences. *)
51let needs_double_quotes s =
52 try
53 String.iter
54 (fun c ->
55 if c = '\n' || c = '\r' || c = '\t' || c = '\\' || c < ' ' || c = '"'
56 then raise Exit)
57 s;
58 false
59 with Exit -> true
60
61(** Choose the appropriate quoting style for a string value *)
62let choose_style s =
63 match (needs_double_quotes s, needs_quoting s) with
64 | true, _ -> `Double_quoted
65 | _, true -> `Single_quoted
66 | _ -> `Plain