Source file element_content.ml
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
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
open Asttypes
open Parsetree
type assembler =
lang:Common.lang ->
loc:Location.t ->
name:string ->
expression Common.value list ->
(arg_label * expression) list
let to_txt = function
| [%expr[%e? {pexp_desc = Pexp_ident f; _}]
( [%e? {pexp_desc = Pexp_ident f2; _}] [%e? arg])] -> begin
match Longident.last_exn f.txt, Longident.last_exn f2.txt, arg.pexp_desc with
| "txt", "return", Pexp_constant (Pconst_string (s, _, _)) -> Some s
| _ -> None
end
| _ -> None
(** Test if the expression is a txt containing only whitespaces. *)
let is_whitespace = function
| Common.Val e -> begin
match to_txt e with
| Some s when String.trim s = "" -> true
| _ -> false
end
| _ -> false
let filter_whitespace = List.filter (fun e -> not @@ is_whitespace e)
let filter_surrounding_whitespace children =
let rec aux = function
| [] -> []
| h :: t when is_whitespace h -> aux t
| l -> List.rev l
in
aux @@ aux children
(** Improve an assembler by first applying [filter_whitespace] on children
Used by the [[@@reflect.filter_whitespace]] annotation *)
let comp_filter_whitespace assembler ~lang ~loc ~name children =
assembler ~lang ~loc ~name (filter_whitespace children)
let is_element_with_name name = function
| Common.Val {pexp_desc = Pexp_apply ({pexp_desc = Pexp_ident {txt}}, _)}
when txt = name -> true
| _ -> false
let partition name children =
List.partition (is_element_with_name name) children
let html local_name =
Longident.Ldot (Lident Common.(implementation Html), local_name)
let nullary ~lang:_ ~loc ~name children =
if children <> [] then
Common.error loc "%s should have no content" name;
[Nolabel, [%expr ()] [@metaloc loc]]
let unary ~lang ~loc ~name children =
match children with
| [child] ->
let child = Common.wrap_value lang loc child in
[Nolabel, child]
| _ -> Common.error loc "%s should have exactly one child" name
let star ~lang ~loc ~name:_ children =
[Nolabel, Common.list_wrap_value lang loc children]
let head ~lang ~loc ~name children =
let title, others = partition (html "title") children in
match title with
| [title] ->
(Nolabel, Common.wrap_value lang loc title) :: star ~lang ~loc ~name others
| _ ->
Common.error loc
"%s element must have exactly one title child element" name
let figure ~lang ~loc ~name children =
let caption, children =
let rec is_first_figcaption = function
| [] -> is_last_figcaption (List.rev children)
| h :: t ->
if is_whitespace h then is_first_figcaption t
else if is_element_with_name (html "figcaption") h then
`Top h,t
else is_last_figcaption (List.rev children)
and is_last_figcaption = function
| [] -> `No, children
| h :: t ->
if is_whitespace h then is_last_figcaption t
else if is_element_with_name (html "figcaption") h then
`Bottom h, (List.rev t)
else `No, children
in
is_first_figcaption children
in
begin match caption with
| `No -> star ~lang ~loc ~name children
| `Top elt ->
(Labelled "figcaption",
[%expr `Top [%e Common.wrap_value lang loc elt]])::
(star ~lang ~loc ~name children)
| `Bottom elt ->
(Labelled "figcaption",
[%expr `Bottom [%e Common.wrap_value lang loc elt]])::
(star ~lang ~loc ~name children)
end [@metaloc loc]
let object_ ~lang ~loc ~name children =
let params, others = partition (html "param") children in
if params <> [] then
(Labelled "params", Common.list_wrap_value lang loc params) ::
star ~lang ~loc ~name others
else
star ~lang ~loc ~name others
let audio_video ~lang ~loc ~name children =
let sources, others = partition (html "source") children in
if sources <> [] then
(Labelled "srcs", Common.list_wrap_value lang loc sources) ::
star ~lang ~loc ~name others
else
star ~lang ~loc ~name others
let table ~lang ~loc ~name children =
let caption, others = partition (html "caption") children in
let columns, others = partition (html "colgroup") others in
let thead, others = partition (html "thead") others in
let , others = partition (html "tfoot") others in
let one label = function
| [] -> []
| [child] -> [Labelled label, Common.wrap_value lang loc child]
| _ -> Common.error loc "%s cannot have more than one %s" name label
in
let columns =
if columns = [] then []
else [Labelled "columns", Common.list_wrap_value lang loc columns]
in
(one "caption" caption) @
columns @
(one "thead" thead) @
(one "tfoot" tfoot) @
(star ~lang ~loc ~name others)
let fieldset ~lang ~loc ~name children =
let legend, others = partition (html "legend") children in
match legend with
| [] -> star ~lang ~loc ~name others
| [legend] ->
(Labelled "legend", Common.wrap_value lang loc legend)::
(star ~lang ~loc ~name others)
| _ -> Common.error loc "%s cannot have more than one legend" name
let datalist ~lang ~loc ~name children =
let options, others = partition (html "option") children in
let children =
begin match others with
| [] ->
Labelled "children",
[%expr `Options [%e Common.list_wrap_value lang loc options]]
| _ ->
Labelled "children",
[%expr `Phras [%e Common.list_wrap_value lang loc children]]
end [@metaloc loc]
in
children::(nullary ~lang ~loc ~name [])
let at_most_one_child ~lang ~loc ~name children =
match children with
| [] ->
let child = Common.txt ~loc ~lang "" in
[Nolabel, child]
| [child] ->
let child = Common.wrap_value lang loc child in
[Nolabel, child]
| _ -> Common.error loc "%s can have at most one child" name
let script = at_most_one_child
let textarea = at_most_one_child
let details ~lang ~loc ~name children =
let summary, others = partition (html "summary") children in
match summary with
| [summary] ->
(Nolabel, Common.wrap_value lang loc summary)::
(star ~lang ~loc ~name others)
| _ -> Common.error loc "%s must have exactly one summary child" name
let ~lang ~loc ~name children =
let children =
Labelled "child",
[%expr `Flows [%e Common.list_wrap_value lang loc children]]
[@metaloc loc]
in
children::(nullary ~lang ~loc ~name [])
let picture ~lang ~loc ~name children =
let img, others = partition (html "img") children in
match img with
| [] -> star ~lang ~loc ~name others
| [img] ->
(Labelled "img", Common.wrap_value lang loc img)::
(star ~lang ~loc ~name others)
| _ -> Common.error loc "%s cannot have more than one img" name
let html ~lang ~loc ~name children =
let head, others = partition (html "head") children in
let body, others = partition (html "body") others in
match head, body, others with
| [head], [body], [] ->
[Nolabel, Common.wrap_value lang loc head;
Nolabel, Common.wrap_value lang loc body]
| _ ->
Common.error loc
"%s element must have exactly head and body child elements" name