1
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
type node = Element of element | Text of string
and element = {
name : string;
attributes : (string * string) list;
extensions : (string * string) list;
origin : Dtd.origin;
children : node list;
}
let element ?(attributes = []) (name : string) (children : node list) : element =
{ name; attributes; extensions = []; origin = Core; children }
let attribute ?(extensions = false) (name : string) (e : element) : string option =
match List.assoc_opt name e.attributes with
| Some v -> Some v
| None when extensions -> List.assoc_opt name e.extensions
| None -> None
let rec find_all (name : string) (e : element) : element list =
(if e.name = name then [ e ] else [])
@ List.concat_map (fun n -> match n with Element c -> find_all name c | Text _ -> []) e.children
let text_content (e : element) : string =
let b = Buffer.create 64 in
let rec go (e : element) =
List.iter (fun n -> match n with Text s -> Buffer.add_string b s | Element c -> go c) e.children
in
go e;
Buffer.contents b
let is_blank (s : string) : bool = String.for_all (fun c -> c = ' ' || c = '\n' || c = '\t') s
let rec without_blank_text (e : element) : element =
{
e with
children =
List.filter_map
(fun n ->
match n with Text s when is_blank s -> None | Text _ -> Some n | Element c -> Some (Element (without_blank_text c)))
e.children;
}
let to_lines (root : element) : string list =
let lines = ref [] in
let add depth s = lines := (String.make (2 * depth) ' ' ^ s) :: !lines in
let rec go depth (e : element) =
let list attributes = List.map (fun (n, v) -> Printf.sprintf "%s=\"%s\"" n v) attributes in
let mark =
match (e.origin, e.extensions) with
| Netscape, _ -> [ "{Netscape}" ]
| Core, [] -> []
| Core, extensions -> [ "{Netscape: " ^ String.concat " " (list extensions) ^ "}" ]
in
add depth (String.concat " " ((e.name :: list e.attributes) @ mark));
List.iter
(fun n ->
match n with
| Element c -> go (depth + 1) c
| Text s ->
let escaped = String.concat "\\n" (String.split_on_char '\n' s) in
add (depth + 1) ("\"" ^ escaped ^ "\""))
e.children
in
go 0 root;
List.rev !lines