src/decode.bend
bend-toon-codec@0.1.0.0 · 174 lines · 27 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/prim.bend as P
5import bend-toon-codec@0.1.0.0/src/hdr.bend as H
6import bend-toon-codec@0.1.0.0/src/entry.bend as E
7import bend-toon-codec@0.1.0.0/src/dline.bend as D
8import bend-toon-codec@0.1.0.0/src/frame.bend as F
9import bend-toon-codec@0.1.0.0/src/step.bend as T
10import bend-toon-codec@0.1.0.0/src/expand.bend as X
11import bend-toon-codec@0.1.0.0/src/head.bend as Hd
12
13
14
15
16
17type DOpts is Data:
18 DOpts{indent: U32, strict: Bool, expand: Bool}
19
20def ddefaults() -> DOpts:
21 DOpts{2, True{}, False{}}
22
23
24def fold(+ls: List<&2, D.DL>, +sr: Result<&2, &2, String, F.St>, +strict: Bool) -> Result<&2, &2, String, F.St>:
25 match ls sr:
26 case Nil{} _:
27 sr
28 case Con{l, t} Done{st}:
29 fold(t, T.step(l, st, strict), strict)
30 case Con{l, t} Fail{e}:
31 Fail{e}
32
33def root.value(+r: Result<&2, &2, String, F.St>) -> Result<&2, &2, String, J.Json>:
34 match r:
35 case Fail{e}:
36 Fail{e}
37 case Done{F.St{stk, Some{v}, bmin}}:
38 Done{v}
39 case Done{st}:
40 Fail{"no value"}
41
42
43def fuel(+r: Result<&2, &2, String, F.St>) -> Nat:
44 match r:
45 case Done{F.St{stk, root, bmin}}:
46 List.length(&2, F.Fr, stk)
47 case Fail{e}:
48 0n
49
50
51def finish(+r: Result<&2, &2, String, F.St>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
52 root.value(F.pop(fuel(r), r, 0, strict))
53
54def all_blank(+ls: List<&2, D.DL>) -> Bool:
55 match ls:
56 case Nil{}:
57 True{}
58 case Con{D.DL{no, depth, text, blank}, t}:
59 blank && all_blank(t)
60
61def skip_blank(+ls: List<&2, D.DL>) -> List<&2, D.DL>:
62 match ls:
63 case Con{D.DL{no, depth, text, True{}}, t}:
64 skip_blank(t)
65 case _:
66 ls
67
68def init(+stk: List<&2, F.Fr>, +root: Maybe<&2, J.Json>) -> Result<&2, &2, String, F.St>:
69 Done{F.St{stk, root, S.none()}}
70
71def object.root() -> Result<&2, &2, String, F.St>:
72 init(Con{F.FObj{0, F.ARoot{}, J.JNil{}}, Nil{}}, None{})
73
74
75def array.root(+r: Result<&2, &2, String, T.Start>) -> Result<&2, &2, String, F.St>:
76 match r:
77 case Fail{e}:
78 Fail{e}
79 case Done{T.SVal{v}}:
80 init(Nil{}, Some{v})
81 case Done{T.SOpen{fr}}:
82 init(Con{fr, Nil{}}, None{})
83
84def run.object(+ls: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
85 finish(fold(ls, object.root(), strict), strict)
86
87def run.array(+ls: List<&2, D.DL>, +r: Result<&2, &2, String, T.Start>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
88 finish(fold(ls, array.root(r), strict), strict)
89
90
91def single.pick(+r: Result<&2, &2, String, E.Ent>, +text: String, +ls: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
92 match r:
93 case Done{E.ENot{}}:
94 P.parse(text)
95 case _:
96 run.object(ls, strict)
97
98def single.if(+empty: Bool, +text: String, +ls: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
99 match empty:
100 case True{}:
101 Done{J.JArr{J.JNil{}}}
102 case False{}:
103 single.pick(E.parse(strict, text), text, ls, strict)
104
105def single(+text: String, +ls: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
106 single.if(String.eq(text, "[]"), text, ls, strict)
107
108
109def root.other(+one: Bool, +text: String, +ls: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
110 match one:
111 case True{}:
112 single(text, ls, strict)
113 case False{}:
114 run.object(ls, strict)
115
116def root.hdr(+r: Result<&2, &2, String, H.Hdr>, +one: Bool, +text: String, +all: List<&2, D.DL>, +rest: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
117 match r:
118 case Done{h}:
119 run.array(rest, T.arr_start(F.ARoot{}, 1, h, strict), strict)
120 case Fail{e}:
121 root.other(one, text, all, strict)
122
123def header.try.pick(+root: Bool, +text: String) -> Result<&2, &2, String, H.Hdr>:
124 match root:
125 case True{}:
126 H.parse(text)
127 case False{}:
128 Fail{"not a root header"}
129
130def header.try(+depth: U32, +text: String) -> Result<&2, &2, String, H.Hdr>:
131 header.try.pick(U32.is_zero(depth) && Hd.lead(text, 91), text)
132
133def start.first(+l: D.DL, +rest: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
134 match l:
135 case D.DL{no, depth, text, blank}:
136 root.hdr(header.try(depth, text), all_blank(rest), text, Con{l, rest}, rest, strict)
137
138def start.skipped(+ls: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
139 match ls:
140 case Nil{}:
141 Done{J.JObj{J.JNil{}}}
142 case Con{l, rest}:
143 start.first(l, rest, strict)
144
145def start(+ls: List<&2, D.DL>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
146 start.skipped(skip_blank(ls), strict)
147
148def run(+r: Result<&2, &2, String, List<&2, D.DL>>, +strict: Bool) -> Result<&2, &2, String, J.Json>:
149 match r:
150 case Fail{e}:
151 Fail{e}
152 case Done{ls}:
153 start(ls, strict)
154
155def post.pick(+expand: Bool, +strict: Bool, +v: J.Json) -> Result<&2, &2, String, J.Json>:
156 match expand:
157 case True{}:
158 X.expand(v, strict)
159 case False{}:
160 Done{X.strip(v)}
161
162
163def post(+expand: Bool, +strict: Bool, +r: Result<&2, &2, String, J.Json>) -> Result<&2, &2, String, J.Json>:
164 match r:
165 case Fail{e}:
166 Fail{e}
167 case Done{v}:
168 post.pick(expand, strict, v)
169
170
171def decode(+src: String, +o: DOpts) -> Result<&2, &2, String, J.Json>:
172 match o:
173 case DOpts{indent, strict, expand}:
174 post(expand, strict, run(D.prep(src, indent, strict), strict))