1 # rule fromrev: a String read off a reversed list: among the significant 2 # tokens (spaces, newlines and comments dropped), four tokens right after one 3 # another whose texts are `String.from_list`, `(`, `List.reverse` and `(`; 4 # one finding, on the `String.from_list` token, for each such run, and 5 # nothing else. Anything between them, another `(` included 6 # (`String.from_list((List.reverse(..)))`), is not the shape, and neither is 7 # `List.reverse.go`. A buffer of chars consed on the front, then reversed 8 # and read into a String, walks the buffer twice and builds a list only to 9 # throw it away (and String.from_list, not a tail call, runs the JS lane out 10 # of stack on a long buffer): fold the buffer onto a String with SCon in one 11 # pass, `go(t, SCon{h, acc})` from `SNil{}` (src/rchars.bend). A LAWS.bend 12 # or a PROOF.bend, which never runs, is exempt. 13 import Base 14 import ../../src.bend as Src 15 import ../../paths.bend as Paths 16 import ../../role.bend as Role 17 import ../../lazy/lazy.bend as Lazy 18 import ../../finding.bend as F 19 import ../../syntax/lex.bend as Lex 20 import ../tokens.bend as T 21 22 # are the four texts `String.from_list`, `(`, `List.reverse` and `(`? 23 def shape(+tt: String, +aa: String, +bb: String, +dd: String) -> Bool: 24 Bool.and(String.eq(tt, "String.from_list"), 25 Bool.and(String.eq(aa, "("), Bool.and(String.eq(bb, "List.reverse"), String.eq(dd, "(")))) 26 27 # a token of text tt, then these: do the four open the shape? 28 def at(+tt: String, toks: List<&2, Lex.Tok>) -> Bool: 29 match toks: 30 case Con{Lex.Tok{k1, +aa, l1, c1}, Con{Lex.Tok{k2, +bb, l2, c2}, Con{Lex.Tok{k3, +dd, l3, c3}, rest}}}: 31 shape(tt, aa, bb, dd) 32 case other: 33 False{} 34 35 # every run of the shape among the tokens, in order 36 def walk(toks: List<&2, Lex.Tok>, +path: String) -> List<&2, F.Finding>: 37 match toks: 38 case Nil{}: 39 Nil{} 40 case Con{Lex.Tok{k, +t, +l, +c}, +rest}: 41 +more = walk(rest, path) 42 Bool.pick(List<&2, F.Finding>, at(t, rest), 43 F.Finding{path, l, c, 16, "fromrev", "String.from_list(List.reverse(..)) walks the list twice and builds one it throws away; fold it onto a String with SCon in one pass."} 44 <> more, 45 more) 46 47 # the rule 48 def check(ss: Src.Src) -> List<&2, F.Finding>: 49 Src.Src{+path, text, toks, tree, bound, items} = ss 50 Lazy.stop(List<&2, F.Finding>, Paths.is_law_file(path), [], _u => walk(T.sig(toks), path)) 51 52 # the rule, told the file's role (src/role.bend): a law or proof file is 53 # skipped 54 def check.role(rr: Role.Role, ss: Src.Src) -> List<&2, F.Finding>: 55 Src.Src{path, text, toks, tree, bound, items} = ss 56 Lazy.stop(List<&2, F.Finding>, Role.is_law_file(rr), [], _u => walk(T.sig(toks), path))