value.bend
yaml-for-bend@0.1.1.0 · 333 lines · 28 definitions
1
2
3import Base
4
5
6
7
8
9
10
11type Yaml is Data:
12 Null{}
13 Boolean{on: Bool}
14 Int{text: String}
15 Float{text: String}
16 Str{text: String}
17 Seq{items: List<&2, Yaml>}
18 Mapping{entries: List<&2, Entry>}
19 Tagged{tag: String, val: Yaml}
20
21type Entry is Data:
22 Entry{key: Yaml, val: Yaml}
23
24def same.bool(a: Bool, b: Bool) -> Bool:
25 match a:
26 case True{}:
27 b
28 case False{}:
29 Bool.not(b)
30
31
32
33def eq(a: Yaml, b: Yaml) -> Bool:
34 match a:
35 case Null{}:
36 match b:
37 case Null{}:
38 True{}
39 case _:
40 False{}
41 case Boolean{x}:
42 match b:
43 case Boolean{y}:
44 same.bool(x, y)
45 case _:
46 False{}
47 case Int{x}:
48 match b:
49 case Int{y}:
50 String.eq(x, y)
51 case _:
52 False{}
53 case Float{x}:
54 match b:
55 case Float{y}:
56 String.eq(x, y)
57 case _:
58 False{}
59 case Str{x}:
60 match b:
61 case Str{y}:
62 String.eq(x, y)
63 case _:
64 False{}
65 case Seq{xs}:
66 match xs:
67 case Nil{}:
68 match b:
69 case Seq{ys}:
70 List.is_empty(&2, Yaml, ys)
71 case _:
72 False{}
73 case Con{x, xt}:
74 match b:
75 case Seq{ys}:
76 match ys:
77 case Nil{}:
78 False{}
79 case Con{y, yt}:
80 Bool.and(eq(x, y), eq(Seq{xt}, Seq{yt}))
81 case _:
82 False{}
83 case Mapping{xs}:
84 match xs:
85 case Nil{}:
86 match b:
87 case Mapping{ys}:
88 List.is_empty(&2, Entry, ys)
89 case _:
90 False{}
91 case Con{Entry{xk, xv}, xt}:
92 match b:
93 case Mapping{ys}:
94 match ys:
95 case Nil{}:
96 False{}
97 case Con{Entry{yk, yv}, yt}:
98 Bool.and(Bool.and(eq(xk, yk), eq(xv, yv)), eq(Mapping{xt}, Mapping{yt}))
99 case _:
100 False{}
101 case Tagged{s, x}:
102 match b:
103 case Tagged{t, y}:
104 Bool.and(String.eq(s, t), eq(x, y))
105 case _:
106 False{}
107
108
109
110
111
112def canon(v: Yaml) -> String:
113 match v:
114 case Null{}:
115 "~"
116 case Boolean{on}:
117 Bool.pick(String, on, "t", "f")
118 case Int{t}:
119 "i" ++ t ++ ";"
120 case Float{t}:
121 "d" ++ t ++ ";"
122 case Str{+t}:
123 "s" ++ Nat.show(String.length(t)) ++ ":" ++ t
124 case Seq{xs}:
125 match xs:
126 case Nil{}:
127 "]"
128 case Con{x, rest}:
129 "[" ++ canon(x) ++ canon(Seq{rest})
130 case Mapping{es}:
131 match es:
132 case Nil{}:
133 "}"
134 case Con{Entry{k, x}, rest}:
135 "{" ++ canon(k) ++ canon(x) ++ canon(Mapping{rest})
136 case Tagged{+tag, x}:
137 "!" ++ Nat.show(String.length(tag)) ++ ":" ++ tag ++ canon(x)
138
139def get.hit(hit: Bool, v: Yaml, more: Unit -> Maybe<&2, Yaml>) -> Maybe<&2, Yaml>:
140 match hit:
141 case True{}:
142 Some{v}
143 case False{}:
144 more(Unit{})
145
146def is_text(k: Yaml, +key: String) -> Bool:
147 match k:
148 case Str{t}:
149 String.eq(t, key)
150 case _:
151 False{}
152
153def get.go(es: List<&2, Entry>, +key: String) -> Maybe<&2, Yaml>:
154 match es:
155 case Nil{}:
156 None{}
157 case Con{e, rest}:
158 match e:
159 case Entry{k, v}:
160 get.hit(is_text(k, key), v, u => get.go(rest, key))
161
162
163def get(v: Yaml, key: String) -> Maybe<&2, Yaml>:
164 match v:
165 case Mapping{es}:
166 get.go(es, key)
167 case _:
168 None{}
169
170def find.go(es: List<&2, Entry>, +key: Yaml) -> Maybe<&2, Yaml>:
171 match es:
172 case Nil{}:
173 None{}
174 case Con{e, rest}:
175 match e:
176 case Entry{k, v}:
177 get.hit(eq(k, key), v, u => find.go(rest, key))
178
179
180def find(v: Yaml, key: Yaml) -> Maybe<&2, Yaml>:
181 match v:
182 case Mapping{es}:
183 find.go(es, key)
184 case _:
185 None{}
186
187
188def at(v: Yaml, key: String) -> Yaml:
189 Maybe.default(&2, Yaml, get(v, key), Null{})
190
191def items(v: Yaml) -> List<&2, Yaml>:
192 match v:
193 case Seq{xs}:
194 xs
195 case _:
196 []
197
198def entries(v: Yaml) -> List<&2, Entry>:
199 match v:
200 case Mapping{es}:
201 es
202 case _:
203 []
204
205def keys.go(es: List<&2, Entry>) -> List<&2, Yaml>:
206 match es:
207 case Nil{}:
208 []
209 case Con{e, rest}:
210 match e:
211 case Entry{k, v}:
212 k <> keys.go(rest)
213
214def keys(v: Yaml) -> List<&2, Yaml>:
215 keys.go(entries(v))
216
217
218def text(v: Yaml) -> Maybe<&2, String>:
219 match v:
220 case Str{t}:
221 Some{t}
222 case _:
223 None{}
224
225def is_null(v: Yaml) -> Bool:
226 match v:
227 case Null{}:
228 True{}
229 case _:
230 False{}
231
232
233
234
235def hex.digit(+n: Nat) -> Char:
236 Chr{U32.from_nat(Bool.pick(Nat, Nat.is_lt(n, 10n), Nat.add(n, 48n), Nat.add(n, 87n)))}
237
238def json.char(c: Char) -> String:
239 match c:
240 case '"':
241 "\\\""
242 case '\\':
243 "\\\\"
244 case '\n':
245 "\\n"
246 case '\t':
247 "\\t"
248 case '\r':
249 "\\r"
250 case Chr{+x}:
251 Bool.pick(String, U32.is_lt(x, 32), "\\u00" ++ SCon{hex.digit(U32.to_nat(U32.div(x, 16))), SCon{hex.digit(U32.to_nat(U32.mod(x, 16))), SNil{}}},
252 SCon{Chr{x}, SNil{}})
253
254def json.chars(s: String) -> String:
255 match s:
256 case SNil{}:
257 ""
258 case SCon{c, t}:
259 json.char(c) ++ json.chars(t)
260
261
262def json.str(s: String) -> String:
263 "\"" ++ json.chars(s) ++ "\""
264
265def zero.point(s: String) -> String:
266 match s:
267 case SNil{}:
268 ""
269 case SCon{c, t}:
270 match c:
271 case '.':
272 match t:
273 case SNil{}:
274 ".0"
275 case SCon{+d, +u}:
276 Bool.pick(String, Char.is_digit(d), SCon{'.', SCon{d, u}}, ".0" ++ SCon{d, u})
277 case _:
278 SCon{c, zero.point(t)}
279
280
281
282def json.float(+t: String) -> String:
283 +neg = String.starts_with(t, "-")
284 +body = Bool.pick(String, Bool.or(neg, String.starts_with(t, "+")), String.drop(t, 1n), t)
285 Bool.pick(String, String.starts_with(body, "."), Bool.pick(String, Char.is_digit(Maybe.default(&2, Char, String.get(body, 1n), ' ')), Bool.pick(String, neg, "-0", "0") ++ zero.point(body), t),
286 Bool.pick(String, neg, "-", "") ++ zero.point(body))
287
288
289def key.json(str: Bool, +text: String) -> String:
290 match str:
291 case True{}:
292 text
293 case False{}:
294 json.str(text)
295
296def is_str(v: Yaml) -> Bool:
297 match v:
298 case Str{t}:
299 True{}
300 case _:
301 False{}
302
303def json.go(v: Yaml, +first: Bool) -> String:
304 match v:
305 case Null{}:
306 "null"
307 case Boolean{on}:
308 Bool.pick(String, on, "true", "false")
309 case Int{t}:
310 t
311 case Float{t}:
312 json.float(t)
313 case Str{t}:
314 json.str(t)
315 case Seq{xs}:
316 match xs:
317 case Nil{}:
318 Bool.pick(String, first, "[]", "]")
319 case Con{x, rest}:
320 Bool.pick(String, first, "[", ",") ++ json.go(x, True{}) ++ json.go(Seq{rest}, False{})
321 case Mapping{es}:
322 match es:
323 case Nil{}:
324 Bool.pick(String, first, "{}", "}")
325 case Con{Entry{+k, x}, rest}:
326 Bool.pick(String, first, "{", ",") ++ key.json(is_str(k), json.go(k, True{})) ++ ":" ++ json.go(x, True{}) ++ json.go(Mapping{rest}, False{})
327 case Tagged{tag, x}:
328 json.go(x, True{})
329
330
331
332def json(v: Yaml) -> String:
333 json.go(v, True{})