iso8601.bend

bend-time-lib@0.4.0.1 · 792 lines · 110 definitions

1import Base
2import bend-scanner@0.1.0.0/parse.bend as P
3import bend-time-lib@0.4.0.1/duration.bend as Dur
4import bend-time-lib@0.4.0.1/period.bend as Per
5type Iso8601.Error is Data:
6  Unexpected{offset: Nat, char: Char}
7  Eof{offset: Nat}
8  Overflow{offset: Nat}
9  Extra{offset: Nat, char: Char}
10  Invalid{}
11type Iso8601.DurS is Data:
12  DurS{
13    days: U32, h: U32, mi: U32, s: U32, nanos: U32,
14    saw_d: Bool, saw_t: Bool, saw_h: Bool, saw_m: Bool, saw_s: Bool
15  }
16type Iso8601.DurM is Data:
17  DSeek{}
18  DNum{acc: U32}
19  DFrac{sec: U32, acc: U32, left: Nat}
20type Iso8601.DurC is Data:
21  DurFail{e: Iso8601.Error}
22  DurCont{st: Iso8601.DurS, mode: Iso8601.DurM}
23type Iso8601.PerS is Data:
24  PerS{
25    years: U32, months: U32, days: U32,
26    saw_y: Bool, saw_m: Bool, saw_w: Bool, saw_d: Bool
27  }
28type Iso8601.PerM is Data:
29  PSeek{}
30  PNum{acc: U32}
31type Iso8601.PerC is Data:
32  PerFail{e: Iso8601.Error}
33  PerCont{st: Iso8601.PerS, mode: Iso8601.PerM}
34def Iso8601.from_parse(e: P.Parse.Error) -> Iso8601.Error:
35  match e:
36    case P.Unexpected{offset, char}:
37      Unexpected{offset, char}
38    case P.Eof{offset}:
39      Eof{offset}
40    case P.Overflow{offset}:
41      Overflow{offset}
42    case P.Extra{offset, char}:
43      Extra{offset, char}
44def Iso8601.digit(n: U32) -> Char:
45  Char.from_u32((Char.to_u32('0') + n : U32))
46def Iso8601.pad_n.go(k: Nat, +n: U32, acc: String) -> String:
47  match k:
48    case 0n:
49      acc
50    case 1n++p:
51      Iso8601.pad_n.go(
52        p, U32.div(n, 10), SCon{Iso8601.digit(U32.mod(n, 10)), acc}
53      )
54def Iso8601.pad_n(n: U32, k: Nat) -> String:
55  Iso8601.pad_n.go(k, n, SNil{})
56def Iso8601.lead0(s: String) -> Bool:
57  match s:
58    case SNil{}:
59      False{}
60    case SCon{h, _}:
61      Char.is_eq(h, '0')
62def Iso8601.strip0(k: Nat, s: String, z: Bool) -> String:
63  match k:
64    case 0n:
65      SCon{'0', SNil{}}
66    case 1n++p:
67      match s:
68        case SNil{}:
69          SCon{'0', SNil{}}
70        case SCon{+h, +t}:
71          match z:
72            case False{}:
73              SCon{h, t}
74            case True{}:
75              Iso8601.strip0(p, t, Iso8601.lead0(t))
76def Iso8601.show_u32(+n: U32) -> String:
77  +s = Iso8601.pad_n(n, 10n)
78  Iso8601.strip0(10n, s, Iso8601.lead0(s))
79def Iso8601.show.unit.go(+n: U32, c: Char, z: Bool) -> String:
80  match z:
81    case True{}:
82      SNil{}
83    case False{}:
84      String.append(Iso8601.show_u32(n), SCon{c, SNil{}})
85def Iso8601.show.unit(+n: U32, c: Char) -> String:
86  Iso8601.show.unit.go(n, c, U32.is_eq(n, 0))
87def Iso8601.frac.strip.go(+k: Nat, +n: U32, z: Bool) -> U32 & Nat:
88  match k:
89    case 0n:
90      (n, 1n)
91    case 1n:
92      (n, 1n)
93    case 1n++p:
94      match z:
95        case True{}:
96          Iso8601.frac.strip.go(
97            p, U32.div(n, 10), U32.is_eq(U32.mod(U32.div(n, 10), 10), 0)
98          )
99        case False{}:
100          (n, k)
101def Iso8601.frac.strip(+n: U32, k: Nat) -> U32 & Nat:
102  Iso8601.frac.strip.go(k, n, U32.is_eq(U32.mod(n, 10), 0))
103def Iso8601.show.frac2(nk: U32 & Nat) -> String:
104  (n, k) = nk
105  Iso8601.pad_n(n, k)
106def Iso8601.show.frac(+nanos: U32) -> String:
107  Iso8601.show.frac2(Iso8601.frac.strip(nanos, 9n))
108def Iso8601.show.s2(+s: U32, +nanos: U32, z: Bool) -> String:
109  match z:
110    case True{}:
111      String.append(Iso8601.show_u32(s), "S")
112    case False{}:
113      String.append(
114        Iso8601.show_u32(s),
115        String.append(".", String.append(Iso8601.show.frac(nanos), "S"))
116      )
117def Iso8601.show.s(+s: U32, +nanos: U32, need: Bool) -> String:
118  match need:
119    case False{}:
120      SNil{}
121    case True{}:
122      Iso8601.show.s2(s, nanos, U32.is_eq(nanos, 0))
123def Iso8601.show.t(
124  +h: U32, +mi: U32, +s: U32, +nanos: U32
125) -> String:
126  String.append(
127    Iso8601.show.unit(h, 'H'),
128    String.append(
129      Iso8601.show.unit(mi, 'M'),
130      Iso8601.show.s(s, nanos, Bool.or(U32.is_gt(s, 0), U32.is_gt(nanos, 0)))
131    )
132  )
133def Iso8601.show_duration.go(
134  +days: U32, +h: U32, +mi: U32, +s: U32, +nanos: U32, has_d: Bool, has_t: Bool
135) -> String:
136  match has_d has_t:
137    case False{} False{}:
138      "PT0S"
139    case True{} False{}:
140      String.append("P", String.append(Iso8601.show_u32(days), "D"))
141    case False{} True{}:
142      String.append("PT", Iso8601.show.t(h, mi, s, nanos))
143    case True{} True{}:
144      String.append(
145        "P",
146        String.append(
147          Iso8601.show_u32(days),
148          String.append("DT", Iso8601.show.t(h, mi, s, nanos))
149        )
150      )
151def Iso8601.show_duration.has(
152  +days: U32, +h: U32, +mi: U32, +s: U32, +nanos: U32
153) -> String:
154  Iso8601.show_duration.go(
155    days, h, mi, s, nanos,
156    U32.is_gt(days, 0),
157    Bool.or(
158      U32.is_gt(h, 0),
159      Bool.or(
160        U32.is_gt(mi, 0),
161        Bool.or(U32.is_gt(s, 0), U32.is_gt(nanos, 0))
162      )
163    )
164  )
165def Iso8601.show_duration(d: Dur.Duration) -> String:
166  match d:
167    case Dur.Duration{+days, +sod, +nanos}:
168      Iso8601.show_duration.has(
169        days,
170        U32.div(sod, 3600),
171        U32.div(U32.mod(sod, 3600), 60),
172        U32.mod(sod, 60),
173        nanos
174      )
175def Iso8601.show_period.body(+y: U32, +m: U32, +d: U32) -> String:
176  String.append(
177    Iso8601.show.unit(y, 'Y'),
178    String.append(Iso8601.show.unit(m, 'M'), Iso8601.show.unit(d, 'D'))
179  )
180def Iso8601.show_period.p(body: String) -> String:
181  match body:
182    case SNil{}:
183      "P0D"
184    case SCon{_, _}:
185      String.append("P", body)
186def Iso8601.show_period(p: Per.Period) -> String:
187  match p:
188    case Per.Period{+y, +m, +d}:
189      Iso8601.show_period.p(Iso8601.show_period.body(y, m, d))
190def Iso8601.dur0() -> Iso8601.DurS:
191  DurS{0, 0, 0, 0, 0, False{}, False{}, False{}, False{}, False{}}
192def Iso8601.per0() -> Iso8601.PerS:
193  PerS{0, 0, 0, False{}, False{}, False{}, False{}}
194def Iso8601.or3(+a: Bool, +b: Bool, +c: Bool) -> Bool:
195  Bool.or(a, Bool.or(b, c))
196def Iso8601.or4(+a: Bool, +b: Bool, +c: Bool, +d: Bool) -> Bool:
197  Bool.or(a, Iso8601.or3(b, c, d))
198def Iso8601.expect.map(
199  r: Result<&2, &2, P.Parse.Error, P.Parse.Cur>
200) -> Result<&2, &2, Iso8601.Error, P.Parse.Cur>:
201  match r:
202    case Fail{e}:
203      Fail{Iso8601.from_parse(e)}
204    case Done{cur}:
205      Done{cur}
206def Iso8601.expect(
207  cur: P.Parse.Cur, +c: Char
208) -> Result<&2, &2, Iso8601.Error, P.Parse.Cur>:
209  Iso8601.expect.map(P.Parse.expect_char(cur, c))
210def Iso8601.dur.mk.ns2(
211  r: Result<&2, &2, Dur.Duration.Error, Dur.Duration>
212) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
213  match r:
214    case Fail{_}:
215      Fail{Invalid{}}
216    case Done{d}:
217      Done{d}
218def Iso8601.dur.mk.ns(
219  r: Result<&2, &2, Dur.Duration.Error, Dur.Duration>, nanos: U32
220) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
221  match r:
222    case Fail{_}:
223      Fail{Invalid{}}
224    case Done{d}:
225      Iso8601.dur.mk.ns2(Dur.Duration.add(d, Dur.Duration{0, 0, nanos}))
226def Iso8601.dur.mk.s(
227  r: Result<&2, &2, Dur.Duration.Error, Dur.Duration>, +s: U32, nanos: U32
228) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
229  match r:
230    case Fail{_}:
231      Fail{Invalid{}}
232    case Done{d}:
233      Iso8601.dur.mk.ns(Dur.Duration.add(d, Dur.Duration.from_secs(s)), nanos)
234def Iso8601.dur.mk.mi2(
235  d: Dur.Duration, rm: Result<&2, &2, Dur.Duration.Error, Dur.Duration>,
236  s: U32, nanos: U32
237) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
238  match rm:
239    case Fail{_}:
240      Fail{Invalid{}}
241    case Done{dm}:
242      Iso8601.dur.mk.s(Dur.Duration.add(d, dm), s, nanos)
243def Iso8601.dur.mk.mi(
244  r: Result<&2, &2, Dur.Duration.Error, Dur.Duration>, +mi: U32, s: U32, nanos: U32
245) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
246  match r:
247    case Fail{_}:
248      Fail{Invalid{}}
249    case Done{d}:
250      Iso8601.dur.mk.mi2(d, Dur.Duration.from_mins(mi), s, nanos)
251def Iso8601.dur.mk.h2(
252  d: Dur.Duration, rh: Result<&2, &2, Dur.Duration.Error, Dur.Duration>,
253  mi: U32, s: U32, nanos: U32
254) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
255  match rh:
256    case Fail{_}:
257      Fail{Invalid{}}
258    case Done{dh}:
259      Iso8601.dur.mk.mi(Dur.Duration.add(d, dh), mi, s, nanos)
260def Iso8601.dur.mk.h(
261  r: Result<&2, &2, Dur.Duration.Error, Dur.Duration>, +h: U32, mi: U32, s: U32, nanos: U32
262) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
263  match r:
264    case Fail{_}:
265      Fail{Invalid{}}
266    case Done{d}:
267      Iso8601.dur.mk.h2(d, Dur.Duration.from_hours(h), mi, s, nanos)
268def Iso8601.dur.mk(st: Iso8601.DurS) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
269  match st:
270    case DurS{+days, +h, +mi, +s, +nanos, _, _, _, _, _}:
271      Iso8601.dur.mk.h(
272        Done{Dur.Duration.from_days(days)}, h, mi, s, nanos
273      )
274def Iso8601.dur.valid3(
275  st: Iso8601.DurS, has: Bool, tok: Bool
276) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
277  match has:
278    case False{}:
279      Fail{Invalid{}}
280    case True{}:
281      match tok:
282        case False{}:
283          Fail{Invalid{}}
284        case True{}:
285          Iso8601.dur.mk(st)
286def Iso8601.dur.valid(+st: Iso8601.DurS) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
287  match st:
288    case DurS{_, _, _, _, _, +saw_d, +saw_t, +saw_h, +saw_m, +saw_s}:
289      Iso8601.dur.valid3(
290        st,
291        Iso8601.or4(saw_d, saw_h, saw_m, saw_s),
292        Bool.or(Bool.not(saw_t), Iso8601.or3(saw_h, saw_m, saw_s))
293      )
294def Iso8601.dur.eof(
295  st: Iso8601.DurS, mode: Iso8601.DurM
296) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
297  match mode:
298    case DSeek{}:
299      Iso8601.dur.valid(st)
300    case DNum{_}:
301      Fail{Invalid{}}
302    case DFrac{_, _, _}:
303      Fail{Invalid{}}
304def Iso8601.dur.put_t2(
305  days: U32, h: U32, mi: U32, s: U32, nanos: U32,
306  saw_d: Bool, saw_h: Bool, saw_m: Bool, saw_s: Bool, dup: Bool
307) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
308  match dup:
309    case True{}:
310      Fail{Invalid{}}
311    case False{}:
312      Done{DurS{days, h, mi, s, nanos, saw_d, True{}, saw_h, saw_m, saw_s}}
313def Iso8601.dur.put_t(st: Iso8601.DurS) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
314  match st:
315    case DurS{days, h, mi, s, nanos, saw_d, +saw_t, saw_h, saw_m, saw_s}:
316      Iso8601.dur.put_t2(
317        days, h, mi, s, nanos, saw_d, saw_h, saw_m, saw_s, saw_t
318      )
319def Iso8601.dur.put_d2(
320  n: U32, h: U32, mi: U32, s: U32, nanos: U32,
321  saw_h: Bool, saw_m: Bool, saw_s: Bool, bad: Bool
322) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
323  match bad:
324    case True{}:
325      Fail{Invalid{}}
326    case False{}:
327      Done{DurS{n, h, mi, s, nanos, True{}, False{}, saw_h, saw_m, saw_s}}
328def Iso8601.dur.put_d(
329  st: Iso8601.DurS, n: U32
330) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
331  match st:
332    case DurS{_, h, mi, s, nanos, +saw_d, +saw_t, saw_h, saw_m, saw_s}:
333      Iso8601.dur.put_d2(
334        n, h, mi, s, nanos, saw_h, saw_m, saw_s, Bool.or(saw_d, saw_t)
335      )
336def Iso8601.dur.put_h2(
337  days: U32, n: U32, mi: U32, s: U32, nanos: U32,
338  saw_d: Bool, saw_m: Bool, saw_s: Bool, bad: Bool
339) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
340  match bad:
341    case True{}:
342      Fail{Invalid{}}
343    case False{}:
344      Done{DurS{days, n, mi, s, nanos, saw_d, True{}, True{}, saw_m, saw_s}}
345def Iso8601.dur.put_h(
346  st: Iso8601.DurS, n: U32
347) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
348  match st:
349    case DurS{days, _, mi, s, nanos, saw_d, +saw_t, +saw_h, +saw_m, +saw_s}:
350      Iso8601.dur.put_h2(
351        days, n, mi, s, nanos, saw_d, saw_m, saw_s,
352        Bool.or(Bool.not(saw_t), Iso8601.or3(saw_h, saw_m, saw_s))
353      )
354def Iso8601.dur.put_m2(
355  days: U32, h: U32, n: U32, s: U32, nanos: U32,
356  saw_d: Bool, saw_h: Bool, saw_s: Bool, bad: Bool
357) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
358  match bad:
359    case True{}:
360      Fail{Invalid{}}
361    case False{}:
362      Done{DurS{days, h, n, s, nanos, saw_d, True{}, saw_h, True{}, saw_s}}
363def Iso8601.dur.put_m(
364  st: Iso8601.DurS, n: U32
365) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
366  match st:
367    case DurS{days, h, _, s, nanos, saw_d, +saw_t, saw_h, +saw_m, +saw_s}:
368      Iso8601.dur.put_m2(
369        days, h, n, s, nanos, saw_d, saw_h, saw_s,
370        Bool.or(Bool.not(saw_t), Bool.or(saw_m, saw_s))
371      )
372def Iso8601.dur.put_s2(
373  days: U32, h: U32, mi: U32, n: U32, nanos: U32,
374  saw_d: Bool, saw_h: Bool, saw_m: Bool, bad: Bool
375) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
376  match bad:
377    case True{}:
378      Fail{Invalid{}}
379    case False{}:
380      Done{DurS{days, h, mi, n, nanos, saw_d, True{}, saw_h, saw_m, True{}}}
381def Iso8601.dur.put_s(
382  st: Iso8601.DurS, n: U32, nanos: U32
383) -> Result<&2, &2, Iso8601.Error, Iso8601.DurS>:
384  match st:
385    case DurS{days, h, mi, _, _, saw_d, +saw_t, saw_h, saw_m, +saw_s}:
386      Iso8601.dur.put_s2(
387        days, h, mi, n, nanos, saw_d, saw_h, saw_m,
388        Bool.or(Bool.not(saw_t), saw_s)
389      )
390def Iso8601.dur.frac.scale(+acc: U32, got: Nat) -> U32:
391  U32.mul(acc, U32.pow(10, Nat.sub(9n, got)))
392def Iso8601.dur.cont_r(
393  r: Result<&2, &2, Iso8601.Error, Iso8601.DurS>, mode: Iso8601.DurM
394) -> Iso8601.DurC:
395  match r:
396    case Fail{e}:
397      DurFail{e}
398    case Done{st}:
399      DurCont{st, mode}
400def Iso8601.dur.ndig2(
401  off: Nat, st: Iso8601.DurS, +acc: U32, +h: Char, s: P.Parse.St
402) -> Iso8601.DurC:
403  match s:
404    case P.StOv{}:
405      DurFail{Overflow{off}}
406    case P.StBad{_}:
407      DurFail{Invalid{}}
408    case P.StOk{}:
409      DurCont{st, DNum{P.Parse.nacc(acc, h)}}
410def Iso8601.dur.ndig(
411  off: Nat, st: Iso8601.DurS, +acc: U32, +h: Char
412) -> Iso8601.DurC:
413  Iso8601.dur.ndig2(off, st, acc, h, P.Parse.st.of(acc, h, U32.not(0)))
414def Iso8601.dur.unit_dot(
415  st: Iso8601.DurS, n: U32, is_dot: Bool
416) -> Iso8601.DurC:
417  match is_dot:
418    case True{}:
419      DurCont{st, DFrac{n, 0, 9n}}
420    case False{}:
421      DurFail{Invalid{}}
422def Iso8601.dur.unit_s(
423  st: Iso8601.DurS, n: U32, +c: Char, is_s: Bool
424) -> Iso8601.DurC:
425  match is_s:
426    case True{}:
427      Iso8601.dur.cont_r(Iso8601.dur.put_s(st, n, 0), DSeek{})
428    case False{}:
429      Iso8601.dur.unit_dot(st, n, Char.is_eq(c, '.'))
430def Iso8601.dur.unit_m(
431  st: Iso8601.DurS, n: U32, +c: Char, is_m: Bool
432) -> Iso8601.DurC:
433  match is_m:
434    case True{}:
435      Iso8601.dur.cont_r(Iso8601.dur.put_m(st, n), DSeek{})
436    case False{}:
437      Iso8601.dur.unit_s(st, n, c, Char.is_eq(c, 'S'))
438def Iso8601.dur.unit_h(
439  st: Iso8601.DurS, n: U32, +c: Char, is_h: Bool
440) -> Iso8601.DurC:
441  match is_h:
442    case True{}:
443      Iso8601.dur.cont_r(Iso8601.dur.put_h(st, n), DSeek{})
444    case False{}:
445      Iso8601.dur.unit_m(st, n, c, Char.is_eq(c, 'M'))
446def Iso8601.dur.unit(
447  st: Iso8601.DurS, n: U32, +c: Char
448) -> Iso8601.DurC:
449  Iso8601.dur.unit_h(st, n, c, Char.is_eq(c, 'H'))
450def Iso8601.dur.unit_d(
451  st: Iso8601.DurS, n: U32, +c: Char, is_d: Bool
452) -> Iso8601.DurC:
453  match is_d:
454    case True{}:
455      Iso8601.dur.cont_r(Iso8601.dur.put_d(st, n), DSeek{})
456    case False{}:
457      Iso8601.dur.unit(st, n, c)
458def Iso8601.dur.num(
459  off: Nat, st: Iso8601.DurS, acc: U32, +h: Char, is_dig: Bool
460) -> Iso8601.DurC:
461  match is_dig:
462    case True{}:
463      Iso8601.dur.ndig(off, st, acc, h)
464    case False{}:
465      Iso8601.dur.unit_d(st, acc, h, Char.is_eq(h, 'D'))
466def Iso8601.dur.frac.s2(
467  st: Iso8601.DurS, sec: U32, acc: U32, left: Nat, empty: Bool
468) -> Iso8601.DurC:
469  match empty:
470    case True{}:
471      DurFail{Invalid{}}
472    case False{}:
473      Iso8601.dur.cont_r(
474        Iso8601.dur.put_s(st, sec, Iso8601.dur.frac.scale(acc, Nat.sub(9n, left))),
475        DSeek{}
476      )
477def Iso8601.dur.frac.s(
478  st: Iso8601.DurS, sec: U32, acc: U32, +left: Nat, is_s: Bool
479) -> Iso8601.DurC:
480  match is_s:
481    case True{}:
482      Iso8601.dur.frac.s2(st, sec, acc, left, Nat.is_eq(left, 9n))
483    case False{}:
484      DurFail{Invalid{}}
485def Iso8601.dur.frac.dig(
486  st: Iso8601.DurS, sec: U32, +acc: U32, left: Nat, +h: Char
487) -> Iso8601.DurC:
488  match left:
489    case 0n:
490      DurFail{Invalid{}}
491    case 1n++p:
492      DurCont{st, DFrac{sec, P.Parse.nacc(acc, h), p}}
493def Iso8601.dur.frac(
494  st: Iso8601.DurS, sec: U32, acc: U32, left: Nat, +h: Char, is_dig: Bool
495) -> Iso8601.DurC:
496  match is_dig:
497    case True{}:
498      Iso8601.dur.frac.dig(st, sec, acc, left, h)
499    case False{}:
500      Iso8601.dur.frac.s(st, sec, acc, left, Char.is_eq(h, 'S'))
501def Iso8601.dur.seek2(
502  off: Nat, st: Iso8601.DurS, +h: Char, is_dig: Bool
503) -> Iso8601.DurC:
504  match is_dig:
505    case True{}:
506      DurCont{st, DNum{P.Parse.nacc(0, h)}}
507    case False{}:
508      DurFail{Extra{off, h}}
509def Iso8601.dur.seek(
510  off: Nat, st: Iso8601.DurS, h: Char, is_t: Bool, is_dig: Bool
511) -> Iso8601.DurC:
512  match is_t:
513    case True{}:
514      Iso8601.dur.cont_r(Iso8601.dur.put_t(st), DSeek{})
515    case False{}:
516      Iso8601.dur.seek2(off, st, h, is_dig)
517def Iso8601.dur.act(
518  off: Nat, st: Iso8601.DurS, mode: Iso8601.DurM, +h: Char, is_t: Bool, is_dig: Bool
519) -> Iso8601.DurC:
520  match mode:
521    case DSeek{}:
522      Iso8601.dur.seek(off, st, h, is_t, is_dig)
523    case DNum{acc}:
524      Iso8601.dur.num(off, st, acc, h, is_dig)
525    case DFrac{sec, acc, left}:
526      Iso8601.dur.frac(st, sec, acc, left, h, is_dig)
527def Iso8601.dur.go(
528  s: String, +off: Nat, c: Iso8601.DurC
529) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
530  match s c:
531    case _ DurFail{e}:
532      Fail{e}
533    case SNil{} DurCont{st, mode}:
534      Iso8601.dur.eof(st, mode)
535    case SCon{+h, t} DurCont{st, mode}:
536      Iso8601.dur.go(
537        t, 1n+off,
538        Iso8601.dur.act(
539          off, st, mode, h, Char.is_eq(h, 'T'), Char.is_digit(h)
540        )
541      )
542def Iso8601.read_duration.p(
543  r: Result<&2, &2, Iso8601.Error, P.Parse.Cur>
544) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
545  match r:
546    case Fail{e}:
547      Fail{e}
548    case Done{P.Cur{s, off}}:
549      Iso8601.dur.go(s, off, DurCont{Iso8601.dur0(), DSeek{}})
550def Iso8601.read_duration(
551  text: String
552) -> Result<&2, &2, Iso8601.Error, Dur.Duration>:
553  Iso8601.read_duration.p(Iso8601.expect(P.Parse.start(text), 'P'))
554def Iso8601.per.mk2(
555  r: Result<&2, &2, Per.Period.Error, Per.Period>
556) -> Result<&2, &2, Iso8601.Error, Per.Period>:
557  match r:
558    case Fail{_}:
559      Fail{Invalid{}}
560    case Done{p}:
561      Done{p}
562def Iso8601.per.valid2(
563  y: U32, m: U32, d: U32, ok: Bool
564) -> Result<&2, &2, Iso8601.Error, Per.Period>:
565  match ok:
566    case False{}:
567      Fail{Invalid{}}
568    case True{}:
569      Iso8601.per.mk2(Per.Period.from(y, m, d))
570def Iso8601.per.valid(st: Iso8601.PerS) -> Result<&2, &2, Iso8601.Error, Per.Period>:
571  match st:
572    case PerS{+y, +m, +d, +saw_y, +saw_m, +saw_w, +saw_d}:
573      Iso8601.per.valid2(y, m, d, Iso8601.or4(saw_y, saw_m, saw_w, saw_d))
574def Iso8601.per.eof(
575  st: Iso8601.PerS, mode: Iso8601.PerM
576) -> Result<&2, &2, Iso8601.Error, Per.Period>:
577  match mode:
578    case PSeek{}:
579      Iso8601.per.valid(st)
580    case PNum{_}:
581      Fail{Invalid{}}
582def Iso8601.add_u32.go(+a: U32, +b: U32, ov: Bool) -> Result<&2, &2, Iso8601.Error, U32>:
583  match ov:
584    case True{}:
585      Fail{Invalid{}}
586    case False{}:
587      Done{U32.add(a, b)}
588def Iso8601.add_u32(+a: U32, +b: U32) -> Result<&2, &2, Iso8601.Error, U32>:
589  Iso8601.add_u32.go(a, b, U32.is_gt(b, U32.sub(U32.not(0), a)))
590def Iso8601.per.weeks2(+n: U32, ov: Bool) -> Result<&2, &2, Iso8601.Error, U32>:
591  match ov:
592    case True{}:
593      Fail{Invalid{}}
594    case False{}:
595      Done{U32.mul(n, 7)}
596def Iso8601.per.weeks(+n: U32) -> Result<&2, &2, Iso8601.Error, U32>:
597  Iso8601.per.weeks2(n, U32.is_gt(n, U32.div(U32.not(0), 7)))
598def Iso8601.per.put_y2(
599  n: U32, m: U32, d: U32, saw_m: Bool, saw_w: Bool, saw_d: Bool, bad: Bool
600) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
601  match bad:
602    case True{}:
603      Fail{Invalid{}}
604    case False{}:
605      Done{PerS{n, m, d, True{}, saw_m, saw_w, saw_d}}
606def Iso8601.per.put_y(
607  st: Iso8601.PerS, n: U32
608) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
609  match st:
610    case PerS{_, m, d, +saw_y, +saw_m, +saw_w, +saw_d}:
611      Iso8601.per.put_y2(
612        n, m, d, saw_m, saw_w, saw_d, Iso8601.or4(saw_y, saw_m, saw_w, saw_d)
613      )
614def Iso8601.per.put_m2(
615  y: U32, n: U32, d: U32, saw_y: Bool, saw_w: Bool, saw_d: Bool, bad: Bool
616) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
617  match bad:
618    case True{}:
619      Fail{Invalid{}}
620    case False{}:
621      Done{PerS{y, n, d, saw_y, True{}, saw_w, saw_d}}
622def Iso8601.per.put_m(
623  st: Iso8601.PerS, n: U32
624) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
625  match st:
626    case PerS{y, _, d, saw_y, +saw_m, +saw_w, +saw_d}:
627      Iso8601.per.put_m2(
628        y, n, d, saw_y, saw_w, saw_d, Iso8601.or3(saw_m, saw_w, saw_d)
629      )
630def Iso8601.per.put_w.go(
631  y: U32, m: U32, saw_y: Bool, saw_m: Bool,
632  rw: Result<&2, &2, Iso8601.Error, U32>
633) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
634  match rw:
635    case Fail{e}:
636      Fail{e}
637    case Done{w}:
638      Done{PerS{y, m, w, saw_y, saw_m, True{}, False{}}}
639def Iso8601.per.put_w2(
640  y: U32, m: U32, saw_y: Bool, saw_m: Bool, n: U32, bad: Bool
641) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
642  match bad:
643    case True{}:
644      Fail{Invalid{}}
645    case False{}:
646      Iso8601.per.put_w.go(y, m, saw_y, saw_m, Iso8601.per.weeks(n))
647def Iso8601.per.put_w(
648  st: Iso8601.PerS, n: U32
649) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
650  match st:
651    case PerS{y, m, _, saw_y, saw_m, +saw_w, +saw_d}:
652      Iso8601.per.put_w2(y, m, saw_y, saw_m, n, Bool.or(saw_w, saw_d))
653def Iso8601.per.put_d.go(
654  y: U32, m: U32, saw_y: Bool, saw_m: Bool, saw_w: Bool,
655  rd: Result<&2, &2, Iso8601.Error, U32>
656) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
657  match rd:
658    case Fail{e}:
659      Fail{e}
660    case Done{days}:
661      Done{PerS{y, m, days, saw_y, saw_m, saw_w, True{}}}
662def Iso8601.per.put_d2(
663  y: U32, m: U32, d: U32, saw_y: Bool, saw_m: Bool, saw_w: Bool, n: U32, bad: Bool
664) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
665  match bad:
666    case True{}:
667      Fail{Invalid{}}
668    case False{}:
669      Iso8601.per.put_d.go(y, m, saw_y, saw_m, saw_w, Iso8601.add_u32(d, n))
670def Iso8601.per.put_d(
671  st: Iso8601.PerS, n: U32
672) -> Result<&2, &2, Iso8601.Error, Iso8601.PerS>:
673  match st:
674    case PerS{y, m, d, saw_y, saw_m, saw_w, +saw_d}:
675      Iso8601.per.put_d2(y, m, d, saw_y, saw_m, saw_w, n, saw_d)
676def Iso8601.per.cont_r(
677  r: Result<&2, &2, Iso8601.Error, Iso8601.PerS>, mode: Iso8601.PerM
678) -> Iso8601.PerC:
679  match r:
680    case Fail{e}:
681      PerFail{e}
682    case Done{st}:
683      PerCont{st, mode}
684def Iso8601.per.ndig2(
685  off: Nat, st: Iso8601.PerS, +acc: U32, +h: Char, s: P.Parse.St
686) -> Iso8601.PerC:
687  match s:
688    case P.StOv{}:
689      PerFail{Overflow{off}}
690    case P.StBad{_}:
691      PerFail{Invalid{}}
692    case P.StOk{}:
693      PerCont{st, PNum{P.Parse.nacc(acc, h)}}
694def Iso8601.per.ndig(
695  off: Nat, st: Iso8601.PerS, +acc: U32, +h: Char
696) -> Iso8601.PerC:
697  Iso8601.per.ndig2(off, st, acc, h, P.Parse.st.of(acc, h, U32.not(0)))
698def Iso8601.per.unit_d(
699  st: Iso8601.PerS, n: U32, is_d: Bool
700) -> Iso8601.PerC:
701  match is_d:
702    case True{}:
703      Iso8601.per.cont_r(Iso8601.per.put_d(st, n), PSeek{})
704    case False{}:
705      PerFail{Invalid{}}
706def Iso8601.per.unit_w(
707  st: Iso8601.PerS, n: U32, +c: Char, is_w: Bool
708) -> Iso8601.PerC:
709  match is_w:
710    case True{}:
711      Iso8601.per.cont_r(Iso8601.per.put_w(st, n), PSeek{})
712    case False{}:
713      Iso8601.per.unit_d(st, n, Char.is_eq(c, 'D'))
714def Iso8601.per.unit_m(
715  st: Iso8601.PerS, n: U32, +c: Char, is_m: Bool
716) -> Iso8601.PerC:
717  match is_m:
718    case True{}:
719      Iso8601.per.cont_r(Iso8601.per.put_m(st, n), PSeek{})
720    case False{}:
721      Iso8601.per.unit_w(st, n, c, Char.is_eq(c, 'W'))
722def Iso8601.per.unit(
723  st: Iso8601.PerS, n: U32, +c: Char
724) -> Iso8601.PerC:
725  Iso8601.per.unit_m(st, n, c, Char.is_eq(c, 'M'))
726def Iso8601.per.unit_y(
727  st: Iso8601.PerS, n: U32, +c: Char, is_y: Bool
728) -> Iso8601.PerC:
729  match is_y:
730    case True{}:
731      Iso8601.per.cont_r(Iso8601.per.put_y(st, n), PSeek{})
732    case False{}:
733      Iso8601.per.unit(st, n, c)
734def Iso8601.per.num(
735  off: Nat, st: Iso8601.PerS, acc: U32, +h: Char, is_dig: Bool
736) -> Iso8601.PerC:
737  match is_dig:
738    case True{}:
739      Iso8601.per.ndig(off, st, acc, h)
740    case False{}:
741      Iso8601.per.unit_y(st, acc, h, Char.is_eq(h, 'Y'))
742def Iso8601.per.seek2(
743  off: Nat, st: Iso8601.PerS, +h: Char, is_dig: Bool
744) -> Iso8601.PerC:
745  match is_dig:
746    case True{}:
747      PerCont{st, PNum{P.Parse.nacc(0, h)}}
748    case False{}:
749      PerFail{Extra{off, h}}
750def Iso8601.per.seek(
751  off: Nat, st: Iso8601.PerS, h: Char, is_t: Bool, is_dig: Bool
752) -> Iso8601.PerC:
753  match is_t:
754    case True{}:
755      PerFail{Invalid{}}
756    case False{}:
757      Iso8601.per.seek2(off, st, h, is_dig)
758def Iso8601.per.act(
759  off: Nat, st: Iso8601.PerS, mode: Iso8601.PerM, +h: Char, is_t: Bool, is_dig: Bool
760) -> Iso8601.PerC:
761  match mode:
762    case PSeek{}:
763      Iso8601.per.seek(off, st, h, is_t, is_dig)
764    case PNum{acc}:
765      Iso8601.per.num(off, st, acc, h, is_dig)
766def Iso8601.per.go(
767  s: String, +off: Nat, c: Iso8601.PerC
768) -> Result<&2, &2, Iso8601.Error, Per.Period>:
769  match s c:
770    case _ PerFail{e}:
771      Fail{e}
772    case SNil{} PerCont{st, mode}:
773      Iso8601.per.eof(st, mode)
774    case SCon{+h, t} PerCont{st, mode}:
775      Iso8601.per.go(
776        t, 1n+off,
777        Iso8601.per.act(
778          off, st, mode, h, Char.is_eq(h, 'T'), Char.is_digit(h)
779        )
780      )
781def Iso8601.read_period.p(
782  r: Result<&2, &2, Iso8601.Error, P.Parse.Cur>
783) -> Result<&2, &2, Iso8601.Error, Per.Period>:
784  match r:
785    case Fail{e}:
786      Fail{e}
787    case Done{P.Cur{s, off}}:
788      Iso8601.per.go(s, off, PerCont{Iso8601.per0(), PSeek{}})
789def Iso8601.read_period(
790  text: String
791) -> Result<&2, &2, Iso8601.Error, Per.Period>:
792  Iso8601.read_period.p(Iso8601.expect(P.Parse.start(text), 'P'))