~/bend-docscommunity

main.bend source

main.bend on the hub · documented module

# eztoml: TOML for Bend 2, a proven parser and renderer for TOML documents.## eztoml reads and writes TOML documents. Values are strings, integers, floats,# booleans, datetimes, arrays, inline tables, tables, and arrays of tables.# Comments and blank lines are passed over.## parse, render, get, at, root, bad, string, digits, flag# str, integer, float, boolean, array, inline, table, pair# key, wfimport Base# the sign of an integer, a float, or a zone offsettype Sign is Data:  Plus{}  Minus{}# a calendar date: year, month, daytype Date is Data:  Date{y: U32, mo: U32, d: U32}# a time of day. frac is the fractional second, empty when there is nonetype Clock is Data:  Clock{h: U32, mi: U32, s: U32, frac: String}# a zone offset: Z, or a numeric offsettype Zone is Data:  Zulu{}  Shift{sign: Sign, h: U32, mi: U32}# the four TOML datetime formstype When is Data:  WOff{date: Date, clock: Clock, zone: Zone}  WLocal{date: Date, clock: Clock}  WDate{date: Date}  WTime{clock: Clock}# one TOML value. Tables remember the dotted path they were opened under.# An inline table is VInl. An array of tables is VAots, and each element is# VAot. VPair is a key in a table. VSpan is a plain string: the first n# characters of a source suffix.type Val is Data:  VStr{text: String}  VSpan{src: String, n: U32}  VInt{sign: Sign, digits: String}  VFlo{sign: Sign, text: String}  VBool{bit: Bool}  VWhen{when: When}  VArr{items: List<&2, Val>}  VInl{rows: List<&2, Val>}  VHead{path: String, rows: List<&2, Val>}  VAot{path: String, rows: List<&2, Val>}  VAots{path: String, elems: List<&2, Val>}  VPair{name: String, val: Val}# a key found in a table, or nottype Hit is Data:  Found{val: Val}  Miss{}# a parsed document: the first error, and the root table in ordertype Doc is Data:  Doc{bad: String, rows: List<&2, Val>}# a value read from a bare word, or the reason it is not onetype Got is Data:  GotVal{val: Val}  GotBad{why: String}# digits taken from the front of a character listtype Dig is Data:  DigOk{n: U32, cs: List<&2, Char>}  DigBad{}# what a datetime scan producedtype WhenOut is Data:  WSome{when: When}  WFail{why: String}  WSkip{}# a decimal numeral being built, least-significant digit firsttype NK is Data:  NLead{}  NSign{}  NZero{}  NDig{}  NBase{}  NFrac{}  NExp{}  NEDig{}  NBad{}# the numeral scantype Num is Data:  Num{nk: NK, sign: Sign, base: U32, ds: List<&2, U32>, us: Bool, saw: Bool,    why: String}# how a basic-string character is writtentype BK is Data:  BKCh{}  BKHex{}  BKEsc{mark: Char}# where the scanner is in a documenttype Lex is Data:  LTop{}  LLine{}  LKey{}  LKeyDot{}  LAfter{}  LKeyQ{}  LKeyE{}  LKeyU{}  LVal{}  LQ{}  LQ2{}  LStr{}  LEsc{}  LUni{}  LFold{}  LMq{}  LMqEnd{}  LTrim{}  LTrim2{}  LBare{}  LDate{}  LCom{}  LHead{}  LHeadGap{}  LHeadDot{}  LHeadQ{}  LHeadE{}  LHeadU{}  LHeadR{}  LArr{}  LArrC{}  LInl{}  LInlK{}  LInlC{}  LSink{}# which quotes a string is usingtype QKind is Data:  QBasic{}  QLit{}  QMulti{}  QMultiLit{}# one character to read againtype Hold is Data:  HNone{}  HChar{c: Char}# an open array or inline tabletype Ctx is Data:  CArr{items: List<&2, Val>, parts: List<&2, String>, here: List<&2, String>}  CInl{rows: List<&2, Val>, parts: List<&2, String>, here: List<&2, String>}# the scannertype St is Data:  St{bad: String, rows: List<&2, Val>, heads: List<&2, String>,    here: List<&2, String>, parts: List<&2, String>, buf: List<&2, Char>,    lex: Lex, back: Lex, qkind: QKind, stack: List<&2, Ctx>, hold: Hold,    uni: U32, left: U32, arrtab: Bool, lead: Bool}# an escape in a basic stringtype Esc is Data:  EChar{c: Char}  EUni{n: U32}  EFold{saw: Bool}  EBad{}# a character a bare key may holddef bare.at(+cc: Char) -> Bool:  Bool.or(Char.is_alpha(cc), Bool.or(Char.is_digit(cc),    Bool.or(Char.is_eq(cc, '_'), Char.is_eq(cc, '-'))))# one step of the bare-key walk, with the head's answer in handdef bare.all.step(here: Bool, rest: Unit -> Bool) -> Bool:  match here:    case True{}:      rest(Unit{})    case False{}:      False{}# whether every character of a key is one a bare key may holddef bare.all(cs: List<&2, Char>) -> Bool:  match cs:    case []:      True{}    case +h <> t:      bare.all.step(bare.at(h), _u => bare.all(t))# whether a key can be written without quotesdef bare(+ss: String) -> Bool:  Bool.and(Bool.not(String.is_empty(ss)), bare.all(String.to_list(ss)))# one hex nibble as a characterdef hex.char.go(dec: Bool, nn: U32) -> Char:  match dec:    case True{}:      Chr{U32.add(48, nn)}    case False{}:      Chr{U32.add(87, nn)}# one hex nibble as a characterdef hex.char(+nn: U32) -> Char:  hex.char.go(U32.is_lt(nn, 10), nn)# four lowercase hex digits of a code point, then the \u introducer, reverseddef basic.hex(+uu: U32, out: List<&2, Char>) -> List<&2, Char>:  hex.char(U32.mod(uu, 16)) <> (hex.char(U32.mod(U32.shrn(uu, 4n), 16)) <>    (hex.char(U32.mod(U32.shrn(uu, 8n), 16)) <>      (hex.char(U32.mod(U32.shrn(uu, 12n), 16)) <> ('u' <> ('\\' <> out)))))# an escape mark reversed onto the output: the mark, then the backslashdef basic.esc(mark: Char, out: List<&2, Char>) -> List<&2, Char>:  mark <> ('\\' <> out)# other controls are \u escapes; the rest are themselvesdef basic.cls.hex(hit: Bool) -> BK:  match hit:    case True{}:      BKHex{}    case False{}:      BKCh{}# backslashdef basic.cls.bslash(hit: Bool, +uu: U32) -> BK:  match hit:    case True{}:      BKEsc{'\\'}    case False{}:      basic.cls.hex(Bool.or(U32.is_lt(uu, 32), U32.is_eq(uu, 127)))# quotation markdef basic.cls.quote(hit: Bool, +uu: U32) -> BK:  match hit:    case True{}:      BKEsc{'"'}    case False{}:      basic.cls.bslash(U32.is_eq(uu, 92), uu)# carriage returndef basic.cls.cr(hit: Bool, +uu: U32) -> BK:  match hit:    case True{}:      BKEsc{'r'}    case False{}:      basic.cls.quote(U32.is_eq(uu, 34), uu)# form feeddef basic.cls.ff(hit: Bool, +uu: U32) -> BK:  match hit:    case True{}:      BKEsc{'f'}    case False{}:      basic.cls.cr(U32.is_eq(uu, 13), uu)# line feeddef basic.cls.lf(hit: Bool, +uu: U32) -> BK:  match hit:    case True{}:      BKEsc{'n'}    case False{}:      basic.cls.ff(U32.is_eq(uu, 12), uu)# tabdef basic.cls.tab(hit: Bool, +uu: U32) -> BK:  match hit:    case True{}:      BKEsc{'t'}    case False{}:      basic.cls.lf(U32.is_eq(uu, 10), uu)# backspace, then the restdef basic.cls.bs(hit: Bool, +uu: U32) -> BK:  match hit:    case True{}:      BKEsc{'b'}    case False{}:      basic.cls.tab(U32.is_eq(uu, 9), uu)# the class of a code point in a basic stringdef basic.cls(+uu: U32) -> BK:  basic.cls.bs(U32.is_eq(uu, 8), uu)# one character of a basic string, reversed onto the outputdef basic.disp(kk: BK, cc: Char, uu: U32, out: List<&2, Char>) -> List<&2, Char>:  match kk:    case BKCh{}:      cc <> out    case BKEsc{mark}:      basic.esc(mark, out)    case BKHex{}:      basic.hex(uu, out)# one character of a basic stringdef basic.one(+cc: Char, out: List<&2, Char>) -> List<&2, Char>:  +u = Char.to_u32(cc)  basic.disp(basic.cls(u), cc, u, out)# the escaped characters of a basic string, reverseddef basic.go(cs: List<&2, Char>, out: List<&2, Char>) -> List<&2, Char>:  match cs:    case []:      out    case +h <> t:      basic.go(t, basic.one(h, out))# a basic string: quotes, with controls and quotes escapeddef key.basic(+ss: String) -> String:  "\"" ++ String.from_list(List.reverse(&2, Char,    basic.go(String.to_list(ss), []))) ++ "\""# bare when the name allows it, otherwise a basic stringdef key.go(ok: Bool, +ss: String) -> String:  match ok:    case True{}:      ss    case False{}:      key.basic(ss)# a key as TOML writes itdef key(+ss: String) -> String:  key.go(bare(ss), ss)# the overflow of a decimal step, least-significant digit firstdef dec.carry.c0(zz: Bool, cc: U32) -> List<&2, U32>:  match zz:    case True{}:      []    case False{}:      [U32.mod(cc, 10)]# the overflow of a decimal stepdef dec.carry.c(+cc: U32) -> List<&2, U32>:  dec.carry.c0(U32.is_zero(cc), cc)# the next digit of an overflowdef dec.carry.b0(zz: Bool, +cc: U32) -> List<&2, U32>:  match zz:    case True{}:      []    case False{}:      U32.mod(cc, 10) <> dec.carry.c(U32.div(cc, 10))# the next digit of an overflowdef dec.carry.b(+cc: U32) -> List<&2, U32>:  dec.carry.b0(U32.is_zero(cc), cc)# the first digit of an overflowdef dec.carry.a(zz: Bool, +cc: U32) -> List<&2, U32>:  match zz:    case True{}:      []    case False{}:      U32.mod(cc, 10) <> dec.carry.b(U32.div(cc, 10))# the digits of a leftover carrydef dec.carry(+cc: U32) -> List<&2, U32>:  dec.carry.a(U32.is_zero(cc), cc)# multiply a decimal numeral by a radix and add a digitdef dec.go(ds: List<&2, U32>, +radix: U32, +carry: U32) -> List<&2, U32>:  match ds:    case []:      dec.carry(carry)    case +d <> t:      U32.mod(U32.add(U32.mul(d, radix), carry), 10) <> dec.go(t, radix,        U32.div(U32.add(U32.mul(d, radix), carry), 10))# a digit characterdef dec.ch(dd: U32) -> Char:  Chr{U32.add(48, dd)}# digits as characters, in the order givendef dec.chars(ds: List<&2, U32>) -> List<&2, Char>:  match ds:    case []:      []    case d <> t:      dec.ch(d) <> dec.chars(t)# whether a leading zero is still being skippeddef dec.drop.lead(lead: Bool, +hh: Char) -> Bool:  match lead:    case False{}:      False{}    case True{}:      Char.is_eq(hh, '0')# a leading zero is skipped; any other character is keptdef dec.drop.outz(zz: Bool, hh: Char, out: List<&2, Char>) -> List<&2, Char>:  match zz:    case True{}:      out    case False{}:      hh <> out# the output after one character, reverseddef dec.drop.out(lead: Bool, +hh: Char, out: List<&2, Char>) -> List<&2, Char>:  match lead:    case False{}:      hh <> out    case True{}:      dec.drop.outz(Char.is_eq(hh, '0'), hh, out)# the numeral, or 0 when every digit was zerodef dec.drop.end(lead: Bool, out: List<&2, Char>) -> String:  match lead:    case True{}:      "0"    case False{}:      String.from_list(List.reverse(&2, Char, out))# drop leading zeros from a most-significant-first digit listdef dec.drop(cs: List<&2, Char>, +lead: Bool, out: List<&2, Char>) -> String:  match cs:    case []:      dec.drop.end(lead, out)    case +h <> t:      dec.drop(t, dec.drop.lead(lead, h), dec.drop.out(lead, h, out))# a decimal numeral. The digit list is least-significant firstdef dec.text(ds: List<&2, U32>) -> String:  dec.drop(dec.chars(List.reverse(&2, U32, ds)), True{}, [])# a code point's digit value, or 99 when the character is not a digitdef dig.up(ok: Bool, uu: U32) -> U32:  match ok:    case True{}:      U32.sub(uu, 55)    case False{}:      99# a lowercase hex digit, otherwise an uppercase onedef dig.low(ok: Bool, +uu: U32) -> U32:  match ok:    case True{}:      U32.sub(uu, 87)    case False{}:      dig.up(Bool.and(U32.is_ge(uu, 65), U32.is_le(uu, 70)), uu)# a decimal digit, otherwise a hex letterdef dig.dec(ok: Bool, +uu: U32) -> U32:  match ok:    case True{}:      U32.sub(uu, 48)    case False{}:      dig.low(Bool.and(U32.is_ge(uu, 97), U32.is_le(uu, 102)), uu)# the value of a digit character, or 99def dig.val(+cc: Char) -> U32:  +u = Char.to_u32(cc)  dig.dec(Bool.and(U32.is_ge(u, 48), U32.is_le(u, 57)), u)# whether a digit value fits a basedef dig.ok(dd: U32, base: U32) -> Bool:  U32.is_lt(dd, base)# exactly k digits, or DigBaddef take.acc.d(digit: Bool, nn: U32, +cc: Char) -> U32:  match digit:    case False{}:      nn    case True{}:      U32.add(U32.mul(nn, 10), U32.sub(Char.to_u32(cc), 48))# the accumulator after one characterdef take.acc(ok: Bool, nn: U32, +cc: Char) -> U32:  match ok:    case False{}:      nn    case True{}:      take.acc.d(Char.is_digit(cc), nn, cc)# the scan stays well only while every character is a digitdef take.ok(ok: Bool, +cc: Char) -> Bool:  match ok:    case False{}:      False{}    case True{}:      Char.is_digit(cc)# the finished takedef take.fin(ok: Bool, nn: U32, cs: List<&2, Char>) -> Dig:  match ok:    case True{}:      DigOk{nn, cs}    case False{}:      DigBad{}# exactly k digits from the front of a listdef take.n(kk: Nat, cs: List<&2, Char>, nn: U32, +ok: Bool) -> Dig:  match kk:    case 0n:      take.fin(ok, nn, cs)    case 1n+p:      match cs:        case []:          DigBad{}        case +h <> t:          take.n(p, t, take.acc(ok, nn, h), take.ok(ok, h))# a number is inside the inclusive boundsdef bound.rng(+nn: U32, lo: U32, hi: U32) -> Bool:  Bool.and(U32.is_ge(nn, lo), U32.is_le(nn, hi))# two digits, zero paddeddef pad2.go(small: Bool, nn: U32) -> String:  match small:    case True{}:      "0" ++ U32.show(nn)    case False{}:      U32.show(nn)# two digits, zero paddeddef text.pad2(+nn: U32) -> String:  pad2.go(U32.is_lt(nn, 10), nn)# the one-zero pad, or the digits themselvesdef pad4.big(nn: U32, big: Bool) -> String:  match big:    case True{}:      "0" ++ U32.show(nn)    case False{}:      U32.show(nn)# the three-digit and two-digit padsdef pad4.mid(nn: U32, mid: Bool, big: Bool) -> String:  match mid:    case True{}:      "00" ++ U32.show(nn)    case False{}:      pad4.big(nn, big)# four digits, zero paddeddef pad4.go(nn: U32, small: Bool, mid: Bool, big: Bool) -> String:  match small:    case True{}:      "000" ++ U32.show(nn)    case False{}:      pad4.mid(nn, mid, big)# four digits, zero paddeddef text.pad4(+nn: U32) -> String:  pad4.go(nn, U32.is_lt(nn, 10), U32.is_lt(nn, 100), U32.is_lt(nn, 1000))# the sign of a zone offsetdef zone.mark(sign: Sign) -> String:  match sign:    case Plus{}:      "+"    case Minus{}:      "-"# a numeric zone offsetdef zone.shift(sign: Sign, hh: U32, mi: U32) -> String:  zone.mark(sign) ++ text.pad2(hh) ++ ":" ++ text.pad2(mi)# a zone suffixdef zone.text(zz: Zone) -> String:  match zz:    case Zulu{}:      "Z"    case Shift{sign, h, mi}:      zone.shift(sign, h, mi)# the fractional second, or nothingdef clock.frac.go(empty: Bool, frac: String) -> String:  match empty:    case True{}:      ""    case False{}:      "." ++ frac# the fractional second, or nothingdef clock.frac(+frac: String) -> String:  clock.frac.go(String.is_empty(frac), frac)# a clock, with a fractional second when it has onedef clock.text(cc: Clock) -> String:  Clock{h, mi, s, frac} = cc  text.pad2(h) ++ ":" ++ text.pad2(mi) ++ ":" ++ text.pad2(s) ++ clock.frac(frac)# a datedef date.text(dd: Date) -> String:  Date{y, mo, day} = dd  text.pad4(y) ++ "-" ++ text.pad2(mo) ++ "-" ++ text.pad2(day)# a datetime in TOML spellingdef when.text(ww: When) -> String:  match ww:    case WOff{date, clock, zone}:      date.text(date) ++ "T" ++ clock.text(clock) ++ zone.text(zone)    case WLocal{date, clock}:      date.text(date) ++ "T" ++ clock.text(clock)    case WDate{date}:      date.text(date)    case WTime{clock}:      clock.text(clock)# the sign of an integer or a float, empty when it is positivedef sign.text(sign: Sign) -> String:  match sign:    case Plus{}:      ""    case Minus{}:      "-"# whether the list still has a characterdef cs.some(cs: List<&2, Char>) -> Bool:  match cs:    case []:      False{}    case _ <> _:      True{}# one character, when the list has onedef cs.head(cs: List<&2, Char>) -> Char:  match cs:    case []:      ' '    case h <> _:      h# the tail of a listdef cs.tail(cs: List<&2, Char>) -> List<&2, Char>:  match cs:    case []:      []    case _ <> t:      t# a clock with its fraction filled indef clock.with(cc: Clock, frac: String) -> Clock:  Clock{h, mi, s, _f} = cc  Clock{h, mi, s, frac}# a finished local datetime, or one with a zone still to readdef when.done(_date: Date, _clock: Clock, ww: When) -> WhenOut:  WSome{ww}# Z ends the tokendef when.zone.z(tt: List<&2, Char>, clock: Clock, date: Date) -> WhenOut:  match tt:    case []:      WSome{WOff{date, clock, Zulu{}}}    case _ <> _:      WFail{"invalid zone"}# the offset minute is in rangedef when.zone.mm.ok2(mi_ok: Bool, sign: Sign, hh: U32, mi: U32, clock: Clock, date: Date) -> WhenOut:  match mi_ok:    case False{}:      WFail{"zone minute is out of range"}    case True{}:      WSome{WOff{date, clock, Shift{sign, hh, mi}}}# the offset fields are in rangedef when.zone.mm.ok(h_ok: Bool, mi_ok: Bool, sign: Sign, hh: U32, mi: U32, clock: Clock, date: Date) -> WhenOut:  match h_ok:    case False{}:      WFail{"zone hour is out of range"}    case True{}:      when.zone.mm.ok2(mi_ok, sign, hh, mi, clock, date)# nothing follows an offset, and its fields are in rangedef when.zone.mm.end(  cs: List<&2, Char>,  h_ok: Bool,  mi_ok: Bool,  sign: Sign,  hh: U32,  mi: U32,  clock: Clock,  date: Date) -> WhenOut:  match cs:    case []:      when.zone.mm.ok(h_ok, mi_ok, sign, hh, mi, clock, date)    case _ <> _:      WFail{"invalid zone"}# the offset minute is in rangedef when.zone.mm.go(dd: Dig, sign: Sign, +hh: U32, clock: Clock, date: Date) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid zone"}    case DigOk{+n, cs}:      when.zone.mm.end(cs, bound.rng(hh, 0, 23), bound.rng(n, 0, 59), sign, hh, n, clock, date)# the offset minutedef when.zone.mm(hit: Bool, dd: Dig, sign: Sign, hh: U32, clock: Clock, date: Date) -> WhenOut:  match hit:    case False{}:      WFail{"invalid zone"}    case True{}:      when.zone.mm.go(dd, sign, hh, clock, date)# the colon in an offsetdef when.zone.hh.colon(cs: List<&2, Char>, sign: Sign, hh: U32, clock: Clock, date: Date) -> WhenOut:  match cs:    case []:      WFail{"invalid zone"}    case +c <> t:      when.zone.mm(Char.is_eq(c, ':'), take.n(2n, t, 0, True{}), sign, hh, clock, date)# the offset hourdef when.zone.hh(dd: Dig, sign: Sign, clock: Clock, date: Date) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid zone"}    case DigOk{n, cs}:      when.zone.hh.colon(cs, sign, n, clock, date)# HH:MM of a numeric offsetdef when.zone.off(sign: Sign, tt: List<&2, Char>, clock: Clock, date: Date) -> WhenOut:  when.zone.hh(take.n(2n, tt, 0, True{}), sign, clock, date)# a minus starts a numeric offsetdef when.zone.minus(minus: Bool, tt: List<&2, Char>, clock: Clock, date: Date) -> WhenOut:  match minus:    case True{}:      when.zone.off(Minus{}, tt, clock, date)    case False{}:      WFail{"invalid zone"}# a plus or a minus starts a numeric offsetdef when.zone.sign(plus: Bool, minus: Bool, tt: List<&2, Char>, clock: Clock, date: Date) -> WhenOut:  match plus:    case True{}:      when.zone.off(Plus{}, tt, clock, date)    case False{}:      when.zone.minus(minus, tt, clock, date)# Z, or a signed numeric offsetdef when.zone.go(zee: Bool, plus: Bool, minus: Bool, tt: List<&2, Char>, clock: Clock, date: Date) -> WhenOut:  match zee:    case True{}:      when.zone.z(tt, clock, date)    case False{}:      when.zone.sign(plus, minus, tt, clock, date)# a zone, or the end of a local datetimedef when.zone(cs: List<&2, Char>, clock: Clock, date: Date) -> WhenOut:  match cs:    case []:      WSome{WLocal{date, clock}}    case +c <> t:      when.zone.go(Bool.or(Char.is_eq(c, 'Z'), Char.is_eq(c, 'z')),        Char.is_eq(c, '+'), Char.is_eq(c, '-'), t, clock, date)# the fraction must hold a digitdef when.frac.end.go(some: Bool, rev: List<&2, Char>, clock: Clock, date: Date, rest: List<&2, Char>) -> WhenOut:  match some:    case False{}:      WFail{"invalid fraction"}    case True{}:      when.zone(rest, clock.with(clock, String.from_list(List.reverse(&2, Char, rev))),        date)# the fraction must hold a digitdef when.frac.end(+rev: List<&2, Char>, clock: Clock, date: Date, rest: List<&2, Char>) -> WhenOut:  when.frac.end.go(cs.some(rev), rev, clock, date, rest)# one fractional digit, or the end of the fractiondef when.frac.digit(  digit: Bool,  +cc: Char,  tt: List<&2, Char>,  rev: List<&2, Char>,  clock: Clock,  date: Date,  more: Unit -> WhenOut) -> WhenOut:  match digit:    case True{}:      more(Unit{})    case False{}:      when.frac.end(rev, clock, date, cc <> tt)# fractional digitsdef when.frac.digits(cs: List<&2, Char>, +rev: List<&2, Char>, +clock: Clock, +date: Date) -> WhenOut:  match cs:    case []:      when.frac.end(rev, clock, date, [])    case +c <> +t:      when.frac.digit(Char.is_digit(c), c, t, rev, clock, date,        _u => when.frac.digits(t, c <> rev, clock, date))# a dot starts a fraction; anything else is a zonedef when.frac.go(dot: Bool, tt: List<&2, Char>, clock: Clock, date: Date, +cc: Char) -> WhenOut:  match dot:    case True{}:      when.frac.digits(tt, [], clock, date)    case False{}:      when.zone(cc <> tt, clock, date)# an optional fractional seconddef when.frac(cs: List<&2, Char>, +clock: Clock, +date: Date) -> WhenOut:  match cs:    case []:      when.done(date, clock, WLocal{date, clock})    case +c <> t:      when.frac.go(Char.is_eq(c, '.'), t, clock, date, c)# the second is in range, then the fraction and the zonedef when.clock.range.s(s_ok: Bool, hh: U32, mi: U32, ss: U32, cs: List<&2, Char>, date: Date) -> WhenOut:  match s_ok:    case False{}:      WFail{"second is out of range"}    case True{}:      when.frac(cs, Clock{hh, mi, ss, ""}, date)# minute and second are in rangedef when.clock.range.m(mi_ok: Bool, s_ok: Bool, hh: U32, mi: U32, ss: U32, cs: List<&2, Char>, date: Date) -> WhenOut:  match mi_ok:    case False{}:      WFail{"minute is out of range"}    case True{}:      when.clock.range.s(s_ok, hh, mi, ss, cs, date)# hour, minute, and second are in rangedef when.clock.range(  h_ok: Bool,  mi_ok: Bool,  s_ok: Bool,  hh: U32,  mi: U32,  ss: U32,  cs: List<&2, Char>,  date: Date) -> WhenOut:  match h_ok:    case False{}:      WFail{"hour is out of range"}    case True{}:      when.clock.range.m(mi_ok, s_ok, hh, mi, ss, cs, date)# seconds, then an optional fraction and zonedef when.clock.ss.go(dd: Dig, +hh: U32, +mi: U32, date: Date) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid time"}    case DigOk{+n, cs}:      when.clock.range(bound.rng(hh, 0, 23), bound.rng(mi, 0, 59), bound.rng(n, 0, 60), hh, mi, n,        cs, date)# seconds after the colondef when.clock.ss(hit: Bool, dd: Dig, hh: U32, mi: U32, date: Date) -> WhenOut:  match hit:    case False{}:      WFail{"invalid time"}    case True{}:      when.clock.ss.go(dd, hh, mi, date)# the colon after the minutesdef when.clock.mm.colon(cs: List<&2, Char>, hh: U32, mi: U32, date: Date) -> WhenOut:  match cs:    case []:      WFail{"invalid time"}    case +c <> t:      when.clock.ss(Char.is_eq(c, ':'), take.n(2n, t, 0, True{}), hh, mi, date)# minutes, then secondsdef when.clock.mm(dd: Dig, hh: U32, date: Date) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid time"}    case DigOk{n, cs}:      when.clock.mm.colon(cs, hh, n, date)# the colon after the hourdef when.clock.colon.go(hit: Bool, hh: U32, tt: List<&2, Char>, date: Date) -> WhenOut:  match hit:    case False{}:      WFail{"invalid time"}    case True{}:      when.clock.mm(take.n(2n, tt, 0, True{}), hh, date)# the colon after the hourdef when.clock.colon(cs: List<&2, Char>, hh: U32, date: Date) -> WhenOut:  match cs:    case []:      WFail{"invalid time"}    case +c <> t:      when.clock.colon.go(Char.is_eq(c, ':'), hh, t, date)# a clock following a datedef when.clock.of(dd: Dig, date: Date) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid time"}    case DigOk{n, cs}:      when.clock.colon(cs, n, date)# a space separates a date from a timedef when.after.sp(sp: Bool, date: Date, tt: List<&2, Char>) -> WhenOut:  match sp:    case True{}:      when.clock.of(take.n(2n, tt, 0, True{}), date)    case False{}:      WFail{"invalid datetime"}# T or a space separates a date from a timedef when.after.sep(tee: Bool, sp: Bool, date: Date, tt: List<&2, Char>) -> WhenOut:  match tee:    case True{}:      when.clock.of(take.n(2n, tt, 0, True{}), date)    case False{}:      when.after.sp(sp, date, tt)# the character after a date: nothing, T, or a space, and then a timedef when.after.date(date: Date, cs: List<&2, Char>) -> WhenOut:  match cs:    case []:      WSome{WDate{date}}    case +c <> t:      when.after.sep(Bool.or(Char.is_eq(c, 'T'), Char.is_eq(c, 't')),        Char.is_eq(c, ' '), date, t)# the day is in range, then the rest of the token is a time or nothingdef when.date.ok.d(d_ok: Bool, yy: U32, mo: U32, dd: U32, cs: List<&2, Char>) -> WhenOut:  match d_ok:    case False{}:      WFail{"day is out of range"}    case True{}:      when.after.date(Date{yy, mo, dd}, cs)# a date whose fields are in rangedef when.date.ok.go(mo_ok: Bool, d_ok: Bool, yy: U32, mo: U32, dd: U32, cs: List<&2, Char>) -> WhenOut:  match mo_ok:    case False{}:      WFail{"month is out of range"}    case True{}:      when.date.ok.d(d_ok, yy, mo, dd, cs)# February in a year divisible by 4 and by 100: 29 days only when the year# is also divisible by 400def when.feb.quad(four_hundred: Bool) -> U32:  match four_hundred:    case True{}:      29    case False{}:      28# February in a year divisible by 4: 29 days unless the year is a centurydef when.feb.cent(hundred: Bool, four_hundred: Bool) -> U32:  match hundred:    case False{}:      29    case True{}:      when.feb.quad(four_hundred)# February: 29 days in a leap year and 28 otherwise. A leap year is divisible# by 4, except by 100 unless also by 400 (RFC 3339, Appendix C)def when.feb.go(four: Bool, hundred: Bool, four_hundred: Bool) -> U32:  match four:    case False{}:      28    case True{}:      when.feb.cent(hundred, four_hundred)# the days of February in a yeardef when.feb(+yy: U32) -> U32:  when.feb.go(U32.is_eq(U32.mod(yy, 4), 0), U32.is_eq(U32.mod(yy, 100), 0),    U32.is_eq(U32.mod(yy, 400), 0))# April, June, September and November have 30 daysdef when.short(+mo: U32) -> Bool:  Bool.or(Bool.or(U32.is_eq(mo, 4), U32.is_eq(mo, 6)),    Bool.or(U32.is_eq(mo, 9), U32.is_eq(mo, 11)))# 30 days in a short month and 31 in the othersdef when.days.short(short: Bool) -> U32:  match short:    case True{}:      30    case False{}:      31# the days of a month, by whether it is February or a short monthdef when.days.go(feb: Bool, short: Bool, +yy: U32) -> U32:  match feb:    case True{}:      when.feb(yy)    case False{}:      when.days.short(short)# the days of month mo in year yydef when.days(+yy: U32, +mo: U32) -> U32:  when.days.go(U32.is_eq(mo, 2), when.short(mo), yy)# a date whose fields are in range: the day within its month in that yeardef when.date.ok(+yy: U32, +mo: U32, +dd: U32, cs: List<&2, Char>) -> WhenOut:  when.date.ok.go(bound.rng(mo, 1, 12), bound.rng(dd, 1, when.days(yy, mo)), yy, mo, dd, cs)# a local time: HH:MM:SS, with the hour already takendef when.time.rest(tt: List<&2, Char>, hh: U32) -> WhenOut:  when.clock.mm(take.n(2n, tt, 0, True{}), hh, Date{0, 1, 1})# a local time uses a dummy date and then keeps only the clockdef when.time.only(ww: WhenOut) -> WhenOut:  match ww:    case WSome{WLocal{_date, clock}}:      WSome{WTime{clock}}    case WSome{WOff{_date, clock, zone}}:      WFail{"invalid time"}    case WSome{other}:      WFail{"invalid time"}    case WFail{why}:      WFail{why}    case WSkip{}:      WFail{"invalid time"}# a complete date, then whatever follows itdef when.day.end(dd: Dig, yy: U32, mo: U32) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid date"}    case DigOk{n, cs}:      when.date.ok(yy, mo, n, cs)# the day is two digitsdef when.day.go(hit: Bool, dd: Dig, yy: U32, mo: U32) -> WhenOut:  match hit:    case False{}:      WFail{"invalid date"}    case True{}:      when.day.end(dd, yy, mo)# the daydef when.day(cs: List<&2, Char>, yy: U32, mo: U32) -> WhenOut:  match cs:    case []:      WFail{"invalid date"}    case +c <> t:      when.day.go(Char.is_eq(c, '-'), take.n(2n, t, 0, True{}), yy, mo)# the dash after the monthdef when.month.go(dd: Dig, yy: U32) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid date"}    case DigOk{n, cs}:      when.day(cs, yy, n)# the monthdef when.month(hit: Bool, dd: Dig, yy: U32) -> WhenOut:  match hit:    case False{}:      WFail{"invalid date"}    case True{}:      when.month.go(dd, yy)# the dash after the yeardef when.year.dash(cs: List<&2, Char>, yy: U32) -> WhenOut:  match cs:    case []:      WFail{"invalid date"}    case +c <> t:      when.month(Char.is_eq(c, '-'), take.n(2n, t, 0, True{}), yy)# the fourth year digit, then the monthdef when.year4(dd: Dig) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid date"}    case DigOk{n, cs}:      when.year.dash(cs, n)# two more year digits, then a dashdef when.year.more(digit: Bool, nn: U32, +cc: Char, tt: List<&2, Char>) -> WhenOut:  match digit:    case False{}:      WFail{"invalid datetime"}    case True{}:      when.year4(take.n(1n, tt, U32.add(U32.mul(nn, 10), dig.val(cc)), True{}))# a colon means a local time; another digit continues a yeardef when.branch(colon: Bool, digit: Bool, nn: U32, +cc: Char, tt: List<&2, Char>) -> WhenOut:  match colon:    case True{}:      when.time.only(when.time.rest(tt, nn))    case False{}:      when.year.more(digit, nn, cc, tt)# two digits decide a time or a datedef when.after2.go(cs: List<&2, Char>, nn: U32) -> WhenOut:  match cs:    case []:      WFail{"invalid datetime"}    case +c <> t:      when.branch(Char.is_eq(c, ':'), Char.is_digit(c), nn, c, t)# two digits decide a time (HH:) or a date (YYYY-)def when.after2(dd: Dig) -> WhenOut:  match dd:    case DigBad{}:      WFail{"invalid datetime"}    case DigOk{n, cs}:      when.after2.go(cs, n)# a datetime tokendef when.read(cs: List<&2, Char>) -> WhenOut:  when.after2(take.n(2n, cs, 0, True{}))# a dash after the first characterdef word.dash.hit(first: Bool, +cc: Char) -> Bool:  match first:    case True{}:      False{}    case False{}:      Char.is_eq(cc, '-')# either flagdef word.or(aa: Bool, bb: Bool) -> Bool:  match aa:    case True{}:      True{}    case False{}:      bb# a colon anywhere in the tokendef word.colon(cs: List<&2, Char>) -> Bool:  match cs:    case []:      False{}    case +c <> t:      word.or(Char.is_eq(c, ':'), word.colon(t))# e or E: an exponent, so the token is a numeral rather than a datetimedef word.ee.hit(+cc: Char) -> Bool:  Bool.or(Char.is_eq(cc, 'e'), Char.is_eq(cc, 'E'))# whether the token holds an exponent markerdef word.ee(cs: List<&2, Char>) -> Bool:  match cs:    case []:      False{}    case +c <> t:      word.or(word.ee.hit(c), word.ee(t))# a datetime scan only when the token is not an exponent numeraldef word.as_date(date: Bool, exp: Bool) -> Bool:  match exp:    case True{}:      False{}    case False{}:      date# a dash after the first characterdef word.dash(cs: List<&2, Char>, first: Bool) -> Bool:  match cs:    case []:      False{}    case +c <> t:      word.or(word.dash.hit(first, c), word.dash(t, False{}))# a negative nan, or not a specialdef word.spec.nan3(cc: Bool) -> Got:  match cc:    case True{}:      GotVal{VFlo{Minus{}, "nan"}}    case False{}:      GotBad{""}# a signed nandef word.spec.nan2(bb: Bool, cc: Bool) -> Got:  match bb:    case True{}:      GotVal{VFlo{Plus{}, "nan"}}    case False{}:      word.spec.nan3(cc)# nandef word.spec.nan(aa: Bool, bb: Bool, cc: Bool) -> Got:  match aa:    case True{}:      GotVal{VFlo{Plus{}, "nan"}}    case False{}:      word.spec.nan2(bb, cc)# character-for-character equality. a mismatch drops the rest and stopsdef str.eq.step(aa: String, bb: String, same: Bool) -> Bool:  match aa bb same:    case SNil{} SNil{} True{}:      True{}    case SCon{x, xt} SCon{y, yt} True{}:      str.eq.step(xt, yt, Char.is_eq(x, y))    case _a _b _s:      False{}# the search only needs the bool, not a rebuilt copy of either stringdef str.eq(aa: String, bb: String) -> Bool:  str.eq.step(aa, bb, True{})# a negative inf, or nandef word.spec.inf3(cc: Bool, +ss: String) -> Got:  match cc:    case True{}:      GotVal{VFlo{Minus{}, "inf"}}    case False{}:      word.spec.nan(str.eq(ss, "nan"), str.eq(ss, "+nan"), str.eq(ss, "-nan"))# a signed inf, or nandef word.spec.inf2(bb: Bool, cc: Bool, +ss: String) -> Got:  match bb:    case True{}:      GotVal{VFlo{Plus{}, "inf"}}    case False{}:      word.spec.inf3(cc, ss)# inf, or nandef word.spec.inf(aa: Bool, bb: Bool, cc: Bool, +ss: String) -> Got:  match aa:    case True{}:      GotVal{VFlo{Plus{}, "inf"}}    case False{}:      word.spec.inf2(bb, cc, ss)# false, or the rest of the specialsdef word.spec.f(hit: Bool, +ss: String) -> Got:  match hit:    case True{}:      GotVal{VBool{False{}}}    case False{}:      word.spec.inf(str.eq(ss, "inf"), str.eq(ss, "+inf"), str.eq(ss, "-inf"), ss)# true, or the rest of the specialsdef word.spec.t(hit: Bool, other: Bool, +ss: String) -> Got:  match hit:    case True{}:      GotVal{VBool{True{}}}    case False{}:      word.spec.f(other, ss)# true, false, inf, and nan, including a sign on inf and nandef word.spec(+ss: String) -> Got:  word.spec.t(str.eq(ss, "true"), str.eq(ss, "false"), ss)# a datetime result as a valuedef word.from.when(ww: WhenOut) -> Got:  match ww:    case WSome{when}:      GotVal{VWhen{when}}    case WFail{why}:      GotBad{why}    case WSkip{}:      GotBad{"invalid datetime"}# the spelling of a numeral with the leading sign and underscores removeddef word.body.put.sign(is_sign: Bool, cc: Char, out: List<&2, Char>) -> List<&2, Char>:  match is_sign:    case True{}:      out    case False{}:      cc <> out# an underscore is dropped; every other character is keptdef word.body.put.us(is_us: Bool, cc: Char, out: List<&2, Char>) -> List<&2, Char>:  match is_us:    case True{}:      out    case False{}:      cc <> out# one character of a numeral spellingdef word.body.put(lead: Bool, +cc: Char, out: List<&2, Char>) -> List<&2, Char>:  match lead:    case True{}:      word.body.put.sign(Bool.or(Char.is_eq(cc, '+'), Char.is_eq(cc, '-')), cc, out)    case False{}:      word.body.put.us(Char.is_eq(cc, '_'), cc, out)# the spelling, reversed, then put back in order at the enddef word.body.go(cs: List<&2, Char>, lead: Bool, out: List<&2, Char>) -> List<&2, Char>:  match cs:    case []:      List.reverse(&2, Char, out)    case +c <> t:      word.body.go(t, False{}, word.body.put(lead, c, out))# the spelling of a numeral with the leading sign and underscores removeddef word.body(cs: List<&2, Char>) -> String:  String.from_list(word.body.go(cs, True{}, []))# a fresh numeral scandef num.start() -> Num:  Num{NLead{}, Plus{}, 10, [], False{}, False{}, ""}# a scan that has faileddef num.bad(_st: Num, why: String) -> Num:  Num{NBad{}, Plus{}, 10, [], False{}, False{}, why}# set the sign and wait for a digitdef num.sign(_st: Num, sign: Sign) -> Num:  Num{NSign{}, sign, 10, [], False{}, False{}, ""}# a digit that fits the basedef num.digit.ok(ok: Bool, +cc: Char, nk: NK, sign: Sign, +base: U32, ds: List<&2, U32>) -> Num:  match ok:    case False{}:      num.bad(Num{NLead{}, Plus{}, 10, [], False{}, False{}, ""}, "invalid digit")    case True{}:      Num{nk, sign, base, dec.go(ds, base, dig.val(cc)), False{}, True{}, ""}# a decimal digit joined to the numeraldef num.digit.go(st: Num, +cc: Char, nk: NK) -> Num:  Num{_nk, sign, +base, ds, _us, _saw, _why} = st  num.digit.ok(dig.ok(dig.val(cc), base), cc, nk, sign, base, ds)# a decimal digit joined to the numeraldef num.digit(st: Num, +cc: Char, nk: NK) -> Num:  num.digit.go(st, cc, nk)# an underscore between digitsdef num.us.go(ok: Bool, nk: NK, sign: Sign, base: U32, ds: List<&2, U32>, why: String) -> Num:  match ok:    case False{}:      num.bad(Num{NLead{}, Plus{}, 10, [], False{}, False{}, ""}, "invalid underscore")    case True{}:      Num{nk, sign, base, ds, True{}, True{}, why}# an underscore between digitsdef num.us(st: Num) -> Num:  Num{nk, sign, base, ds, us, saw, why} = st  num.us.go(Bool.and(saw, Bool.not(us)), nk, sign, base, ds, why)# the base prefix after a leading zerodef num.base(st: Num, base: U32) -> Num:  Num{_nk, sign, _b, _ds, _us, _saw, _why} = st  Num{NBase{}, sign, base, [], False{}, False{}, ""}# a dot begins the fractiondef num.dot.go(ok: Bool, sign: Sign) -> Num:  match ok:    case False{}:      num.bad(Num{NLead{}, Plus{}, 10, [], False{}, False{}, ""}, "invalid fraction")    case True{}:      Num{NFrac{}, sign, 10, [], False{}, False{}, ""}# a dot begins the fractiondef num.dot(st: Num) -> Num:  Num{_nk, sign, _b, _ds, us, saw, _why} = st  num.dot.go(Bool.and(saw, Bool.not(us)), sign)# an exponent markerdef num.exp.go(ok: Bool, sign: Sign) -> Num:  match ok:    case False{}:      num.bad(Num{NLead{}, Plus{}, 10, [], False{}, False{}, ""}, "invalid exponent")    case True{}:      Num{NExp{}, sign, 10, [], False{}, False{}, ""}# an exponent markerdef num.exp(st: Num) -> Num:  Num{_nk, sign, _b, _ds, us, saw, _why} = st  num.exp.go(Bool.and(saw, Bool.not(us)), sign)# the sign already chosendef num.sign.of(st: Num) -> Sign:  Num{_nk, sign, _b, _ds, _us, _saw, _why} = st  sign# a first digitdef num.lead.digit(digit: Bool, st: Num, +cc: Char) -> Num:  match digit:    case True{}:      num.digit(st, cc, NDig{})    case False{}:      num.bad(st, "invalid number")# a leading zero, or another digitdef num.lead.zero(zero: Bool, digit: Bool, st: Num, +cc: Char) -> Num:  match zero:    case True{}:      Num{NZero{}, num.sign.of(st), 10, [], False{}, True{}, ""}    case False{}:      num.lead.digit(digit, st, cc)# a minus, a zero, or a digitdef num.lead.minus(minus: Bool, zero: Bool, digit: Bool, st: Num, +cc: Char) -> Num:  match minus:    case True{}:      num.sign(st, Minus{})    case False{}:      num.lead.zero(zero, digit, st, cc)# a sign, a zero, or a digitdef num.lead.go(plus: Bool, minus: Bool, zero: Bool, digit: Bool, st: Num, +cc: Char) -> Num:  match plus:    case True{}:      num.sign(st, Plus{})    case False{}:      num.lead.minus(minus, zero, digit, st, cc)# the first character after an optional signdef num.lead(st: Num, +cc: Char) -> Num:  num.lead.go(Char.is_eq(cc, '+'), Char.is_eq(cc, '-'), Char.is_eq(cc, '0'),    Char.is_digit(cc), st, cc)# a second sign is refused; any other character is read as the first one of# an unsigned numeral, with the sign keptdef num.after.sign.go(twice: Bool, sign: Sign, +cc: Char) -> Num:  match twice:    case True{}:      num.bad(num.start(), "invalid number")    case False{}:      num.lead(Num{NLead{}, sign, 10, [], False{}, False{}, ""}, cc)# the character after a signdef num.after.sign(st: Num, +cc: Char) -> Num:  num.after.sign.go(Bool.or(Char.is_eq(cc, '+'), Char.is_eq(cc, '-')), num.sign.of(st), cc)# a capital exponent; any other character is refused, since a digit or an# underscore after a leading zero (`01`, `0_1`) would hide itdef num.zero.exp2(ee: Bool, st: Num, _c: Char) -> Num:  match ee:    case True{}:      num.exp(st)    case False{}:      num.bad(st, "invalid number")# an exponent after zero, or a rejected extra digitdef num.zero.exp(eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match eee:    case True{}:      num.exp(st)    case False{}:      num.zero.exp2(ee, st, cc)# a fraction or an exponent after zerodef num.zero.dot(dot: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match dot:    case True{}:      num.dot(st)    case False{}:      num.zero.exp(eee, ee, st, cc)# a binary prefix, or a floatdef num.zero.b(bb: Bool, dot: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match bb:    case True{}:      num.base(st, 2)    case False{}:      num.zero.dot(dot, eee, ee, st, cc)# an octal prefix, or the restdef num.zero.o(oo: Bool, bb: Bool, dot: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match oo:    case True{}:      num.base(st, 8)    case False{}:      num.zero.b(bb, dot, eee, ee, st, cc)# a base prefix, a fraction, or an exponentdef num.zero.go(xx: Bool, oo: Bool, bb: Bool, dot: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match xx:    case True{}:      num.base(st, 16)    case False{}:      num.zero.o(oo, bb, dot, eee, ee, st, cc)# the character after a lone zerodef num.zero(st: Num, +cc: Char) -> Num:  num.zero.go(Char.is_eq(cc, 'x'), Char.is_eq(cc, 'o'), Char.is_eq(cc, 'b'),    Char.is_eq(cc, '.'), Char.is_eq(cc, 'e'), Char.is_eq(cc, 'E'), st, cc)# a capital exponent, or a rejected characterdef num.digs.exp2(ee: Bool, st: Num, _c: Char) -> Num:  match ee:    case True{}:      num.exp(st)    case False{}:      num.bad(st, "invalid number")# an exponentdef num.digs.exp(eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match eee:    case True{}:      num.exp(st)    case False{}:      num.digs.exp2(ee, st, cc)# a dot or an exponentdef num.digs.dot(dot: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match dot:    case True{}:      num.dot(st)    case False{}:      num.digs.exp(eee, ee, st, cc)# an underscore, a dot, or an exponentdef num.digs.us(us: Bool, dot: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match us:    case True{}:      num.us(st)    case False{}:      num.digs.dot(dot, eee, ee, st, cc)# a decimal digit, an underscore, a dot, or an exponentdef num.digs.go(digit: Bool, us: Bool, dot: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match digit:    case True{}:      num.digit(st, cc, NDig{})    case False{}:      num.digs.us(us, dot, eee, ee, st, cc)# a decimal digit, an underscore, a dot, or an exponentdef num.digs(st: Num, +cc: Char) -> Num:  num.digs.go(Char.is_digit(cc), Char.is_eq(cc, '_'), Char.is_eq(cc, '.'),    Char.is_eq(cc, 'e'), Char.is_eq(cc, 'E'), st, cc)# an underscore, or a digit of the basedef num.based.go(us: Bool, st: Num, +cc: Char) -> Num:  match us:    case True{}:      num.us(st)    case False{}:      num.digit(st, cc, NBase{})# a digit of a prefixed integer, or an underscoredef num.based(st: Num, +cc: Char) -> Num:  num.based.go(Char.is_eq(cc, '_'), st, cc)# a capital exponent, or a rejected characterdef num.frac.exp2(ee: Bool, st: Num, _c: Char) -> Num:  match ee:    case True{}:      num.exp(st)    case False{}:      num.bad(st, "invalid fraction")# an exponent in a fractiondef num.frac.exp(eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match eee:    case True{}:      num.exp(st)    case False{}:      num.frac.exp2(ee, st, cc)# an underscore or an exponent in a fractiondef num.frac.us(us: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match us:    case True{}:      num.us(st)    case False{}:      num.frac.exp(eee, ee, st, cc)# a fraction digit, an underscore, or an exponentdef num.frac.go(digit: Bool, us: Bool, eee: Bool, ee: Bool, st: Num, +cc: Char) -> Num:  match digit:    case True{}:      num.digit(st, cc, NFrac{})    case False{}:      num.frac.us(us, eee, ee, st, cc)# a fraction digit, an underscore, or an exponentdef num.frac(st: Num, +cc: Char) -> Num:  num.frac.go(Char.is_digit(cc), Char.is_eq(cc, '_'), Char.is_eq(cc, 'e'),    Char.is_eq(cc, 'E'), st, cc)# the exponent sign is part of the spelling; digits followdef num.exp.sign(st: Num) -> Num:  Num{_nk, sign, _b, _ds, _us, _saw, _why} = st  Num{NEDig{}, sign, 10, [], False{}, False{}, ""}# a digit of the exponentdef num.expon.digit(digit: Bool, st: Num, +cc: Char) -> Num:  match digit:    case True{}:      num.digit(st, cc, NEDig{})    case False{}:      num.bad(st, "invalid exponent")# a minus or a digit in an exponentdef num.expon.minus(minus: Bool, digit: Bool, st: Num, +cc: Char) -> Num:  match minus:    case True{}:      num.exp.sign(st)    case False{}:      num.expon.digit(digit, st, cc)# the sign or the first digit of an exponentdef num.expon.go(plus: Bool, minus: Bool, digit: Bool, st: Num, +cc: Char) -> Num:  match plus:    case True{}:      num.exp.sign(st)    case False{}:      num.expon.minus(minus, digit, st, cc)# the sign or the first digit of an exponentdef num.expon(st: Num, +cc: Char) -> Num:  num.expon.go(Char.is_eq(cc, '+'), Char.is_eq(cc, '-'), Char.is_digit(cc), st, cc)# an underscore in an exponentdef num.edig.us(us: Bool, st: Num, _c: Char) -> Num:  match us:    case True{}:      num.us(st)    case False{}:      num.bad(st, "invalid exponent")# an exponent digit or an underscoredef num.edig.go(digit: Bool, us: Bool, st: Num, +cc: Char) -> Num:  match digit:    case True{}:      num.digit(st, cc, NEDig{})    case False{}:      num.edig.us(us, st, cc)# an exponent digit or an underscoredef num.edig(st: Num, +cc: Char) -> Num:  num.edig.go(Char.is_digit(cc), Char.is_eq(cc, '_'), st, cc)# one character of a numeraldef num.on(nk: NK, st: Num, +cc: Char) -> Num:  match nk:    case NLead{}:      num.lead(st, cc)    case NSign{}:      num.after.sign(st, cc)    case NZero{}:      num.zero(st, cc)    case NDig{}:      num.digs(st, cc)    case NBase{}:      num.based(st, cc)    case NFrac{}:      num.frac(st, cc)    case NExp{}:      num.expon(st, cc)    case NEDig{}:      num.edig(st, cc)    case NBad{}:      st# one character of a numeraldef num.step.go(st: Num, +cc: Char) -> Num:  Num{+nk, sign, base, ds, us, saw, why} = st  num.on(nk, Num{nk, sign, base, ds, us, saw, why}, cc)# one character of a numeraldef num.step(st: Num, +cc: Char) -> Num:  num.step.go(st, cc)# the walk over a numeraldef num.go(cs: List<&2, Char>, st: Num) -> Num:  match cs:    case []:      st    case +h <> t:      num.go(t, num.step(st, h))# the positive limit is 2^63-1; the negative limit is 2^63def i64.limit(sign: Sign) -> String:  match sign:    case Plus{}:      "9223372036854775807"    case Minus{}:      "9223372036854775808"# the first differing digit stands; a tie takes the new orderdef i64.ord(seen: Cmp, now: Cmp) -> Cmp:  match seen now:    case EQ{} n:      n    case LT{} _n:      LT{}    case GT{} _n:      GT{}# a shorter digit string fits and a longer one does not. equal length uses# digit order. the walk does not rebuild either stringdef i64.step(+digits: String, +limit: String, ord: Cmp) -> Bool:  match digits limit:    case SNil{} SNil{}:      Cmp.is_le(ord)    case SNil{} SCon{_h, _t}:      True{}    case SCon{_h, _t} SNil{}:      False{}    case SCon{x, xt} SCon{y, yt}:      i64.step(xt, yt, i64.ord(ord, U32.cmp(Char.to_u32(x), Char.to_u32(y))))# whether the integer fits in a signed 64-bit integerdef i64.fits(+sign: Sign, +digits: String) -> Bool:  i64.step(digits, i64.limit(sign), EQ{})# an integer inside the signed 64-bit rangedef num.fin.range(ok: Bool, sign: Sign, digits: String) -> Got:  match ok:    case True{}:      GotVal{VInt{sign, digits}}    case False{}:      GotBad{"out of range"}# a finished integer, rejected when it falls outside signed 64-bitdef num.fin.int(saw: Bool, +sign: Sign, +digits: String) -> Got:  match saw:    case False{}:      GotBad{"invalid number"}    case True{}:      num.fin.range(i64.fits(sign, digits), sign, digits)# a numeral needs a digit in its last rundef num.fin.saw(saw: Bool, val: Val) -> Got:  match saw:    case False{}:      GotBad{"invalid number"}    case True{}:      GotVal{val}# a prefixed integer may not carry a signdef num.signed(cs: List<&2, Char>) -> Bool:  match cs:    case []:      False{}    case +c <> _:      Bool.or(Char.is_eq(c, '+'), Char.is_eq(c, '-'))# a prefixed integer, rejected when a sign was writtendef num.fin.base(signed: Bool, saw: Bool, sign: Sign, ds: List<&2, U32>) -> Got:  match signed:    case True{}:      GotBad{"invalid number"}    case False{}:      num.fin.int(saw, sign, dec.text(ds))# the kind of numeral the scan finished indef num.fin.nk(nk: NK, sign: Sign, ds: List<&2, U32>, saw: Bool, why: String, cs: List<&2, Char>) -> Got:  match nk:    case NBad{}:      GotBad{why}    case NLead{}:      GotBad{"invalid number"}    case NSign{}:      GotBad{"invalid number"}    case NZero{}:      GotVal{VInt{sign, "0"}}    case NDig{}:      num.fin.int(saw, sign, dec.text(ds))    case NBase{}:      num.fin.base(num.signed(cs), saw, sign, ds)    case NFrac{}:      num.fin.saw(saw, VFlo{sign, word.body(cs)})    case NExp{}:      GotBad{"invalid exponent"}    case NEDig{}:      num.fin.saw(saw, VFlo{sign, word.body(cs)})# a trailing underscore is rejecteddef num.fin.us(us: Bool, nk: NK, sign: Sign, ds: List<&2, U32>, saw: Bool, why: String, cs: List<&2, Char>) -> Got:  match us:    case True{}:      GotBad{"invalid underscore"}    case False{}:      num.fin.nk(nk, sign, ds, saw, why, cs)# the value of a finished numeraldef num.fin(st: Num, cs: List<&2, Char>) -> Got:  Num{nk, sign, _base, ds, us, saw, why} = st  num.fin.us(us, nk, sign, ds, saw, why, cs)# a numeral tokendef word.num(+cs: List<&2, Char>) -> Got:  num.fin(num.go(cs, num.start()), cs)# a datetime when the token has a colon or a dash, otherwise a numeraldef word.route.date(date: Bool, cs: List<&2, Char>) -> Got:  match date:    case True{}:      word.from.when(when.read(cs))    case False{}:      word.num(cs)# a special word, or a numeraldef word.route(gg: Got, date: Bool, +cs: List<&2, Char>) -> Got:  match gg:    case GotVal{val}:      GotVal{val}    case GotBad{_}:      word.route.date(word.as_date(date, word.ee(cs)), cs)# '-' can start a signed inf or nandef word.special.minus(+cc: Char) -> Bool:  Char.is_eq(cc, '-')# '+' or '-'def word.special.plus(hit: Bool, +cc: Char) -> Bool:  match hit:    case True{}:      True{}    case False{}:      word.special.minus(cc)# 'n', or a signdef word.special.n(hit: Bool, +cc: Char) -> Bool:  match hit:    case True{}:      True{}    case False{}:      word.special.plus(Char.is_eq(cc, '+'), cc)# 'i', or nan, or a signdef word.special.i(hit: Bool, +cc: Char) -> Bool:  match hit:    case True{}:      True{}    case False{}:      word.special.n(Char.is_eq(cc, 'n'), cc)# 'f', or inf, or nan, or a signdef word.special.f(hit: Bool, +cc: Char) -> Bool:  match hit:    case True{}:      True{}    case False{}:      word.special.i(Char.is_eq(cc, 'i'), cc)# 't', or false, or inf, or nan, or a signdef word.special.t(hit: Bool, +cc: Char) -> Bool:  match hit:    case True{}:      True{}    case False{}:      word.special.f(Char.is_eq(cc, 'f'), cc)# whether this character can start a special bare worddef word.special(+cc: Char) -> Bool:  word.special.t(Char.is_eq(cc, 't'), cc)# the first character, when the word has onedef word.lead(cs: List<&2, Char>) -> Bool:  match cs:    case []:      False{}    case +h <> _t:      word.special(h)# a numeral or a datetime. the token cannot be a keyworddef word.val.num(+cs: List<&2, Char>) -> Got:  word.route(GotBad{""}, word.or(word.colon(cs), word.dash(cs, True{})), cs)# a special word, or a numeral. a digit does not build a string to comparedef word.val.pick(special: Bool, +cs: List<&2, Char>) -> Got:  match special:    case True{}:      word.route(word.spec(String.from_list(cs)),        word.or(word.colon(cs), word.dash(cs, True{})), cs)    case False{}:      word.val.num(cs)# a bare word: a special, a datetime, or a numeraldef word.val(+cs: List<&2, Char>) -> Got:  word.val.pick(word.lead(cs), cs)# a change applied at the end of a key pathtype Job is Data:  JPut{val: Val}  JDef{}  JAot{}# one table the walk has enteredtype Fr is Data:  FrNew{name: String, parent: List<&2, Val>, path: String}  FrRep{name: String, parent: List<&2, Val>, path: String}  FrAot{name: String, parent: List<&2, Val>, pre: List<&2, Val>, path: String}# the walk: the table being edited, and the frames above ittype Tw is Data:  Tw{vs: List<&2, Val>, job: Job, stack: List<&2, Fr>, bad: String, pre: String,    heads: List<&2, String>, inl: Bool}# the root table after a walktype End is Data:  End{vs: List<&2, Val>, bad: String, heads: List<&2, String>}# what a key in a table refers totype Look is Data:  KMiss{}  KHead{path: String, rows: List<&2, Val>}  KInl{}  KAots{path: String, elems: List<&2, Val>}  KNo{why: String}# the last element of an array of tablestype Last is Data:  LastSome{path: String, pre: List<&2, Val>, rows: List<&2, Val>}  LastNo{why: String}# the first error is keptdef err.first.go(empty: Bool, bad: String, why: String) -> String:  match empty:    case True{}:      why    case False{}:      bad# the first error is keptdef err.first(+bad: String, why: String) -> String:  err.first.go(String.is_empty(bad), bad, why)# a key found earlier winsdef find.step(eq: Bool, vv: Val, more: Unit -> Hit) -> Hit:  match eq:    case True{}:      Found{vv}    case False{}:      more(Unit{})# the first pair of this namedef rows.find(rows: List<&2, Val>, +name: String) -> Hit:  match rows:    case []:      Miss{}    case VPair{n, v} <> t:      find.step(str.eq(n, name), v, _u => rows.find(t, name))    case _ <> t:      rows.find(t, name)# the value written for a key, or Missdef get(rows: List<&2, Val>, +name: String) -> Hit:  rows.find(rows, name)# replace the value of one pairdef rows.repl.step(eq: Bool, nn: String, old: Val, val: Val, more: Unit -> List<&2, Val>) -> List<&2, Val>:  match eq:    case True{}:      VPair{nn, val} <> more(Unit{})    case False{}:      VPair{nn, old} <> more(Unit{})# replace every pair of this namedef rows.repl(rows: List<&2, Val>, +name: String, +val: Val) -> List<&2, Val>:  match rows:    case []:      []    case VPair{+n, old} <> t:      rows.repl.step(str.eq(n, name), n, old, val, _u => rows.repl(t, name, val))    case h <> t:      h <> rows.repl(t, name, val)# either flagdef heads.or(aa: Bool, bb: Bool) -> Bool:  match aa:    case True{}:      True{}    case False{}:      bb# heads records each table the document has defined as its path behind one# mark: `[` when a header defined it, `.` when dotted keys diddef heads.by.head(path: String) -> String:  SCon{'[', path}# a table dotted keys defined, as heads records itdef heads.by.dot(path: String) -> String:  SCon{'.', path}# whether a table path was defined, by a header or by dotted keysdef heads.has(hs: List<&2, String>, +pp: String) -> Bool:  match hs:    case []:      False{}    case SCon{_mark, h} <> t:      heads.or(str.eq(h, pp), heads.has(t, pp))    case SNil{} <> t:      heads.has(t, pp)# whether a table path was defined by a headerdef heads.head(hs: List<&2, String>, +pp: String) -> Bool:  match hs:    case []:      False{}    case SCon{mark, h} <> t:      heads.or(Bool.and(Char.is_eq(mark, '['), str.eq(h, pp)), heads.head(t, pp))    case SNil{} <> t:      heads.head(t, pp)# a recorded table under the prefix is droppeddef heads.drop.step(  under: Bool,  mark: Char,  path: String,  rest: List<&2, String>) -> List<&2, String>:  match under:    case True{}:      rest    case False{}:      SCon{mark, path} <> rest# forget every table whose path starts with the prefix. A new element of an# array of tables starts with none of the tables the element before defineddef heads.drop(hs: List<&2, String>, +pre: String) -> List<&2, String>:  match hs:    case []:      []    case SCon{mark, +path} <> t:      heads.drop.step(String.starts_with(path, pre), mark, path, heads.drop(t, pre))    case SNil{} <> t:      heads.drop(t, pre)# a table defined by a dotted key is recorded oncedef heads.define.new(seen: Bool, path: String, heads: List<&2, String>) -> List<&2, String>:  match seen:    case True{}:      heads    case False{}:      heads.by.dot(path) <> heads# a dotted key defines each table before its last segmentdef heads.define(job: Job, +path: String, +heads: List<&2, String>) -> List<&2, String>:  match job:    case JPut{_val}:      heads.define.new(heads.has(heads, path), path, heads)    case _:      heads# a dotted path from a prefix and one segmentdef tree.pre.go(empty: Bool, pre: String, name: String) -> String:  match empty:    case True{}:      key(name)    case False{}:      pre ++ "." ++ key(name)# a dotted path from a prefix and one segmentdef tree.pre(+pre: String, name: String) -> String:  tree.pre.go(String.is_empty(pre), pre, name)# the value a key already holdsdef look.of(vv: Val) -> Look:  match vv:    case VHead{path, rows}:      KHead{path, rows}    case VInl{_rows}:      KInl{}    case VAots{path, elems}:      KAots{path, elems}    case _:      KNo{"not a table"}# the matching pair, or the rest of the tabledef look.step(eq: Bool, vv: Val, more: Unit -> Look) -> Look:  match eq:    case True{}:      look.of(vv)    case False{}:      more(Unit{})# what a key in this table refers todef rows.look(rows: List<&2, Val>, +name: String) -> Look:  match rows:    case []:      KMiss{}    case VPair{n, v} <> t:      look.step(str.eq(n, name), v, _u => rows.look(t, name))    case _ <> t:      rows.look(t, name)# the newest element. rows stay newest-first until the document is sealeddef look.last.one(vv: Val, path: String, older: List<&2, Val>) -> Last:  match vv:    case VAot{_p, rows}:      LastSome{path, older, rows}    case _:      LastNo{"not a table"}# the newest element of an array of tables, and the older elements behind itdef look.last(elems: List<&2, Val>, path: String) -> Last:  match elems:    case []:      LastNo{"empty array of tables"}    case h <> t:      look.last.one(h, path, t)# put the child rows back into the parent table. A new implicit table is# consed, not copied on. Inside an inline table it is a table too until the# inline table closes, so the next dotted key can go on in it (inl.seal)def tree.wrap(fr: Fr, child: List<&2, Val>) -> List<&2, Val>:  match fr:    case FrNew{name, parent, path}:      VPair{name, VHead{path, child}} <> parent    case FrRep{name, parent, path}:      rows.repl(parent, name, VHead{path, child})    case FrAot{name, parent, pre, +path}:      rows.repl(parent, name, VAots{path, VAot{path, child} <> pre})# walk the frames back to the rootdef tree.rebuild(stack: List<&2, Fr>, vs: List<&2, Val>, bad: String, heads: List<&2, String>) -> End:  match stack:    case []:      End{vs, bad, heads}    case fr <> t:      tree.rebuild(t, tree.wrap(fr, vs), bad, heads)# a key that is already present is a duplicatedef tree.leaf.put(  hit: Hit,  name: String,  vs: List<&2, Val>,  val: Val,  stack: List<&2, Fr>,  bad: String,  heads: List<&2, String>) -> End:  match hit:    case Found{_v}:      tree.rebuild(stack, vs, err.first(bad, "duplicate key"), heads)    case Miss{}:      tree.rebuild(stack, VPair{name, val} <> vs, bad, heads)# an explicit header for a table that already existsdef tree.leaf.def.head(  seen: Bool,  name: String,  vs: List<&2, Val>,  rows: List<&2, Val>,  stack: List<&2, Fr>,  bad: String,  +path: String,  heads: List<&2, String>) -> End:  match seen:    case True{}:      tree.rebuild(stack, vs, err.first(bad, "duplicate table"), heads)    case False{}:      tree.rebuild(stack, rows.repl(vs, name, VHead{path, rows}), bad,        heads.by.head(path) <> heads)# open a standard tabledef tree.leaf.def.hit(  hit: Look,  name: String,  vs: List<&2, Val>,  stack: List<&2, Fr>,  bad: String,  +path: String,  +heads: List<&2, String>) -> End:  match hit:    case KMiss{}:      tree.rebuild(stack, VPair{name, VHead{path, []}} <> vs, bad,        heads.by.head(path) <> heads)    case KHead{_p, rows}:      tree.leaf.def.head(heads.has(heads, path), name, vs, rows, stack, bad, path,        heads)    case KInl{}:      tree.rebuild(stack, vs, err.first(bad, "inline table is closed"), heads)    case KAots{_p, _elems}:      tree.rebuild(stack, vs, err.first(bad, "not a table"), heads)    case KNo{why}:      tree.rebuild(stack, vs, err.first(bad, why), heads)# open a standard tabledef tree.leaf.def(  hit: Look,  +name: String,  vs: List<&2, Val>,  stack: List<&2, Fr>,  bad: String,  pre: String,  heads: List<&2, String>) -> End:  tree.leaf.def.hit(hit, name, vs, stack, bad, tree.pre(pre, name), heads)# append one element of an array of tablesdef tree.leaf.aot.hit(  hit: Look,  name: String,  vs: List<&2, Val>,  stack: List<&2, Fr>,  bad: String,  +path: String,  heads: List<&2, String>) -> End:  match hit:    case KMiss{}:      tree.rebuild(stack, VPair{name, VAots{path, [VAot{path, []}]}} <> vs, bad, heads)    case KAots{_p, elems}:      tree.rebuild(stack, rows.repl(vs, name, VAots{path, VAot{path, []} <> elems}),        bad, heads.drop(heads, path ++ "."))    case KHead{_p, _rows}:      tree.rebuild(stack, vs, err.first(bad, "not a table"), heads)    case KInl{}:      tree.rebuild(stack, vs, err.first(bad, "inline table is closed"), heads)    case KNo{why}:      tree.rebuild(stack, vs, err.first(bad, why), heads)# append one element of an array of tablesdef tree.leaf.aot(  hit: Look,  +name: String,  vs: List<&2, Val>,  stack: List<&2, Fr>,  bad: String,  pre: String,  heads: List<&2, String>) -> End:  tree.leaf.aot.hit(hit, name, vs, stack, bad, tree.pre(pre, name), heads)# the key at the end of the pathdef tree.leaf.job(  job: Job,  +name: String,  +vs: List<&2, Val>,  stack: List<&2, Fr>,  bad: String,  pre: String,  heads: List<&2, String>) -> End:  match job:    case JPut{val}:      tree.leaf.put(get(vs, name), name, vs, val, stack, bad, heads)    case JDef{}:      tree.leaf.def(rows.look(vs, name), name, vs, stack, bad, pre, heads)    case JAot{}:      tree.leaf.aot(rows.look(vs, name), name, vs, stack, bad, pre, heads)# a failed walk rebuilds what it hasdef tree.leaf.bad(  ok: Bool,  name: String,  vs: List<&2, Val>,  job: Job,  stack: List<&2, Fr>,  bad: String,  pre: String,  heads: List<&2, String>) -> End:  match ok:    case False{}:      tree.rebuild(stack, vs, bad, heads)    case True{}:      tree.leaf.job(job, name, vs, stack, bad, pre, heads)# the key at the end of the pathdef tree.leaf(name: String, tw: Tw) -> End:  Tw{vs, job, stack, +bad, pre, heads, _inl} = tw  tree.leaf.bad(String.is_empty(bad), name, vs, job, stack, bad, pre, heads)# enter the last element of an array of tablesdef tree.sub.aots(  last: Last,  +name: String,  +vs: List<&2, Val>,  job: Job,  stack: List<&2, Fr>,  +pre: String,  heads: List<&2, String>,  inl: Bool,  path: String) -> Tw:  match last:    case LastSome{_p, prefix, rows}:      Tw{rows, job, FrAot{name, vs, prefix, path} <> stack, "", tree.pre(pre, name),        heads, inl}    case LastNo{why}:      Tw{vs, job, stack, why, pre, heads, inl}# a prefix of the current table is navigation; a dotted segment defines a tabledef tree.sub.job(mark: Bool, job: Job) -> Job:  match mark:    case True{}:      job    case False{}:      JDef{}# a segment of a dotted key, which defines tables as it goesdef tree.sub.dot(job: Job) -> Bool:  match job:    case JPut{_val}:      True{}    case _:      False{}# enter a table, unless a dotted key would add to a table a header defineddef tree.sub.head.go(  shut: Bool,  path: String,  rows: List<&2, Val>,  +name: String,  vs: List<&2, Val>,  +job: Job,  stack: List<&2, Fr>,  +pre: String,  heads: List<&2, String>,  inl: Bool,  mark: Bool) -> Tw:  match shut:    case True{}:      Tw{vs, job, stack, "duplicate table", pre, heads, inl}    case False{}:      Tw{rows, job, FrRep{name, vs, path} <> stack, "", tree.pre(pre, name),        heads.define(tree.sub.job(mark, job), tree.pre(pre, name), heads), inl}# a dotted key asks whether a header defined the table; a header walk does notdef tree.sub.head(  dot: Bool,  path: String,  rows: List<&2, Val>,  +name: String,  vs: List<&2, Val>,  job: Job,  stack: List<&2, Fr>,  +pre: String,  +heads: List<&2, String>,  inl: Bool,  mark: Bool) -> Tw:  match dot:    case True{}:      tree.sub.head.go(heads.head(heads, tree.pre(pre, name)), path, rows, name, vs, job,        stack, pre, heads, inl, mark)    case False{}:      tree.sub.head.go(False{}, path, rows, name, vs, job, stack, pre, heads, inl, mark)# a header enters the last element of an array of tables; a dotted key cannot# add to an array of tablesdef tree.sub.arr(  dot: Bool,  +path: String,  elems: List<&2, Val>,  +name: String,  +vs: List<&2, Val>,  job: Job,  stack: List<&2, Fr>,  +pre: String,  heads: List<&2, String>,  inl: Bool) -> Tw:  match dot:    case True{}:      Tw{vs, job, stack, "duplicate table", pre, heads, inl}    case False{}:      tree.sub.aots(look.last(elems, path), name, vs, job, stack, pre, heads, inl, path)# descend into one key, or record why it cannot be entereddef tree.sub.look(  hit: Look,  +name: String,  +vs: List<&2, Val>,  +job: Job,  stack: List<&2, Fr>,  +pre: String,  heads: List<&2, String>,  +inl: Bool,  +mark: Bool) -> Tw:  match hit:    case KMiss{}:      Tw{[], job, FrNew{name, vs, tree.pre(pre, name)} <> stack, "",        tree.pre(pre, name), heads.define(tree.sub.job(mark, job), tree.pre(pre, name),          heads), inl}    case KHead{path, rows}:      tree.sub.head(tree.sub.dot(tree.sub.job(mark, job)), path, rows, name, vs, job,        stack, pre, heads, inl, mark)    case KInl{}:      Tw{vs, job, stack, "inline table is closed", pre, heads, inl}    case KAots{path, elems}:      tree.sub.arr(tree.sub.dot(tree.sub.job(mark, job)), path, elems, name, vs, job,        stack, pre, heads, inl)    case KNo{why}:      Tw{vs, job, stack, why, pre, heads, inl}# descend into one key when the walk is still welldef tree.sub.bad(  ok: Bool,  +name: String,  +vs: List<&2, Val>,  job: Job,  stack: List<&2, Fr>,  bad: String,  pre: String,  heads: List<&2, String>,  inl: Bool,  mark: Bool) -> Tw:  match ok:    case False{}:      Tw{vs, job, stack, bad, pre, heads, inl}    case True{}:      tree.sub.look(rows.look(vs, name), name, vs, job, stack, pre, heads, inl, mark)# descend into one keydef tree.sub(name: String, tw: Tw, mark: Bool) -> Tw:  Tw{+vs, job, stack, +bad, pre, heads, inl} = tw  tree.sub.bad(String.is_empty(bad), name, vs, job, stack, bad, pre, heads, inl, mark)# how many segments are already the current tabledef path.len(xs: List<&2, String>, nn: U32) -> U32:  match xs:    case []:      nn    case _ <> t:      path.len(t, U32.add(nn, 1))# one fewer prefix segment, staying at zerodef path.dec.z(zz: Bool, skip: U32) -> U32:  match zz:    case True{}:      0    case False{}:      U32.sub(skip, 1)# one fewer prefix segment, staying at zerodef path.dec(+skip: U32) -> U32:  path.dec.z(U32.is_zero(skip), skip)# an empty path records an errordef tree.walk.none(tw: Tw) -> End:  Tw{vs, _job, stack, bad, _pre, heads, _inl} = tw  tree.rebuild(stack, vs, err.first(bad, "empty key"), heads)# follow a key path and apply the job at its end. skip is the current tabledef tree.walk(path: List<&2, String>, tw: Tw, +skip: U32) -> End:  match path:    case []:      tree.walk.none(tw)    case name <> rest:      match rest:        case []:          tree.leaf(name, tw)        case _ <> _:          tree.walk(rest, tree.sub(name, tw, U32.is_zero(skip)), path.dec(skip))# space or tabdef ch.sp(+cc: Char) -> Bool:  Bool.or(U32.is_eq(Char.to_u32(cc), 32), U32.is_eq(Char.to_u32(cc), 9))# the space character itself, U+0020, the only one a datetime may holddef ch.space(+cc: Char) -> Bool:  U32.is_eq(Char.to_u32(cc), 32)# line feed or carriage returndef ch.nl(+cc: Char) -> Bool:  Bool.or(U32.is_eq(Char.to_u32(cc), 10), U32.is_eq(Char.to_u32(cc), 13))# a control: U+0000 to U+001F, or U+007Fdef ch.ctl(+cc: Char) -> Bool:  Bool.or(U32.is_lt(Char.to_u32(cc), 32), U32.is_eq(Char.to_u32(cc), 127))# a character that ends a bare valuedef ch.delim(+cc: Char) -> Bool:  Bool.or(ch.sp(cc), Bool.or(ch.nl(cc), Bool.or(Char.is_eq(cc, '#'),    Bool.or(Char.is_eq(cc, ','), Bool.or(Char.is_eq(cc, ']'), Char.is_eq(cc, '}'))))))# whitespace that a bare value swallowsdef ch.skip(+cc: Char) -> Bool:  Bool.or(ch.sp(cc), ch.nl(cc))# the quote character of a string kinddef q.char(kk: QKind) -> Char:  match kk:    case QLit{}:      '\''    case QMultiLit{}:      '\''    case _:      '"'# a multiline stringdef q.multi(kk: QKind) -> Bool:  match kk:    case QMulti{}:      True{}    case QMultiLit{}:      True{}    case _:      False{}# a basic string, which has escapesdef q.basic(kk: QKind) -> Bool:  match kk:    case QBasic{}:      True{}    case QMulti{}:      True{}    case _:      False{}# the multiline kind of a one-line kinddef q.as.multi(kk: QKind) -> QKind:  match kk:    case QLit{}:      QMultiLit{}    case _:      QMulti{}# a pattern character that stands for a digitdef date.mode(pp: Char) -> Bool:  U32.is_eq(Char.to_u32(pp), 68)# whether the rest of the shape matchesdef date.eq.go(ok: Bool, more: Unit -> Bool) -> Bool:  match ok:    case False{}:      False{}    case True{}:      more(Unit{})# one character of a date shapedef date.step(mode: Bool, digit: Bool, same: Bool, more: Unit -> Bool) -> Bool:  match mode:    case True{}:      date.eq.go(digit, more)    case False{}:      date.eq.go(same, more)# both lists ended togetherdef date.eq.nil(pat: List<&2, Char>) -> Bool:  match pat:    case []:      True{}    case _ <> _:      False{}# YYYY-MM-DD, with D standing for a digitdef date.eq(cs: List<&2, Char>, pat: List<&2, Char>) -> Bool:  match cs:    case []:      date.eq.nil(pat)    case +h <> t:      match pat:        case []:          False{}        case +p <> r:          date.step(date.mode(p), Char.is_digit(h), Char.is_eq(h, p),            _u => date.eq(t, r))# the buffer is a calendar datedef date.shaped(buf: List<&2, Char>) -> Bool:  date.eq(List.reverse(&2, Char, buf), String.to_list("DDDD-DD-DD"))# the buffer is reversed, so each head goes on the front of the textdef buf.str.go(buf: List<&2, Char>, acc: String) -> String:  match buf:    case []:      acc    case +h <> t:      buf.str.go(t, SCon{h, acc})# the buffer, in orderdef buf.str(buf: List<&2, Char>) -> String:  buf.str.go(buf, "")# the first n characters of a source spandef span.str(src: String, +nn: U32, zz: Bool) -> String:  match src zz:    case _s True{}:      ""    case SNil{} False{}:      ""    case SCon{+h, t} False{}:      SCon{h, span.str(t, U32.sub(nn, 1), U32.is_zero(U32.sub(nn, 1)))}# the first n characters, reversed, so an escape can keep copyingdef span.rev(src: String, +nn: U32, zz: Bool, acc: List<&2, Char>) -> List<&2, Char>:  match src zz:    case _s True{}:      acc    case SNil{} False{}:      acc    case SCon{h, t} False{}:      span.rev(t, U32.sub(nn, 1), U32.is_zero(U32.sub(nn, 1)), h <> acc)# the unread suffix of a span that will not be storeddef span.drop(src: String) -> Unit:  match src:    case SNil{}:      Unit{}    case SCon{_h, t}:      span.drop(t)# the key path, document table then this keydef path.cat(here: List<&2, String>, parts: List<&2, String>) -> List<&2, String>:  List.append(&2, String, here, List.reverse(&2, String, parts))# a multiline string keeps a newlinedef str.bad.nl(nl: Bool, multi: Bool) -> Bool:  match nl:    case False{}:      True{}    case True{}:      Bool.not(multi)# tab is keptdef str.bad.tab(tab: Bool, nl: Bool, multi: Bool) -> Bool:  match tab:    case True{}:      False{}    case False{}:      str.bad.nl(nl, multi)# a raw control is rejecteddef str.bad.go(ctrl: Bool, tab: Bool, nl: Bool, multi: Bool) -> Bool:  match ctrl:    case False{}:      False{}    case True{}:      str.bad.tab(tab, nl, multi)# a raw control is rejected. Tab is kept. A multiline string also keeps newlinesdef str.bad(+uu: U32, multi: Bool) -> Bool:  str.bad.go(Bool.or(U32.is_lt(uu, 32), U32.is_eq(uu, 127)), U32.is_eq(uu, 9),    Bool.or(U32.is_eq(uu, 10), U32.is_eq(uu, 13)), multi)# the scanner has faileddef st.err(st: St, why: String) -> St:  St{+bad, rows, heads, here, parts, buf, _lex, back, qkind, stack, _hold, uni, left,    arrtab, lead} = st  St{err.first(bad, why), rows, heads, here, parts, buf, LSink{}, back, qkind, stack,    HNone{}, uni, left, arrtab, lead}# replace the lex statedef st.lex(st: St, lex: Lex) -> St:  St{bad, rows, heads, here, parts, buf, _lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left,    arrtab, lead}# hold one character and set the lex statedef st.hold(st: St, +lex: Lex, cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, _lex, _back, qkind, stack, _hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, lex, lex, qkind, stack, HChar{cc}, uni, left,    arrtab, lead}# append one characterdef st.push(st: St, cc: Char, lex: Lex) -> St:  St{bad, rows, heads, here, parts, buf, _lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, cc <> buf, lex, back, qkind, stack, hold, uni, left,    arrtab, lead}# the segment in the buffer joins the key, and the buffer is cleareddef seg.push(st: St, lex: Lex) -> St:  St{bad, rows, heads, here, parts, buf, _lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, buf.str(buf) <> parts, [], lex, back, qkind, stack, hold,    uni, left, arrtab, lead}# a finished value at the document rootdef take.root(  end: End,  here: List<&2, String>,  back: Lex,  qkind: QKind,  uni: U32,  left: U32,  arrtab: Bool,  lead: Bool) -> St:  End{rows, bad, heads} = end  St{bad, rows, heads, here, [], [], LLine{}, back, qkind, [], HNone{}, uni, left, arrtab,    lead}# a finished value inside an inline tabledef take.inl(  end: End,  rows: List<&2, Val>,  heads: List<&2, String>,  +here: List<&2, String>,  +parts: List<&2, String>,  rest: List<&2, Ctx>,  back: Lex,  qkind: QKind,  uni: U32,  left: U32,  arrtab: Bool,  lead: Bool) -> St:  End{inl, bad, _h} = end  St{bad, rows, heads, here, [], [], LInlC{}, back, qkind,    CInl{inl, parts, here} <> rest, HNone{}, uni, left, arrtab, lead}# place a finished value into the current table, array, or inline tabledef take.top(  stack: List<&2, Ctx>,  val: Val,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  +here: List<&2, String>,  parts: List<&2, String>,  back: Lex,  qkind: QKind,  uni: U32,  left: U32,  arrtab: Bool,  lead: Bool) -> St:  match stack:    case []:      take.root(tree.walk(path.cat(here, parts),        Tw{rows, JPut{val}, [], bad, "", heads, False{}}, path.len(here, 0)), here,        back, qkind, uni, left, arrtab, lead)    case CArr{items, +saved, +at} <> rest:      St{bad, rows, heads, at, saved, [], LArrC{}, back, qkind,        CArr{val <> items, saved, at} <> rest, HNone{}, uni, left, arrtab, lead}    case CInl{inl, +saved, +at} <> rest:      take.inl(tree.walk(List.reverse(&2, String, parts),        Tw{inl, JPut{val}, [], bad, "", [], True{}}, 0), rows, heads, at, saved, rest,        back,        qkind, uni, left, arrtab, lead)# place a finished valuedef val.take(st: St, val: Val) -> St:  St{bad, rows, heads, here, parts, _buf, _lex, back, qkind, stack, _hold, uni, left,    arrtab, lead} = st  take.top(stack, val, bad, rows, heads, here, parts, back, qkind, uni, left, arrtab,    lead)# hold the delimiter when the following state must see itdef bare.hold.on(st: St, cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, _h, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, HChar{cc}, uni, left,    arrtab, lead}# hold the delimiter when the following state must see it# a newline that ended a root value already separates the next expressiondef bare.line.lex(lex: Lex, st: St) -> St:  match lex:    case LLine{}:      st.lex(st, LTop{})    case _:      st# a consumed newline returns to the top level after a root valuedef bare.line.go(st: St) -> St:  St{bad, rows, heads, here, parts, buf, +lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  bare.line.lex(lex, St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack,    hold, uni, left, arrtab, lead})# a consumed newline returns to the top level after a root valuedef bare.line(st: St, nl: Bool) -> St:  match nl:    case True{}:      bare.line.go(st)    case False{}:      st# hold the delimiter when the following state must see itdef bare.hold(st: St, cc: Char, hold: Bool) -> St:  match hold:    case False{}:      bare.line(st, ch.nl(cc))    case True{}:      bare.hold.on(st, cc)# a bare word has been classifieddef bare.got(gg: Got, st: St, cc: Char, hold: Bool) -> St:  match gg:    case GotBad{why}:      st.err(st, why)    case GotVal{val}:      bare.hold(val.take(st, val), cc, hold)# finish a bare word. hold is true when the delimiter is not whitespacedef bare.finish(st: St, cc: Char, hold: Bool) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, h, uni, left, arrtab,    lead} = st  bare.got(word.val(List.reverse(&2, Char, buf)),    St{bad, rows, heads, here, parts, [], lex, back, qkind, stack, h, uni, left, arrtab,      lead}, cc, hold)# close the header that was just readdef head.apply(end: End, segs: List<&2, String>, back: Lex, qkind: QKind, uni: U32, left: U32, lead: Bool) -> St:  End{rows, bad, heads} = end  St{bad, rows, heads, segs, [], [], LLine{}, back, qkind, [], HNone{}, uni, left,    False{}, lead}# an array-of-tables header, or a table headerdef head.job(  arr: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  +segs: List<&2, String>,  back: Lex,  qkind: QKind,  uni: U32,  left: U32,  lead: Bool) -> St:  match arr:    case True{}:      head.apply(tree.walk(segs, Tw{rows, JAot{}, [], bad, "", heads, False{}}, 0),        segs,        back, qkind, uni, left, lead)    case False{}:      head.apply(tree.walk(segs, Tw{rows, JDef{}, [], bad, "", heads, False{}}, 0),        segs,        back, qkind, uni, left, lead)# open the table or the array of tables named by the headerdef head.on(st: St, segs: List<&2, String>) -> St:  St{bad, rows, heads, _here, _parts, _buf, _lex, back, qkind, _stack, _hold, uni, left,    arrtab, lead} = st  head.job(arrtab, bad, rows, heads, segs, back, qkind, uni, left, lead)# the header key is readydef head.go(empty: Bool, st: St, segs: List<&2, String>) -> St:  match empty:    case True{}:      st.err(st, "empty key")    case False{}:      head.on(st, segs)# the segment was pusheddef head.use(st: St) -> St:  St{bad, rows, heads, here, +parts, buf, lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  head.go(List.is_empty(&2, String, parts),    St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left,      arrtab, lead}, List.reverse(&2, String, parts))# the buffer is the last segment when it holds onedef head.fin.buf(empty: Bool, st: St) -> St:  match empty:    case True{}:      head.use(st)    case False{}:      head.use(seg.push(st, LTop{}))# push a trailing segment, then open the headerdef head.fin(st: St) -> St:  St{bad, rows, heads, here, parts, +buf, lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  head.fin.buf(List.is_empty(&2, Char, buf),    St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left,      arrtab, lead})# close an array and place itdef arr.top(  stack: List<&2, Ctx>,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  back: Lex,  qkind: QKind,  uni: U32,  left: U32,  arrtab: Bool,  lead: Bool) -> St:  match stack:    case CArr{items, parts, here} <> rest:      val.take(St{bad, rows, heads, here, parts, [], LTop{}, back, qkind, rest, HNone{},        uni, left, arrtab, lead}, VArr{List.reverse(&2, Val, items)})    case _:      St{err.first(bad, "unclosed"), rows, heads, [], [], [], LSink{}, back, qkind, [],        HNone{}, uni, left, arrtab, lead}# close an array and place itdef arr.close(st: St) -> St:  St{bad, rows, heads, _here, _parts, _buf, _lex, back, qkind, stack, _hold, uni, left,    arrtab, lead} = st  arr.top(stack, bad, rows, heads, back, qkind, uni, left, arrtab, lead)# an inline table is closed once written: the tables its dotted keys made,# open while it was read, close with itdef inl.seal(rows: List<&2, Val>) -> List<&2, Val>:  match rows:    case []:      []    case VPair{name, VHead{_path, sub}} <> t:      VPair{name, VInl{inl.seal(sub)}} <> inl.seal(t)    case h <> t:      h <> inl.seal(t)# close an inline table and place itdef inl.top(  stack: List<&2, Ctx>,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  back: Lex,  qkind: QKind,  uni: U32,  left: U32,  arrtab: Bool,  lead: Bool) -> St:  match stack:    case CInl{inl, parts, here} <> rest:      val.take(St{bad, rows, heads, here, parts, [], LTop{}, back, qkind, rest, HNone{},        uni, left, arrtab, lead}, VInl{inl.seal(inl)})    case _:      St{err.first(bad, "unclosed"), rows, heads, [], [], [], LSink{}, back, qkind, [],        HNone{}, uni, left, arrtab, lead}# close an inline table and place itdef inl.close(st: St) -> St:  St{bad, rows, heads, _here, _parts, _buf, _lex, back, qkind, stack, _hold, uni, left,    arrtab, lead} = st  inl.top(stack, bad, rows, heads, back, qkind, uni, left, arrtab, lead)# the innermost frame is an inline tabledef inl.open(stack: List<&2, Ctx>) -> Bool:  match stack:    case CInl{_rows, _parts, _here} <> _rest:      True{}    case _:      False{}# a string is finisheddef str.fin(st: St) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left, arrtab,    lead} = st  val.take(St{bad, rows, heads, here, parts, [], lex, back, qkind, stack, hold, uni, left,    arrtab, lead}, VStr{buf.str(buf)})# one or two quotes that did not close a multiline stringdef mq.put(one: Bool, +qq: Char, buf: List<&2, Char>) -> List<&2, Char>:  match one:    case True{}:      qq <> buf    case False{}:      qq <> (qq <> buf)# a code point may be written as a characterdef uni.ok(+uu: U32) -> Bool:  Bool.and(U32.is_le(uu, 1114111),    Bool.not(Bool.and(U32.is_ge(uu, 55296), U32.is_le(uu, 57343))))# a finished unicode escape written into the bufferdef uni.finish.on(st: St, uu: U32, back: Lex) -> St:  St{bad, rows, heads, here, parts, buf, _lex, b, qkind, stack, _hold, _uni, _left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, Char.from_u32(uu) <> buf, back, b, qkind, stack,    HNone{}, 0, 0, arrtab, lead}# a finished unicode escapedef uni.finish(ok: Bool, st: St, uu: U32, back: Lex) -> St:  match ok:    case False{}:      st.err(st, "invalid escape")    case True{}:      uni.finish.on(st, uu, back)# another hex digit is still requireddef uni.next.more(st: St, uu: U32, left: U32) -> St:  St{bad, rows, heads, here, parts, buf, lex, b, qkind, stack, hold, _uni, _left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, lex, b, qkind, stack, hold, uu,    U32.sub(left, 1), arrtab, lead}# the escape is finished, or another hex digit is requireddef uni.next(last: Bool, st: St, +uu: U32, back: Lex, left: U32) -> St:  match last:    case True{}:      uni.finish(uni.ok(uu), st, uu, back)    case False{}:      uni.next.more(st, uu, left)# one hex digit of a unicode escapedef uni.go(ok: Bool, st: St, +cc: Char, back: Lex, uni: U32, +left: U32) -> St:  match ok:    case False{}:      st.err(st, "invalid escape")    case True{}:      uni.next(U32.is_eq(left, 1), st, U32.add(U32.mul(uni, 16), dig.val(cc)), back, left)# one hex digit of a unicode escapedef uni.step(st: St, +cc: Char, back: Lex) -> St:  St{bad, rows, heads, here, parts, buf, lex, b, qkind, stack, hold, +uni, +left, arrtab,    lead} = st  uni.go(dig.ok(dig.val(cc), 16),    St{bad, rows, heads, here, parts, buf, lex, b, qkind, stack, hold, uni, left, arrtab,      lead}, cc, back, uni, left)# a long unicode escape, or an unknown escapedef esc.uu(uu: Bool) -> Esc:  match uu:    case True{}:      EUni{8}    case False{}:      EBad{}# a unicode escapedef esc.u(uuu: Bool, uu: Bool) -> Esc:  match uuu:    case True{}:      EUni{4}    case False{}:      esc.uu(uu)# carriage return or a unicode escapedef esc.r(rr: Bool, uuu: Bool, uu: Bool) -> Esc:  match rr:    case True{}:      EChar{'\r'}    case False{}:      esc.u(uuu, uu)# form feed or the restdef esc.f(ff: Bool, rr: Bool, uuu: Bool, uu: Bool) -> Esc:  match ff:    case True{}:      EChar{Chr{12}}    case False{}:      esc.r(rr, uuu, uu)# newline or the restdef esc.n(nn: Bool, ff: Bool, rr: Bool, uuu: Bool, uu: Bool) -> Esc:  match nn:    case True{}:      EChar{'\n'}    case False{}:      esc.f(ff, rr, uuu, uu)# tab, newline, form feed, carriage return, or a unicode escapedef esc.t(tt: Bool, nn: Bool, ff: Bool, rr: Bool, uuu: Bool, uu: Bool) -> Esc:  match tt:    case True{}:      EChar{'\t'}    case False{}:      esc.n(nn, ff, rr, uuu, uu)# backslash, quotation mark, or a letter escapedef esc.letter(bb: Bool, tt: Bool, nn: Bool, ff: Bool, rr: Bool, uuu: Bool, uu: Bool) -> Esc:  match bb:    case True{}:      EChar{Chr{8}}    case False{}:      esc.t(tt, nn, ff, rr, uuu, uu)# spaces before the newline of a line-ending backslashdef esc.sp(sp: Bool, bb: Bool, tt: Bool, nn: Bool, ff: Bool, rr: Bool, uuu: Bool, uu: Bool) -> Esc:  match sp:    case True{}:      EFold{False{}}    case False{}:      esc.letter(bb, tt, nn, ff, rr, uuu, uu)# a line-ending backslash, or a letterdef esc.fold(nl: Bool, sp: Bool, bb: Bool, tt: Bool, nn: Bool, ff: Bool, rr: Bool, uuu: Bool, uu: Bool) -> Esc:  match nl:    case True{}:      EFold{True{}}    case False{}:      esc.sp(sp, bb, tt, nn, ff, rr, uuu, uu)# a backslash or a letterdef esc.bs(bs: Bool, bb: Bool, tt: Bool, nn: Bool, ff: Bool, rr: Bool, uuu: Bool, uu: Bool, nl: Bool, sp: Bool) -> Esc:  match bs:    case True{}:      EChar{'\\'}    case False{}:      esc.fold(nl, sp, bb, tt, nn, ff, rr, uuu, uu)# a quote, a backslash, or a letterdef esc.of.go(  qq: Bool,  bs: Bool,  bb: Bool,  tt: Bool,  nn: Bool,  ff: Bool,  rr: Bool,  uuu: Bool,  uu: Bool,  nl: Bool,  sp: Bool) -> Esc:  match qq:    case True{}:      EChar{'"'}    case False{}:      esc.bs(bs, bb, tt, nn, ff, rr, uuu, uu, nl, sp)# classify one escaped characterdef esc.of(+cc: Char) -> Esc:  esc.of.go(Char.is_eq(cc, '"'), Char.is_eq(cc, '\\'), Char.is_eq(cc, 'b'),    Char.is_eq(cc, 't'), Char.is_eq(cc, 'n'), Char.is_eq(cc, 'f'), Char.is_eq(cc, 'r'),    Char.is_eq(cc, 'u'), Char.is_eq(cc, 'U'), ch.nl(cc), ch.sp(cc))# the lex state that reads the hex digits of an escapedef esc.uni.lex(back: Lex) -> Lex:  match back:    case LKeyQ{}:      LKeyU{}    case LHeadQ{}:      LHeadU{}    case _:      LUni{}# a unicode escape starts with no digits seendef esc.uni(st: St, back: Lex, nn: U32) -> St:  St{bad, rows, heads, here, parts, buf, _lex, b, qkind, stack, hold, _uni, _left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, esc.uni.lex(back), b, qkind, stack, hold, 0, nn,    arrtab, lead}# 1 once a newline has been seendef esc.fold.bit(saw: Bool) -> U32:  match saw:    case True{}:      1    case False{}:      0# a line-ending backslash skips following whitespacedef esc.fold.on(st: St, saw: Bool) -> St:  St{bad, rows, heads, here, parts, buf, _lex, b, qkind, stack, hold, _uni, _left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, LFold{}, b, qkind, stack, hold, 0,    esc.fold.bit(saw), arrtab, lead}# a line-ending backslash is only part of a multiline stringdef esc.fold.kind(multi: Bool, st: St, saw: Bool) -> St:  match multi:    case True{}:      esc.fold.on(st, saw)    case False{}:      st.err(st, "invalid escape")# a line-ending backslash is only part of a multiline stringdef esc.fold.str(st: St, saw: Bool) -> St:  St{bad, rows, heads, here, parts, buf, lex, b, +qkind, stack, hold, uni, left,    arrtab, lead} = st  esc.fold.kind(q.multi(qkind), St{bad, rows, heads, here, parts, buf, lex, b, qkind,    stack, hold, uni, left, arrtab, lead}, saw)# a line-ending backslash is only part of a stringdef esc.fold.use(back: Lex, st: St, saw: Bool) -> St:  match back:    case LStr{}:      esc.fold.str(st, saw)    case _:      st.err(st, "invalid escape")# apply one escape and return to the stringdef esc.use(ee: Esc, st: St, back: Lex) -> St:  match ee:    case EChar{c}:      st.push(st, c, back)    case EUni{n}:      esc.uni(st, back, n)    case EFold{saw}:      esc.fold.use(back, st, saw)    case EBad{}:      st.err(st, "invalid escape")# the lex state a comment returns todef com.enter(st: St, back: Lex) -> St:  St{bad, rows, heads, here, parts, buf, _lex, _back, qkind, stack, hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, LCom{}, back, qkind, stack, hold, uni, left,    arrtab, lead}# a bare key, or a rejected characterdef lex.top.bare(bare: Bool, st: St, cc: Char) -> St:  match bare:    case True{}:      st.push(st, cc, LKey{})    case False{}:      st.err(st, "invalid key")# a header starts with a clear keydef lex.bracket(st: St) -> St:  St{bad, rows, heads, here, _parts, _buf, _lex, back, qkind, stack, hold, uni, left,    _arr, lead} = st  St{bad, rows, heads, here, [], [], LHead{}, back, qkind, stack, hold, uni, left,    False{}, lead}# a quoted key starts with an empty bufferdef lex.qkey(st: St, kind: QKind, lex: Lex) -> St:  St{bad, rows, heads, here, parts, _buf, _lex, back, _q, stack, hold, uni, left, arrtab,    lead} = st  St{bad, rows, heads, here, parts, [], lex, back, kind, stack, hold, uni, left, arrtab,    lead}# a literal key or a bare keydef lex.top.lit(lit: Bool, bare: Bool, st: St, cc: Char) -> St:  match lit:    case True{}:      lex.qkey(st, QLit{}, LKeyQ{})    case False{}:      lex.top.bare(bare, st, cc)# a quoted key or a bare keydef lex.top.q(qq: Bool, lit: Bool, bare: Bool, st: St, cc: Char) -> St:  match qq:    case True{}:      lex.qkey(st, QBasic{}, LKeyQ{})    case False{}:      lex.top.lit(lit, bare, st, cc)# a header or a keydef lex.top.br(br: Bool, qq: Bool, lit: Bool, bare: Bool, st: St, cc: Char) -> St:  match br:    case True{}:      lex.bracket(st)    case False{}:      lex.top.q(qq, lit, bare, st, cc)# a comment, a header, or a keydef lex.top.hash(hash: Bool, br: Bool, qq: Bool, lit: Bool, bare: Bool, st: St, cc: Char) -> St:  match hash:    case True{}:      com.enter(st, LTop{})    case False{}:      lex.top.br(br, qq, lit, bare, st, cc)# a newline, a comment, a header, or a keydef lex.top.nl(nl: Bool, hash: Bool, br: Bool, qq: Bool, lit: Bool, bare: Bool, st: St, cc: Char) -> St:  match nl:    case True{}:      st    case False{}:      lex.top.hash(hash, br, qq, lit, bare, st, cc)# whitespace, a comment, a header, or a keydef lex.top.go(sp: Bool, nl: Bool, hash: Bool, br: Bool, qq: Bool, lit: Bool, bare: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.top.nl(nl, hash, br, qq, lit, bare, st, cc)# top level: a key, a header, a comment, or whitespacedef lex.top(st: St, +cc: Char) -> St:  lex.top.go(ch.sp(cc), ch.nl(cc), Char.is_eq(cc, '#'), Char.is_eq(cc, '['),    Char.is_eq(cc, '"'), Char.is_eq(cc, '\''), bare.at(cc), st, cc)# a newline before equals is rejecteddef lex.key.nl(nl: Bool, st: St, _c: Char) -> St:  match nl:    case True{}:      st.err(st, "newline in key")    case False{}:      st.err(st, "invalid key")# an empty segment is rejecteddef lex.key.end.go(empty: Bool, st: St, lex: Lex) -> St:  match empty:    case True{}:      st.err(st, "empty key")    case False{}:      seg.push(st, lex)# end a nonempty segmentdef lex.key.end(st: St, next: Lex) -> St:  St{bad, rows, heads, here, parts, +buf, lx, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.key.end.go(List.is_empty(&2, Char, buf),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, next)# a comment ends the segment and waits for equalsdef lex.key.hash(hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match hash:    case True{}:      com.enter(lex.key.end(st, LAfter{}), LAfter{})    case False{}:      lex.key.nl(nl, st, cc)# an equals ends the keydef lex.key.eq(eq: Bool, hash: Bool, nl: Bool, st: St, +cc: Char) -> St:  match eq:    case True{}:      lex.key.end(st, LVal{})    case False{}:      lex.key.hash(hash, nl, st, cc)# a dot ends the segment and skips whitespace before the next onedef lex.key.dot(dot: Bool, eq: Bool, hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match dot:    case True{}:      lex.key.end(st, LKeyDot{})    case False{}:      lex.key.eq(eq, hash, nl, st, cc)# whitespace ends the segmentdef lex.key.sp(sp: Bool, dot: Bool, eq: Bool, hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      lex.key.end(st, LAfter{})    case False{}:      lex.key.dot(dot, eq, hash, nl, st, cc)# a bare character, whitespace, a dot, or an equalsdef lex.key.go(bare: Bool, sp: Bool, dot: Bool, eq: Bool, hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match bare:    case True{}:      st.push(st, cc, LKey{})    case False{}:      lex.key.sp(sp, dot, eq, hash, nl, st, cc)# inside a bare key segmentdef lex.key(st: St, +cc: Char) -> St:  lex.key.go(bare.at(cc), ch.sp(cc), Char.is_eq(cc, '.'), Char.is_eq(cc, '='),    Char.is_eq(cc, '#'), ch.nl(cc), st, cc)# a newline before equals is rejecteddef lex.after.nl(nl: Bool, st: St, _c: Char) -> St:  match nl:    case True{}:      st.err(st, "newline in key")    case False{}:      st.err(st, "invalid key")# a comment waits for equalsdef lex.after.hash(hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match hash:    case True{}:      com.enter(st, LAfter{})    case False{}:      lex.after.nl(nl, st, cc)# equals starts the valuedef lex.after.eq(eq: Bool, hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match eq:    case True{}:      st.lex(st, LVal{})    case False{}:      lex.after.hash(hash, nl, st, cc)# a dot starts the next segment, after any whitespacedef lex.after.dot(dot: Bool, eq: Bool, hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match dot:    case True{}:      st.lex(st, LKeyDot{})    case False{}:      lex.after.eq(eq, hash, nl, st, cc)# whitespace, a dot, or an equalsdef lex.after.go(sp: Bool, dot: Bool, eq: Bool, hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.after.dot(dot, eq, hash, nl, st, cc)# after a key segment, before equals or a dotdef lex.after(st: St, +cc: Char) -> St:  lex.after.go(ch.sp(cc), Char.is_eq(cc, '.'), Char.is_eq(cc, '='), Char.is_eq(cc, '#'),    ch.nl(cc), st, cc)# a value starts on the line of its keydef lex.val.nl3(inside: Bool, st: St) -> St:  match inside:    case True{}:      st.err(st, "newline in inline table")    case False{}:      st.err(st, "newline before value")# a newline before a value is rejecteddef lex.val.nl2(st: St, _c: Char) -> St:  St{bad, rows, heads, here, parts, buf, lx, back, qkind, +stack, hold, uni, left,    arrtab, lead} = st  lex.val.nl3(inl.open(stack),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead})# an array starts a frame and waits for a valuedef lex.array(st: St) -> St:  St{bad, rows, heads, +here, +parts, _buf, _lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, [], [], LArr{}, back, qkind,    CArr{[], parts, here} <> stack, hold, uni, left, arrtab, lead}# an inline table starts a frame and waits for a keydef lex.inline(st: St) -> St:  St{bad, rows, heads, +here, +parts, _buf, _lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, [], [], LInl{}, back, qkind,    CInl{[], parts, here} <> stack, hold, uni, left, arrtab, lead}# an inline table, or a bare valuedef lex.val.inl(inl: Bool, st: St, cc: Char) -> St:  match inl:    case True{}:      lex.inline(st)    case False{}:      st.push(st, cc, LBare{})# an array, or another valuedef lex.val.br(br: Bool, inl: Bool, st: St, cc: Char) -> St:  match br:    case True{}:      lex.array(st)    case False{}:      lex.val.inl(inl, st, cc)# a literal string, or another valuedef lex.val.lit(lit: Bool, br: Bool, inl: Bool, st: St, cc: Char) -> St:  match lit:    case True{}:      lex.qkey(st, QLit{}, LQ{})    case False{}:      lex.val.br(br, inl, st, cc)# a basic string, or another valuedef lex.val.q(qq: Bool, lit: Bool, br: Bool, inl: Bool, st: St, cc: Char) -> St:  match qq:    case True{}:      lex.qkey(st, QBasic{}, LQ{})    case False{}:      lex.val.lit(lit, br, inl, st, cc)# a comment, a string, an array, an inline table, or a bare valuedef lex.val.hash(hash: Bool, qq: Bool, lit: Bool, br: Bool, inl: Bool, st: St, cc: Char) -> St:  match hash:    case True{}:      com.enter(st, LVal{})    case False{}:      lex.val.q(qq, lit, br, inl, st, cc)# a newline before a value is rejecteddef lex.val.nl(nl: Bool, hash: Bool, qq: Bool, lit: Bool, br: Bool, inl: Bool, st: St, cc: Char) -> St:  match nl:    case True{}:      lex.val.nl2(st, cc)    case False{}:      lex.val.hash(hash, qq, lit, br, inl, st, cc)# whitespace or the value itselfdef lex.val.go(sp: Bool, nl: Bool, hash: Bool, qq: Bool, lit: Bool, br: Bool, inl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.val.nl(nl, hash, qq, lit, br, inl, st, cc)# the start of a valuedef lex.val(st: St, +cc: Char) -> St:  lex.val.go(ch.sp(cc), ch.nl(cc), Char.is_eq(cc, '#'), Char.is_eq(cc, '"'),    Char.is_eq(cc, '\''), Char.is_eq(cc, '['), Char.is_eq(cc, '{'), st, cc)# a second quote may start a multiline string; otherwise the first character is helddef lex.q.go(same: Bool, st: St, cc: Char) -> St:  match same:    case True{}:      st.lex(st, LQ2{})    case False{}:      st.hold(st.lex(st, LStr{}), LStr{}, cc)# the first quote has been seendef lex.q(st: St, +cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lx, back, +qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.q.go(Char.is_eq(cc, q.char(qkind)),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, cc)# a multiline string, about to trim one opening newlinedef lex.multi(st: St, kind: QKind) -> St:  St{bad, rows, heads, here, parts, buf, _lex, back, _q, stack, hold, uni, left, arrtab,    lead} = st  St{bad, rows, heads, here, parts, buf, LTrim{}, back, q.as.multi(kind), stack, hold,    uni, left, arrtab, lead}# a third quote starts a multiline string; two quotes are an empty stringdef lex.q2.go(same: Bool, st: St, cc: Char, kind: QKind) -> St:  match same:    case True{}:      lex.multi(st, kind)    case False{}:      bare.hold(val.take(st, VStr{""}), cc, True{})# two quotes have been seendef lex.q2(st: St, +cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lx, back, +qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.q2.go(Char.is_eq(cc, q.char(qkind)),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, cc, qkind)# a line feed is trimmed; any other character is the first of the stringdef lex.trim.nl(nl: Bool, st: St, cc: Char) -> St:  match nl:    case True{}:      st.lex(st, LStr{})    case False{}:      st.hold(st.lex(st, LStr{}), LStr{}, cc)# a carriage return may still have a line feed after itdef lex.trim.go(cr: Bool, nl: Bool, st: St, cc: Char) -> St:  match cr:    case True{}:      st.lex(st, LTrim2{})    case False{}:      lex.trim.nl(nl, st, cc)# trim one opening newline of a multiline stringdef lex.trim(st: St, +cc: Char) -> St:  lex.trim.go(U32.is_eq(Char.to_u32(cc), 13), ch.nl(cc), st, cc)# the line feed of a CRLF opening newlinedef lex.trim2.go(lf: Bool, st: St, cc: Char) -> St:  match lf:    case True{}:      st.lex(st, LStr{})    case False{}:      st.hold(st.lex(st, LStr{}), LStr{}, cc)# the line feed of a CRLF opening newlinedef lex.trim2(st: St, +cc: Char) -> St:  lex.trim2.go(U32.is_eq(Char.to_u32(cc), 10), st, cc)# a forbidden control, or one character of the stringdef lex.str.char.go(bad: Bool, st: St, cc: Char) -> St:  match bad:    case True{}:      st.err(st, "invalid control")    case False{}:      st.push(st, cc, LStr{})# a raw character, rejected when it is a forbidden controldef lex.str.char(multi: Bool, st: St, +cc: Char) -> St:  lex.str.char.go(str.bad(Char.to_u32(cc), multi), st, cc)# an escape, or a plain characterdef lex.str.bs(bs: Bool, multi: Bool, st: St, cc: Char, _kind: QKind) -> St:  match bs:    case True{}:      st.lex(st, LEsc{})    case False{}:      lex.str.char(multi, st, cc)# a backslash starts an escape in a basic stringdef lex.str.esc(basic: Bool, multi: Bool, st: St, +cc: Char, kind: QKind) -> St:  match basic:    case True{}:      lex.str.bs(Char.is_eq(cc, '\\'), multi, st, cc, kind)    case False{}:      lex.str.char(multi, st, cc)# count closing quotes of a multiline stringdef lex.mq(st: St, nn: U32) -> St:  St{bad, rows, heads, here, parts, buf, _lex, back, qkind, stack, hold, _uni, _left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, LMq{}, back, qkind, stack, hold, 0, nn, arrtab,    lead}# one quote closes a one-line string and may close a multiline stringdef lex.str.quote(multi: Bool, st: St) -> St:  match multi:    case False{}:      str.fin(st)    case True{}:      lex.mq(st, 1)# a quote, an escape, or a characterdef lex.str.go(quote: Bool, multi: Bool, basic: Bool, st: St, +cc: Char, kind: QKind) -> St:  match quote:    case True{}:      lex.str.quote(multi, st)    case False{}:      lex.str.esc(basic, multi, st, cc, kind)# inside a stringdef lex.str(st: St, +cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lx, back, +qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.str.go(Char.is_eq(cc, q.char(qkind)), q.multi(qkind), q.basic(qkind),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, cc, qkind)# a closing delimiter has been read. up to two more quotes are still the string'sdef lex.mq.shut(st: St) -> St:  St{bad, rows, heads, here, parts, buf, _lex, back, qkind, stack, hold, uni, _left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, LMqEnd{}, back, qkind, stack, hold, uni, 0,    arrtab, lead}# the second quote waits; the third is a closing delimiterdef lex.mq.one(one: Bool, st: St) -> St:  match one:    case True{}:      lex.mq(st, 2)    case False{}:      lex.mq.shut(st)# quotes that were not a closer return to the string, and the character is helddef lex.mq.back(st: St, cc: Char, buf: List<&2, Char>) -> St:  St{bad, rows, heads, here, parts, _buf, _lex, back, qkind, stack, _hold, uni, _left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, LStr{}, back, qkind, stack, HChar{cc}, uni, 0,    arrtab, lead}# a quote continues the closer; anything else is contentdef lex.mq.go(quote: Bool, one: Bool, st: St, cc: Char, buf: List<&2, Char>, kind: QKind, _left: U32) -> St:  match quote:    case True{}:      lex.mq.one(one, st)    case False{}:      lex.mq.back(st, cc, mq.put(one, q.char(kind), buf))# one character while closing a multiline stringdef lex.mq.step(st: St, +cc: Char) -> St:  St{bad, rows, heads, here, parts, +buf, lx, back, +qkind, stack, hold, uni, +left,    arrtab, lead} = st  lex.mq.go(Char.is_eq(cc, q.char(qkind)), U32.is_eq(left, 1),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, cc, buf, qkind, left)# a quote after the closing delimiter: the delimiter moves one quote ondef lex.mq.more(st: St, cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left, arrtab,    lead} = st  St{bad, rows, heads, here, parts, cc <> buf, lx, back, qkind, stack, hold, uni,    U32.add(left, 1), arrtab, lead}# the string ends before this character, which the next state readsdef lex.mq.fin(st: St, cc: Char) -> St:  bare.hold(str.fin(st), cc, True{})# a third quote after the delimiter would make three in the stringdef lex.mq.room(room: Bool, st: St, cc: Char) -> St:  match room:    case True{}:      lex.mq.more(st, cc)    case False{}:      lex.mq.fin(st, cc)# a quote may still belong to the string; anything else follows itdef lex.mq.end.go(quote: Bool, room: Bool, st: St, cc: Char) -> St:  match quote:    case True{}:      lex.mq.room(room, st, cc)    case False{}:      lex.mq.fin(st, cc)# one character after a multiline string's closing delimiterdef lex.mq.end(st: St, +cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lx, back, +qkind, stack, hold, uni, +left,    arrtab, lead} = st  lex.mq.end.go(Char.is_eq(cc, q.char(qkind)), U32.is_lt(left, 2),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, cc)# one escaped characterdef lex.esc(st: St, +cc: Char) -> St:  esc.use(esc.of(cc), st, LStr{})# remember that a newline was seendef lex.fold.saw(st: St) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, _left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, 1, arrtab,    lead}# a character before the newline is rejecteddef lex.fold.end.go(saw: Bool, st: St, cc: Char) -> St:  match saw:    case True{}:      st.hold(st.lex(st, LStr{}), LStr{}, cc)    case False{}:      st.err(st, "invalid escape")# the next character ends the fold only after a newlinedef lex.fold.end(st: St, cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, +left,    arrtab, lead} = st  lex.fold.end.go(U32.is_eq(left, 1),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, cc)# a newline has been seen, so later whitespace may still be skippeddef lex.fold.nl(nl: Bool, st: St, cc: Char) -> St:  match nl:    case True{}:      lex.fold.saw(st)    case False{}:      lex.fold.end(st, cc)# more whitespace, or the next character of the stringdef lex.fold.go(sp: Bool, nl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.fold.nl(nl, st, cc)# whitespace after a line-ending backslashdef lex.fold(st: St, +cc: Char) -> St:  lex.fold.go(ch.sp(cc), ch.nl(cc), st, cc)# keep the space only when the buffer is a date; otherwise it ends the tokendef lex.bare.date.go(date: Bool, st: St, cc: Char) -> St:  match date:    case True{}:      st.lex(st, LDate{})    case False{}:      bare.finish(st, cc, False{})# a date may continue after one spacedef lex.bare.date(st: St, cc: Char) -> St:  St{bad, rows, heads, here, parts, +buf, lx, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.bare.date.go(date.shaped(buf),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, cc)# a newline inside an inline table is held, and so is any other delimiterdef lex.bare.hold(skip: Bool, nl: Bool, inside: Bool) -> Bool:  Bool.or(Bool.not(skip), Bool.and(nl, inside))# a delimiter other than a date-space is held unless it is whitespacedef lex.bare.end(st: St, +cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lx, back, qkind, +stack, hold, uni, left,    arrtab, lead} = st  bare.finish(St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold,    uni, left, arrtab, lead}, cc, lex.bare.hold(ch.skip(cc), ch.nl(cc), inl.open(stack)))# a space may be the space inside a datetimedef lex.bare.sp(sp: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      lex.bare.date(st, cc)    case False{}:      lex.bare.end(st, cc)# a delimiter ends the tokendef lex.bare.go(delim: Bool, sp: Bool, st: St, cc: Char) -> St:  match delim:    case False{}:      st.push(st, cc, LBare{})    case True{}:      lex.bare.sp(sp, st, cc)# a bare valuedef lex.bare(st: St, +cc: Char) -> St:  lex.bare.go(ch.delim(cc), ch.space(cc), st, cc)# the space and the digit belong to the datetimedef lex.date.digit(st: St, cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, _lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, cc <> (' ' <> buf), LBare{}, back, qkind, stack, hold,    uni, left, arrtab, lead}# the character after the space of a possible datetimedef lex.date.go(digit: Bool, st: St, cc: Char) -> St:  match digit:    case True{}:      lex.date.digit(st, cc)    case False{}:      bare.finish(st, cc, True{})# the character after the space of a possible datetimedef lex.date(st: St, +cc: Char) -> St:  lex.date.go(Char.is_digit(cc), st, cc)# the newline is read again in the state that was waitingdef lex.com.nl(st: St, cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, _lex, +back, qkind, stack, _hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, parts, buf, back, back, qkind, stack, HChar{cc}, uni, left,    arrtab, lead}# tab is the one control a comment may holddef lex.com.tab(tab: Bool, st: St) -> St:  match tab:    case True{}:      st    case False{}:      st.err(st, "invalid control in comment")# any other character is part of the commentdef lex.com.ctl(ctl: Bool, tab: Bool, st: St) -> St:  match ctl:    case False{}:      st    case True{}:      lex.com.tab(tab, st)# a comment runs to the end of the line and holds no control but tabdef lex.com.go(nl: Bool, ctl: Bool, tab: Bool, st: St, cc: Char) -> St:  match nl:    case False{}:      lex.com.ctl(ctl, tab, st)    case True{}:      lex.com.nl(st, cc)# a comment runs to the end of the linedef lex.com(st: St, +cc: Char) -> St:  lex.com.go(ch.nl(cc), ch.ctl(cc), U32.is_eq(Char.to_u32(cc), 9), st, cc)# the header is an array of tablesdef lex.head.arr(st: St) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left, _arr,    lead} = st  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left,    True{}, lead}# one extra bracketdef lex.head.br2(once: Bool, st: St) -> St:  match once:    case False{}:      st.err(st, "invalid header")    case True{}:      lex.head.arr(st)# the second bracket is only legal at the startdef lex.head.br.go(fresh: Bool, once: Bool, st: St) -> St:  match fresh:    case False{}:      st.err(st, "invalid header")    case True{}:      lex.head.br2(once, st)# [[ opens an array of tables when the key has not starteddef lex.head.br(st: St) -> St:  St{bad, rows, heads, here, +parts, +buf, lx, back, qkind, stack, hold, uni, left,    +arrtab, lead} = st  lex.head.br.go(Bool.and(List.is_empty(&2, String, parts),    List.is_empty(&2, Char, buf)), Bool.not(arrtab),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead})# between segments a dot is requireddef lex.head.ch.go(gap: Bool, st: St, cc: Char) -> St:  match gap:    case True{}:      st.err(st, "invalid header")    case False{}:      st.push(st, cc, LHead{})# a bare character continues the segment, or starts one at the beginningdef lex.head.ch(st: St, cc: Char) -> St:  St{bad, rows, heads, here, +parts, +buf, lx, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.head.ch.go(Bool.and(Bool.not(List.is_empty(&2, String, parts)),    List.is_empty(&2, Char, buf)),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, cc)# whitespace with no segment open still needs a segment after it, so a second# bracket there is refused: `[[` is two brackets with nothing betweendef lex.head.gap.go(empty: Bool, st: St) -> St:  match empty:    case True{}:      st.lex(st, LHeadDot{})    case False{}:      seg.push(st, LHeadGap{})# whitespace ends a segment when one is opendef lex.head.gap(st: St) -> St:  St{bad, rows, heads, here, parts, +buf, lx, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.head.gap.go(List.is_empty(&2, Char, buf),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead})# a dot with no segment is rejecteddef lex.head.dot3(empty: Bool, st: St) -> St:  match empty:    case True{}:      st.err(st, "empty key")    case False{}:      seg.push(st, LHeadDot{})# a dot ends the open segmentdef lex.head.dot2(st: St) -> St:  St{bad, rows, heads, here, parts, +buf, lx, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.head.dot3(List.is_empty(&2, Char, buf),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead})# an array of tables still needs one ]def lex.head.close2(arr: Bool, st: St) -> St:  match arr:    case True{}:      st.lex(st, LHeadR{})    case False{}:      head.fin(st)# push the last segment when the buffer holds onedef lex.head.close.go(empty: Bool, arr: Bool, st: St) -> St:  match empty:    case True{}:      lex.head.close2(arr, st)    case False{}:      lex.head.close2(arr, seg.push(st, LHead{}))# ] closes, or waits for the second ] of an array of tablesdef lex.head.close(st: St) -> St:  St{bad, rows, heads, here, parts, +buf, lx, back, qkind, stack, hold, uni, left,    +arrtab, lead} = st  lex.head.close.go(List.is_empty(&2, Char, buf), arrtab,    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead})# a comment in a headerdef lex.head.hash(hash: Bool, st: St) -> St:  match hash:    case True{}:      com.enter(st, LHead{})    case False{}:      st.err(st, "invalid header")# a quote opens a segment only where none has started. After a bare character# it would glue two keys into one segment, which toml.abnf does not allowdef lex.head.quote.go(fresh: Bool, st: St, kind: QKind) -> St:  match fresh:    case True{}:      lex.qkey(st, kind, LHeadQ{})    case False{}:      st.err(st, "invalid header")# a quoted header segment, right after the bracketdef lex.head.quote(st: St, kind: QKind) -> St:  St{bad, rows, heads, here, +parts, +buf, lx, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  lex.head.quote.go(Bool.and(List.is_empty(&2, Char, buf),    List.is_empty(&2, String, parts)),    St{bad, rows, heads, here, parts, buf, lx, back, qkind, stack, hold, uni, left,      arrtab, lead}, kind)# a literal header segment or a commentdef lex.head.lit(lit: Bool, hash: Bool, st: St) -> St:  match lit:    case True{}:      lex.head.quote(st, QLit{})    case False{}:      lex.head.hash(hash, st)# a quoted header segment or a commentdef lex.head.q(qq: Bool, lit: Bool, hash: Bool, st: St, _c: Char) -> St:  match qq:    case True{}:      lex.head.quote(st, QBasic{})    case False{}:      lex.head.lit(lit, hash, st)# the closing bracket, a quoted key, or a commentdef lex.head.end(end: Bool, qq: Bool, lit: Bool, hash: Bool, st: St, cc: Char) -> St:  match end:    case True{}:      lex.head.close(st)    case False{}:      lex.head.q(qq, lit, hash, st, cc)# a dot, the end, or a quoted segmentdef lex.head.dot(dot: Bool, end: Bool, qq: Bool, lit: Bool, hash: Bool, st: St, cc: Char) -> St:  match dot:    case True{}:      lex.head.dot2(st)    case False{}:      lex.head.end(end, qq, lit, hash, st, cc)# whitespace, a dot, or the closing bracketdef lex.head.sp(sp: Bool, dot: Bool, end: Bool, qq: Bool, lit: Bool, hash: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      lex.head.gap(st)    case False{}:      lex.head.dot(dot, end, qq, lit, hash, st, cc)# a bare character, whitespace, a dot, or the enddef lex.head.bare(bare: Bool, sp: Bool, dot: Bool, end: Bool, qq: Bool, lit: Bool, hash: Bool, st: St, cc: Char) -> St:  match bare:    case True{}:      lex.head.ch(st, cc)    case False{}:      lex.head.sp(sp, dot, end, qq, lit, hash, st, cc)# a second bracket, a key character, or whitespacedef lex.head.go(  br: Bool,  bare: Bool,  sp: Bool,  dot: Bool,  end: Bool,  qq: Bool,  lit: Bool,  hash: Bool,  st: St,  cc: Char) -> St:  match br:    case True{}:      lex.head.br(st)    case False{}:      lex.head.bare(bare, sp, dot, end, qq, lit, hash, st, cc)# inside a headerdef lex.head(st: St, +cc: Char) -> St:  lex.head.go(Char.is_eq(cc, '['), bare.at(cc), ch.sp(cc), Char.is_eq(cc, '.'),    Char.is_eq(cc, ']'), Char.is_eq(cc, '"'), Char.is_eq(cc, '\''), Char.is_eq(cc, '#'),    st, cc)# the end, or a commentdef lex.hgap.end(end: Bool, hash: Bool, st: St) -> St:  match end:    case True{}:      lex.head.close(st)    case False{}:      lex.head.hash(hash, st)# a dot starts the next segmentdef lex.hgap.dot(dot: Bool, end: Bool, hash: Bool, st: St) -> St:  match dot:    case True{}:      st.lex(st, LHeadDot{})    case False{}:      lex.hgap.end(end, hash, st)# whitespace, a dot, or the enddef lex.hgap(sp: Bool, dot: Bool, end: Bool, hash: Bool, st: St, _c: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.hgap.dot(dot, end, hash, st)# between header segmentsdef lex.head.gap.step(st: St, +cc: Char) -> St:  lex.hgap(ch.sp(cc), Char.is_eq(cc, '.'), Char.is_eq(cc, ']'), Char.is_eq(cc, '#'), st, cc)# a literal segment, or a rejected characterdef lex.hdot.lit(lit: Bool, st: St, _c: Char) -> St:  match lit:    case True{}:      lex.qkey(st, QLit{}, LHeadQ{})    case False{}:      st.err(st, "invalid header")# a quoted segmentdef lex.hdot.q(qq: Bool, lit: Bool, st: St, cc: Char) -> St:  match qq:    case True{}:      lex.qkey(st, QBasic{}, LHeadQ{})    case False{}:      lex.hdot.lit(lit, st, cc)# a bare segment or a quoted onedef lex.hdot.bare(bare: Bool, qq: Bool, lit: Bool, st: St, cc: Char) -> St:  match bare:    case True{}:      st.push(st, cc, LHead{})    case False{}:      lex.hdot.q(qq, lit, st, cc)# whitespace or the next segmentdef lex.hdot(sp: Bool, bare: Bool, qq: Bool, lit: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.hdot.bare(bare, qq, lit, st, cc)# after a dot, a segment must startdef lex.head.dot.step(st: St, +cc: Char) -> St:  lex.hdot(ch.sp(cc), bare.at(cc), Char.is_eq(cc, '"'), Char.is_eq(cc, '\''), st, cc)# a forbidden control, or one character of the keydef lex.kq.char.go(bad: Bool, st: St, cc: Char, stay: Lex) -> St:  match bad:    case True{}:      st.err(st, "invalid control")    case False{}:      st.push(st, cc, stay)# one character of a quoted keydef lex.kq.char(st: St, +cc: Char, stay: Lex) -> St:  lex.kq.char.go(str.bad(Char.to_u32(cc), False{}), st, cc, stay)# a newline in a key is rejecteddef lex.kq.nl(nl: Bool, st: St, +cc: Char, stay: Lex) -> St:  match nl:    case True{}:      st.err(st, "newline in key")    case False{}:      lex.kq.char(st, cc, stay)# the escape state for a quoted keydef lex.esc.lex(stay: Lex) -> Lex:  match stay:    case LHeadQ{}:      LHeadE{}    case _:      LKeyE{}# the escape returns to the quoted keydef lex.kesc(st: St, +stay: Lex) -> St:  St{bad, rows, heads, here, parts, buf, _lex, _b, qkind, stack, hold, uni, left, arrtab,    lead} = st  St{bad, rows, heads, here, parts, buf, lex.esc.lex(stay), stay, qkind, stack, hold,    uni, left, arrtab, lead}# only a basic key treats the backslash as an escapedef lex.kq.esc(basic: Bool, st: St, stay: Lex) -> St:  match basic:    case True{}:      lex.kesc(st, stay)    case False{}:      st.push(st, '\\', stay)# an escape in a basic keydef lex.kq.bs(bs: Bool, basic: Bool, nl: Bool, st: St, cc: Char, stay: Lex) -> St:  match bs:    case True{}:      lex.kq.esc(basic, st, stay)    case False{}:      lex.kq.nl(nl, st, cc, stay)# a quoted segment may be emptydef lex.qkey.end(st: St, next: Lex) -> St:  seg.push(st, next)# the closer, an escape, or a characterdef lex.kq.go(quote: Bool, bs: Bool, basic: Bool, nl: Bool, st: St, cc: Char, back: Lex, stay: Lex) -> St:  match quote:    case True{}:      lex.qkey.end(st, back)    case False{}:      lex.kq.bs(bs, basic, nl, st, cc, stay)# a quoted key segment. back is the state after the closer; stay is this quotedef lex.kq(st: St, +cc: Char, back: Lex, stay: Lex) -> St:  St{bad, rows, heads, here, parts, buf, lex, b, +qkind, stack, hold, uni, left, arrtab,    lead} = st  lex.kq.go(Char.is_eq(cc, q.char(qkind)), Char.is_eq(cc, '\\'), q.basic(qkind), ch.nl(cc),    St{bad, rows, heads, here, parts, buf, lex, b, qkind, stack, hold, uni, left, arrtab,      lead}, cc, back, stay)# the second ] of an array of tablesdef lex.head.r.go(end: Bool, st: St) -> St:  match end:    case True{}:      head.fin(st)    case False{}:      st.err(st, "invalid header")# the second ] of an array of tablesdef lex.head.r(st: St, +cc: Char) -> St:  lex.head.r.go(Char.is_eq(cc, ']'), st)# ] closes; anything else is a valuedef lex.arr.end(end: Bool, st: St, cc: Char) -> St:  match end:    case True{}:      arr.close(st)    case False{}:      st.hold(st.lex(st, LVal{}), LVal{}, cc)# a comment, the end, or a valuedef lex.arr.hash(hash: Bool, end: Bool, st: St, cc: Char) -> St:  match hash:    case True{}:      com.enter(st, LArr{})    case False{}:      lex.arr.end(end, st, cc)# a newline is whitespace in an arraydef lex.arr.nl(nl: Bool, hash: Bool, end: Bool, st: St, cc: Char) -> St:  match nl:    case True{}:      st    case False{}:      lex.arr.hash(hash, end, st, cc)# whitespace, a comment, the end, or a valuedef lex.arr.go(sp: Bool, nl: Bool, hash: Bool, end: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.arr.nl(nl, hash, end, st, cc)# an array waits for a value or ]def lex.arr(st: St, +cc: Char) -> St:  lex.arr.go(ch.sp(cc), ch.nl(cc), Char.is_eq(cc, '#'), Char.is_eq(cc, ']'), st, cc)# ] closes the arraydef lex.arrc.end(end: Bool, st: St) -> St:  match end:    case True{}:      arr.close(st)    case False{}:      st.err(st, "invalid array")# a comma waits for another valuedef lex.arrc.comma(comma: Bool, end: Bool, st: St) -> St:  match comma:    case True{}:      st.lex(st, LArr{})    case False{}:      lex.arrc.end(end, st)# a comment, a comma, or the enddef lex.arrc.hash(hash: Bool, comma: Bool, end: Bool, st: St, _c: Char) -> St:  match hash:    case True{}:      com.enter(st, LArrC{})    case False{}:      lex.arrc.comma(comma, end, st)# a newline is whitespacedef lex.arrc.nl(nl: Bool, hash: Bool, comma: Bool, end: Bool, st: St, cc: Char) -> St:  match nl:    case True{}:      st    case False{}:      lex.arrc.hash(hash, comma, end, st, cc)# whitespace, a comment, a comma, or the enddef lex.arrc.go(sp: Bool, nl: Bool, hash: Bool, comma: Bool, end: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.arrc.nl(nl, hash, comma, end, st, cc)# after an array value: a comma, ], or whitespacedef lex.arrc(st: St, +cc: Char) -> St:  lex.arrc.go(ch.sp(cc), ch.nl(cc), Char.is_eq(cc, '#'), Char.is_eq(cc, ','),    Char.is_eq(cc, ']'), st, cc)# a new key starts with an empty segment listdef lex.key.clear(st: St) -> St:  St{bad, rows, heads, here, _parts, _buf, _lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  St{bad, rows, heads, here, [], [], LKey{}, back, qkind, stack, hold, uni, left,    arrtab, lead}# a bare character opens the keydef lex.key.bare(ok: Bool, st: St, cc: Char) -> St:  match ok:    case True{}:      st.push(st, cc, LKey{})    case False{}:      st.err(st, "invalid key")# a literal quote or a bare character opens the keydef lex.key.lit(lit: Bool, st: St, +cc: Char) -> St:  match lit:    case True{}:      lex.qkey(st, QLit{}, LKeyQ{})    case False{}:      lex.key.bare(bare.at(cc), st, cc)# a quote or a bare character opens the keydef lex.key.q(qq: Bool, st: St, +cc: Char) -> St:  match qq:    case True{}:      lex.qkey(st, QBasic{}, LKeyQ{})    case False{}:      lex.key.lit(Char.is_eq(cc, '\''), st, cc)# the first character of a key inside an inline tabledef lex.key.start(st: St, +cc: Char) -> St:  lex.key.q(Char.is_eq(cc, '"'), lex.key.clear(st), cc)# a newline is rejected; anything else is a keydef lex.inl.nl(nl: Bool, st: St, cc: Char) -> St:  match nl:    case True{}:      st.err(st, "newline in inline table")    case False{}:      lex.key.start(st, cc)# } closes an empty inline tabledef lex.inl.end(end: Bool, nl: Bool, st: St, cc: Char) -> St:  match end:    case True{}:      inl.close(st)    case False{}:      lex.inl.nl(nl, st, cc)# a comment, the end, or a keydef lex.inl.hash(hash: Bool, end: Bool, nl: Bool, st: St, cc: Char) -> St:  match hash:    case True{}:      com.enter(st, LInl{})    case False{}:      lex.inl.end(end, nl, st, cc)# whitespace, a comment, the end, or a keydef lex.inl.go(sp: Bool, hash: Bool, end: Bool, nl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.inl.hash(hash, end, nl, st, cc)# an inline table waits for a key or }def lex.inl(st: St, +cc: Char) -> St:  lex.inl.go(ch.sp(cc), Char.is_eq(cc, '#'), Char.is_eq(cc, '}'), ch.nl(cc), st, cc)# a comment or a keydef lex.inlk.hash(hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match hash:    case True{}:      com.enter(st, LInlK{})    case False{}:      lex.inl.nl(nl, st, cc)# whitespace or a keydef lex.inlk.go(sp: Bool, hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.inlk.hash(hash, nl, st, cc)# after a comma, a key is requireddef lex.inlk(st: St, +cc: Char) -> St:  lex.inlk.go(ch.sp(cc), Char.is_eq(cc, '#'), ch.nl(cc), st, cc)# a newline is rejecteddef lex.inlc.nl(nl: Bool, st: St) -> St:  match nl:    case True{}:      st.err(st, "newline in inline table")    case False{}:      st.err(st, "invalid inline table")# } closes the inline tabledef lex.inlc.end(end: Bool, nl: Bool, st: St, _c: Char) -> St:  match end:    case True{}:      inl.close(st)    case False{}:      lex.inlc.nl(nl, st)# a comma requires another keydef lex.inlc.comma(comma: Bool, end: Bool, nl: Bool, st: St, cc: Char) -> St:  match comma:    case True{}:      st.lex(st, LInlK{})    case False{}:      lex.inlc.end(end, nl, st, cc)# a comment, a comma, or the enddef lex.inlc.hash(hash: Bool, comma: Bool, end: Bool, nl: Bool, st: St, cc: Char) -> St:  match hash:    case True{}:      com.enter(st, LInlC{})    case False{}:      lex.inlc.comma(comma, end, nl, st, cc)# whitespace, a comment, a comma, or the enddef lex.inlc.go(sp: Bool, hash: Bool, comma: Bool, end: Bool, nl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.inlc.hash(hash, comma, end, nl, st, cc)# after an inline value: a comma or }def lex.inlc(st: St, +cc: Char) -> St:  lex.inlc.go(ch.sp(cc), Char.is_eq(cc, '#'), Char.is_eq(cc, ','), Char.is_eq(cc, '}'),    ch.nl(cc), st, cc)# the rest of a failed document is ignoreddef lex.sink(st: St, _c: Char) -> St:  st# anything else on the same line is another expressiondef lex.line.nl(nl: Bool, st: St) -> St:  match nl:    case True{}:      st.lex(st, LTop{})    case False{}:      st.err(st, "newline required")# a comment may follow an expressiondef lex.line.hash(hash: Bool, nl: Bool, st: St, _c: Char) -> St:  match hash:    case True{}:      com.enter(st, LLine{})    case False{}:      lex.line.nl(nl, st)# whitespace, a comment, or a newline after an expressiondef lex.line.go(sp: Bool, hash: Bool, nl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.line.hash(hash, nl, st, cc)# after an expression, the rest of the line is whitespace or a commentdef lex.line(st: St, +cc: Char) -> St:  lex.line.go(ch.sp(cc), Char.is_eq(cc, '#'), ch.nl(cc), st, cc)# a newline in a dotted key is rejecteddef lex.kdot.nl(nl: Bool, st: St) -> St:  match nl:    case True{}:      st.err(st, "newline in key")    case False{}:      st.err(st, "invalid key")# a second dot is an empty segmentdef lex.kdot.dot(dot: Bool, nl: Bool, st: St) -> St:  match dot:    case True{}:      st.err(st, "empty key")    case False{}:      lex.kdot.nl(nl, st)# a bare character starts the segmentdef lex.kdot.bare(bare: Bool, dot: Bool, nl: Bool, st: St, cc: Char) -> St:  match bare:    case True{}:      st.push(st, cc, LKey{})    case False{}:      lex.kdot.dot(dot, nl, st)# a literal quote starts the segmentdef lex.kdot.lit(lit: Bool, dot: Bool, nl: Bool, st: St, +cc: Char) -> St:  match lit:    case True{}:      lex.qkey(st, QLit{}, LKeyQ{})    case False{}:      lex.kdot.bare(bare.at(cc), dot, nl, st, cc)# a quote starts the segmentdef lex.kdot.q(qq: Bool, lit: Bool, dot: Bool, nl: Bool, st: St, cc: Char) -> St:  match qq:    case True{}:      lex.qkey(st, QBasic{}, LKeyQ{})    case False{}:      lex.kdot.lit(lit, dot, nl, st, cc)# whitespace or the next segmentdef lex.kdot.go(sp: Bool, qq: Bool, lit: Bool, dot: Bool, nl: Bool, st: St, cc: Char) -> St:  match sp:    case True{}:      st    case False{}:      lex.kdot.q(qq, lit, dot, nl, st, cc)# after a dot, whitespace and then a segmentdef lex.kdot(st: St, +cc: Char) -> St:  lex.kdot.go(ch.sp(cc), Char.is_eq(cc, '"'), Char.is_eq(cc, '\''), Char.is_eq(cc, '.'),    ch.nl(cc), st, cc)# a quoted header segmentdef lex.hq(st: St, +cc: Char) -> St:  lex.kq(st, cc, LHeadGap{}, LHeadQ{})# one character, dispatched on the lex statedef read.on(lex: Lex, st: St, cc: Char) -> St:  match lex:    case LTop{}:      lex.top(st, cc)    case LLine{}:      lex.line(st, cc)    case LKey{}:      lex.key(st, cc)    case LKeyDot{}:      lex.kdot(st, cc)    case LAfter{}:      lex.after(st, cc)    case LKeyQ{}:      lex.kq(st, cc, LAfter{}, LKeyQ{})    case LKeyE{}:      esc.use(esc.of(cc), st, LKeyQ{})    case LKeyU{}:      uni.step(st, cc, LKeyQ{})    case LVal{}:      lex.val(st, cc)    case LQ{}:      lex.q(st, cc)    case LQ2{}:      lex.q2(st, cc)    case LStr{}:      lex.str(st, cc)    case LEsc{}:      lex.esc(st, cc)    case LUni{}:      uni.step(st, cc, LStr{})    case LFold{}:      lex.fold(st, cc)    case LMq{}:      lex.mq.step(st, cc)    case LMqEnd{}:      lex.mq.end(st, cc)    case LTrim{}:      lex.trim(st, cc)    case LTrim2{}:      lex.trim2(st, cc)    case LBare{}:      lex.bare(st, cc)    case LDate{}:      lex.date(st, cc)    case LCom{}:      lex.com(st, cc)    case LHead{}:      lex.head(st, cc)    case LHeadGap{}:      lex.head.gap.step(st, cc)    case LHeadDot{}:      lex.head.dot.step(st, cc)    case LHeadQ{}:      lex.hq(st, cc)    case LHeadE{}:      esc.use(esc.of(cc), st, LHeadQ{})    case LHeadU{}:      uni.step(st, cc, LHeadQ{})    case LHeadR{}:      lex.head.r(st, cc)    case LArr{}:      lex.arr(st, cc)    case LArrC{}:      lex.arrc(st, cc)    case LInl{}:      lex.inl(st, cc)    case LInlK{}:      lex.inlk(st, cc)    case LInlC{}:      lex.inlc(st, cc)    case LSink{}:      lex.sink(st, cc)# read the lex state and stepdef read.on.st(st: St, cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, +lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  read.on(lex, St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni,    left, arrtab, lead}, cc)# one character, after a leading mark has been decideddef read.step.bom(bom: Bool, st: St, cc: Char) -> St:  match bom:    case True{}:      st    case False{}:      read.on.st(st, cc)# the first character may be a byte-order markdef read.step.lead(lead: Bool, st: St, +cc: Char) -> St:  match lead:    case False{}:      read.on.st(st, cc)    case True{}:      read.step.bom(U32.is_eq(Char.to_u32(cc), 65279), st, cc)# one character of the documentdef read.step(st: St, +cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left,    arrtab, +lead} = st  read.step.lead(lead, St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack,    hold, uni, left, arrtab, False{}}, cc)# a held character is read oncedef read.once.hold(hold: Hold, st: St) -> St:  match hold:    case HNone{}:      st    case HChar{c}:      read.step(st, c)# a held character is read oncedef read.once2(st: St) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  read.once.hold(hold, St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack,    HNone{}, uni, left, arrtab, lead})# one new character, then a held character if the step asked for onedef read.once(st: St, cc: Char) -> St:  read.once2(read.step(st, cc))# a bare token, an empty string or a closed multiline string still open at the enddef read.fin.lex(lex: Lex, st: St) -> St:  match lex:    case LBare{}:      bare.finish(st, ' ', False{})    case LDate{}:      bare.finish(st, ' ', False{})    case LQ2{}:      val.take(st, VStr{""})    case LMqEnd{}:      str.fin(st)    case _:      st# a bare token still open at the end is closeddef read.fin.close(st: St) -> St:  St{bad, rows, heads, here, parts, buf, +lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  read.fin.lex(lex, St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold,    uni, left, arrtab, lead})# builder order is newest-first. a kept list is reversed back; a builder list is notdef rows.seal.fin(rev: Bool, acc: List<&2, Val>) -> List<&2, Val>:  match rev:    case True{}:      acc    case False{}:      List.reverse(&2, Val, acc)# put every table back in insertion order. one pass, no append along the waydef rows.seal.go(vs: List<&2, Val>, rev: Bool, acc: List<&2, Val>) -> List<&2, Val>:  match vs:    case []:      rows.seal.fin(rev, acc)    case VPair{name, VHead{path, rows}} <> t:      rows.seal.go(t, rev, VPair{name, VHead{path, rows.seal.go(rows, True{}, [])}} <> acc)    case VPair{name, VAots{path, elems}} <> t:      rows.seal.go(t, rev,        VPair{name, VAots{path, rows.seal.go(elems, True{}, [])}} <> acc)    case VPair{name, VInl{rows}} <> t:      rows.seal.go(t, rev, VPair{name, VInl{rows.seal.go(rows, True{}, [])}} <> acc)    case VPair{name, VArr{items}} <> t:      rows.seal.go(t, rev, VPair{name, VArr{rows.seal.go(items, False{}, [])}} <> acc)    case VAot{path, rows} <> t:      rows.seal.go(t, rev, VAot{path, rows.seal.go(rows, True{}, [])} <> acc)    case VAots{path, elems} <> t:      rows.seal.go(t, rev, VAots{path, rows.seal.go(elems, True{}, [])} <> acc)    case VInl{rows} <> t:      rows.seal.go(t, rev, VInl{rows.seal.go(rows, True{}, [])} <> acc)    case VArr{items} <> t:      rows.seal.go(t, rev, VArr{rows.seal.go(items, False{}, [])} <> acc)    case h <> t:      rows.seal.go(t, rev, h <> acc)# insertion order of a table built newest-firstdef rows.seal(rows: List<&2, Val>) -> List<&2, Val>:  rows.seal.go(rows, True{}, [])# an open frame at the end is an errordef read.fin.stack(stack: List<&2, Ctx>, bad: String, rows: List<&2, Val>) -> Doc:  match stack:    case []:      Doc{bad, rows.seal(rows)}    case _ <> _:      Doc{err.first(bad, "unclosed"), rows.seal(rows)}# the document from a finished statedef read.fin.doc(st: St) -> Doc:  St{bad, rows, _heads, _here, _parts, _buf, _lex, _back, _q, stack, _hold, _uni, _left,    _arr, _lead} = st  read.fin.stack(stack, bad, rows)# an unfinished document keeps the first errordef read.fin.unclosed(st: St) -> Doc:  St{bad, rows, _heads, _here, _parts, _buf, _lex, _back, _q, _stack, _hold, _uni,    _left, _arr, _lead} = st  Doc{err.first(bad, "unclosed"), rows.seal(rows)}# a comment may end a document only where the line already coulddef read.fin.end(lex: Lex) -> Bool:  match lex:    case LTop{}:      True{}    case LLine{}:      True{}    case LSink{}:      True{}    case _:      False{}# a comment at the end is unfinished when it interrupted a key or a valuedef read.fin.com(ok: Bool, st: St) -> Doc:  match ok:    case True{}:      read.fin.doc(st)    case False{}:      read.fin.unclosed(st)# a comment at the end is unfinished when it interrupted a key or a valuedef read.fin.com.st(st: St) -> Doc:  St{bad, rows, heads, here, parts, buf, lex, +back, qkind, stack, hold, uni, left,    arrtab, lead} = st  read.fin.com(read.fin.end(back), St{bad, rows, heads, here, parts, buf, lex, back,    qkind, stack, hold, uni, left, arrtab, lead})# any other state is unfinisheddef read.fin.bad(st: St) -> Doc:  read.fin.unclosed(st)# the states that may end a documentdef read.fin.ok(lex: Lex, st: St) -> Doc:  match lex:    case LTop{}:      read.fin.doc(st)    case LLine{}:      read.fin.doc(st)    case LCom{}:      read.fin.com.st(st)    case LSink{}:      read.fin.doc(st)    case _:      read.fin.bad(st)# the document once the last token has been closeddef read.fin2(st: St) -> Doc:  St{bad, rows, heads, here, parts, buf, +lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  read.fin.ok(lex, St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold,    uni, left, arrtab, lead})# the document at the end of the textdef read.fin(st: St) -> Doc:  read.fin2(read.fin.close(st))# one scanner state, with the hold cleared and the first character already takendef read.fast.st(  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  lex: Lex,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, HNone{}, uni, left,    arrtab, False{}}# the character joins the buffer and the state staysdef read.fast.cons(  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  lex: Lex,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  read.fast.st(bad, rows, heads, here, parts, cc <> buf, lex, back, qkind, stack, uni,    left, arrtab)# a quote, an escape, or a state this step does not special-casedef read.fast.old(  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  lex: Lex,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  read.once2(read.on.st(read.fast.st(bad, rows, heads, here, parts, buf, lex, back,    qkind, stack, uni, left, arrtab), cc))# a forbidden control takes the full step; a plain character is storeddef read.fast.push(  ctrl: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  lex: Lex,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match ctrl:    case False{}:      read.fast.cons(bad, rows, heads, here, parts, buf, lex, back, qkind, stack, uni,        left, arrtab, cc)    case True{}:      read.fast.old(bad, rows, heads, here, parts, buf, lex, back, qkind, stack, uni,        left, arrtab, cc)# a basic-string character that is not a quotedef read.fast.basic.bs(  bs: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match bs:    case True{}:      read.fast.old(bad, rows, heads, here, parts, buf, LStr{}, back, QBasic{}, stack,        uni, left, arrtab, cc)    case False{}:      read.fast.push(str.bad(Char.to_u32(cc), False{}), bad, rows, heads, here, parts,        buf, LStr{}, back, QBasic{}, stack, uni, left, arrtab, cc)# a basic-string character: a quote or a backslash still takes the full stepdef read.fast.basic(  quote: Bool,  bs: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match quote:    case True{}:      read.fast.old(bad, rows, heads, here, parts, buf, LStr{}, back, QBasic{}, stack,        uni, left, arrtab, cc)    case False{}:      read.fast.basic.bs(bs, bad, rows, heads, here, parts, buf, back, stack, uni, left,        arrtab, cc)# a literal-string character: a quote still takes the full stepdef read.fast.lit(  quote: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match quote:    case True{}:      read.fast.old(bad, rows, heads, here, parts, buf, LStr{}, back, QLit{}, stack,        uni, left, arrtab, cc)    case False{}:      read.fast.push(str.bad(Char.to_u32(cc), False{}), bad, rows, heads, here, parts,        buf, LStr{}, back, QLit{}, stack, uni, left, arrtab, cc)# one character of a stringdef read.fast.str(  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match qkind:    case QBasic{}:      read.fast.basic(Char.is_eq(cc, '"'), Char.is_eq(cc, '\\'), bad, rows, heads, here,        parts, buf, back, stack, uni, left, arrtab, cc)    case QLit{}:      read.fast.lit(Char.is_eq(cc, '\''), bad, rows, heads, here, parts, buf, back,        stack, uni, left, arrtab, cc)    case other:      read.fast.old(bad, rows, heads, here, parts, buf, LStr{}, back, other, stack,        uni, left, arrtab, cc)# one character of a bare keydef read.fast.key(  ok: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match ok:    case True{}:      read.fast.cons(bad, rows, heads, here, parts, buf, LKey{}, back, qkind, stack,        uni, left, arrtab, cc)    case False{}:      read.fast.old(bad, rows, heads, here, parts, buf, LKey{}, back, qkind, stack,        uni, left, arrtab, cc)# one character of a bare valuedef read.fast.bare(  delim: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match delim:    case False{}:      read.fast.cons(bad, rows, heads, here, parts, buf, LBare{}, back, qkind, stack,        uni, left, arrtab, cc)    case True{}:      read.fast.old(bad, rows, heads, here, parts, buf, LBare{}, back, qkind, stack,        uni, left, arrtab, cc)# one character of a comment. a control, a newline among them, takes the full stepdef read.fast.com(  ctl: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match ctl:    case False{}:      read.fast.st(bad, rows, heads, here, parts, buf, LCom{}, back, qkind, stack,        uni, left, arrtab)    case True{}:      read.fast.old(bad, rows, heads, here, parts, buf, LCom{}, back, qkind, stack,        uni, left, arrtab, cc)# whitespace that this state ignores. keep is true when a newline is also ignoreddef read.fast.ws.keep(  keep: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  lex: Lex,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match keep:    case True{}:      read.fast.st(bad, rows, heads, here, parts, buf, lex, back, qkind, stack, uni,        left, arrtab)    case False{}:      read.fast.old(bad, rows, heads, here, parts, buf, lex, back, qkind, stack, uni,        left, arrtab, cc)# a newline this state ignores, or the full stepdef read.fast.ws.nl(  nl: Bool,  keep: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  lex: Lex,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match nl:    case True{}:      read.fast.ws.keep(keep, bad, rows, heads, here, parts, buf, lex, back, qkind,        stack, uni, left, arrtab, cc)    case False{}:      read.fast.old(bad, rows, heads, here, parts, buf, lex, back, qkind, stack, uni,        left, arrtab, cc)# space stays in this state; a newline stays only when keep is setdef read.fast.ws(  sp: Bool,  nl: Bool,  keep: Bool,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  lex: Lex,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match sp:    case True{}:      read.fast.st(bad, rows, heads, here, parts, buf, lex, back, qkind, stack, uni,        left, arrtab)    case False{}:      read.fast.ws.nl(nl, keep, bad, rows, heads, here, parts, buf, lex, back, qkind,        stack, uni, left, arrtab, cc)# the states whose plain characters do not change the scannerdef read.fast.lex(  lex: Lex,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match lex:    case LStr{}:      read.fast.str(qkind, bad, rows, heads, here, parts, buf, back, stack, uni, left,        arrtab, cc)    case LKey{}:      read.fast.key(bare.at(cc), bad, rows, heads, here, parts, buf, back, qkind,        stack, uni, left, arrtab, cc)    case LBare{}:      read.fast.bare(ch.delim(cc), bad, rows, heads, here, parts, buf, back, qkind,        stack, uni, left, arrtab, cc)    case LCom{}:      read.fast.com(ch.ctl(cc), bad, rows, heads, here, parts, buf, back, qkind, stack,        uni, left, arrtab, cc)    case LTop{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), True{}, bad, rows, heads, here, parts, buf,        LTop{}, back, qkind, stack, uni, left, arrtab, cc)    case LArr{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), True{}, bad, rows, heads, here, parts, buf,        LArr{}, back, qkind, stack, uni, left, arrtab, cc)    case LArrC{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), True{}, bad, rows, heads, here, parts, buf,        LArrC{}, back, qkind, stack, uni, left, arrtab, cc)    case LLine{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), False{}, bad, rows, heads, here, parts, buf,        LLine{}, back, qkind, stack, uni, left, arrtab, cc)    case LVal{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), False{}, bad, rows, heads, here, parts, buf,        LVal{}, back, qkind, stack, uni, left, arrtab, cc)    case LAfter{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), False{}, bad, rows, heads, here, parts, buf,        LAfter{}, back, qkind, stack, uni, left, arrtab, cc)    case LInl{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), False{}, bad, rows, heads, here, parts, buf,        LInl{}, back, qkind, stack, uni, left, arrtab, cc)    case LInlK{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), False{}, bad, rows, heads, here, parts, buf,        LInlK{}, back, qkind, stack, uni, left, arrtab, cc)    case LInlC{}:      read.fast.ws(ch.sp(cc), ch.nl(cc), False{}, bad, rows, heads, here, parts, buf,        LInlC{}, back, qkind, stack, uni, left, arrtab, cc)    case other:      read.fast.old(bad, rows, heads, here, parts, buf, other, back, qkind, stack,        uni, left, arrtab, cc)# the first character may be a byte-order mark; later characters take the fast stepdef read.fast.lead(  lead: Bool,  lex: Lex,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match lead:    case True{}:      read.once(St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack,        HNone{}, uni, left, arrtab, True{}}, cc)    case False{}:      read.fast.lex(lex, qkind, bad, rows, heads, here, parts, buf, back, stack, uni,        left, arrtab, cc)# a held character is still read by the full step, then this characterdef read.fast.hold(  hold: Hold,  lead: Bool,  lex: Lex,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> St:  match hold:    case HChar{held}:      read.once(read.once2(St{bad, rows, heads, here, parts, buf, lex, back, qkind,        stack, HChar{held}, uni, left, arrtab, lead}), cc)    case HNone{}:      read.fast.lead(lead, lex, qkind, bad, rows, heads, here, parts, buf, back,        stack, uni, left, arrtab, cc)# one source characterdef read.fast(st: St, +cc: Char) -> St:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  read.fast.hold(hold, lead, lex, qkind, bad, rows, heads, here, parts, buf, back,    stack, uni, left, arrtab, cc)# where the next source character is read. MEat is the body of a plain string, and# MCr follows a carriage return, which only a line feed may followtype Mode is Data:  MScan{st: St}  MCr{st: St}  MOpen{basic: Bool, q: Char, qkind: QKind, bad: String, rows: List<&2, Val>,    heads: List<&2, String>, here: List<&2, String>, parts: List<&2, String>,    buf: List<&2, Char>, back: Lex, stack: List<&2, Ctx>, uni: U32, left: U32,    arrtab: Bool}  MEat{basic: Bool, q: Char, cut: String, n: U32, bad: String, rows: List<&2, Val>,    heads: List<&2, String>, here: List<&2, String>, parts: List<&2, String>,    qkind: QKind, stack: List<&2, Ctx>, uni: U32, left: U32, arrtab: Bool}# an unclosed stringdef eat.fin(  _n: U32,  _u: Unit,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Doc:  read.fin.unclosed(St{bad, rows, heads, here, parts, [], LStr{}, LStr{}, qkind, stack,    HNone{}, uni, left, arrtab, False{}})# a plain string is the first n characters of the source suffixdef eat.close(  cut: String,  nn: U32,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> St:  val.take(St{bad, rows, heads, here, parts, [], LStr{}, LStr{}, qkind, stack, HNone{},    uni, left, arrtab, False{}}, VSpan{cut, nn})# an escape or a forbidden control copies the span so far and continuesdef eat.copy(  +cc: Char,  cut: String,  +nn: U32,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Mode:  MScan{lex.str(St{bad, rows, heads, here, parts, span.rev(cut, nn, U32.is_zero(nn), []),    LStr{}, LStr{}, qkind, stack, HNone{}, uni, left, arrtab, False{}}, cc)}# a forbidden control leaves the span; a plain character extends itdef eat.ctrl(  badc: Bool,  basic: Bool,  qq: Char,  cc: Char,  cut: String,  nn: U32,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Mode:  match badc:    case True{}:      eat.copy(cc, cut, nn, bad, rows, heads, here, parts, qkind, stack, uni, left,        arrtab)    case False{}:      MEat{basic, qq, cut, U32.add(nn, 1), bad, rows, heads, here, parts, qkind, stack,        uni, left, arrtab}# a backslash in a basic string leaves the spandef eat.bs(  bs: Bool,  basic: Bool,  qq: Char,  +cc: Char,  cut: String,  nn: U32,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Mode:  match bs:    case True{}:      eat.copy(cc, cut, nn, bad, rows, heads, here, parts, qkind, stack, uni, left,        arrtab)    case False{}:      eat.ctrl(str.bad(Char.to_u32(cc), False{}), basic, qq, cc, cut, nn, bad, rows, heads,        here, parts, qkind, stack, uni, left, arrtab)# a literal string keeps a backslash; a basic string does notdef eat.basic(  basic: Bool,  qq: Char,  +cc: Char,  cut: String,  nn: U32,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Mode:  match basic:    case False{}:      eat.ctrl(str.bad(Char.to_u32(cc), False{}), False{}, qq, cc, cut, nn, bad, rows, heads,        here, parts, qkind, stack, uni, left, arrtab)    case True{}:      eat.bs(Char.is_eq(cc, '\\'), True{}, qq, cc, cut, nn, bad, rows, heads, here, parts,        qkind, stack, uni, left, arrtab)# a quote closes the spandef eat.feed.q(  quote: Bool,  basic: Bool,  qq: Char,  cc: Char,  cut: String,  nn: U32,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Mode:  match quote:    case True{}:      MScan{eat.close(cut, nn, bad, rows, heads, here, parts, qkind, stack, uni, left,        arrtab)}    case False{}:      eat.basic(basic, qq, cc, cut, nn, bad, rows, heads, here, parts, qkind, stack, uni,        left, arrtab)# one character of a plain string. cut is the suffix where the text begandef eat.feed(  basic: Bool,  +qq: Char,  cut: String,  nn: U32,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  qkind: QKind,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char) -> Mode:  eat.feed.q(Char.is_eq(cc, qq), basic, qq, cc, cut, nn, bad, rows, heads, here, parts,    qkind, stack, uni, left, arrtab)# the opening quote's back state is not kept on the spandef doc.open.eat(_back: Lex, mode: Mode) -> Mode:  mode# a suffix the span does not keepdef doc.drop(_tail: String, mode: Mode) -> Mode:  mode# an empty buffer starts a span; a buffer already in use stays on the old pathdef doc.open.buf(  basic: Bool,  qq: Char,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  cc: Char,  tail: String) -> Mode:  match buf:    case []:      doc.open.eat(back, MEat{basic, qq, SCon{cc, tail}, 1, bad, rows, heads, here, parts,        qkind, stack, uni, left, arrtab})    case h <> t:      doc.drop(tail, MScan{read.fast.lex(LQ{}, qkind, bad, rows, heads, here, parts,        h <> t, back, stack, uni, left, arrtab, cc)})# a forbidden control leaves the spandef doc.open.ok(  badc: Bool,  basic: Bool,  qq: Char,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  cc: Char,  tail: String) -> Mode:  match badc:    case True{}:      doc.drop(tail, MScan{read.fast.lex(LQ{}, qkind, bad, rows, heads, here, parts,        buf, back, stack, uni, left, arrtab, cc)})    case False{}:      doc.open.buf(basic, qq, qkind, bad, rows, heads, here, parts, buf, back, stack,        uni, left, arrtab, cc, tail)# a backslash in a basic string leaves the spandef doc.open.bs(  bs: Bool,  qq: Char,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char,  tail: String) -> Mode:  match bs:    case True{}:      doc.drop(tail, MScan{read.fast.lex(LQ{}, qkind, bad, rows, heads, here, parts,        buf, back, stack, uni, left, arrtab, cc)})    case False{}:      doc.open.ok(str.bad(Char.to_u32(cc), False{}), True{}, qq, qkind, bad, rows, heads,        here, parts, buf, back, stack, uni, left, arrtab, cc, tail)# a literal string keeps a backslash; a basic string does notdef doc.open.b(  basic: Bool,  qq: Char,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char,  tail: String) -> Mode:  match basic:    case False{}:      doc.open.ok(str.bad(Char.to_u32(cc), False{}), False{}, qq, qkind, bad, rows, heads,        here, parts, buf, back, stack, uni, left, arrtab, cc, tail)    case True{}:      doc.open.bs(Char.is_eq(cc, '\\'), qq, qkind, bad, rows, heads, here, parts, buf,        back, stack, uni, left, arrtab, cc, tail)# a second quote is an empty string or the start of a multiline stringdef doc.open.q(  same: Bool,  basic: Bool,  qq: Char,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  cc: Char,  tail: String) -> Mode:  match same:    case True{}:      doc.drop(tail, MScan{read.fast.lex(LQ{}, qkind, bad, rows, heads, here, parts,        buf, back, stack, uni, left, arrtab, cc)})    case False{}:      doc.open.b(basic, qq, qkind, bad, rows, heads, here, parts, buf, back, stack, uni,        left, arrtab, cc, tail)# the character after an opening quotedef doc.open(  basic: Bool,  +qq: Char,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool,  +cc: Char,  tail: String) -> Mode:  doc.open.q(Char.is_eq(cc, qq), basic, qq, qkind, bad, rows, heads, here, parts, buf,    back, stack, uni, left, arrtab, cc, tail)# a one-line string may be a span; a multiline string stays on the old pathdef doc.scan.kind(  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Mode:  match qkind:    case QBasic{}:      MOpen{True{}, '"', QBasic{}, bad, rows, heads, here, parts, buf, back, stack,        uni, left, arrtab}    case QLit{}:      MOpen{False{}, '\'', QLit{}, bad, rows, heads, here, parts, buf, back, stack,        uni, left, arrtab}    case other:      MScan{St{bad, rows, heads, here, parts, buf, LQ{}, back, other, stack, HNone{},        uni, left, arrtab, False{}}}# the first character of the file is still the old stepdef doc.scan.lead(  lead: Bool,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Mode:  match lead:    case True{}:      MScan{St{bad, rows, heads, here, parts, buf, LQ{}, back, qkind, stack, HNone{},        uni, left, arrtab, True{}}}    case False{}:      doc.scan.kind(qkind, bad, rows, heads, here, parts, buf, back, stack, uni, left,        arrtab)# a held character is not the start of a spandef doc.scan.hold(  hold: Hold,  lead: Bool,  qkind: QKind,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  stack: List<&2, Ctx>,  uni: U32,  left: U32,  arrtab: Bool) -> Mode:  match hold:    case HChar{held}:      MScan{St{bad, rows, heads, here, parts, buf, LQ{}, back, qkind, stack,        HChar{held}, uni, left, arrtab, lead}}    case HNone{}:      doc.scan.lead(lead, qkind, bad, rows, heads, here, parts, buf, back, stack, uni,        left, arrtab)# a quote opened a one-line string, or the state is rebuilt around that quotedef doc.wrap.lex(  lex: Lex,  bad: String,  rows: List<&2, Val>,  heads: List<&2, String>,  here: List<&2, String>,  parts: List<&2, String>,  buf: List<&2, Char>,  back: Lex,  qkind: QKind,  stack: List<&2, Ctx>,  hold: Hold,  uni: U32,  left: U32,  arrtab: Bool,  lead: Bool) -> Mode:  match lex:    case LQ{}:      doc.scan.hold(hold, lead, qkind, bad, rows, heads, here, parts, buf, back, stack,        uni, left, arrtab)    case other:      MScan{St{bad, rows, heads, here, parts, buf, other, back, qkind, stack, hold,        uni, left, arrtab, lead}}# a quote may have opened a stringdef doc.wrap(st: St) -> Mode:  St{bad, rows, heads, here, parts, buf, lex, back, qkind, stack, hold, uni, left,    arrtab, lead} = st  doc.wrap.lex(lex, bad, rows, heads, here, parts, buf, back, qkind, stack, hold,    uni, left, arrtab, lead)# a carriage return must be followed by a line feeddef doc.scan.cr(cr: Bool, st: St, cc: Char) -> Mode:  match cr:    case True{}:      MCr{read.fast(st, cc)}    case False{}:      MScan{read.fast(st, cc)}# an apostrophe may open a literal stringdef doc.scan.sq(sq: Bool, cr: Bool, st: St, cc: Char) -> Mode:  match sq:    case True{}:      doc.wrap(read.fast(st, cc))    case False{}:      doc.scan.cr(cr, st, cc)# a quote may open a basic string. any other character is the old stepdef doc.scan.q(dq: Bool, sq: Bool, cr: Bool, st: St, cc: Char) -> Mode:  match dq:    case True{}:      doc.wrap(read.fast(st, cc))    case False{}:      doc.scan.sq(sq, cr, st, cc)# one source character outside a plain stringdef doc.scan(st: St, +cc: Char) -> Mode:  doc.scan.q(Char.is_eq(cc, '"'), Char.is_eq(cc, '\''), U32.is_eq(Char.to_u32(cc), 13),    st, cc)# the character after a carriage return: a line feed ends the line, anything else failsdef doc.cr(lf: Bool, st: St, cc: Char) -> Mode:  match lf:    case True{}:      MScan{read.fast(st, cc)}    case False{}:      MScan{st.err(st, "carriage return without line feed")}# one character at a time. a plain string keeps a span of this suffixdef doc.read(text: String, mode: Mode) -> Doc:  match text mode:    case SNil{} MScan{st}:      read.fin(read.once2(st))    case SNil{} MCr{st}:      read.fin(read.once2(st.err(st, "carriage return without line feed")))    case SNil{} MOpen{_basic, _q, qkind, bad, rows, heads, here, parts, buf, back,      stack, uni, left, arrtab}:      read.fin(read.once2(St{bad, rows, heads, here, parts, buf, LQ{}, back, qkind,        stack, HNone{}, uni, left, arrtab, False{}}))    case SNil{} MEat{_basic, _q, cut, n, bad, rows, heads, here, parts, qkind, stack,      uni, left, arrtab}:      eat.fin(n, span.drop(cut), bad, rows, heads, here, parts, qkind, stack, uni,        left, arrtab)    case SCon{+h, +t} MOpen{basic, q, qkind, bad, rows, heads, here, parts, buf, back,      stack, uni, left, arrtab}:      doc.read(t, doc.open(basic, q, qkind, bad, rows, heads, here, parts, buf, back,        stack, uni, left, arrtab, h, t))    case SCon{+h, t} MEat{basic, q, cut, n, bad, rows, heads, here, parts, qkind,      stack, uni, left, arrtab}:      doc.read(t, eat.feed(basic, q, cut, n, bad, rows, heads, here, parts, qkind,        stack, uni, left, arrtab, h))    case SCon{+h, t} MScan{st}:      doc.read(t, doc.scan(st, h))    case SCon{+h, t} MCr{st}:      doc.read(t, doc.cr(Char.is_eq(h, '\n'), st, h))# a scanner at the start of a documentdef st.start() -> St:  St{"", [], [], [], [], [], LTop{}, LTop{}, QBasic{}, [], HNone{}, 0, 0, False{},    True{}}# a span walked for quoting. SqOk is the body reversed, with the closing quote already ontype Sq is Data:  SqOk{rev: List<&2, Char>}  SqNo{}# the reversed body while a span is still plain. SqStop means an escape was seentype SqAcc is Data:  SqRun{rev: List<&2, Char>}  SqStop{}# quote, backslash, or a control. those still take the escaping writerdef span.esc(+uu: U32) -> Bool:  Bool.or(U32.is_lt(uu, 32), Bool.or(U32.is_eq(uu, 34), Bool.or(U32.is_eq(uu, 92),    U32.is_eq(uu, 127))))# cons one plain character. an escape, or a stop already seen, stays stoppeddef span.quote.push(esc: Bool, acc: SqAcc, +hh: Char) -> SqAcc:  match esc acc:    case True{} _a:      SqStop{}    case False{} SqStop{}:      SqStop{}    case False{} SqRun{rev}:      SqRun{hh <> rev}# the reversed body, or a stop, as the quoting resultdef span.quote.end(acc: SqAcc) -> Sq:  match acc:    case SqStop{}:      SqNo{}    case SqRun{rev}:      SqOk{rev}# the first n characters, reversed onto acc. a character that needs an escape stopsdef span.quote.go(src: String, +nn: U32, zz: Bool, acc: SqAcc) -> Sq:  match src zz:    case _s True{}:      span.quote.end(acc)    case SNil{} False{}:      span.quote.end(acc)    case SCon{+h, t} False{}:      span.quote.go(t, U32.sub(nn, 1), U32.is_zero(U32.sub(nn, 1)),        span.quote.push(span.esc(Char.to_u32(h)), acc, h))# quotes around a plain span, or the escaping writer when a character needs itdef span.quote.fin(hit: Sq, src: String, +nn: U32) -> String:  match hit:    case SqNo{}:      key.basic(span.str(src, nn, U32.is_zero(nn)))    case SqOk{rev}:      String.from_list(List.reverse(&2, Char, '"' <> rev))# a plain string span, quoted without rebuilding an escaped copydef span.quote(+src: String, +nn: U32) -> String:  span.quote.fin(span.quote.go(src, nn, U32.is_zero(nn), SqRun{'"' <> []}), src, nn)# true or falsedef render.bool(bit: Bool) -> String:  match bit:    case True{}:      "true"    case False{}:      "false"# a scalar valuedef render.atom(vv: Val) -> String:  match vv:    case VStr{text}:      key.basic(text)    case VSpan{src, +n}:      span.quote(src, n)    case VInt{sign, digits}:      sign.text(sign) ++ digits    case VFlo{sign, text}:      sign.text(sign) ++ text    case VBool{bit}:      render.bool(bit)    case VWhen{w}:      when.text(w)    case _:      ""# a separator that is omitted after the last itemdef render.suf.last(last: Bool, gap: String) -> String:  match last:    case True{}:      ""    case False{}:      gap# a separator. trail keeps the separator after the last itemdef render.suf(+gap: String, trail: Bool, last: Bool) -> String:  match trail:    case True{}:      gap    case False{}:      render.suf.last(last, gap)# the values of a table, an array, or an inline tabledef render.go(vs: List<&2, Val>, +gap: String, +trail: Bool) -> String:  match vs:    case []:      ""    case VPair{_name, VHead{path, rows}} <> +t:      "[" ++ path ++ "]\n" ++ render.go(rows, "\n", True{}) ++        render.suf(gap, trail, List.is_empty(&2, Val, t)) ++ render.go(t, gap, trail)    case VPair{_name, VAots{_path, elems}} <> +t:      render.go(elems, "\n", True{}) ++        render.suf(gap, trail, List.is_empty(&2, Val, t)) ++ render.go(t, gap, trail)    case VAot{path, rows} <> +t:      "[[" ++ path ++ "]]\n" ++ render.go(rows, "\n", True{}) ++        render.suf(gap, trail, List.is_empty(&2, Val, t)) ++ render.go(t, gap, trail)    case VPair{name, VArr{items}} <> +t:      key(name) ++ " = [" ++ render.go(items, ", ", False{}) ++ "]" ++        render.suf(gap, trail, List.is_empty(&2, Val, t)) ++ render.go(t, gap, trail)    case VPair{name, VInl{rows}} <> +t:      key(name) ++ " = {" ++ render.go(rows, ", ", False{}) ++ "}" ++        render.suf(gap, trail, List.is_empty(&2, Val, t)) ++ render.go(t, gap, trail)    case VPair{name, val} <> +t:      key(name) ++ " = " ++ render.atom(val) ++        render.suf(gap, trail, List.is_empty(&2, Val, t)) ++ render.go(t, gap, trail)    case VArr{items} <> +t:      "[" ++ render.go(items, ", ", False{}) ++ "]" ++        render.suf(gap, trail, List.is_empty(&2, Val, t)) ++ render.go(t, gap, trail)    case VInl{rows} <> +t:      "{" ++ render.go(rows, ", ", False{}) ++ "}" ++        render.suf(gap, trail, List.is_empty(&2, Val, t)) ++ render.go(t, gap, trail)    case v <> +t:      render.atom(v) ++ render.suf(gap, trail, List.is_empty(&2, Val, t)) ++        render.go(t, gap, trail)# a nested table rendering that this pass does not printdef render.skip(  _path: String,  _rows: List<&2, Val>,  _tail: List<&2, Val>,  _gap: String,  _trail: Bool,  nest: Unit -> String,  rest: Unit -> String) -> String:  _n = nest  rest(Unit{})# the rows are already closed over by the nested renderingdef render.use(_rows: List<&2, Val>, text: String) -> String:  text# a header already ends in a newlinedef render.rest(_tail: List<&2, Val>, _gap: String, _trail: Bool, rest: Unit -> String) -> String:  rest(Unit{})# a table header. Direct keys are printed by the other passdef render.head(  tabs: Bool,  path: String,  rows: List<&2, Val>,  tail: List<&2, Val>,  gap: String,  trail: Bool,  nest: Unit -> String,  rest: Unit -> String) -> String:  match tabs:    case False{}:      render.skip(path, rows, tail, gap, trail, nest, rest)    case True{}:      "[" ++ path ++ "]\n" ++ render.use(rows, nest(Unit{})) ++        render.rest(tail, gap, trail, rest)# an array of tables. Its elements are headersdef render.aots(  tabs: Bool,  elems: List<&2, Val>,  tail: List<&2, Val>,  gap: String,  trail: Bool,  nest: Unit -> String,  rest: Unit -> String) -> String:  match tabs:    case False{}:      render.skip("", elems, tail, gap, trail, nest, rest)    case True{}:      render.use(elems, nest(Unit{})) ++ render.rest(tail, gap, trail, rest)# one element of an array of tablesdef render.aot(  tabs: Bool,  path: String,  rows: List<&2, Val>,  tail: List<&2, Val>,  gap: String,  trail: Bool,  nest: Unit -> String,  rest: Unit -> String) -> String:  match tabs:    case False{}:      render.skip(path, rows, tail, gap, trail, nest, rest)    case True{}:      "[[" ++ path ++ "]]\n" ++ render.use(rows, nest(Unit{})) ++        render.rest(tail, gap, trail, rest)# a key, an array, or an inline table. The text is built only by the pass that writes keysdef render.late(  tabs: Bool,  text: Unit -> String,  tail: List<&2, Val>,  gap: String,  trail: Bool,  rest: Unit -> String) -> String:  match tabs:    case True{}:      rest(Unit{})    case False{}:      text(Unit{}) ++ render.suf(gap, trail, List.is_empty(&2, Val, tail)) ++ rest(Unit{})# one pass: keys, or the tables that follow themdef render.pass(vs: List<&2, Val>, +gap: String, +trail: Bool, +tabs: Bool) -> String:  match vs:    case []:      ""    case VPair{_name, VHead{path, +rows}} <> +t:      render.head(tabs, path, rows, t, gap, trail,        _u => render.pass(rows, "\n", True{}, False{}) ++          render.pass(rows, "\n", True{}, True{}),        _u => render.pass(t, gap, trail, tabs))    case VPair{_name, VAots{_path, +elems}} <> +t:      render.aots(tabs, elems, t, gap, trail,        _u => render.pass(elems, "\n", True{}, False{}) ++          render.pass(elems, "\n", True{}, True{}),        _u => render.pass(t, gap, trail, tabs))    case VAot{path, +rows} <> +t:      render.aot(tabs, path, rows, t, gap, trail,        _u => render.pass(rows, "\n", True{}, False{}) ++          render.pass(rows, "\n", True{}, True{}),        _u => render.pass(t, gap, trail, tabs))    case VPair{name, VArr{items}} <> +t:      render.late(tabs, _u => key(name) ++ " = [" ++ render.go(items, ", ", False{}) ++ "]",        t, gap, trail, _v => render.pass(t, gap, trail, tabs))    case VPair{name, VInl{rows}} <> +t:      render.late(tabs, _u => key(name) ++ " = {" ++ render.go(rows, ", ", False{}) ++ "}",        t, gap, trail, _v => render.pass(t, gap, trail, tabs))    case VPair{name, val} <> +t:      render.late(tabs, _u => key(name) ++ " = " ++ render.atom(val), t, gap, trail,        _v => render.pass(t, gap, trail, tabs))    case VArr{items} <> +t:      render.late(tabs, _u => "[" ++ render.go(items, ", ", False{}) ++ "]",        t, gap, trail, _v => render.pass(t, gap, trail, tabs))    case VInl{rows} <> +t:      render.late(tabs, _u => "{" ++ render.go(rows, ", ", False{}) ++ "}",        t, gap, trail, _v => render.pass(t, gap, trail, tabs))    case v <> +t:      render.late(tabs, _u => render.atom(v), t, gap, trail,        _v => render.pass(t, gap, trail, tabs))# keys first, then tables and arrays of tablesdef render.both(+vs: List<&2, Val>, +gap: String, +trail: Bool) -> String:  render.pass(vs, gap, trail, False{}) ++ render.pass(vs, gap, trail, True{})# the text of a string valuedef string(vv: Val) -> String:  match vv:    case VStr{text}:      text    case VSpan{src, +n}:      span.str(src, n, U32.is_zero(n))    case _:      ""# the digits of an integer valuedef digits(vv: Val) -> String:  match vv:    case VInt{_sign, text}:      text    case _:      ""# the bit of a boolean valuedef flag(vv: Val) -> Bool:  match vv:    case VBool{bit}:      bit    case _:      False{}# the rows of a table valuedef at.rows(vv: Val) -> List<&2, Val>:  match vv:    case VHead{_path, rows}:      rows    case VInl{rows}:      rows    case VAot{_path, rows}:      rows    case _:      []# the rows of a hit, when it is a tabledef at.rows_of(hh: Hit) -> List<&2, Val>:  match hh:    case Found{val}:      at.rows(val)    case Miss{}:      []# the rest of a path after the first keydef at.step(rest: List<&2, String>, hit: Hit) -> Hit:  match rest:    case []:      hit    case name <> t:      at.step(t, get(at.rows_of(hit), name))# the value at a dotted pathdef at(rows: List<&2, Val>, path: List<&2, String>) -> Hit:  match path:    case []:      Miss{}    case name <> rest:      at.step(rest, get(rows, name))# the root tabledef root(doc: Doc) -> List<&2, Val>:  Doc{_bad, rows} = doc  rows# the first error, or "" when the document is well formeddef bad(doc: Doc) -> String:  Doc{text, _rows} = doc  text# a string valuedef str(text: String) -> Val:  VStr{text}# an integer valuedef integer(sign: Sign, text: String) -> Val:  VInt{sign, text}# a float valuedef float(sign: Sign, text: String) -> Val:  VFlo{sign, text}# a boolean valuedef boolean(bit: Bool) -> Val:  VBool{bit}# an array valuedef array(items: List<&2, Val>) -> Val:  VArr{items}# an inline tabledef inline(rows: List<&2, Val>) -> Val:  VInl{rows}# a tabledef table(path: String, rows: List<&2, Val>) -> Val:  VHead{path, rows}# a key and a valuedef pair(name: String, val: Val) -> Val:  VPair{name, val}# a document written back as TOMLdef render(doc: Doc) -> String:  Doc{_bad, rows} = doc  render.both(rows, "\n", True{})# a document read from TOML textdef parse(text: String) -> Doc:  doc.read(text, MScan{st.start()})# Well-formed documents: the shapes parse returns. The builders make any shape,# and render writes TOML only for these.# where a list of values sits: a table's rows under its path (the root's is# ""), an inline table's rows, an array's items, or the elements of an array of# tables under its pathtype WfAt is Data:  WfRows{path: String}  WfInl{}  WfItems{}  WfElems{path: String}# how far a float's spelling has been read: toml.abnf's float with no sign# and no underscore, `dec-int ( exp / frac [ exp ] )`type Fl is Data:  FlStart{}  FlZero{}  FlInt{}  FlDot{}  FlFrac{}  FlE{}  FlESign{}  FlExp{}  FlNo{}# the rest when this part holds, and False otherwisedef wf.then(ok: Bool, more: Unit -> Bool) -> Bool:  match ok:    case True{}:      more(Unit{})    case False{}:      False{}# a Unicode scalar value: no surrogate, and nothing past U+10FFFFdef wf.char(+cc: Char) -> Bool:  uni.ok(Char.to_u32(cc))# every character of a string or a key is a Unicode scalar valuedef wf.text(ss: String) -> Bool:  match ss:    case SNil{}:      True{}    case SCon{+h, t}:      wf.then(wf.char(h), _u => wf.text(t))# a span's source holds its n characters, each a Unicode scalar valuedef wf.span(src: String, +nn: U32, zz: Bool) -> Bool:  match src zz:    case _s True{}:      True{}    case SNil{} False{}:      False{}    case SCon{+h, t} False{}:      wf.then(wf.char(h), _u => wf.span(t, U32.sub(nn, 1), U32.is_zero(U32.sub(nn, 1))))# every character is an ASCII digitdef wf.digits(ss: String) -> Bool:  match ss:    case SNil{}:      True{}    case SCon{+h, t}:      wf.then(Char.is_digit(h), _u => wf.digits(t))# a lone 0, or a first digit that is not 0 and more digitsdef wf.dec.lead(zero: Bool, +cc: Char, rest: String) -> Bool:  match zero:    case True{}:      String.is_empty(rest)    case False{}:      Bool.and(Char.is_digit(cc), wf.digits(rest))# decimal digits with no leading zero, except 0 itselfdef wf.dec(ss: String) -> Bool:  match ss:    case SNil{}:      False{}    case SCon{+h, t}:      wf.dec.lead(Char.is_eq(h, '0'), h, t)# an integer: decimal digits within signed 64 bitsdef wf.int(+sign: Sign, +digits: String) -> Bool:  Bool.and(wf.dec(digits), i64.fits(sign, digits))# e or Edef wf.flo.is_e(+cc: Char) -> Bool:  Bool.or(Char.is_eq(cc, 'e'), Char.is_eq(cc, 'E'))# after the integer part: a fraction or an exponentdef wf.flo.tail(+cc: Char) -> Fl:  Bool.pick(Fl, Char.is_eq(cc, '.'), FlDot{}, Bool.pick(Fl, wf.flo.is_e(cc), FlE{}, FlNo{}))# one character of a float's spellingdef wf.flo.step(st: Fl, +cc: Char) -> Fl:  match st:    case FlStart{}:      Bool.pick(Fl, Char.is_eq(cc, '0'), FlZero{}, Bool.pick(Fl, Char.is_digit(cc), FlInt{}, FlNo{}))    case FlZero{}:      wf.flo.tail(cc)    case FlInt{}:      Bool.pick(Fl, Char.is_digit(cc), FlInt{}, wf.flo.tail(cc))    case FlDot{}:      Bool.pick(Fl, Char.is_digit(cc), FlFrac{}, FlNo{})    case FlFrac{}:      Bool.pick(Fl, Char.is_digit(cc), FlFrac{}, Bool.pick(Fl, wf.flo.is_e(cc), FlE{}, FlNo{}))    case FlE{}:      Bool.pick(Fl, Bool.or(Char.is_eq(cc, '+'), Char.is_eq(cc, '-')), FlESign{},        Bool.pick(Fl, Char.is_digit(cc), FlExp{}, FlNo{}))    case FlESign{}:      Bool.pick(Fl, Char.is_digit(cc), FlExp{}, FlNo{})    case FlExp{}:      Bool.pick(Fl, Char.is_digit(cc), FlExp{}, FlNo{})    case FlNo{}:      FlNo{}# a float's spelling read from the given statedef wf.flo.go(cs: List<&2, Char>, st: Fl) -> Fl:  match cs:    case []:      st    case +h <> t:      wf.flo.go(t, wf.flo.step(st, h))# a spelling ends after a fraction's digit or an exponent's digitdef wf.flo.end(st: Fl) -> Bool:  match st:    case FlFrac{}:      True{}    case FlExp{}:      True{}    case _:      False{}# a float's spelling: inf, nan, or toml.abnf's float with no sign and no `_`def wf.flo(+text: String) -> Bool:  Bool.or(str.eq(text, "inf"), Bool.or(str.eq(text, "nan"),    wf.flo.end(wf.flo.go(String.to_list(text), FlStart{}))))# a date: a four-digit year, a month, and a day within that month in that yeardef wf.date(dd: Date) -> Bool:  Date{+y, +mo, +day} = dd  Bool.and(U32.is_le(y, 9999), Bool.and(bound.rng(mo, 1, 12), bound.rng(day, 1, when.days(y, mo))))# a time of day: hour, minute and second in range, and a fraction of digitsdef wf.clock(cc: Clock) -> Bool:  Clock{+h, +mi, +s, frac} = cc  Bool.and(U32.is_le(h, 23), Bool.and(U32.is_le(mi, 59), Bool.and(U32.is_le(s, 60), wf.digits(frac))))# a zone offset in rangedef wf.zone(zz: Zone) -> Bool:  match zz:    case Zulu{}:      True{}    case Shift{_sign, +h, +mi}:      Bool.and(U32.is_le(h, 23), U32.is_le(mi, 59))# a datetime whose fields are in rangedef wf.when(ww: When) -> Bool:  match ww:    case WOff{date, clock, zone}:      Bool.and(wf.date(date), Bool.and(wf.clock(clock), wf.zone(zone)))    case WLocal{date, clock}:      Bool.and(wf.date(date), wf.clock(clock))    case WDate{date}:      wf.date(date)    case WTime{clock}:      wf.clock(clock)# a string, a number, a boolean or a datetime, well-formed. Nothing else is a# scalardef wf.atom(vv: Val) -> Bool:  match vv:    case VStr{text}:      wf.text(text)    case VSpan{src, +n}:      wf.span(src, n, U32.is_zero(n))    case VInt{+sign, +digits}:      wf.int(sign, digits)    case VFlo{_sign, +text}:      wf.flo(text)    case VBool{_bit}:      True{}    case VWhen{w}:      wf.when(w)    case _:      False{}# a name no later pair of the table hasdef wf.fresh(hit: Hit) -> Bool:  match hit:    case Found{_v}:      False{}    case Miss{}:      True{}# a pair's key: Unicode scalar values, and no later pair of the same namedef wf.key(+name: String, +rest: List<&2, Val>) -> Bool:  Bool.and(wf.text(name), wf.fresh(get(rest, name)))# a table as a pair's value, in a table's rows, at the path its name givesdef wf.head(at: WfAt, +name: String, +path: String, +rest: List<&2, Val>, more: Unit -> Bool) -> Bool:  match at:    case WfRows{+here}:      wf.then(Bool.and(wf.key(name, rest), str.eq(path, tree.pre(here, name))), more)    case _:      False{}# an array of tables as a pair's value, in a table's rows, at the path its# name gives, with at least one elementdef wf.aots(  at: WfAt,  +name: String,  +path: String,  some: Bool,  +rest: List<&2, Val>,  more: Unit -> Bool) -> Bool:  match at:    case WfRows{+here}:      wf.then(Bool.and(some, Bool.and(wf.key(name, rest), str.eq(path, tree.pre(here, name)))), more)    case _:      False{}# an element of an array of tables, at the array's pathdef wf.aot(at: WfAt, +path: String, more: Unit -> Bool) -> Bool:  match at:    case WfElems{here}:      wf.then(str.eq(path, here), more)    case _:      False{}# a pair of any other value, in a table's or an inline table's rowsdef wf.pair(at: WfAt, +name: String, +rest: List<&2, Val>, ok: Bool, more: Unit -> Bool) -> Bool:  match at:    case WfRows{_here}:      wf.then(Bool.and(ok, wf.key(name, rest)), more)    case WfInl{}:      wf.then(Bool.and(ok, wf.key(name, rest)), more)    case _:      False{}# a value that is not a pair, as an array's itemdef wf.item(at: WfAt, ok: Bool, more: Unit -> Bool) -> Bool:  match at:    case WfItems{}:      wf.then(ok, more)    case _:      False{}# a list of values, well-formed where it sitsdef wf.go(vs: List<&2, Val>, +at: WfAt) -> Bool:  match vs:    case []:      True{}    case VPair{+name, VHead{+path, rows}} <> +t:      wf.head(at, name, path, t,        _u => wf.then(wf.go(rows, WfRows{path}), _v => wf.go(t, at)))    case VPair{+name, VAots{+path, +elems}} <> +t:      wf.aots(at, name, path, Bool.not(List.is_empty(&2, Val, elems)), t,        _u => wf.then(wf.go(elems, WfElems{path}), _v => wf.go(t, at)))    case VPair{+name, VArr{items}} <> +t:      wf.pair(at, name, t, True{},        _u => wf.then(wf.go(items, WfItems{}), _v => wf.go(t, at)))    case VPair{+name, VInl{rows}} <> +t:      wf.pair(at, name, t, True{},        _u => wf.then(wf.go(rows, WfInl{}), _v => wf.go(t, at)))    case VPair{+name, val} <> +t:      wf.pair(at, name, t, wf.atom(val), _u => wf.go(t, at))    case VAot{+path, rows} <> t:      wf.aot(at, path,        _u => wf.then(wf.go(rows, WfRows{path}), _v => wf.go(t, at)))    case VArr{items} <> t:      wf.item(at, True{},        _u => wf.then(wf.go(items, WfItems{}), _v => wf.go(t, at)))    case VInl{rows} <> t:      wf.item(at, True{},        _u => wf.then(wf.go(rows, WfInl{}), _v => wf.go(t, at)))    case val <> t:      wf.item(at, wf.atom(val), _u => wf.go(t, at))# whether a document is well-formed: the shapes parse returns. Tables and# arrays of tables sit only as a pair's value in a table's rows, each at the# path its parent's path and its name give; a table's keys are distinct;# numbers, datetimes and strings are as parse reads them. The error is not# looked atdef wf(doc: Doc) -> Bool:  Doc{_bad, rows} = doc  wf.go(rows, WfRows{""})