yaml11.bend
yaml-for-bend@0.1.1.0 · 252 lines · 41 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14import Base
15
16import yaml-for-bend@0.1.1.0/value.bend as V
17
18import yaml-for-bend@0.1.1.0/re.bend as R
19
20import yaml-for-bend@0.1.1.0/num.bend as N
21
22import yaml-for-bend@0.1.1.0/core.bend as C
23
24def when(-A: Type, hit: Bool, yes: Unit -> A, no: Unit -> A) -> A:
25 match hit:
26 case True{}:
27 yes(Unit{})
28 case False{}:
29 no(Unit{})
30
31def timestamp() -> String:
32 C.yaml.tag("timestamp")
33
34def is_true(+s: String) -> Bool:
35 List.contains(~String, ~String.eq, ["yes", "Yes", "YES", "true", "True", "TRUE", "on", "On", "ON"], s)
36
37def is_false(+s: String) -> Bool:
38 List.contains(~String, ~String.eq, ["no", "No", "NO", "false", "False", "FALSE", "off", "Off", "OFF"], s)
39
40
41
42
43def d() -> R.Pat:
44 R.digit()
45
46def digits_() -> R.Pat:
47 R.Star{R.of("0123456789_")}
48
49def bin.pat() -> List<&2, R.Pat>:
50 [R.sign(), R.c('0'), R.c('b'), R.Plus{R.of("01_")}]
51
52def oct.pat() -> List<&2, R.Pat>:
53 [R.sign(), R.c('0'), R.Plus{R.of("01234567_")}]
54
55def dec.pat() -> List<&2, R.Pat>:
56 [R.sign(), R.Alt{[[R.c('0')], [R.One{R.Range{'1', '9'}}, digits_()]]}]
57
58def hex.pat() -> List<&2, R.Pat>:
59 [R.sign(), R.c('0'), R.c('x'), R.Plus{R.Alt{[[R.One{R.Hex{}}], [R.c('_')]]}}]
60
61def sexa.part() -> R.Pat:
62 R.seq([R.c(':'), R.Opt{R.One{R.Range{'0', '5'}}}, d()])
63
64def sexa.int.pat() -> List<&2, R.Pat>:
65 [R.sign(), R.One{R.Range{'1', '9'}}, digits_(), R.Plus{sexa.part()}]
66
67def float.pat() -> List<&2, R.Pat>:
68 [R.sign(), R.Opt{R.seq([d(), digits_()])}, R.c('.'), digits_(), R.Opt{R.seq([R.of("eE"), R.of("-+"), R.digits()])}]
69
70def sexa.float.pat() -> List<&2, R.Pat>:
71 [R.sign(), d(), digits_(), R.Plus{sexa.part()}, R.c('.'), digits_()]
72
73def ymd.pat() -> List<&2, R.Pat>:
74 [d(), d(), d(), d(), R.c('-'), d(), d(), R.c('-'), d(), d()]
75
76def blank() -> R.Pat:
77 R.of(" \t")
78
79def time.pat() -> List<&2, R.Pat>:
80 [d(), d(), d(), d(), R.c('-'), d(), R.Opt{d()}, R.c('-'), d(), R.Opt{d()},
81 R.Alt{[[R.of("Tt")], [R.Plus{blank()}]]}, d(), R.Opt{d()}, R.c(':'), d(), d(), R.c(':'), d(), d(),
82 R.Opt{R.seq([R.c('.'), R.Star{d()}])},
83 R.Opt{R.seq([R.Star{blank()}, R.Alt{[[R.c('Z')], [R.of("-+"), d(), R.Opt{d()}, R.Opt{R.seq([R.c(':'), d(), d()])}]]}])}]
84
85def is_timestamp(+s: String) -> Bool:
86 Bool.or(R.matches(ymd.pat(), s), R.matches(time.pat(), s))
87
88
89
90
91def no.seps(s: String) -> String:
92 match s:
93 case SNil{}:
94 ""
95 case SCon{c, t}:
96 match c:
97 case '_':
98 no.seps(t)
99 case _:
100 SCon{c, no.seps(t)}
101
102def has_digit(s: String) -> Bool:
103 match s:
104 case SNil{}:
105 False{}
106 case SCon{+c, t}:
107 Bool.or(Char.is_digit(c), has_digit(t))
108
109
110def sexa.go(parts: List<&2, String>, acc: List<&2, Nat>) -> List<&2, Nat>:
111 match parts:
112 case Nil{}:
113 acc
114 case Con{p, rest}:
115 sexa.go(rest, N.big.mul.add(acc, 60n, N.big.small(p, 0n)))
116
117def sexa.big(parts: List<&2, String>) -> List<&2, Nat>:
118 match parts:
119 case Nil{}:
120 []
121 case Con{p, rest}:
122 sexa.go(rest, N.big.read(p, 10n, []))
123
124def int.of.digits(+ds: String) -> String:
125 when(String, String.starts_with(ds, "0b"), u => N.big.show(N.big.read(String.drop(ds, 2n), 2n, [])),
126 u => when(String, String.starts_with(ds, "0x"), u => N.big.show(N.big.read(String.drop(ds, 2n), 16n, [])),
127 u => when(String, String.contains(ds, ":"), u => N.big.show(sexa.big(String.split(ds, ':'))),
128 u => when(String, String.starts_with(ds, "0"), u => N.big.show(N.big.read(ds, 8n, [])), u => N.decimal(ds)))))
129
130
131def int.text(+s: String) -> String:
132 +t = no.seps(s)
133 N.signed(C.is_neg(t), int.of.digits(C.unsigned(t)))
134
135def is_int(+s: String) -> Bool:
136 Bool.or(Bool.or(R.matches(dec.pat(), s), R.matches(oct.pat(), s)),
137 Bool.or(Bool.or(R.matches(hex.pat(), s), R.matches(bin.pat(), s)), R.matches(sexa.int.pat(), s)))
138
139
140
141def in.base(+s: String) -> Bool:
142 Bool.or(Bool.or(R.matches(oct.pat(), s), R.matches(hex.pat(), s)),
143 Bool.or(Bool.or(R.matches(bin.pat(), s), R.matches(sexa.int.pat(), s)), R.matches(sexa.float.pat(), s)))
144
145def before.dot(s: String) -> String:
146 match s:
147 case SNil{}:
148 ""
149 case SCon{c, t}:
150 match c:
151 case '.':
152 ""
153 case _:
154 SCon{c, before.dot(t)}
155
156def after.dot(s: String) -> String:
157 match s:
158 case SNil{}:
159 ""
160 case SCon{c, t}:
161 match c:
162 case '.':
163 t
164 case _:
165 after.dot(t)
166
167
168def sexa.float(+s: String) -> String:
169 +t = no.seps(s)
170 +body = C.unsigned(t)
171 N.dec.exact(Bool.pick(String, C.is_neg(t), "-", "") ++ N.big.show(sexa.big(String.split(before.dot(body), ':'))) ++ "." ++ after.dot(body))
172
173def float.of(+s: String) -> Maybe<&2, String>:
174 +body = C.unsigned(s)
175 when(Maybe<&2, String>, C.is_inf(body), u => Some{Bool.pick(String, C.is_neg(s), "-.inf", ".inf")},
176 u => when(Maybe<&2, String>, C.is_nan(s), u => Some{".nan"},
177 u => when(Maybe<&2, String>, Bool.and(R.matches(float.pat(), s), has_digit(s)), u => Some{N.dec.exact(no.seps(s))},
178 u => when(Maybe<&2, String>, R.matches(sexa.float.pat(), s), u => Some{sexa.float(s)}, u => None{}))))
179
180def int.of(+s: String) -> Maybe<&2, String>:
181 when(Maybe<&2, String>, is_int(s), u => Some{int.text(s)}, u => None{})
182
183def or.int(m: Maybe<&2, String>, rest: Unit -> V.Yaml) -> V.Yaml:
184 match m:
185 case Some{t}:
186 V.Int{t}
187 case None{}:
188 rest(Unit{})
189
190def or.float(m: Maybe<&2, String>, rest: Unit -> V.Yaml) -> V.Yaml:
191 match m:
192 case Some{t}:
193 V.Float{t}
194 case None{}:
195 rest(Unit{})
196
197
198def plain(+s: String) -> V.Yaml:
199 when(V.Yaml, C.is_null(s), u => V.Null{},
200 u => when(V.Yaml, is_true(s), u => V.Boolean{True{}},
201 u => when(V.Yaml, is_false(s), u => V.Boolean{False{}},
202 u => or.int(int.of(s), u => or.float(float.of(s),
203 u => when(V.Yaml, is_timestamp(s), u => V.Tagged{timestamp(), V.Str{s}}, u => V.Str{s}))))))
204
205def int.yaml(m: Maybe<&2, String>) -> Maybe<&2, V.Yaml>:
206 match m:
207 case Some{t}:
208 Some{V.Int{t}}
209 case None{}:
210 None{}
211
212
213def float.yaml(m: Maybe<&2, String>, i: Maybe<&2, String>) -> Maybe<&2, V.Yaml>:
214 match m:
215 case Some{t}:
216 Some{V.Float{t}}
217 case None{}:
218 match i:
219 case Some{t}:
220 Some{V.Float{N.dec.exact(t)}}
221 case None{}:
222 None{}
223
224def bool.yaml(+s: String) -> Maybe<&2, V.Yaml>:
225 Bool.pick(Maybe<&2, V.Yaml>, is_true(s), Some{V.Boolean{True{}}}, Bool.pick(Maybe<&2, V.Yaml>, is_false(s), Some{V.Boolean{False{}}}, None{}))
226
227
228
229def known(+tag: String) -> Bool:
230 Bool.or(Bool.or(Bool.or(String.eq(tag, "!"), String.eq(tag, C.yaml.tag("str"))), String.eq(tag, C.yaml.tag("merge"))),
231 Bool.or(Bool.or(String.eq(tag, C.yaml.tag("null")), String.eq(tag, C.yaml.tag("bool"))), Bool.or(String.eq(tag, C.yaml.tag("int")), String.eq(tag, C.yaml.tag("float")))))
232
233
234
235def read(+tag: String, +s: String) -> Result<&2, &2, String, V.Yaml>:
236 when(Result<&2, &2, String, V.Yaml>, Bool.or(Bool.or(String.eq(tag, "!"), String.eq(tag, C.yaml.tag("str"))), String.eq(tag, C.yaml.tag("merge"))), u => Done{V.Str{s}},
237 u => when(Result<&2, &2, String, V.Yaml>, String.eq(tag, C.yaml.tag("null")), u => C.must(Bool.pick(Maybe<&2, V.Yaml>, C.is_null(s), Some{V.Null{}}, None{}), "!!null", s),
238 u => when(Result<&2, &2, String, V.Yaml>, String.eq(tag, C.yaml.tag("bool")), u => C.must(bool.yaml(s), "!!bool", s),
239 u => when(Result<&2, &2, String, V.Yaml>, String.eq(tag, C.yaml.tag("int")), u => C.must(int.yaml(int.of(s)), "!!int", s),
240 u => C.must(float.yaml(float.of(s), int.of(s)), "!!float", s)))))
241
242
243def read.or.keep(hit: Bool, +tag: String, +s: String) -> Result<&2, &2, String, V.Yaml>:
244 match hit:
245 case True{}:
246 read(tag, s)
247 case False{}:
248 Done{V.Tagged{tag, V.Str{s}}}
249
250
251def tagged(+tag: String, +s: String) -> Result<&2, &2, String, V.Yaml>:
252 read.or.keep(known(tag), tag, s)