src/toml/toml.bend
ezx@1.11.0.0 · 341 lines · 46 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14import Base
15import emerging-eztoml@0.9.0.0/main.bend as E
16
17
18type Kv is Data:
19 Kv{key: String, val: String}
20
21
22type Sect is Data:
23 Sect{name: String, pairs: List<&2, Kv>}
24
25
26
27
28type Toml is Data:
29 Toml{bad: String, name: String, pairs: List<&2, Kv>, done: List<&2, Sect>}
30
31
32
33def value.at(kv: Kv, rest: String, key: String) -> String:
34 Kv{kk, vv} = kv
35 Bool.pick(String, String.eq(kk, key), vv, rest)
36
37
38
39def value(pairs: List<&2, Kv>, +key: String) -> String:
40 match pairs:
41 case []:
42 ""
43 case hd <> tl:
44 value.at(hd, value(tl, key), key)
45
46
47def sects(doc: Toml) -> List<&2, Sect>:
48 Toml{_bad, _name, _pairs, done} = doc
49 done
50
51
52
53
54
55
56type Sym is Data:
57 SQuote{}
58 SDot{}
59 SKeep{ch: Char}
60
61
62def seg.sym(+ch: Char) -> Sym:
63 Bool.pick(Sym, Char.is_eq(ch, '"'), SQuote{},
64 Bool.pick(Sym, Char.is_eq(ch, '.'), SDot{}, SKeep{ch}))
65
66
67
68type Split is Data:
69 Split{quoted: Bool, cur: List<&2, Char>, out: List<&2, String>}
70
71
72def seg.chars(buf: List<&2, Char>, acc: String) -> String:
73 match buf:
74 case []:
75 acc
76 case hd <> tl:
77 seg.chars(tl, SCon{hd, acc})
78
79
80def seg.done(cur: List<&2, Char>) -> String:
81 seg.chars(cur, "")
82
83
84def seg.dot(quoted: Bool, cur: List<&2, Char>, out: List<&2, String>) -> Split:
85 match quoted:
86 case True{}:
87 Split{True{}, '.' <> cur, out}
88 case False{}:
89 Split{False{}, [], seg.done(cur) <> out}
90
91
92def seg.go(st: Split, sym: Sym) -> Split:
93 Split{quoted, cur, out} = st
94 match sym:
95 case SQuote{}:
96 Split{Bool.not(quoted), cur, out}
97 case SDot{}:
98 seg.dot(quoted, cur, out)
99 case SKeep{ch}:
100 Split{quoted, ch <> cur, out}
101
102
103def seg.step(st: Split, ch: Char) -> Split:
104 seg.go(st, seg.sym(ch))
105
106
107def seg.fin(st: Split) -> List<&2, String>:
108 Split{_quoted, cur, out} = st
109 List.reverse(&2, String, seg.done(cur) <> out)
110
111
112def seg.walk(cs: List<&2, Char>, st: Split) -> Split:
113 match cs:
114 case []:
115 st
116 case hd <> tl:
117 seg.walk(tl, seg.step(st, hd))
118
119
120
121def segments(head: String) -> List<&2, String>:
122 seg.fin(seg.walk(String.to_list(head), Split{False{}, [], []}))
123
124
125
126def quote(+text: String) -> String:
127 "\"" ++ text ++ "\""
128
129
130
131def bare.at(+ch: Char) -> Bool:
132 Bool.or(Char.is_alpha(ch), Bool.or(Char.is_digit(ch),
133 Bool.or(Char.is_eq(ch, '_'), Char.is_eq(ch, '-'))))
134
135
136
137
138def bare.all.step(here: Bool, rest: Unit -> Bool) -> Bool:
139 match here:
140 case True{}:
141 rest(Unit{})
142 case False{}:
143 False{}
144
145
146
147
148def bare.all(cs: List<&2, Char>) -> Bool:
149 match cs:
150 case []:
151 True{}
152 case +hd <> tl:
153 bare.all.step(bare.at(hd), _u => bare.all(tl))
154
155
156def bare(+text: String) -> Bool:
157 Bool.and(Bool.not(String.is_empty(text)), bare.all(String.to_list(text)))
158
159
160def key(+text: String) -> String:
161 Bool.pick(String, bare(text), text, quote(text))
162
163
164
165
166
167def render.pair(kv: Kv) -> String:
168 Kv{kk, vv} = kv
169 key(kk) ++ " = " ++ quote(vv) ++ "\n"
170
171
172def render.pairs(ps: List<&2, Kv>) -> String:
173 match ps:
174 case []:
175 ""
176 case hd <> tl:
177 render.pair(hd) ++ render.pairs(tl)
178
179
180
181def render.sect(sect: Sect) -> String:
182 Sect{nn, ps} = sect
183 "[" ++ nn ++ "]\n" ++ render.pairs(ps)
184
185
186def render.all(ss: List<&2, Sect>) -> List<&2, String>:
187 match ss:
188 case []:
189 []
190 case hd <> tl:
191 render.sect(hd) <> render.all(tl)
192
193
194
195def render(ss: List<&2, Sect>) -> String:
196 String.join(render.all(ss), "\n")
197
198
199def normal(text: String) -> String:
200 E.render(E.parse(text))
201
202
203
204
205
206def flag.text(bit: Bool) -> String:
207 match bit:
208 case True{}:
209 "true"
210 case False{}:
211 "false"
212
213
214
215
216def pair.of(+name: String, val: E.Val) -> List<&2, Kv>:
217 match val:
218 case E.VStr{text}:
219 [Kv{name, text}]
220 case E.VSpan{src, nn}:
221 [Kv{name, E.string(E.VSpan{src, nn})}]
222 case E.VBool{bit}:
223 [Kv{name, flag.text(bit)}]
224 case E.VInt{_sign, digits}:
225 [Kv{name, digits}]
226 case _:
227 []
228
229
230def pairs.of(rows: List<&2, E.Val>) -> List<&2, Kv>:
231 match rows:
232 case []:
233 []
234 case E.VPair{name, val} <> tl:
235 List.append(&2, Kv, pair.of(name, val), pairs.of(tl))
236 case _hd <> tl:
237 pairs.of(tl)
238
239
240def row.table(row: E.Val) -> Bool:
241 match row:
242 case E.VPair{_name, E.VHead{_path, _rows}}:
243 True{}
244 case _:
245 False{}
246
247
248def has.table.step(here: Bool, rest: Unit -> Bool) -> Bool:
249 match here:
250 case True{}:
251 True{}
252 case False{}:
253 rest(Unit{})
254
255
256def has.table(rows: List<&2, E.Val>) -> Bool:
257 match rows:
258 case []:
259 False{}
260 case hd <> tl:
261 has.table.step(row.table(hd), _u => has.table(tl))
262
263
264def head.of(segs: List<&2, String>) -> String:
265 match segs:
266 case []:
267 ""
268 case +hd <> []:
269 key(hd)
270 case +hd <> tl:
271 key(hd) ++ "." ++ head.of(tl)
272
273
274
275
276
277
278def sect.put(skip: Bool, +ps: List<&2, Kv>, +segs: List<&2, String>) -> List<&2, Sect>:
279 match skip:
280 case True{}:
281 []
282 case False{}:
283 [Sect{head.of(List.reverse(&2, String, segs)), ps}]
284
285def sect.keep(+ps: List<&2, Kv>, parent: Bool, +segs: List<&2, String>) -> List<&2, Sect>:
286 sect.put(Bool.and(List.is_empty(&2, Kv, ps), parent), ps, segs)
287
288
289def table.sects(+segs: List<&2, String>, +sub: List<&2, E.Val>, inner: List<&2, Sect>) -> List<&2, Sect>:
290 List.append(&2, Sect, sect.keep(pairs.of(sub), has.table(sub), segs), inner)
291
292
293
294
295def tables(rows: List<&2, E.Val>, +segs: List<&2, String>) -> List<&2, Sect>:
296 match rows:
297 case []:
298 []
299 case E.VPair{name, E.VHead{_path, +sub}} <> tl:
300 +at : List<&2, String> = name <> segs
301 List.append(&2, Sect, table.sects(at, sub, tables(sub, at)), tables(tl, segs))
302 case _hd <> tl:
303 tables(tl, segs)
304
305
306def parse.of(+doc: E.Doc) -> Toml:
307 +rows = E.root(doc)
308 Toml{E.bad(doc), "", pairs.of(rows), tables(rows, [])}
309
310
311def parse(text: String) -> Toml:
312 parse.of(E.parse(text))
313
314
315
316
317
318def show.kv(kv: Kv) -> String:
319 Kv{kk, vv} = kv
320 kk ++ "=" ++ vv
321
322
323def show.kvs(ps: List<&2, Kv>) -> List<&2, String>:
324 match ps:
325 case []:
326 []
327 case hd <> tl:
328 show.kv(hd) <> show.kvs(tl)
329
330
331def show.sect(sect: Sect) -> String:
332 Sect{nn, ps} = sect
333 String.join(nn <> show.kvs(ps), " ")
334
335
336def show.sects(ss: List<&2, Sect>) -> List<&2, String>:
337 match ss:
338 case []:
339 []
340 case hd <> tl:
341 show.sect(hd) <> show.sects(tl)