Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Event_printer.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(** Convert a parser event list to the yaml-test-suite tree notation. The tree notation is a human-readable, line-by-line representation of the event stream, used by the test suite to specify expected output. *) (* Notation summary ~~~~~~~~~~~~~~~~ +STR stream start -STR stream end +DOC [---] document start (with or without explicit marker) -DOC [...] document end (with or without explicit marker) +MAP [{}] mapping start (flow=true; optionally &anchor and tag URI) -MAP mapping end +SEQ [[]] sequence start (flow=true; optionally &anchor and tag URI) -SEQ sequence end =VAL [&anchor] [<tag>] STYLE VALUE where STYLE is one of: : plain ' single-quoted '"' double-quoted | literal block > folded block =ALI *name alias The VALUE in =VAL lines has special characters escaped: actual newline -> \n actual tab -> \t actual backslash -> \\ The tree lines in the test suite have leading spaces for visual nesting; we strip those when comparing (see [normalize_tree]). *) open Types (* ------------------------------------------------------------------ *) (* Value escaping *) (* ------------------------------------------------------------------ *) (** Escape a scalar value string for display in the tree format. Newlines, tabs, backslashes, and carriage returns are made visible. Trailing spaces are replaced with U+2423 OPEN BOX (␣) per the yaml-test-suite convention, so they are not invisible in the output. *) let escape_value (s : string) : string = let buf = Buffer.create (String.length s) in String.iter (fun c -> match c with | '\n' -> Buffer.add_string buf "\\n" | '\t' -> Buffer.add_string buf "\\t" | '\r' -> Buffer.add_string buf "\\r" | '\\' -> Buffer.add_string buf "\\\\" | '\x08' -> Buffer.add_string buf "\\b" | _ -> Buffer.add_char buf c) s; (* Replace trailing ASCII spaces with U+2423 (OPEN BOX = ␣) *) let escaped = Buffer.contents buf in let n = String.length escaped in let i = ref n in while !i > 0 && escaped.[!i - 1] = ' ' do decr i done; if !i = n then escaped else begin let buf2 = Buffer.create (n + ((!i - n) * 2)) in Buffer.add_string buf2 (String.sub escaped 0 !i); for _ = !i + 1 to n do Buffer.add_string buf2 "\xE2\x90\xA3" (* U+2423 ␣ *) done; Buffer.contents buf2 end (* ------------------------------------------------------------------ *) (* Style prefix *) (* ------------------------------------------------------------------ *) let style_prefix = function | Plain -> ":" | Single_quoted -> "'" | Double_quoted -> "\"" | Literal -> "|" | Folded -> ">" (* ------------------------------------------------------------------ *) (* Anchor and tag formatting *) (* ------------------------------------------------------------------ *) let format_anchor = function | None -> "" | Some a -> " &" ^ a let format_tag = function | None -> "" | Some t -> " <" ^ t ^ ">" (* ------------------------------------------------------------------ *) (* Event to tree line(s) *) (* ------------------------------------------------------------------ *) (** Render a single event as a tree-format line. Returns one string per event. *) let event_to_line (ev : event) : string = match ev.kind with | Stream_start -> "+STR" | Stream_end -> "-STR" | Document_start { explicit; _ } -> if explicit then "+DOC ---" else "+DOC" | Document_end { explicit } -> if explicit then "-DOC ..." else "-DOC" | Mapping_start { anchor; tag; flow; _ } -> let flow_marker = if flow then " {}" else "" in "+MAP" ^ flow_marker ^ format_anchor anchor ^ format_tag tag | Mapping_end -> "-MAP" | Sequence_start { anchor; tag; flow; _ } -> let flow_marker = if flow then " []" else "" in "+SEQ" ^ flow_marker ^ format_anchor anchor ^ format_tag tag | Sequence_end -> "-SEQ" | Scalar { anchor; tag; value; style } -> "=VAL" ^ format_anchor anchor ^ format_tag tag ^ " " ^ style_prefix style ^ escape_value value | Alias name -> "=ALI *" ^ name (** Convert an event list to the multi-line tree string. Each event becomes one line; lines are separated by newlines. The result ends with a final newline. *) let to_tree (events : event list) : string = let lines = List_ext.map event_to_line events in String.concat "\n" lines ^ "\n" (* ------------------------------------------------------------------ *) (* Comparison helpers *) (* ------------------------------------------------------------------ *) (** Split [s] into trimmed, non-empty lines for comparison. Leading whitespace (the visual indentation in the test suite's tree format) is stripped since it carries no semantic meaning. *) let normalize_tree (s : string) : string list = String.split_on_char '\n' s |> List_ext.map String.trim |> List_ext.filter (fun l -> l <> "") (** Return a human-readable diff between [expected] and [actual] tree strings. Only lists the first differing line. *) let diff_trees ~expected ~actual : string option = let exp_lines = normalize_tree expected in let act_lines = normalize_tree actual in let rec go i = function | [], [] -> None | e :: es, a :: ax -> if e = a then go (i + 1) (es, ax) else Some (Printf.sprintf "line %d: expected %S, got %S" (i + 1) e a) | e :: _, [] -> Some (Printf.sprintf "line %d: expected %S, got <end>" (i + 1) e) | [], a :: _ -> Some (Printf.sprintf "line %d: expected <end>, got %S" (i + 1) a) in go 1 (exp_lines, act_lines)