src/config.bend
bolt@1.26.0.0 · 300 lines · 36 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import Base
21import bolt@1.26.0.0/src/lazy/lazy.bend as Lazy
22import bolt@1.26.0.0/src/syntax/lex.bend as Lex
23import bolt@1.26.0.0/src/rchars.bend as Rchars
24import bolt@1.26.0.0/src/syntax/tree.bend as Tree
25import bolt@1.26.0.0/src/syntax/bind.bend as Bind
26import bolt@1.26.0.0/src/codes.bend as Codes
27
28
29type Level is Data:
30 Off{}
31 Warn{}
32 Error{}
33
34
35type LevelSet is Data:
36 LevelSet{name: String, level: Level}
37
38
39type Config is Data:
40 Config{sets: List<&2, LevelSet>}
41
42
43def none() -> Config:
44 Config{[]}
45
46
47def show(ll: Level) -> String:
48 match ll:
49 case Off{}:
50 "off"
51 case Warn{}:
52 "warning"
53 case Error{}:
54 "error"
55
56
57def is_error(ll: Level) -> Bool:
58 match ll:
59 case Error{}:
60 True{}
61 case other:
62 False{}
63
64
65def is_off(ll: Level) -> Bool:
66 match ll:
67 case Off{}:
68 True{}
69 case other:
70 False{}
71
72
73
74
75
76def of_word(+ww: String) -> Level:
77 Bool.pick(Level, String.eq(ww, "off"), Off{}, Bool.pick(Level, String.eq(ww, "warn"), Warn{}, Error{}))
78
79
80def unquote(+tt: String) -> String:
81 Bool.pick(String, Bool.and(String.starts_with(tt, "\""), String.ends_with(tt, "\"")),
82 String.take(String.drop(tt, 1n), (String.length(tt) - 2n : Nat)), tt)
83
84
85def first_str(toks: List<&2, Lex.Tok>) -> Maybe<&2, String>:
86 match toks:
87 case Nil{}:
88 None{}
89 case Con{Lex.Tok{Lex.TStr{}, t, l, c}, rest}:
90 Some{unquote(t)}
91 case Con{h, rest}:
92 first_str(rest)
93
94
95
96type Setting is Data:
97 Setting{name: String, word: String, line: U32}
98
99
100def setting(name: Maybe<&2, String>, word: Maybe<&2, String>, +line: U32) -> List<&2, Setting>:
101 match name word:
102 case Some{n} Some{w}:
103 [Setting{n, w, line}]
104 case _a _b:
105 []
106
107
108def settings.go(root: Tree.Node) -> List<&2, Setting>:
109 match root:
110 case Tree.NCons{Tree.Stmt{Tree.SDef{}, +kids, body}, rest}:
111 +ln = Tree.line(kids)
112 List.append(&2, Setting, setting(Bind.declared(kids), first_str(Tree.leaves(body)), ln), settings.go(rest))
113 case Tree.NCons{_h, rest}:
114 settings.go(rest)
115 case _other:
116 Nil{}
117
118
119def settings(text: String) -> List<&2, Setting>:
120 settings.go(Tree.parse(text))
121
122
123def levels(ss: List<&2, Setting>) -> List<&2, LevelSet>:
124 match ss:
125 case Nil{}:
126 Nil{}
127 case Con{Setting{n, w, _line}, rest}:
128 LevelSet{n, of_word(w)} <> levels(rest)
129
130
131def parse(text: String) -> Config:
132 Config{levels(settings(text))}
133
134
135
136
137
138
139
140
141def known.group(rows: List<&2, Codes.Entry>, +name: String) -> Bool:
142 match rows:
143 case Nil{}:
144 False{}
145 case Con{Codes.Entry{_s, _id, +grp}, rest}:
146 Lazy.or_else(String.eq(grp, name), _u => known.group(rest, name))
147
148
149def known.in(+rows: List<&2, Codes.Entry>, +name: String) -> Bool:
150 +slug = Maybe.is_some(&2, Codes.Entry, Codes.find(rows, name))
151 +grp = known.group(rows, name)
152 Bool.or(slug, grp)
153
154
155
156def unknown.in(ss: List<&2, Setting>, +rows: List<&2, Codes.Entry>) -> List<&2, Setting>:
157 match ss:
158 case Nil{}:
159 Nil{}
160 case Con{Setting{+n, w, line}, rest}:
161 +more = unknown.in(rest, rows)
162 Bool.pick(List<&2, Setting>, known.in(rows, n),
163 more, Setting{n, w, line} <> more)
164
165
166
167def unknown(ss: List<&2, Setting>) -> List<&2, Setting>:
168 unknown.in(ss, Codes.table())
169
170
171def dir_of.go(cs: List<&2, Char>, +acc: List<&2, Char>, best: List<&2, Char>) -> String:
172 match cs:
173 case Nil{}:
174 Rchars.text(best)
175 case Con{'/', t}:
176 dir_of.go(t, '/' <> acc, '/' <> acc)
177 case Con{c, t}:
178 dir_of.go(t, c <> acc, best)
179
180
181def dir_of(path: String) -> String:
182 dir_of.go(String.to_list(path), [], [])
183
184
185
186
187
188
189
190
191
192
193
194
195def absolute(+cwd: String, +path: String) -> String:
196 Bool.pick(String, String.starts_with(path, "/"), path, cwd ++ "/" ++ path)
197
198
199def home.up(above: List<&2, String>) -> List<&2, String>:
200 match above:
201 case Nil{}:
202 Nil{}
203 case Con{_d, rest}:
204 rest
205
206
207
208
209def home.step(+seg: String, +above: List<&2, String>) -> List<&2, String>:
210 Bool.pick(List<&2, String>, Bool.or(String.is_empty(seg), String.eq(seg, ".")), above,
211 Bool.pick(List<&2, String>, String.eq(seg, ".."), home.up(above), seg <> above))
212
213
214
215
216def home.segs(cs: List<&2, Char>, seg: List<&2, Char>, above: List<&2, String>) -> List<&2, String>:
217 match cs:
218 case Nil{}:
219 home.step(Rchars.text(seg), above)
220 case Con{'/', t}:
221 home.segs(t, [], home.step(Rchars.text(seg), above))
222 case Con{c, t}:
223 home.segs(t, c <> seg, above)
224
225
226
227
228def home(cwd: String, dir: String) -> List<&2, String>:
229 home.segs(String.to_list(absolute(cwd, dir)), [], [])
230
231
232def root(ds: List<&2, String>, acc: String) -> String:
233 match ds:
234 case Nil{}:
235 "/" ++ acc
236 case Con{d, rest}:
237 root(rest, d ++ "/" ++ acc)
238
239
240
241
242def chain(ds: List<&2, String>) -> List<&2, String>:
243 match ds:
244 case Nil{}:
245 ["/bolt.bend"]
246 case Con{d, +rest}:
247 (root(d <> rest, "") ++ "bolt.bend") <> chain(rest)
248
249
250def nearest(rs: List<&2, Maybe<&2, String>>) -> Config:
251 match rs:
252 case Nil{}:
253 none()
254 case Con{Some{text}, rest}:
255 parse(text)
256 case Con{None{}, rest}:
257 nearest(rest)
258
259
260
261
262
263
264def set_level.go(sets: List<&2, LevelSet>, +name: String, hit: Maybe<&2, Level>) -> Maybe<&2, Level>:
265 match sets:
266 case Nil{}:
267 hit
268 case Con{LevelSet{+n, l}, rest}:
269 match hit:
270 case Some{x}:
271 Some{x}
272 case None{}:
273 set_level.go(rest, name, Bool.pick(Maybe<&2, Level>, String.eq(n, name), Some{l}, None{}))
274
275
276def set_level(sets: List<&2, LevelSet>, +name: String) -> Maybe<&2, Level>:
277 set_level.go(sets, name, None{})
278
279
280def default(+group: String) -> Level:
281 Bool.pick(Level, String.eq(group, "correctness"), Error{},
282 Bool.pick(Level, String.eq(group, "pedantic"), Off{}, Warn{}))
283
284
285
286
287
288def opt_in(+rule: String) -> Bool:
289 List.contains(~String, ~String.eq, ["thunk", "transcong", "coverage", "closed"], rule)
290
291
292
293def level.of(+sets: List<&2, LevelSet>, +group: String, +rule: String) -> Level:
294 Maybe.default(&2, Level, set_level(sets, rule),
295 Lazy.stop(Level, opt_in(rule), Off{}, _u => Maybe.default(&2, Level, set_level(sets, group), default(group))))
296
297
298def level(cfg: Config, group: String, rule: String) -> Level:
299 Config{ss} = cfg
300 level.of(ss, group, rule)