adam@0
|
1 task initialize = fn () => FeedFfi.init
|
adam@0
|
2
|
adam@4
|
3 con pattern internal output = {Initial : internal,
|
adam@4
|
4 EnterTag : {Tag : string, Attrs : list (string * string), Cdata : option string} -> internal -> option internal,
|
adam@4
|
5 ExitTag : internal -> option internal,
|
adam@4
|
6 Finished : internal -> option (output * bool)}
|
adam@4
|
7
|
adam@4
|
8 val null : pattern unit (variant []) =
|
adam@4
|
9 {Initial = (),
|
adam@4
|
10 EnterTag = fn _ () => Some (),
|
adam@4
|
11 ExitTag = fn () => Some (),
|
adam@4
|
12 Finished = fn () => None}
|
adam@1
|
13
|
adam@6
|
14 con tagInternal (attrs :: {Unit}) = option {Attrs : $(mapU (option string) attrs), Cdata : option string}
|
adam@1
|
15
|
adam@6
|
16 fun tagG [attrs ::: {Unit}] [t ::: Type] (fl : folder attrs) (accept : {Attrs : $(mapU (option string) attrs), Cdata : option string} -> option t)
|
adam@3
|
17 (name : string) (attrs : $(mapU string attrs))
|
adam@3
|
18 : pattern (tagInternal attrs) t =
|
adam@4
|
19 {Initial = None,
|
adam@4
|
20 EnterTag = fn tinfo _ =>
|
adam@4
|
21 if tinfo.Tag <> name then
|
adam@4
|
22 None
|
adam@4
|
23 else
|
adam@6
|
24 let
|
adam@6
|
25 val v = {Attrs = @mp [fn _ => string] [fn _ => option string]
|
adam@6
|
26 (fn [u] aname => List.assoc aname tinfo.Attrs)
|
adam@6
|
27 fl attrs,
|
adam@6
|
28 Cdata = tinfo.Cdata}
|
adam@6
|
29 in
|
adam@6
|
30 case accept v of
|
adam@6
|
31 None => None
|
adam@6
|
32 | Some _ => Some (Some v)
|
adam@6
|
33 end,
|
adam@4
|
34 ExitTag = fn _ => None,
|
adam@4
|
35 Finished = fn state => case state of
|
adam@4
|
36 None => None
|
adam@4
|
37 | Some state =>
|
adam@4
|
38 case accept state of
|
adam@4
|
39 None => None
|
adam@4
|
40 | Some v => Some (v, False)}
|
adam@3
|
41
|
adam@6
|
42 fun allPresent [attrs ::: {Unit}] (fl : folder attrs) (attrs : $(mapU (option string) attrs)) : option $(mapU string attrs) =
|
adam@6
|
43 @foldUR [option string] [fn attrs => option $(mapU string attrs)]
|
adam@6
|
44 (fn [nm ::_] [r ::_] [[nm] ~ r] os acc =>
|
adam@6
|
45 case (os, acc) of
|
adam@6
|
46 (Some s, Some acc) => Some ({nm = s} ++ acc)
|
adam@6
|
47 | _ => None)
|
adam@6
|
48 (Some {}) fl attrs
|
adam@6
|
49
|
kkallio@9
|
50 fun allPresentE [attrs ::: {Unit}] (fl : folder attrs) (vs : $(mapU (option string) attrs)) (attrs : $(mapU (option string) attrs))
|
kkallio@9
|
51 : option $(mapU string attrs) =
|
kkallio@9
|
52 @foldUR2 [option string] [option string] [fn attrs => option $(mapU string attrs)]
|
kkallio@9
|
53 (fn [nm ::_] [r ::_] [[nm] ~ r] os os' acc =>
|
kkallio@9
|
54 case (os, os', acc) of
|
kkallio@9
|
55 (Some s, Some s', Some acc) => if s = s' then Some ({nm = s'} ++ acc) else None
|
kkallio@9
|
56 | (None, Some s', Some acc) => Some ({nm = s'} ++ acc)
|
kkallio@9
|
57 | _ => None)
|
kkallio@9
|
58 (Some {}) fl vs attrs
|
kkallio@9
|
59
|
adam@3
|
60 fun tag [attrs ::: {Unit}] (fl : folder attrs) (name : string) (attrs : $(mapU string attrs))
|
adam@3
|
61 : pattern (tagInternal attrs) {Attrs : $(mapU string attrs), Cdata : option string} =
|
adam@6
|
62 @tagG fl (fn r =>
|
adam@6
|
63 case @allPresent fl r.Attrs of
|
adam@6
|
64 None => None
|
adam@6
|
65 | Some attrs => Some (r -- #Attrs ++ {Attrs = attrs}))
|
adam@6
|
66 name attrs
|
adam@3
|
67
|
adam@3
|
68 fun tagA [attrs ::: {Unit}] (fl : folder attrs) (name : string) (attrs : $(mapU string attrs))
|
adam@3
|
69 : pattern (tagInternal attrs) $(mapU string attrs) =
|
adam@6
|
70 @tagG fl (fn r => @allPresent fl r.Attrs) name attrs
|
kkallio@9
|
71
|
kkallio@9
|
72 fun tagAV [attrs ::: {Unit}] (fl : folder attrs) (name : string) (attrs : $(mapU (string * option string) attrs))
|
kkallio@9
|
73 : pattern (tagInternal attrs) $(mapU string attrs) =
|
kkallio@9
|
74 let
|
kkallio@9
|
75 val as = @mp [fn _ => (string * option string)] [fn _ => string] (fn [u] (x, _) => x) fl attrs
|
kkallio@9
|
76 val vs = @mp [fn _ => (string * option string)] [fn _ => option string] (fn [u] (_, x) => x) fl attrs
|
kkallio@9
|
77 in
|
kkallio@9
|
78 @tagG fl (fn r => @allPresentE fl vs r.Attrs) name as
|
kkallio@9
|
79 end
|
kkallio@9
|
80
|
adam@6
|
81 fun tagAO [attrs ::: {Unit}] (fl : folder attrs) (name : string) (attrs : $(mapU string attrs))
|
adam@6
|
82 : pattern (tagInternal attrs) $(mapU (option string) attrs) =
|
adam@21
|
83 @tagG fl (fn r => Some r.Attrs) name attrs
|
adam@21
|
84
|
adam@21
|
85 fun tagAOR [optional ::: {Unit}] [required ::: {Unit}] [optional ~ required]
|
adam@21
|
86 (ofl : folder optional) (rfl : folder required)
|
adam@21
|
87 (name : string) (required : $(mapU string required)) (optional : $(mapU string optional))
|
adam@21
|
88 : pattern (tagInternal (optional ++ required)) $(mapU string required ++ mapU (option string) optional) =
|
adam@21
|
89 @tagG (@Folder.concat ! ofl rfl)
|
adam@21
|
90 (fn r => case @allPresent rfl (r.Attrs --- mapU (option string) optional) of
|
adam@21
|
91 None => None
|
adam@21
|
92 | Some req => Some (r.Attrs --- mapU (option string) required ++ req))
|
adam@21
|
93 name (required ++ optional)
|
adam@3
|
94
|
adam@3
|
95 fun tagC (name : string) : pattern (tagInternal []) string =
|
adam@3
|
96 tagG (fn r => r.Cdata) name {}
|
adam@1
|
97
|
adam@4
|
98 datatype status a = Initial | Pending of a | Matched of a
|
adam@1
|
99
|
adam@1
|
100 con childrenInternal (parent :: Type) (children :: {Type}) = option (parent * int * $(map status children))
|
adam@1
|
101
|
adam@6
|
102 fun childrenG [parentI ::: Type] [parent ::: Type] [children ::: {(Type * Type)}] [t ::: Type]
|
adam@6
|
103 (ready : $(map (fn (i, d) => option d) children) -> option t)
|
adam@6
|
104 (parent : pattern parentI parent) (children : $(map (fn (i, d) => pattern i d) children)) (fl : folder children)
|
adam@6
|
105 : pattern (childrenInternal parentI (map fst children)) (parent * t) =
|
adam@4
|
106 {Initial = None,
|
adam@4
|
107 EnterTag = fn tinfo state =>
|
adam@4
|
108 case state of
|
adam@4
|
109 None =>
|
adam@4
|
110 (case parent.EnterTag tinfo parent.Initial of
|
adam@4
|
111 None => None
|
adam@4
|
112 | Some pstate => Some (Some (pstate, 1, @map0 [status] (fn [t ::_] => Initial)
|
adam@4
|
113 (@@Folder.mp [fst] [_] fl))))
|
adam@4
|
114 | Some (pstate, depth, cstates) =>
|
adam@6
|
115 if depth = 0 then
|
adam@6
|
116 case parent.EnterTag tinfo parent.Initial of
|
adam@6
|
117 None => None
|
adam@6
|
118 | Some pstate => Some (Some (pstate, 1, @map0 [status] (fn [t ::_] => Initial)
|
adam@6
|
119 (@@Folder.mp [fst] [_] fl)))
|
adam@6
|
120 else
|
adam@6
|
121 Some (Some (pstate,
|
adam@6
|
122 depth+1,
|
adam@6
|
123 @map2 [fn (i, d) => pattern i d] [fn (i, d) => status i] [fn (i, d) => status i]
|
adam@6
|
124 (fn [p] (ch : pattern p.1 p.2) (cstate : status p.1) =>
|
adam@6
|
125 case cstate of
|
adam@6
|
126 Initial =>
|
adam@6
|
127 (case ch.EnterTag tinfo ch.Initial of
|
adam@6
|
128 None => Initial
|
adam@6
|
129 | Some v =>
|
adam@6
|
130 case ch.Finished v of
|
adam@6
|
131 None => Pending v
|
adam@6
|
132 | _ => Matched v)
|
adam@6
|
133 | Pending cstate =>
|
adam@6
|
134 (case ch.EnterTag tinfo cstate of
|
adam@6
|
135 None => Initial
|
adam@6
|
136 | Some v =>
|
adam@6
|
137 case ch.Finished v of
|
adam@6
|
138 None => Pending v
|
adam@6
|
139 | _ => Matched v)
|
adam@6
|
140 | v => v)
|
adam@6
|
141 fl children cstates)),
|
adam@4
|
142 ExitTag = fn state =>
|
adam@4
|
143 case state of
|
adam@4
|
144 None => None
|
adam@6
|
145 | Some (pstate, 1, cstates) => Some (Some (pstate, 0, cstates))
|
adam@4
|
146 | Some (pstate, depth, cstates) =>
|
adam@4
|
147 Some (Some (pstate, depth-1,
|
adam@4
|
148 @map2 [fn (i, d) => pattern i d] [fn (i, d) => status i] [fn (i, d) => status i]
|
adam@4
|
149 (fn [p] (ch : pattern p.1 p.2) (cstate : status p.1) =>
|
adam@4
|
150 case cstate of
|
adam@4
|
151 Pending cstate =>
|
adam@4
|
152 (case ch.ExitTag cstate of
|
adam@4
|
153 None => Initial
|
adam@4
|
154 | Some cstate' =>
|
adam@4
|
155 case ch.Finished cstate' of
|
adam@4
|
156 None => Pending cstate'
|
adam@4
|
157 | _ => Matched cstate')
|
adam@4
|
158 | _ => cstate)
|
adam@4
|
159 fl children cstates)),
|
adam@4
|
160 Finished = fn state =>
|
adam@4
|
161 case state of
|
adam@6
|
162 Some (pstate, 0, cstates) =>
|
adam@4
|
163 (case parent.Finished pstate of
|
adam@4
|
164 None => None
|
adam@4
|
165 | Some (pdata, pcont) =>
|
adam@6
|
166 case ready (@map2 [fn (i, d) => status i] [fn (i, d) => pattern i d] [fn (i, d) => option d]
|
adam@6
|
167 (fn [p] (cstate : status p.1) (ch : pattern p.1 p.2) =>
|
adam@6
|
168 case cstate of
|
adam@6
|
169 Matched v => Option.mp (fn p => p.1) (ch.Finished v)
|
adam@6
|
170 | _ => None) fl cstates children) of
|
adam@4
|
171 None => None
|
adam@4
|
172 | Some cdata => Some ((pdata, cdata), pcont))
|
adam@4
|
173 | _ => None}
|
adam@1
|
174
|
adam@6
|
175 fun children [parentI ::: Type] [parent ::: Type] [children ::: {(Type * Type)}]
|
adam@6
|
176 (parent : pattern parentI parent) (children : $(map (fn (i, d) => pattern i d) children)) (fl : folder children)
|
adam@6
|
177 : pattern (childrenInternal parentI (map fst children)) (parent * $(map snd children)) =
|
adam@6
|
178 @childrenG (@foldR [fn (i, d) => option d] [fn cs => option $(map snd cs)]
|
adam@6
|
179 (fn [nm ::_] [p ::_] [r ::_] [[nm] ~ r] (cstate : option p.2) acc =>
|
adam@6
|
180 case (cstate, acc) of
|
adam@6
|
181 (Some cstate, Some acc) => Some ({nm = cstate} ++ acc)
|
adam@6
|
182 | _ => None)
|
adam@6
|
183 (Some {}) fl) parent children fl
|
adam@6
|
184
|
adam@6
|
185 fun childrenO [parentI ::: Type] [parent ::: Type] [children ::: {(Type * Type)}]
|
adam@6
|
186 (parent : pattern parentI parent) (children : $(map (fn (i, d) => pattern i d) children)) (fl : folder children)
|
adam@6
|
187 : pattern (childrenInternal parentI (map fst children)) (parent * $(map (fn (i, d) => option d) children)) =
|
adam@6
|
188 @childrenG Some parent children fl
|
adam@6
|
189
|
kkallio@11
|
190 datatype required t = Required of t | Optional of t
|
kkallio@11
|
191
|
kkallio@11
|
192 fun childrenO' [parentI ::: Type] [parent ::: Type] [children ::: {(Type * Type)}]
|
kkallio@11
|
193 (parent : pattern parentI parent) (children : $(map (fn (i, d) => required (pattern i d)) children)) (fl : folder children)
|
kkallio@11
|
194 : pattern (childrenInternal parentI (map fst children)) (parent * $(map (fn (i, d) => option d) children)) =
|
kkallio@11
|
195 let
|
kkallio@11
|
196 val os = @mp [fn (i, d) => required (pattern i d)] [fn (i, d) => bool]
|
kkallio@11
|
197 (fn [u] pat => case pat of
|
kkallio@11
|
198 Required _ => False
|
kkallio@11
|
199 | Optional _ => True) fl children
|
kkallio@11
|
200 val vs = @mp [fn (i, d) => required (pattern i d)] [fn (i, d) => pattern i d]
|
kkallio@11
|
201 (fn [u] pat => case pat of
|
kkallio@11
|
202 Required pat' => pat'
|
kkallio@11
|
203 | Optional pat' => pat') fl children
|
kkallio@11
|
204 in
|
kkallio@11
|
205 @childrenG (@foldR2 [fn _ => bool] [fn (i, d) => option d] [fn r => option $(map (fn (i, d) => option d) r)]
|
kkallio@11
|
206 (fn [nm ::_] [p ::_] [r ::_] [[nm] ~ r] (isO : bool) (cstate : option p.2) acc =>
|
kkallio@11
|
207 case acc of
|
kkallio@11
|
208 None => None
|
kkallio@11
|
209 | Some acc =>
|
kkallio@11
|
210 if isO then
|
kkallio@11
|
211 Some ({nm = cstate} ++ acc)
|
kkallio@11
|
212 else
|
kkallio@11
|
213 case cstate of
|
kkallio@11
|
214 None => None
|
kkallio@11
|
215 | Some _ => Some ({nm = cstate} ++ acc))
|
kkallio@11
|
216 (Some {}) fl os) parent vs fl
|
kkallio@11
|
217 end
|
kkallio@11
|
218
|
adam@4
|
219 con treeInternal (parent :: Type) (child :: Type) = option (parent * int * option child)
|
adam@4
|
220
|
adam@4
|
221 fun tree [parentI ::: Type] [parent ::: Type] [childI ::: Type] [child ::: Type]
|
adam@4
|
222 (parent : pattern parentI parent) (child : pattern childI child)
|
adam@4
|
223 : pattern (treeInternal parentI childI) (parent * child) =
|
adam@4
|
224 {Initial = None,
|
adam@4
|
225 EnterTag = fn tinfo state =>
|
adam@4
|
226 case state of
|
adam@4
|
227 None =>
|
adam@4
|
228 (case parent.EnterTag tinfo parent.Initial of
|
adam@4
|
229 None => None
|
adam@4
|
230 | Some pstate => Some (Some (pstate, 1, None)))
|
adam@4
|
231 | Some (pstate, depth, cstate) =>
|
adam@4
|
232 Some (Some (pstate,
|
adam@4
|
233 depth+1,
|
adam@4
|
234 child.EnterTag tinfo (Option.get child.Initial cstate))),
|
adam@4
|
235 ExitTag = fn state =>
|
adam@4
|
236 case state of
|
adam@4
|
237 None => None
|
adam@6
|
238 | Some (_, 1, _) => None
|
adam@4
|
239 | Some (pstate, depth, cstate) =>
|
adam@4
|
240 Some (Some (pstate, depth-1, Option.bind child.ExitTag cstate)),
|
adam@4
|
241 Finished = fn state =>
|
adam@4
|
242 case state of
|
adam@4
|
243 None => None
|
adam@4
|
244 | Some (pstate, _, cstate) =>
|
adam@4
|
245 case parent.Finished pstate of
|
adam@4
|
246 None => None
|
adam@4
|
247 | Some (pdata, _) =>
|
adam@4
|
248 case cstate of
|
adam@4
|
249 None => None
|
adam@4
|
250 | Some cstate =>
|
adam@4
|
251 case child.Finished cstate of
|
adam@4
|
252 None => None
|
adam@4
|
253 | Some (cdata, _) => Some ((pdata, cdata), True)}
|
adam@4
|
254
|
kkallio@14
|
255 con gatherInternal (parent :: Type) (child :: Type) (data :: Type) = option (parent * bool * int * option child * list data)
|
kkallio@14
|
256
|
kkallio@14
|
257 fun gather [parentI ::: Type] [parent ::: Type] [childI ::: Type] [child ::: Type]
|
kkallio@14
|
258 (parent : pattern parentI parent) (child : pattern childI child)
|
kkallio@14
|
259 : pattern (gatherInternal parentI childI child) (parent * list child) =
|
kkallio@14
|
260 {Initial = None,
|
kkallio@14
|
261 EnterTag = fn tinfo state =>
|
kkallio@14
|
262 case state of
|
kkallio@14
|
263 None =>
|
kkallio@14
|
264 (case parent.EnterTag tinfo parent.Initial of
|
kkallio@14
|
265 None => None
|
kkallio@14
|
266 | Some pstate => Some (Some (pstate, False, 1, None, Nil)))
|
kkallio@14
|
267 | Some (pstate, return, depth, cstate, clist) =>
|
kkallio@14
|
268 let
|
kkallio@14
|
269 val cstate' = child.EnterTag tinfo (Option.get child.Initial cstate)
|
kkallio@14
|
270 in
|
kkallio@14
|
271 case child.Finished (Option.get child.Initial cstate') of
|
kkallio@14
|
272 None =>
|
kkallio@14
|
273 Some (Some (pstate, return, depth+1, cstate', clist))
|
kkallio@14
|
274 | Some (cdata, _) =>
|
kkallio@14
|
275 Some (Some (pstate, return, depth+1, None, cdata :: clist))
|
kkallio@14
|
276 end,
|
kkallio@14
|
277 ExitTag = fn state =>
|
kkallio@14
|
278 case state of
|
kkallio@14
|
279 None => None
|
kkallio@14
|
280 | Some (pstate, _, 1, cstate, clist) =>
|
kkallio@14
|
281 Some (Some (pstate, True, 1, cstate, clist))
|
kkallio@14
|
282 | Some (pstate, return, depth, cstate, clist) =>
|
kkallio@14
|
283 let
|
kkallio@14
|
284 val cstate' = child.ExitTag (Option.get child.Initial cstate)
|
kkallio@14
|
285 in
|
kkallio@14
|
286 case child.Finished (Option.get child.Initial cstate') of
|
kkallio@14
|
287 None =>
|
kkallio@14
|
288 Some (Some (pstate, return, depth-1, cstate', clist))
|
kkallio@14
|
289 | Some (cdata, _) =>
|
kkallio@14
|
290 Some (Some (pstate, return, depth-1, None, cdata :: clist))
|
kkallio@14
|
291 end,
|
kkallio@14
|
292 Finished = fn state =>
|
kkallio@14
|
293 case state of
|
kkallio@14
|
294 None => None
|
kkallio@14
|
295 | Some (pstate, return, _, _, clist) =>
|
kkallio@14
|
296 case parent.Finished pstate of
|
kkallio@14
|
297 None => None
|
kkallio@14
|
298 | Some (pdata, _) =>
|
kkallio@14
|
299 if return then
|
kkallio@14
|
300 Some ((pdata, List.rev clist), False)
|
kkallio@14
|
301 else
|
kkallio@14
|
302 None}
|
kkallio@14
|
303
|
adam@5
|
304 type document = string
|
adam@7
|
305 val show_document = _
|
adam@5
|
306
|
adam@5
|
307 val fetch = FeedFfi.fetch
|
adam@5
|
308
|
kkallio@10
|
309 fun app' [internal ::: Type] [data ::: Type] [acc ::: Type] (p : pattern internal data) (f : data -> acc -> transaction acc)
|
kkallio@10
|
310 (doc : document) (acc : acc) : transaction acc =
|
adam@1
|
311 let
|
kkallio@10
|
312 fun recur xml acc state =
|
adam@4
|
313 case String.seek xml #"<" of
|
kkallio@10
|
314 None => return acc
|
adam@4
|
315 | Some xml =>
|
adam@1
|
316 if xml <> "" && String.sub xml 0 = #"/" then
|
adam@4
|
317 case String.seek xml #"\x3E" of
|
kkallio@10
|
318 None => return acc
|
adam@4
|
319 | Some xml =>
|
adam@1
|
320 case p.ExitTag state of
|
kkallio@10
|
321 None => recur xml acc p.Initial
|
adam@1
|
322 | Some state =>
|
adam@1
|
323 case p.Finished state of
|
kkallio@10
|
324 None => recur xml acc state
|
adam@4
|
325 | Some (data, cont) =>
|
kkallio@10
|
326 acc <- f data acc;
|
kkallio@10
|
327 recur xml acc (if cont then state else p.Initial)
|
adam@1
|
328 else if xml <> "" && String.sub xml 0 = #"?" then
|
adam@4
|
329 case String.seek xml #"\x3E" of
|
kkallio@10
|
330 None => return acc
|
kkallio@10
|
331 | Some xml => recur xml acc state
|
adam@1
|
332 else if xml <> "" && String.sub xml 0 = #"!" then
|
adam@2
|
333 if String.lengthGe xml 3 && String.sub xml 1 = #"-" && String.sub xml 2 = #"-" then
|
adam@1
|
334 let
|
adam@1
|
335 fun skipper xml =
|
adam@4
|
336 case String.seek xml #"-" of
|
adam@1
|
337 None => xml
|
adam@4
|
338 | Some xml =>
|
adam@2
|
339 if String.lengthGe xml 2 && String.sub xml 0 = #"-" && String.sub xml 1 = #"\x3E" then
|
adam@1
|
340 String.suffix xml 2
|
adam@1
|
341 else
|
adam@1
|
342 skipper xml
|
adam@1
|
343 in
|
kkallio@10
|
344 recur (skipper (String.suffix xml 3)) acc state
|
adam@1
|
345 end
|
adam@1
|
346 else
|
adam@4
|
347 case String.seek xml #"]" of
|
kkallio@10
|
348 None => return acc
|
adam@4
|
349 | Some xml =>
|
adam@4
|
350 case String.seek xml #"\x3E" of
|
kkallio@10
|
351 None => return acc
|
kkallio@10
|
352 | Some xml => recur xml acc state
|
adam@1
|
353 else
|
adam@1
|
354 case String.msplit {Needle = " >/", Haystack = xml} of
|
kkallio@10
|
355 None => return acc
|
adam@1
|
356 | Some (tagName, ch, xml) =>
|
adam@1
|
357 let
|
adam@1
|
358 fun readAttrs ch xml acc =
|
adam@1
|
359 case ch of
|
adam@1
|
360 #"\x3E" => (xml, acc, False)
|
adam@1
|
361 | #"/" =>
|
adam@4
|
362 (case String.seek xml #"\x3E" of
|
adam@1
|
363 None => (xml, acc, True)
|
adam@4
|
364 | Some xml => (xml, acc, True))
|
adam@1
|
365 | _ =>
|
adam@2
|
366 if String.lengthGe xml 2 && Char.isSpace (String.sub xml 0) then
|
adam@1
|
367 readAttrs (String.sub xml 0) (String.suffix xml 1) acc
|
adam@1
|
368 else if xml <> "" && String.sub xml 0 = #"\x3E" then
|
adam@1
|
369 (String.suffix xml 1, acc, False)
|
adam@1
|
370 else if xml <> "" && String.sub xml 0 = #"/" then
|
adam@4
|
371 (case String.seek xml #"\x3E" of
|
adam@1
|
372 None => (xml, acc, True)
|
adam@4
|
373 | Some xml => (xml, acc, True))
|
adam@1
|
374 else
|
adam@1
|
375 case String.split xml #"=" of
|
adam@1
|
376 None => (xml, acc, False)
|
adam@1
|
377 | Some (aname, xml) =>
|
adam@6
|
378 if xml = "" || (String.sub xml 0 <> #"\"" && String.sub xml 0 <> #"'") then
|
adam@1
|
379 (xml, (aname, "") :: acc, False)
|
adam@1
|
380 else
|
adam@6
|
381 case String.split (String.suffix xml 1) (String.sub xml 0) of
|
adam@1
|
382 None => (xml, (aname, "") :: acc, False)
|
adam@1
|
383 | Some (value, xml) =>
|
adam@1
|
384 if xml = "" then
|
adam@1
|
385 (xml, (aname, value) :: acc, False)
|
adam@1
|
386 else
|
adam@1
|
387 readAttrs (String.sub xml 0) (String.suffix xml 1) ((aname, value) :: acc)
|
adam@1
|
388
|
adam@1
|
389 val (xml, attrs, ended) = readAttrs ch xml []
|
adam@1
|
390
|
adam@1
|
391 fun skipSpaces xml =
|
adam@1
|
392 if xml <> "" && Char.isSpace (String.sub xml 0) then
|
adam@1
|
393 skipSpaces (String.suffix xml 1)
|
adam@1
|
394 else
|
adam@1
|
395 xml
|
adam@1
|
396
|
adam@1
|
397 val xml = skipSpaces xml
|
adam@1
|
398
|
adam@1
|
399 val (xml, cdata) =
|
adam@1
|
400 if ended then
|
adam@1
|
401 (xml, None)
|
adam@1
|
402 else if String.isPrefix {Prefix = "<![CDATA[", Full = xml} then
|
adam@1
|
403 let
|
adam@1
|
404 fun skipper xml acc =
|
adam@1
|
405 case String.split xml #"]" of
|
adam@1
|
406 None => (acc ^ xml, None)
|
adam@1
|
407 | Some (pre, xml) =>
|
adam@2
|
408 if String.lengthGe xml 2 && String.sub xml 0 = #"]" && String.sub xml 1 = #"\x3E" then
|
adam@1
|
409 (String.suffix xml 2, Some (acc ^ pre))
|
adam@1
|
410 else
|
adam@1
|
411 skipper xml (acc ^ "]" ^ pre)
|
adam@1
|
412 in
|
adam@1
|
413 skipper (String.suffix xml 9) ""
|
adam@1
|
414 end
|
adam@1
|
415 else
|
adam@4
|
416 case String.split' xml #"<" of
|
adam@1
|
417 None => (xml, None)
|
adam@4
|
418 | Some (cdata, xml) => (xml, Some cdata)
|
adam@1
|
419 in
|
adam@1
|
420 case p.EnterTag {Tag = tagName, Attrs = attrs, Cdata = cdata} state of
|
kkallio@10
|
421 None => recur xml acc p.Initial
|
adam@1
|
422 | Some state =>
|
adam@4
|
423 case p.Finished state of
|
adam@4
|
424 None =>
|
adam@4
|
425 (case (if ended then p.ExitTag state else Some state) of
|
kkallio@10
|
426 None => recur xml acc p.Initial
|
adam@4
|
427 | Some state =>
|
adam@4
|
428 case p.Finished state of
|
kkallio@10
|
429 None => recur xml acc state
|
adam@4
|
430 | Some (data, cont) =>
|
kkallio@10
|
431 acc <- f data acc;
|
kkallio@10
|
432 recur xml acc (if cont then state else p.Initial))
|
adam@4
|
433 | Some (data, cont) =>
|
kkallio@10
|
434 acc <- f data acc;
|
kkallio@13
|
435 state <- return (if ended then
|
kkallio@13
|
436 Option.get p.Initial (p.ExitTag state)
|
kkallio@13
|
437 else
|
kkallio@13
|
438 state);
|
kkallio@13
|
439
|
kkallio@10
|
440 recur xml acc (if cont then state else p.Initial)
|
adam@1
|
441 end
|
adam@1
|
442 in
|
kkallio@10
|
443 recur doc acc p.Initial
|
adam@1
|
444 end
|
kkallio@10
|
445
|
kkallio@10
|
446 fun app [internal ::: Type] [data ::: Type] (p : pattern internal data) (f : data -> transaction {}) (doc : document) : transaction {} =
|
kkallio@10
|
447 app' p (fn data acc => f data) doc ()
|