src/qs.bend
emerging-ezhttp@0.21.0.0 · 439 lines · 56 definitions
1
2
3
4
5
6
7
8import Base
9import emerging-ezhttp@0.21.0.0/src/pct.bend as Pct
10import emerging-ezhttp@0.21.0.0/src/http.bend as Http
11
12
13
14def Kv() -> Data:
15 Sigma<&2, &2, String, _ => String>
16
17
18
19
20
21type Tok is Data:
22 B{byte: U32}
23 W{c: Char}
24
25
26
27type Esc is Data:
28 Plain{}
29 Pct{}
30 Pct1{d: Char}
31
32
33def tok.hex(c: Char) -> Bool:
34 U32.is_lt(Http.hex.val(c), 16)
35
36
37def tok.byte(plus: Bool, +c: Char) -> U32:
38 Bool.pick(U32, Bool.and(plus, Char.is_eq(c, '+')), 32, Char.to_u32(c))
39
40
41def tok.ascii(plus: Bool, +c: Char, k: Esc -> List<&2, Tok>, pct: Bool)
42 -> List<&2, Tok>:
43 match pct:
44 case True{}:
45 k(Pct{})
46 case False{}:
47 B{tok.byte(plus, c)} <> k(Plain{})
48
49
50def tok.wide(plus: Bool, +c: Char, k: Esc -> List<&2, Tok>, wide: Bool)
51 -> List<&2, Tok>:
52 match wide:
53 case True{}:
54 W{c} <> k(Plain{})
55 case False{}:
56 tok.ascii(plus, c, k, Char.is_eq(c, '%'))
57
58
59def tok.plain(plus: Bool, +c: Char, k: Esc -> List<&2, Tok>) -> List<&2, Tok>:
60 tok.wide(plus, c, k, U32.is_ge(Char.to_u32(c), 128))
61
62
63
64def tok.pct(plus: Bool, +c: Char, k: Esc -> List<&2, Tok>, hex: Bool)
65 -> List<&2, Tok>:
66 match hex:
67 case True{}:
68 k(Pct1{c})
69 case False{}:
70 B{37} <> tok.plain(plus, c, k)
71
72
73
74def tok.pct1(plus: Bool, +d: Char, +c: Char, k: Esc -> List<&2, Tok>,
75 hex: Bool) -> List<&2, Tok>:
76 match hex:
77 case True{}:
78 B{U32.add(U32.mul(Http.hex.val(d), 16), Http.hex.val(c))} <> k(Plain{})
79 case False{}:
80 B{37} <> (B{Char.to_u32(d)} <> tok.plain(plus, c, k))
81
82
83def tok.step(plus: Bool, st: Esc, +c: Char, k: Esc -> List<&2, Tok>)
84 -> List<&2, Tok>:
85 match st:
86 case Plain{}:
87 tok.plain(plus, c, k)
88 case Pct{}:
89 tok.pct(plus, c, k, tok.hex(c))
90 case Pct1{d}:
91 tok.pct1(plus, d, c, k, tok.hex(c))
92
93
94def tok.end(st: Esc) -> List<&2, Tok>:
95 match st:
96 case Plain{}:
97 []
98 case Pct{}:
99 [B{37}]
100 case Pct1{d}:
101 [B{37}, B{Char.to_u32(d)}]
102
103
104def toks(s: String, +plus: Bool, st: Esc) -> List<&2, Tok>:
105 match s:
106 case SNil{}:
107 tok.end(st)
108 case SCon{+c, t}:
109 tok.step(plus, st, c, st2 => toks(t, plus, st2))
110
111
112
113
114
115
116type Utf is Data:
117 Idle{}
118 Need{cp: U32, left: Nat, lo: U32, hi: U32}
119
120
121
122type Lead is Data:
123 Ascii{}
124 Bad{}
125 Opens{cp: U32, left: Nat, lo: U32, hi: U32}
126
127
128def lead.f4(f4: Bool) -> Lead:
129 match f4:
130 case True{}:
131 Opens{4, 2n, 128, 143}
132 case False{}:
133 Bad{}
134
135
136def lead.f1(+b: U32, lt: Bool) -> Lead:
137 match lt:
138 case True{}:
139 Opens{U32.and(b, 7), 2n, 128, 191}
140 case False{}:
141 lead.f4(U32.is_eq(b, 244))
142
143
144def lead.f0(+b: U32, f0: Bool) -> Lead:
145 match f0:
146 case True{}:
147 Opens{0, 2n, 144, 191}
148 case False{}:
149 lead.f1(b, U32.is_lt(b, 244))
150
151
152def lead.ee(+b: U32, lt: Bool) -> Lead:
153 match lt:
154 case True{}:
155 Opens{U32.and(b, 15), 1n, 128, 191}
156 case False{}:
157 lead.f0(b, U32.is_eq(b, 240))
158
159
160def lead.ed(+b: U32, ed: Bool) -> Lead:
161 match ed:
162 case True{}:
163 Opens{13, 1n, 128, 159}
164 case False{}:
165 lead.ee(b, U32.is_lt(b, 240))
166
167
168def lead.e1(+b: U32, lt: Bool) -> Lead:
169 match lt:
170 case True{}:
171 Opens{U32.and(b, 15), 1n, 128, 191}
172 case False{}:
173 lead.ed(b, U32.is_eq(b, 237))
174
175
176def lead.e0(+b: U32, e0: Bool) -> Lead:
177 match e0:
178 case True{}:
179 Opens{0, 1n, 160, 191}
180 case False{}:
181 lead.e1(b, U32.is_lt(b, 237))
182
183
184def lead.two(+b: U32, lt: Bool) -> Lead:
185 match lt:
186 case True{}:
187 Opens{U32.and(b, 31), 0n, 128, 191}
188 case False{}:
189 lead.e0(b, U32.is_eq(b, 224))
190
191
192def lead.low(+b: U32, lt: Bool) -> Lead:
193 match lt:
194 case True{}:
195 Bad{}
196 case False{}:
197 lead.two(b, U32.is_lt(b, 224))
198
199
200def lead.ascii(+b: U32, lt: Bool) -> Lead:
201 match lt:
202 case True{}:
203 Ascii{}
204 case False{}:
205 lead.low(b, U32.is_lt(b, 194))
206
207
208def lead(+b: U32) -> Lead:
209 lead.ascii(b, U32.is_lt(b, 128))
210
211
212def fffd() -> Char:
213 Chr{65533}
214
215
216def utf.add(+cp: U32, +b: U32) -> U32:
217 U32.or(U32.shln(cp, 6n), U32.and(b, 63))
218
219
220def utf.lead(+b: U32, k: Utf -> String, l: Lead) -> String:
221 match l:
222 case Ascii{}:
223 SCon{Chr{b}, k(Idle{})}
224 case Bad{}:
225 SCon{fffd(), k(Idle{})}
226 case Opens{cp, left, lo, hi}:
227 k(Need{cp, left, lo, hi})
228
229
230def utf.idle(t: Tok, k: Utf -> String) -> String:
231 match t:
232 case W{c}:
233 SCon{c, k(Idle{})}
234 case B{+b}:
235 utf.lead(b, k, lead(b))
236
237
238def utf.more(+cp: U32, left: Nat, +b: U32, k: Utf -> String) -> String:
239 match left:
240 case 0n:
241 SCon{Chr{utf.add(cp, b)}, k(Idle{})}
242 case 1n+l:
243 k(Need{utf.add(cp, b), l, 128, 191})
244
245
246
247def utf.byte(+cp: U32, left: Nat, +b: U32, k: Utf -> String, fits: Bool)
248 -> String:
249 match fits:
250 case True{}:
251 utf.more(cp, left, b, k)
252 case False{}:
253 SCon{fffd(), utf.lead(b, k, lead(b))}
254
255
256def utf.need(+cp: U32, left: Nat, +lo: U32, +hi: U32, t: Tok,
257 k: Utf -> String) -> String:
258 match t:
259 case W{c}:
260 SCon{fffd(), SCon{c, k(Idle{})}}
261 case B{+b}:
262 utf.byte(cp, left, b, k, Bool.and(U32.is_le(lo, b), U32.is_le(b, hi)))
263
264
265def utf.step(st: Utf, t: Tok, k: Utf -> String) -> String:
266 match st:
267 case Idle{}:
268 utf.idle(t, k)
269 case Need{cp, left, lo, hi}:
270 utf.need(cp, left, lo, hi, t, k)
271
272
273def utf.end(st: Utf) -> String:
274 match st:
275 case Idle{}:
276 ""
277 case Need{cp, left, lo, hi}:
278 SCon{fffd(), ""}
279
280
281def utf.dec(ts: List<&2, Tok>, st: Utf) -> String:
282 match ts:
283 case []:
284 utf.end(st)
285 case t <> rest:
286 utf.step(st, t, st2 => utf.dec(rest, st2))
287
288
289
290
291def unquote(s: String) -> String:
292 utf.dec(toks(s, False{}, Plain{}), Idle{})
293
294
295def unquote.plus(s: String) -> String:
296 utf.dec(toks(s, True{}, Plain{}), Idle{})
297
298
299
300
301def plus.one(+cp: U32, rest: String, space: Bool) -> String:
302 match space:
303 case True{}:
304 SCon{'+', rest}
305 case False{}:
306 Pct.one(False{}, cp, rest)
307
308
309def quote.plus(s: String) -> String:
310 match s:
311 case SNil{}:
312 SNil{}
313 case SCon{Chr{+cp}, tail}:
314 plus.one(cp, quote.plus(tail), U32.is_eq(cp, 32))
315
316
317def form.pair(name: String, value: String) -> String:
318 quote.plus(name) ++ "=" ++ quote.plus(value)
319
320
321def form.more(ps: List<&2, Kv()>) -> String:
322 match ps:
323 case []:
324 ""
325 case (name, value) <> t:
326 "&" ++ form.pair(name, value) ++ form.more(t)
327
328
329def urlencode(ps: List<&2, Kv()>) -> String:
330 match ps:
331 case []:
332 ""
333 case (name, value) <> t:
334 form.pair(name, value) ++ form.more(t)
335
336
337
338
339def cut.push(+c: Char, m: Maybe<&2, Kv()>) -> Maybe<&2, Kv()>:
340 match m:
341 case None{}:
342 None{}
343 case Some{(before, after)}:
344 Some{(SCon{c, before}, after)}
345
346
347def cut.at(+c: Char, t: String, eq: Bool, rest: Unit -> Maybe<&2, Kv()>)
348 -> Maybe<&2, Kv()>:
349 match eq:
350 case True{}:
351 Some{("", t)}
352 case False{}:
353 cut.push(c, rest(Unit{}))
354
355
356
357def cut(s: String) -> Maybe<&2, Kv()>:
358 match s:
359 case SNil{}:
360 None{}
361 case SCon{+c, +t}:
362 cut.at(c, t, Char.is_eq(c, '='), _u => cut(t))
363
364
365
366def field.of(m: Maybe<&2, Kv()>, whole: String) -> Kv():
367 match m:
368 case None{}:
369 (unquote.plus(whole), "")
370 case Some{(name, value)}:
371 (unquote.plus(name), unquote.plus(value))
372
373
374def field(+piece: String) -> Kv():
375 field.of(cut(piece), piece)
376
377
378def fields.put(+piece: String, rest: List<&2, Kv()>, empty: Bool)
379 -> List<&2, Kv()>:
380 match empty:
381 case True{}:
382 rest
383 case False{}:
384 field(piece) <> rest
385
386
387def fields(ps: List<&2, String>) -> List<&2, Kv()>:
388 match ps:
389 case []:
390 []
391 case +p <> t:
392 fields.put(p, fields(t), String.is_empty(p))
393
394
395
396def parse(text: String) -> List<&2, Kv()>:
397 fields(String.split(text, '&'))
398
399
400def get.pick(later: Maybe<&2, String>, value: String, hit: Bool)
401 -> Maybe<&2, String>:
402 match later hit:
403 case Some{x} _:
404 Some{x}
405 case None{} True{}:
406 Some{value}
407 case None{} False{}:
408 None{}
409
410
411def get(ps: List<&2, Kv()>, +name: String) -> Maybe<&2, String>:
412 match ps:
413 case []:
414 None{}
415 case (key, value) <> t:
416 get.pick(get(t, name), value, String.eq(key, name))
417
418
419
420
421def octets.toks(s: String) -> List<&2, Tok>:
422 match s:
423 case SNil{}:
424 []
425 case SCon{c, t}:
426 B{Char.to_u32(c)} <> octets.toks(t)
427
428
429
430def octets.text(o: String) -> String:
431 utf.dec(octets.toks(o), Idle{})
432
433
434def octets.headers(hs: List<&2, Http.Header>) -> List<&2, Http.Header>:
435 match hs:
436 case []:
437 []
438 case Http.H{name, value} <> t:
439 Http.H{octets.text(name), octets.text(value)} <> octets.headers(t)