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.
9 Returns true if the 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 first = '-' || first = '?' || first = ':' || first = ',' ||
21 first = '[' || first = ']' || first = '{' || first = '}' ||
22 first = '#' || first = '&' || first = '*' || first = '!' ||
23 first = '|' || first = '>' || first = '\'' || first = '"' ||
24 first = '%' || first = '@' || first = '`' || first = ' ' then
25 true
26 else
27 (* Check for reserved/special values *)
28 let lower = String.lowercase_ascii s in
29 if lower = "null" || lower = "true" || lower = "false" ||
30 lower = "yes" || lower = "no" || lower = "on" || lower = "off" ||
31 lower = "~" || lower = ".inf" || lower = "-.inf" || lower = ".nan" then
32 true
33 else
34 (* Check for problematic characters *)
35 try
36 String.iter (fun c ->
37 if c = ':' || c = '#' || c = '\n' || c = '\r' then
38 raise Exit
39 ) s;
40 (* Check if it looks like a number *)
41 (try ignore (Float.of_string s); true with _ -> false)
42 with Exit -> true
43
44(** Check if a string requires double quotes (vs single quotes).
45 Returns true if the string contains characters that need escape sequences. *)
46let needs_double_quotes s =
47 try
48 String.iter (fun c ->
49 if c = '\n' || c = '\r' || c = '\t' || c = '\\' ||
50 c < ' ' || c = '"' then
51 raise Exit
52 ) s;
53 false
54 with Exit -> true
55
56(** Choose the appropriate quoting style for a string value *)
57let choose_style s =
58 match (needs_double_quotes s, needs_quoting s) with
59 | (true, _) -> `Double_quoted
60 | (_, true) -> `Single_quoted
61 | _ -> `Plain
62