src/frame.bend
bend-toon-codec@0.1.0.0 · 241 lines · 32 definitions
1import Base
2import bend-toon-codec@0.1.0.0/src/json.bend as J
3import bend-toon-codec@0.1.0.0/src/split.bend as S
4import bend-toon-codec@0.1.0.0/src/head.bend as Hd
5
6
7
8
9
10
11
12
13type Att is Data:
14 AKey{k: String}
15 AItem{}
16 ARoot{}
17
18type Fr is Data:
19 FObj{depth: U32, att: Att, fs: J.Json}
20 FLst{depth: U32, att: Att, n: U32, c: U32, xs: J.Json}
21 FTab{depth: U32, att: Att, n: U32, c: U32, d: U32, ks: List<&2, String>, xs: J.Json}
22
23
24
25type St is Data:
26 St{stk: List<&2, Fr>, root: Maybe<&2, J.Json>, bmin: U32}
27
28def stk.of(+st: St) -> List<&2, Fr>:
29 match st:
30 case St{stk, root, bmin}:
31 stk
32
33def bmin.of(+st: St) -> U32:
34 match st:
35 case St{stk, root, bmin}:
36 bmin
37
38def fr.depth(+f: Fr) -> U32:
39 match f:
40 case FObj{d, a, fs}:
41 d
42 case FLst{d, a, n, c, xs}:
43 d
44 case FTab{d, a, n, c, dl, ks, xs}:
45 d
46
47def fr.att(+f: Fr) -> Att:
48 match f:
49 case FObj{d, a, fs}:
50 a
51 case FLst{d, a, n, c, xs}:
52 a
53 case FTab{d, a, n, c, dl, ks, xs}:
54 a
55
56
57
58def rev.items(+xs: J.Json, +acc: J.Json) -> J.Json:
59 match xs:
60 case J.JCons{h, t}:
61 rev.items(t, J.JCons{h, acc})
62 case _:
63 acc
64
65def rev.fields(+fs: J.Json, +acc: J.Json) -> J.Json:
66 match fs:
67 case J.JField{k, v, t}:
68 rev.fields(t, J.JField{k, v, acc})
69 case _:
70 acc
71
72def has_key(+k: String, +fs: J.Json) -> Bool:
73 match fs:
74 case J.JField{k2, v, t}:
75 Hd.keq(k, k2) || has_key(k, t)
76 case _:
77 False{}
78
79def set.pick(+hit: Bool, +k: String, +v: J.Json, +old: J.Json, +rest: J.Json, +t: J.Json) -> J.Json:
80 match hit:
81 case True{}:
82 J.JField{k, v, t}
83 case False{}:
84 J.JField{k, old, rest}
85
86
87def set(+k: String, +v: J.Json, +fs: J.Json) -> J.Json:
88 match fs:
89 case J.JField{k2, old, t}:
90 set.pick(Hd.keq(k, k2), k2, v, old, set(k, v, t), t)
91 case _:
92 fs
93
94def add.if(+dup: Bool, +strict: Bool, +k: String, +v: J.Json, +fs: J.Json) -> Result<&2, &2, String, J.Json>:
95 match dup strict:
96 case False{} _:
97 Done{J.JField{k, v, fs}}
98 case True{} True{}:
99 Fail{"duplicate key"}
100 case True{} False{}:
101 Done{set(k, v, fs)}
102
103
104
105def add_field(+strict: Bool, +fs: J.Json, +k: String, +v: J.Json) -> Result<&2, &2, String, J.Json>:
106 add.if(has_key(k, fs), strict, k, v, fs)
107
108
109
110def count.ok(+strict: Bool, +n: U32, +c: U32) -> Bool:
111 Bool.not(strict) || U32.is_eq(n, c)
112
113def fin.arr(+ok: Bool, +xs: J.Json) -> Result<&2, &2, String, J.Json>:
114 match ok:
115 case True{}:
116 Done{J.JArr{rev.items(xs, J.JNil{})}}
117 case False{}:
118 Fail{"array length does not match its header"}
119
120def finish(+f: Fr, +strict: Bool) -> Result<&2, &2, String, J.Json>:
121 match f:
122 case FObj{d, a, fs}:
123 Done{J.JObj{rev.fields(fs, J.JNil{})}}
124 case FLst{d, a, n, c, xs}:
125 fin.arr(count.ok(strict, n, c), xs)
126 case FTab{d, a, n, c, dl, ks, xs}:
127 fin.arr(count.ok(strict, n, c), xs)
128
129def put.field.done(+r: Result<&2, &2, String, J.Json>, +d: U32, +a: Att, +rest: List<&2, Fr>, +root: Maybe<&2, J.Json>, +bmin: U32) -> Result<&2, &2, String, St>:
130 match r:
131 case Fail{e}:
132 Fail{e}
133 case Done{fs2}:
134 Done{St{Con{FObj{d, a, fs2}, rest}, root, bmin}}
135
136def put.field(+strict: Bool, +d: U32, +a: Att, +fs: J.Json, +k: String, +v: J.Json, +rest: List<&2, Fr>, +root: Maybe<&2, J.Json>, +bmin: U32) -> Result<&2, &2, String, St>:
137 put.field.done(add_field(strict, fs, k, v), d, a, rest, root, bmin)
138
139
140def attach.top(+top: Fr, +att: Att, +v: J.Json, +rest: List<&2, Fr>, +root: Maybe<&2, J.Json>, +strict: Bool, +bmin: U32) -> Result<&2, &2, String, St>:
141 match top att:
142 case FObj{d, a, fs} AKey{k}:
143 put.field(strict, d, a, fs, k, v, rest, root, bmin)
144 case FLst{d, a, n, c, xs} AItem{}:
145 Done{St{Con{FLst{d, a, n, c, J.JCons{v, xs}}, rest}, root, bmin}}
146 case _ _:
147 Fail{"misplaced value"}
148
149def attach(+att: Att, +v: J.Json, +stk: List<&2, Fr>, +root: Maybe<&2, J.Json>, +strict: Bool, +bmin: U32) -> Result<&2, &2, String, St>:
150 match stk:
151 case Nil{}:
152 Done{St{Nil{}, Some{v}, bmin}}
153 case Con{top, rest}:
154 attach.top(top, att, v, rest, root, strict, bmin)
155
156def close.go(+r: Result<&2, &2, String, J.Json>, +att: Att, +rest: List<&2, Fr>, +root: Maybe<&2, J.Json>, +strict: Bool, +bmin: U32) -> Result<&2, &2, String, St>:
157 match r:
158 case Fail{e}:
159 Fail{e}
160 case Done{v}:
161 attach(att, v, rest, root, strict, bmin)
162
163
164def close(+st: St, +strict: Bool) -> Result<&2, &2, String, St>:
165 match st:
166 case St{Con{top, rest}, root, bmin}:
167 close.go(finish(top, strict), fr.att(top), rest, root, strict, bmin)
168 case _:
169 Done{st}
170
171def close.if(+b: Bool, +st: St, +strict: Bool) -> Result<&2, &2, String, St>:
172 match b:
173 case True{}:
174 close(st, strict)
175 case False{}:
176 Done{st}
177
178
179def should_pop(+stk: List<&2, Fr>, +t: U32) -> Bool:
180 match stk:
181 case Con{top, rest}:
182 U32.is_ge(fr.depth(top), t)
183 case Nil{}:
184 False{}
185
186
187
188def pop(+fuel: Nat, +sr: Result<&2, &2, String, St>, +t: U32, +strict: Bool) -> Result<&2, &2, String, St>:
189 match fuel sr:
190 case 0n _:
191 sr
192 case 1n+p Fail{e}:
193 Fail{e}
194 case 1n+p Done{st}:
195 pop(p, close.if(should_pop(stk.of(st), t), st, strict), t, strict)
196
197
198
199def arr.depth.if(+empty: Bool, +d: U32) -> U32:
200 match empty:
201 case True{}:
202 S.none()
203 case False{}:
204 d
205
206def arr.depth(+f: Fr) -> U32:
207 match f:
208 case FLst{d, a, n, c, xs}:
209 arr.depth.if(U32.is_zero(c), d)
210 case FTab{d, a, n, c, dl, ks, xs}:
211 arr.depth.if(U32.is_zero(c), d)
212 case _:
213 S.none()
214
215
216def arr.min(+stk: List<&2, Fr>) -> U32:
217 match stk:
218 case Nil{}:
219 S.none()
220 case Con{top, rest}:
221 U32.min(arr.depth(top), arr.min(rest))
222
223
224def blank.note(+st: St) -> St:
225 match st:
226 case St{stk, root, bmin}:
227 St{stk, root, U32.min(bmin, arr.min(stk))}
228
229def blank.if(+bad: Bool, +st: St) -> Result<&2, &2, String, St>:
230 match bad:
231 case True{}:
232 Fail{"blank line inside an array"}
233 case False{}:
234 match st:
235 case St{stk, root, bmin}:
236 Done{St{stk, root, S.none()}}
237
238
239
240def blank.check(+st: St, +d: U32, +strict: Bool) -> Result<&2, &2, String, St>:
241 blank.if(strict && U32.is_le(bmin.of(st), d), st)