fmt/yscal.bend
cswap-bend-cli@0.1.0.0 · 349 lines · 53 definitions
1import Base
2import cswap-bend-cli@0.1.0.0/json.bend as Json
3import cswap-bend-cli@0.1.0.0/fmt/ylex.bend as L
4
5
6
7
8
9
10def comment.pick(hit: Bool, n: Nat, rest: Maybe<&2, Nat>) -> Maybe<&2, Nat>:
11 match hit:
12 case True{}:
13 Some{n}
14 case False{}:
15 rest
16
17
18def comment.find(s: String, +n: Nat, +prev: Bool) -> Maybe<&2, Nat>:
19 match s:
20 case SNil{}:
21 None{}
22 case SCon{Chr{+c}, t}:
23 comment.pick(prev && U32.is_eq(c, 35), n, comment.find(t, Nat.add(n, 1n), L.is_space(c)))
24
25def comment.cut(m: Maybe<&2, Nat>, +s: String) -> String:
26 match m:
27 case None{}:
28 s
29 case Some{n}:
30 String.take(s, n)
31
32
33def uncomment(+s: String) -> String:
34 String.trim(comment.cut(comment.find(s, 0n, True{}), s))
35
36
37
38def sq.emit(skip: Bool, +c: U32, rest: String) -> String:
39 match skip:
40 case True{}:
41 rest
42 case False{}:
43 SCon{Chr{c}, rest}
44
45
46
47def sq.unesc(s: String, +prevq: Bool) -> String:
48 match s:
49 case SNil{}:
50 ""
51 case SCon{Chr{+c}, t}:
52 sq.emit(prevq && U32.is_eq(c, 39), c, sq.unesc(t, Bool.not(prevq) && U32.is_eq(c, 39)))
53
54def dq.val(r: Result<&2, &2, String, Json.Json>) -> L.JR():
55 match r:
56 case Done{Json.JStr{s}}:
57 Done{Json.JStr{s}}
58 case Done{x}:
59 Fail{"bad double-quoted string"}
60 case Fail{e}:
61 Fail{"bad double-quoted string: " ++ e}
62
63def first_code(s: String) -> U32:
64 match s:
65 case SNil{}:
66 0
67 case SCon{Chr{c}, t}:
68 c
69
70def unquote.q(isd: Bool, +tok: String) -> L.JR():
71 match isd:
72 case True{}:
73 dq.val(Json.parse(tok))
74 case False{}:
75 Done{Json.JStr{sq.unesc(String.take(String.drop(tok, 1n), Nat.sub(String.length(tok), 2n)), False{})}}
76
77
78def unquote(+tok: String) -> L.JR():
79 unquote.q(U32.is_eq(first_code(tok), 34), tok)
80
81def str_of(r: L.JR()) -> Result<&2, &2, String, String>:
82 match r:
83 case Done{Json.JStr{s}}:
84 Done{s}
85 case Done{x}:
86 Fail{"expected a string"}
87 case Fail{e}:
88 Fail{e}
89
90
91
92def after_ok(+after: String) -> Bool:
93 String.is_empty(after) || U32.is_eq(first_code(after), 35)
94
95def quoted.fin(ok: Bool, +tok: String) -> L.JR():
96 match ok:
97 case True{}:
98 unquote(tok)
99 case False{}:
100 Fail{"unexpected text after a quoted string"}
101
102def quoted.tok(m: Maybe<&2, Nat>, +s: String) -> L.JR():
103 match m:
104 case None{}:
105 Fail{"unterminated quoted string"}
106 case Some{+n}:
107 quoted.fin(after_ok(String.trim(String.drop(s, n))), String.take(s, n))
108
109def special(+c: U32) -> Bool:
110 U32.is_eq(c, 124) || U32.is_eq(c, 62)
111
112def tagged(+c: U32) -> Bool:
113 U32.is_eq(c, 38) || U32.is_eq(c, 42) || U32.is_eq(c, 33)
114
115def atom.pick(isq: Bool, isb: Bool, ist: Bool, +s: String) -> L.JR():
116 match isq isb ist:
117 case True{} _ _:
118 quoted.tok(L.token_len(s), s)
119 case False{} True{} _:
120 Fail{"block scalars are not supported"}
121 case False{} False{} True{}:
122 Fail{"anchors, aliases and tags are not supported"}
123 case False{} False{} False{}:
124 Done{L.resolve(uncomment(s))}
125
126
127def atom(+s: String) -> L.JR():
128 atom.pick(U32.is_eq(first_code(s), 34) || U32.is_eq(first_code(s), 39), special(first_code(s)), tagged(first_code(s)), s)
129
130
131
132type Sp is Data:
133 Sp{done: List<&2, String>, cur: String, q: U32}
134
135def sp.push(+c: U32, +sp: Sp) -> Sp:
136 match sp:
137 case Sp{done, cur, q}:
138 Sp{done, SCon{Chr{c}, cur}, q}
139
140def sp.cut(+sp: Sp) -> Sp:
141 match sp:
142 case Sp{done, cur, q}:
143 Sp{String.trim(String.reverse(cur)) <> done, "", q}
144
145def sp.quote(+c: U32, +sp: Sp) -> Sp:
146 match sp:
147 case Sp{done, cur, q}:
148 Sp{done, SCon{Chr{c}, cur}, c}
149
150def sp.close(+c: U32, +sp: Sp) -> Sp:
151 match sp:
152 case Sp{done, cur, q}:
153 Sp{done, SCon{Chr{c}, cur}, 0}
154
155def sp.outside(+c: U32, +sp: Sp) -> Sp:
156 Bool.pick(Sp, U32.is_eq(c, 44), sp.cut(sp),
157 Bool.pick(Sp, U32.is_eq(c, 34) || U32.is_eq(c, 39), sp.quote(c, sp), sp.push(c, sp)))
158
159def sp.q_of(sp: Sp) -> U32:
160 match sp:
161 case Sp{done, cur, q}:
162 q
163
164def sp.inside(+c: U32, +sp: Sp) -> Sp:
165 Bool.pick(Sp, U32.is_eq(c, sp.q_of(sp)), sp.close(c, sp), sp.push(c, sp))
166
167def sp.step(+c: U32, +sp: Sp) -> Sp:
168 Bool.pick(Sp, U32.is_eq(sp.q_of(sp), 0), sp.outside(c, sp), sp.inside(c, sp))
169
170def sp.go(s: String, +sp: Sp) -> Sp:
171 match s:
172 case SNil{}:
173 sp
174 case SCon{Chr{c}, t}:
175 sp.go(t, sp.step(c, sp))
176
177def sp.fin(+sp: Sp) -> List<&2, String>:
178 match sp:
179 case Sp{done, cur, q}:
180 List.reverse(&2, String, String.trim(String.reverse(cur)) <> done)
181
182
183def split_top(+s: String) -> List<&2, String>:
184 Bool.pick(List<&2, String>, String.is_empty(String.trim(s)), [], sp.fin(sp.go(s, Sp{[], "", 0})))
185
186def item.cons(x: L.JR(), rest: L.JR()) -> L.JR():
187 match x rest:
188 case Done{a} Done{b}:
189 Done{Json.JCons{a, b}}
190 case Fail{e} _:
191 Fail{e}
192 case _ Fail{e}:
193 Fail{e}
194
195def flow.items(ps: List<&2, String>) -> L.JR():
196 match ps:
197 case Nil{}:
198 Done{Json.JNil{}}
199 case Con{+p, r}:
200 item.cons(atom(p), flow.items(r))
201
202def wrap.arr(r: L.JR()) -> L.JR():
203 match r:
204 case Done{c}:
205 Done{Json.JArr{c}}
206 case Fail{e}:
207 Fail{e}
208
209def wrap.obj(r: L.JR()) -> L.JR():
210 match r:
211 case Done{c}:
212 Done{Json.JObj{c}}
213 case Fail{e}:
214 Fail{e}
215
216def inner(+s: String) -> String:
217 String.take(String.drop(s, 1n), Nat.sub(String.length(s), 2n))
218
219def last_code(s: String) -> U32:
220 first_code(String.reverse(s))
221
222def seq.fin(closed: Bool, +s: String) -> L.JR():
223 match closed:
224 case True{}:
225 wrap.arr(flow.items(split_top(inner(s))))
226 case False{}:
227 Fail{"unterminated flow sequence"}
228
229
230
231type KV is Data:
232 KNone{}
233 KBad{msg: String}
234 KOk{key: String, rest: String}
235
236
237def colon.pick(hit: Bool, n: Nat, rest: Maybe<&2, Nat>) -> Maybe<&2, Nat>:
238 match hit:
239 case True{}:
240 Some{n}
241 case False{}:
242 rest
243
244def colon.find(s: String, +n: Nat) -> Maybe<&2, Nat>:
245 match s:
246 case SNil{}:
247 None{}
248 case SCon{Chr{c}, SNil{}}:
249 colon.pick(U32.is_eq(c, 58), n, None{})
250 case SCon{Chr{+c}, SCon{Chr{+d}, t}}:
251 colon.pick(U32.is_eq(c, 58) && L.is_space(d), n, colon.find(SCon{Chr{d}, t}, Nat.add(n, 1n)))
252
253def kv.plain(m: Maybe<&2, Nat>, +s: String) -> KV:
254 match m:
255 case None{}:
256 KNone{}
257 case Some{+n}:
258 KOk{String.trim(String.take(s, n)), String.trim(String.drop(s, Nat.add(n, 1n)))}
259
260def kv.colon(+k: String, +after: String) -> KV:
261 match after:
262 case SCon{Chr{c}, t}:
263 Bool.pick(KV, U32.is_eq(c, 58) && (String.is_empty(t) || L.is_space(first_code(t))), KOk{k, String.trim(t)}, KNone{})
264 case SNil{}:
265 KNone{}
266
267def kv.after(r: Result<&2, &2, String, String>, +after: String) -> KV:
268 match r:
269 case Fail{e}:
270 KBad{e}
271 case Done{k}:
272 kv.colon(k, after)
273
274def kv.quoted(m: Maybe<&2, Nat>, +s: String) -> KV:
275 match m:
276 case None{}:
277 KNone{}
278 case Some{+n}:
279 kv.after(str_of(unquote(String.take(s, n))), String.drop(s, n))
280
281def kv.pick(isq: Bool, isflow: Bool, +s: String) -> KV:
282 match isq isflow:
283 case True{} _:
284 kv.quoted(L.token_len(s), s)
285 case False{} True{}:
286 KNone{}
287 case False{} False{}:
288 kv.plain(colon.find(s, 0n), s)
289
290
291def split_kv(+s: String) -> KV:
292 kv.pick(U32.is_eq(first_code(s), 34) || U32.is_eq(first_code(s), 39), U32.is_eq(first_code(s), 91) || U32.is_eq(first_code(s), 123), s)
293
294def field.cons(k: String, x: L.JR(), rest: L.JR()) -> L.JR():
295 match x rest:
296 case Done{a} Done{b}:
297 Done{Json.JField{k, a, b}}
298 case Fail{e} _:
299 Fail{e}
300 case _ Fail{e}:
301 Fail{e}
302
303def map.piece(kv: KV) -> L.JR():
304 match kv:
305 case KOk{k, rest}:
306 field.cons(k, atom(rest), Done{Json.JNil{}})
307 case KBad{m}:
308 Fail{m}
309 case KNone{}:
310 Fail{"expected key: value in a flow mapping"}
311
312
313def map.cons(x: L.JR(), rest: L.JR()) -> L.JR():
314 match x rest:
315 case Done{Json.JField{k, v, n}} Done{b}:
316 Done{Json.JField{k, v, b}}
317 case Fail{e} _:
318 Fail{e}
319 case _ Fail{e}:
320 Fail{e}
321 case _ _:
322 Fail{"bad flow mapping"}
323
324def map.items(ps: List<&2, String>) -> L.JR():
325 match ps:
326 case Nil{}:
327 Done{Json.JNil{}}
328 case Con{+p, r}:
329 map.cons(map.piece(split_kv(p)), map.items(r))
330
331def map.fin(closed: Bool, +s: String) -> L.JR():
332 match closed:
333 case True{}:
334 wrap.obj(map.items(split_top(inner(s))))
335 case False{}:
336 Fail{"unterminated flow mapping"}
337
338def flow.pick(isseq: Bool, ismap: Bool, +s: String) -> L.JR():
339 match isseq ismap:
340 case True{} _:
341 seq.fin(U32.is_eq(last_code(s), 93), s)
342 case False{} True{}:
343 map.fin(U32.is_eq(last_code(s), 125), s)
344 case False{} False{}:
345 atom(s)
346
347
348def scalar(+s: String) -> L.JR():
349 flow.pick(U32.is_eq(first_code(s), 91), U32.is_eq(first_code(s), 123), s)