src/rules/perf/strings.bend
bolt@1.27.0.0 · 113 lines · 12 definitions
1
2
3
4
5
6
7
8
9
10
11import Base
12import bolt@1.27.0.0/src/src.bend as Src
13import bolt@1.27.0.0/src/finding.bend as F
14import bolt@1.27.0.0/src/syntax/lex.bend as Lex
15import bolt@1.27.0.0/src/syntax/tree.bend as Tree
16import bolt@1.27.0.0/src/lazy/lazy.bend as Lazy
17import bolt@1.27.0.0/src/rules/tokens.bend as T
18
19
20type Tally is Data:
21 Tally{arms: U32, chars: U32}
22
23
24
25
26def closes(cs: List<&2, Char>, +esc: Bool, +shut: Bool) -> Bool:
27 match cs:
28 case Nil{}:
29 shut
30 case Con{ch, rest}:
31 +code = Char.to_u32(ch)
32 closes(rest, Bool.and(Bool.not(esc), U32.is_eq(code, 92)), Bool.and(Bool.not(esc), U32.is_eq(code, 34)))
33
34
35def opened(cs: List<&2, Char>) -> Bool:
36 match cs:
37 case Nil{}:
38 False{}
39 case Con{_q, rest}:
40 closes(rest, False{}, False{})
41
42
43
44def terminated(+tt: String) -> Bool:
45 opened(String.to_list(tt))
46
47
48
49def literal(kids: Tree.Node) -> Maybe<&2, U32>:
50 match kids:
51 case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TStr{}, +t, l, c}}, rest}:
52 +w = T.width(t)
53 Bool.pick(Maybe<&2, U32>, terminated(t), Some{(w - 2 : U32)}, None{})
54 case other:
55 None{}
56
57
58def one(mm: Maybe<&2, U32>) -> U32:
59 match mm:
60 case None{}:
61 0
62 case Some{n}:
63 1
64
65
66def size(mm: Maybe<&2, U32>) -> U32:
67 match mm:
68 case None{}:
69 0
70 case Some{n}:
71 n
72
73
74def tally(body: Tree.Node, +nn: U32, +chars: U32) -> Tally:
75 match body:
76 case Tree.NCons{Tree.Stmt{Tree.SCase{}, kids, b}, rest}:
77 +m = literal(T.pattern(kids))
78 +k = one(m)
79 +ch = size(m)
80 tally(rest, (nn + k : U32), (chars + ch : U32))
81 case Tree.NCons{h, rest}:
82 tally(rest, nn, chars)
83 case other:
84 Tally{nn, chars}
85
86
87def report(tt: Tally, +path: String, +ll: U32, +cc: U32) -> List<&2, F.Finding>:
88 Tally{+n, +chars} = tt
89 Bool.pick(List<&2, F.Finding>, U32.is_gt(chars, 64),
90 [F.Finding{path, ll, cc, 5, "strings", "This match has " ++ U32.show(n) ++ " string-literal cases totalling " ++ U32.show(chars)
91 ++ " characters, over the limit of 64, which blows up compile time and memory; map the string to a sum type once and match on that."}],
92 [])
93
94
95def on_stmt(+kids: Tree.Node, +body: Tree.Node, +path: String) -> List<&2, F.Finding>:
96 +hit = T.keyword(kids, "match")
97 Lazy.stop(List<&2, F.Finding>, Bool.not(hit), [],
98 _u => report(tally(body, 0, 0), path, Tree.line(kids), Tree.col(kids)))
99
100
101def walk(nn: Tree.Node, +path: String) -> List<&2, F.Finding>:
102 match nn:
103 case Tree.NCons{Tree.Stmt{kind, kids, +body}, rest}:
104 List.concat(&2, F.Finding, [on_stmt(kids, body, path), walk(body, path), walk(rest, path)])
105 case Tree.NCons{h, rest}:
106 walk(rest, path)
107 case other:
108 Nil{}
109
110
111def check(ss: Src.Src) -> List<&2, F.Finding>:
112 Src.Src{path, text, toks, tree, bound, items} = ss
113 walk(tree, path)