src/rules/suspicious/prefix.bend
bolt@1.27.0.0 · 293 lines · 33 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.27.0.0/src/src.bend as Src
22import bolt@1.27.0.0/src/paths.bend as Paths
23import bolt@1.27.0.0/src/role.bend as Role
24import bolt@1.27.0.0/src/lazy/lazy.bend as Lazy
25import bolt@1.27.0.0/src/finding.bend as F
26import bolt@1.27.0.0/src/syntax/lex.bend as Lex
27import bolt@1.27.0.0/src/rules/tokens.bend as T
28
29
30
31
32type Mode is Data:
33 MOpen{}
34 MPlain{}
35 MEsc{}
36 MUni{}
37 MDone{}
38
39
40def is_cp(+cc: Char, +xx: U32) -> Bool:
41 U32.is_eq(Char.to_u32(cc), xx)
42
43
44
45def len.add(mm: Mode, +cc: Char) -> Nat:
46 match mm:
47 case MPlain{}:
48 Bool.pick(Nat, is_cp(cc, 34), 0n, 1n)
49 case other:
50 0n
51
52
53
54
55def len.next(mm: Mode, +cc: Char) -> Mode:
56 match mm:
57 case MOpen{}:
58 MPlain{}
59 case MPlain{}:
60 Bool.pick(Mode, is_cp(cc, 92), MEsc{}, Bool.pick(Mode, is_cp(cc, 34), MDone{}, MPlain{}))
61 case MEsc{}:
62 Bool.pick(Mode, is_cp(cc, 117), MUni{}, MPlain{})
63 case MUni{}:
64 Bool.pick(Mode, is_cp(cc, 125), MPlain{}, MUni{})
65 case MDone{}:
66 MDone{}
67
68
69def len.go(tt: String, +mm: Mode) -> Nat:
70 match tt:
71 case SNil{}:
72 0n
73 case SCon{+cc, rest}:
74 Nat.add(len.add(mm, cc), len.go(rest, len.next(mm, cc)))
75
76
77
78def len(tt: String) -> Nat:
79 len.go(tt, MOpen{})
80
81
82def fits(+kk: String, lit: String) -> Bool:
83 String.eq(kk, Nat.show(len(lit)) ++ "n")
84
85
86def say(+ss: String, +kk: String, +lit: String) -> String:
87 "String.eq(String.take(" ++ ss ++ ", " ++ kk ++ "), " ++ lit ++ ") copies " ++ kk
88 ++ " chars only to compare them; String.starts_with(" ++ ss ++ ", " ++ lit ++ ") gives the same answer in place."
89
90
91
92
93type Skip is Data:
94 SGo{depth: Nat}
95 SComma{}
96 SClose{}
97
98
99type Arg is Data:
100 Arg{text: String, rest: List<&2, Lex.Tok>}
101
102
103def skip.comma(dd: Nat) -> Skip:
104 match dd:
105 case 0n:
106 SComma{}
107 case 1n+p:
108 SGo{1n+p}
109
110
111
112def skip.close(dd: Nat) -> Skip:
113 match dd:
114 case 0n:
115 SClose{}
116 case 1n+p:
117 SGo{p}
118
119
120def skip.step(kk: Lex.TokKind, dd: Nat) -> Skip:
121 match kk:
122 case Lex.TComma{}:
123 skip.comma(dd)
124 case Lex.TOpen{}:
125 SGo{1n+dd}
126 case Lex.TClose{}:
127 skip.close(dd)
128 case other:
129 SGo{dd}
130
131
132
133def piece(kk: Lex.TokKind, +tt: String) -> String:
134 match kk:
135 case Lex.TComma{}:
136 ", "
137 case Lex.TOp{}:
138 " " ++ tt ++ " "
139 case other:
140 tt
141
142
143
144def skip.piece(sk: Skip, kk: Lex.TokKind, +tt: String) -> String:
145 match sk:
146 case SComma{}:
147 ""
148 case other:
149 piece(kk, tt)
150
151
152
153
154
155def skip(toks: List<&2, Lex.Tok>, sk: Skip, +acc: List<&2, String>) -> Maybe<&2, Arg>:
156 match toks:
157 case Nil{}:
158 None{}
159 case Con{Lex.Tok{+k, +t, l, c}, rest}:
160 match sk:
161 case SComma{}:
162 Some{Arg{String.join(List.reverse(&2, String, acc), ""), Lex.Tok{k, t, l, c} <> rest}}
163 case SClose{}:
164 None{}
165 case SGo{dd}:
166 +nx = skip.step(k, dd)
167 skip(rest, nx, skip.piece(nx, k, t) <> acc)
168
169
170def dotted(kk: Lex.TokKind) -> Bool:
171 match kk:
172 case Lex.TDotted{}:
173 True{}
174 case other:
175 False{}
176
177
178
179def tail_a.ok(+kk: String, +t7: String, +t8: String, k9: Lex.TokKind, +t9: String, +t10: String) -> Bool:
180 Bool.and(String.eq(t7, ")"), Bool.and(String.eq(t8, ","), Bool.and(Lex.is_str(k9),
181 Bool.and(String.eq(t10, ")"), fits(kk, t9)))))
182
183
184def tail_a(ma: Maybe<&2, Arg>) -> Maybe<&2, String>:
185 match ma:
186 case None{}:
187 None{}
188 case Some{Arg{+ss, r}}:
189 match r:
190 case Con{Lex.Tok{k6, +t6, l6, c6}, Con{Lex.Tok{k7, +t7, l7, c7}, Con{Lex.Tok{k8, +t8, l8, c8},
191 Con{Lex.Tok{k9, +t9, l9, c9}, Con{Lex.Tok{k10, +t10, l10, c10}, rest}}}}}:
192 Bool.pick(Maybe<&2, String>, tail_a.ok(t6, t7, t8, k9, t9, t10), Some{say(ss, t6, t9)}, None{})
193 case other:
194 None{}
195
196
197def tail_b.ok(+kk: String, +t9: String, +t10: String, +lit: String) -> Bool:
198 Bool.and(String.eq(t9, ")"), Bool.and(String.eq(t10, ")"), fits(kk, lit)))
199
200
201def tail_b(ma: Maybe<&2, Arg>, +lit: String) -> Maybe<&2, String>:
202 match ma:
203 case None{}:
204 None{}
205 case Some{Arg{+ss, r}}:
206 match r:
207 case Con{Lex.Tok{k8, +t8, l8, c8}, Con{Lex.Tok{k9, +t9, l9, c9}, Con{Lex.Tok{k10, +t10, l10, c10}, rest}}}:
208 Bool.pick(Maybe<&2, String>, tail_b.ok(t8, t9, t10, lit), Some{say(ss, t8, lit)}, None{})
209 case other:
210 None{}
211
212
213def head_a(k0: Lex.TokKind, +t0: String, +t1: String, +t2: String, +t3: String) -> Bool:
214 Bool.and(dotted(k0), Bool.and(String.eq(t0, "String.eq"), Bool.and(String.eq(t1, "("),
215 Bool.and(String.eq(t3, "("), String.eq(t2, "String.take")))))
216
217
218def at_a.go(ok: Bool, r3: List<&2, Lex.Tok>) -> Maybe<&2, String>:
219 match ok:
220 case True{}:
221 tail_a(skip(r3, SGo{0n}, []))
222 case False{}:
223 None{}
224
225
226def at_a(k0: Lex.TokKind, +t0: String, toks: List<&2, Lex.Tok>) -> Maybe<&2, String>:
227 match toks:
228 case Con{Lex.Tok{k1, +t1, l1, c1}, Con{Lex.Tok{k2, +t2, l2, c2}, Con{Lex.Tok{k3, +t3, l3, c3}, r3}}}:
229 at_a.go(head_a(k0, t0, t1, t2, t3), r3)
230 case other:
231 None{}
232
233
234def head_b(k0: Lex.TokKind, +t0: String, +t1: String, k2: Lex.TokKind, +t3: String, +t4: String, +t5: String) -> Bool:
235 Bool.and(dotted(k0), Bool.and(String.eq(t0, "String.eq"), Bool.and(String.eq(t1, "("), Bool.and(Lex.is_str(k2),
236 Bool.and(String.eq(t3, ","), Bool.and(String.eq(t4, "String.take"), String.eq(t5, "(")))))))
237
238
239def at_b.go(ok: Bool, r5: List<&2, Lex.Tok>, +lit: String) -> Maybe<&2, String>:
240 match ok:
241 case True{}:
242 tail_b(skip(r5, SGo{0n}, []), lit)
243 case False{}:
244 None{}
245
246
247def at_b(k0: Lex.TokKind, +t0: String, toks: List<&2, Lex.Tok>) -> Maybe<&2, String>:
248 match toks:
249 case Con{Lex.Tok{k1, +t1, l1, c1}, Con{Lex.Tok{k2, +t2, l2, c2}, Con{Lex.Tok{k3, +t3, l3, c3},
250 Con{Lex.Tok{k4, +t4, l4, c4}, Con{Lex.Tok{k5, +t5, l5, c5}, r5}}}}}:
251 at_b.go(head_b(k0, t0, t1, k2, t3, t4, t5), r5, t2)
252 case other:
253 None{}
254
255
256def either(ma: Maybe<&2, String>, mb: Maybe<&2, String>) -> Maybe<&2, String>:
257 match ma:
258 case None{}:
259 mb
260 case Some{x}:
261 Some{x}
262
263
264
265def at(+k0: Lex.TokKind, +t0: String, +toks: List<&2, Lex.Tok>) -> Maybe<&2, String>:
266 either(at_a(k0, t0, toks), at_b(k0, t0, toks))
267
268
269def hit(mm: Maybe<&2, String>, +path: String, +ll: U32, +cc: U32, more: List<&2, F.Finding>) -> List<&2, F.Finding>:
270 match mm:
271 case None{}:
272 more
273 case Some{msg}:
274 F.Finding{path, ll, cc, 9, "prefix", msg} <> more
275
276
277def walk(toks: List<&2, Lex.Tok>, +path: String) -> List<&2, F.Finding>:
278 match toks:
279 case Nil{}:
280 Nil{}
281 case Con{Lex.Tok{+k, +t, +l, +c}, +rest}:
282 hit(at(k, t, rest), path, l, c, walk(rest, path))
283
284
285def check(ss: Src.Src) -> List<&2, F.Finding>:
286 Src.Src{+path, text, toks, tree, bound, items} = ss
287 Lazy.stop(List<&2, F.Finding>, Paths.is_law_file(path), [], _u => walk(T.sig(toks), path))
288
289
290
291def check.role(rr: Role.Role, ss: Src.Src) -> List<&2, F.Finding>:
292 Src.Src{path, text, toks, tree, bound, items} = ss
293 Lazy.stop(List<&2, F.Finding>, Role.is_law_file(rr), [], _u => walk(T.sig(toks), path))