src/parse.bend
emerging-ezjson@1.1.0.0 · 251 lines · 0 definitions
1# src/parse: tokens to one JSON value. Commas separate elements, a colon
2# separates a key from its value, and a text has one root.
3import Base
4import emerging-ezjson@1.1.0.0/src/value.bend as V
5import emerging-ezjson@1.1.0.0/src/lex.bend as Lex
6
7# where the next token of an open array or object has to be
8type Slot is Data:
9 SValue{}
10 SComma{}
11 SKey{}
12 SColon{}
13
14# an object key: owned text, or the first `n` chars of a source suffix
15type Key is Data:
16 KOwn{s: String}
17 KCut{src: String, n: U32}
18
19# an open array (its items, reversed) or an open object (its pairs, reversed,
20# and the key awaiting its value)
21type Frame is Data:
22 FArr{items: V.Json, slot: Slot}
23 FObj{pairs: V.Json, key: Key, slot: Slot}
24
25# the parser's state: an error was seen, the finished root, the open frames
26type P is Data:
27 P{bad: Bool, root: Maybe<&2, V.Json>, stack: List<&2, Frame>}
28
29def word.ok(+raw: String) -> Bool:
30 Bool.or(String.eq(raw, "null"),
31 Bool.or(String.eq(raw, "true"),
32 Bool.or(String.eq(raw, "false"), V.num.ok(raw))))
33
34# a bare word as a value: null, true, false, else a number kept as text
35def word(+raw: String) -> V.Json:
36 Bool.pick(V.Json, String.eq(raw, "null"), V.JNull{},
37 Bool.pick(V.Json, String.eq(raw, "true"), V.JBool{True{}},
38 Bool.pick(V.Json, String.eq(raw, "false"), V.JBool{False{}}, V.JNum{raw})))
39
40def step.ready(stack: List<&2, Frame>, root: Maybe<&2, V.Json>) -> Bool:
41 match stack root:
42 case Nil{} None{}:
43 True{}
44 case Con{FArr{items, SValue{}}, rest} r:
45 True{}
46 case Con{FObj{pairs, key, SValue{}}, rest} r:
47 True{}
48 case s r:
49 False{}
50
51def put.pair(key: Key, val: V.Json, pairs: V.Json) -> V.Json:
52 match key:
53 case KOwn{s}:
54 V.JPair{s, val, pairs}
55 case KCut{src, n}:
56 V.JBind{src, n, val, pairs}
57
58def put.text(key: Key) -> V.Json:
59 match key:
60 case KOwn{s}:
61 V.JStr{s}
62 case KCut{src, n}:
63 V.JSpan{src, n}
64
65# a finished value lands in the innermost open frame, or becomes the root
66def put(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, vv: V.Json, bad: Bool) -> P:
67 match stack root vv:
68 case Nil{} None{} val:
69 P{bad, Some{val}, Nil{}}
70 case Nil{} Some{r} val:
71 P{True{}, Some{r}, Nil{}}
72 case Con{FArr{items, SValue{}}, rest} r val:
73 P{bad, r, FArr{V.JCons{val, items}, SComma{}} <> rest}
74 case Con{FObj{pairs, k, SValue{}}, rest} r val:
75 P{bad, r, FObj{put.pair(k, val, pairs), KOwn{""}, SComma{}} <> rest}
76 case s r val:
77 P{True{}, r, Nil{}}
78
79def put.str(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, key: Key, bad: Bool) -> P:
80 match stack:
81 case Con{FObj{pairs, k, SKey{}}, rest}:
82 P{bad, root, FObj{pairs, key, SColon{}} <> rest}
83 case other:
84 put(stack, root, put.text(key), bad)
85
86def step.open_arr.go(bad: Bool, root: Maybe<&2, V.Json>, stack: List<&2, Frame>, ok: Bool) -> P:
87 match ok:
88 case True{}:
89 P{bad, root, FArr{V.JNil{}, SValue{}} <> stack}
90 case False{}:
91 P{True{}, root, Nil{}}
92
93def step.open_arr(st: P) -> P:
94 P{bad, +root, +stack} = st
95 step.open_arr.go(bad, root, stack, step.ready(stack, root))
96
97def step.open_obj.go(bad: Bool, root: Maybe<&2, V.Json>, stack: List<&2, Frame>, ok: Bool) -> P:
98 match ok:
99 case True{}:
100 P{bad, root, FObj{V.JNil{}, KOwn{""}, SKey{}} <> stack}
101 case False{}:
102 P{True{}, root, Nil{}}
103
104def step.open_obj(st: P) -> P:
105 P{bad, +root, +stack} = st
106 step.open_obj.go(bad, root, stack, step.ready(stack, root))
107
108def step.comma(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, bad: Bool) -> P:
109 match stack:
110 case Con{FArr{items, SComma{}}, rest}:
111 P{bad, root, FArr{items, SValue{}} <> rest}
112 case Con{FObj{pairs, k, SComma{}}, rest}:
113 P{bad, root, FObj{pairs, KOwn{""}, SKey{}} <> rest}
114 case other:
115 P{True{}, root, Nil{}}
116
117def step.colon(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, bad: Bool) -> P:
118 match stack:
119 case Con{FObj{pairs, k, SColon{}}, rest}:
120 P{bad, root, FObj{pairs, k, SValue{}} <> rest}
121 case other:
122 P{True{}, root, Nil{}}
123
124def step.word.go(raw: String, st: P, ok: Bool) -> P:
125 P{bad, root, stack} = st
126 match ok:
127 case True{}:
128 put(stack, root, word(raw), bad)
129 case False{}:
130 P{True{}, root, stack}
131
132def step.word(+raw: String, st: P) -> P:
133 step.word.go(raw, st, word.ok(raw))
134
135# a `]`: the innermost frame must be an array, empty or just after a value
136def close_arr(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, bad: Bool) -> P:
137 match stack:
138 case Con{FArr{V.JNil{}, SValue{}}, rest}:
139 put(rest, root, V.JArr{V.JNil{}}, bad)
140 case Con{FArr{items, SComma{}}, rest}:
141 put(rest, root, V.JArr{V.reverse(items, V.JNil{})}, bad)
142 case other:
143 P{True{}, root, Nil{}}
144
145# a `}`: the innermost frame must be an object, empty or just after a value
146def close_obj(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, bad: Bool) -> P:
147 match stack:
148 case Con{FObj{V.JNil{}, k, SKey{}}, rest}:
149 put(rest, root, V.JObj{V.JNil{}}, bad)
150 case Con{FObj{pairs, k, SComma{}}, rest}:
151 put(rest, root, V.JObj{V.reverse(pairs, V.JNil{})}, bad)
152 case other:
153 P{True{}, root, Nil{}}
154
155def step.span.ok(+raw: String, ok: Bool) -> (V.Json & Bool):
156 match ok:
157 case True{}:
158 (V.JNum{raw}, True{})
159 case False{}:
160 (V.JNull{}, False{})
161
162def step.span.use(+raw: String) -> (V.Json & Bool):
163 step.span.ok(raw, V.num.ok(raw))
164
165def step.span.chk(src: String, +nn: U32) -> (V.Json & Bool):
166 step.span.use(V.span.str(src, nn, U32.is_zero(nn)))
167
168def step.span.go(src: String, nn: U32, is_null: Bool, is_true: Bool, is_false: Bool) -> (V.Json & Bool):
169 match is_null is_true is_false:
170 case True{} x y:
171 (V.JNull{}, True{})
172 case False{} True{} y:
173 (V.JBool{True{}}, True{})
174 case False{} False{} True{}:
175 (V.JBool{False{}}, True{})
176 case False{} False{} False{}:
177 step.span.chk(src, nn)
178
179def step.span.use2(st: P, val: V.Json, ok: Bool) -> P:
180 P{bad, root, stack} = st
181 match ok:
182 case True{}:
183 put(stack, root, val, bad)
184 case False{}:
185 P{True{}, root, stack}
186
187def step.span.open(st: P, got: (V.Json & Bool)) -> P:
188 (v, ok) = got
189 step.span.use2(st, v, ok)
190
191def step.span(+src: String, +nn: U32, st: P) -> P:
192 step.span.open(st, step.span.go(src, nn, V.span.eq(src, nn, "null"),
193 V.span.eq(src, nn, "true"), V.span.eq(src, nn, "false")))
194
195# one token
196def step(tok: Lex.Tok, st: P) -> P:
197 match tok:
198 case Lex.TOpenArr{}:
199 step.open_arr(st)
200 case Lex.TOpenObj{}:
201 step.open_obj(st)
202 case Lex.TCloseArr{}:
203 P{bad, root, stack} = st
204 close_arr(stack, root, bad)
205 case Lex.TCloseObj{}:
206 P{bad, root, stack} = st
207 close_obj(stack, root, bad)
208 case Lex.TColon{}:
209 P{bad, root, stack} = st
210 step.colon(stack, root, bad)
211 case Lex.TComma{}:
212 P{bad, root, stack} = st
213 step.comma(stack, root, bad)
214 case Lex.TStr{s}:
215 P{bad, root, stack} = st
216 put.str(stack, root, KOwn{s}, bad)
217 case Lex.TWord{raw}:
218 step.word(raw, st)
219 case Lex.TStrS{src, n}:
220 P{bad, root, stack} = st
221 put.str(stack, root, KCut{src, n}, bad)
222 case Lex.TWordS{src, n}:
223 step.span(src, n, st)
224 case Lex.TBad{}:
225 P{bad, root, stack} = st
226 P{True{}, root, stack}
227
228# every token, in order
229def run(toks: List<&2, Lex.Tok>, st: P) -> P:
230 match toks:
231 case Nil{}:
232 st
233 case Con{t, rest}:
234 run(rest, step(t, st))
235
236# the root, when nothing went wrong and nothing stayed open
237def result(bad: Bool, stack: List<&2, Frame>, root: Maybe<&2, V.Json>) -> Maybe<&2, V.Json>:
238 match bad stack root:
239 case False{} Nil{} Some{j}:
240 Some{j}
241 case b s r:
242 None{}
243
244# the value once the tokens are over
245def finish(st: P) -> Maybe<&2, V.Json>:
246 P{bad, root, stack} = st
247 result(bad, stack, root)
248
249# a text as a value, or None when it is not JSON
250def parse(txt: String) -> Maybe<&2, V.Json>:
251 finish(run(Lex.tokens(txt), P{False{}, None{}, Nil{}}))