1 # src/rules/calls: what the recursion rules (pick, strict, tail, concat, 2 # index) share. A file's defs, each with its name, its signature and its body; 3 # a call's arguments; whether a def calls itself; its parameters, and the type 4 # of the one it walks; and whether it is exempt (a law or a proof never runs, 5 # and a def with no type fills a law: it is a proof). 6 import Base 7 import ../paths.bend as Paths 8 import ../syntax/lex.bend as Lex 9 import ../syntax/tree.bend as Tree 10 import ../syntax/bind.bend as Bind 11 import ../lazy/lazy.bend as Lazy 12 13 # a def: its name, its own tokens (the signature) and the statements under it 14 type Def is Data: 15 Def{name: String, sig: Tree.Node, body: Tree.Node} 16 17 # a def with a name joins the others; one without (half-written) does not 18 def defs.push(mm: Maybe<&2, String>, sig: Tree.Node, body: Tree.Node, acc: List<&2, Def>) -> List<&2, Def>: 19 match mm: 20 case None{}: 21 acc 22 case Some{name}: 23 Def{name, sig, body} <> acc 24 25 # the defs of a parsed source, in order 26 def defs(root: Tree.Node) -> List<&2, Def>: 27 match root: 28 case Tree.NCons{Tree.Stmt{Tree.SDef{}, +kids, body}, rest}: 29 defs.push(Bind.declared(kids), kids, body, defs(rest)) 30 case Tree.NCons{h, rest}: 31 defs(rest) 32 case other: 33 Nil{} 34 35 # a leaf's kind, none for anything else: kind tests read this, never a text 36 def leaf.kind(nn: Tree.Node) -> Maybe<&2, Lex.TokKind>: 37 match nn: 38 case Tree.Leaf{Lex.Tok{k, t, l, c}}: 39 Some{k} 40 case other: 41 None{} 42 43 # a kind test on a kind, when there is one 44 def kind.of(~test: Lex.TokKind -> Bool, mm: Maybe<&2, Lex.TokKind>) -> Bool: 45 match mm: 46 case None{}: 47 False{} 48 case Some{k}: 49 test(k) 50 51 # a leaf of this kind? 52 def kind.leaf(~test: Lex.TokKind -> Bool, nn: Tree.Node) -> Bool: 53 kind.of(~test, leaf.kind(nn)) 54 55 # a `:` 56 def kind.colon(kk: Lex.TokKind) -> Bool: 57 match kk: 58 case Lex.TColon{}: 59 True{} 60 case other: 61 False{} 62 63 # a capitalized name 64 def kind.upper(kk: Lex.TokKind) -> Bool: 65 match kk: 66 case Lex.TUpper{}: 67 True{} 68 case other: 69 False{} 70 71 # `->` 72 def kind.arrow(kk: Lex.TokKind) -> Bool: 73 match kk: 74 case Lex.TArrow{}: 75 True{} 76 case other: 77 False{} 78 79 # a plain operator (`-`, `~`, `+`) 80 def kind.oper(kk: Lex.TokKind) -> Bool: 81 match kk: 82 case Lex.TOp{}: 83 True{} 84 case other: 85 False{} 86 87 # a leaf's text, "" for anything else 88 def leaf.text(nn: Tree.Node) -> String: 89 match nn: 90 case Tree.Leaf{Lex.Tok{k, t, l, c}}: 91 t 92 case other: 93 "" 94 95 # the arguments of a group, split at its top-level commas (each a chain) 96 def args.go(kids: Tree.Node, +cur: Tree.Node, +acc: List<&2, Tree.Node>) -> List<&2, Tree.Node>: 97 match kids: 98 case Tree.NCons{+h, +rest}: 99 Lazy.either(List<&2, Tree.Node>, kind.leaf(~Lex.is_comma, h), 100 _u => args.go(rest, Tree.NNil{}, Tree.reverse(cur, Tree.NNil{}) <> acc), 101 _v => args.go(rest, Tree.NCons{h, cur}, acc)) 102 case other: 103 List.reverse(&2, Tree.Node, Tree.reverse(cur, Tree.NNil{}) <> acc) 104 105 # the arguments of a group 106 def args(kids: Tree.Node) -> List<&2, Tree.Node>: 107 args.go(kids, Tree.NNil{}, []) 108 109 # the nth argument, or nothing 110 def arg(as: List<&2, Tree.Node>, nn: Nat) -> Tree.Node: 111 match as nn: 112 case Con{h, t} 0n: 113 h 114 case Con{h, t} 1n+p: 115 arg(t, p) 116 case Nil{} m: 117 Tree.NNil{} 118 119 # is there a call of the name, `name(..)`, anywhere under the node? 120 def calls(nn: Tree.Node, +name: String) -> Bool: 121 match nn: 122 case Tree.NCons{Tree.Leaf{Lex.Tok{k, +t, l, c}}, Tree.NCons{Tree.Group{Lex.Tok{_, +o, _, _}, kids, _}, rest}}: 123 +a = calls(kids, name) 124 +b = calls(rest, name) 125 Bool.or(Bool.and(String.eq(o, "("), String.eq(t, name)), Bool.or(a, b)) 126 case Tree.Group{open, kids, close}: 127 calls(kids, name) 128 case Tree.Stmt{kind, kids, body}: 129 +a = calls(kids, name) 130 +b = calls(body, name) 131 Bool.or(a, b) 132 case Tree.NCons{h, t}: 133 +a = calls(h, name) 134 +b = calls(t, name) 135 Bool.or(a, b) 136 case other: 137 False{} 138 139 # the kids of a signature's first `(` group: the parameters 140 def params.of(sig: Tree.Node) -> Tree.Node: 141 match sig: 142 case Tree.NCons{Tree.Group{Lex.Tok{k, +o, l, c}, +kids, close}, rest}: 143 +more = params.of(rest) 144 Bool.pick(Tree.Node, String.eq(o, "("), kids, more) 145 case Tree.NCons{h, rest}: 146 params.of(rest) 147 case other: 148 Tree.NNil{} 149 150 # a def's parameters, each its chain up to a comma (`+xs: List<&2, U32>`) 151 def params(sig: Tree.Node) -> List<&2, Tree.Node>: 152 args(params.of(sig)) 153 154 # a parameter's name: its first lowercase name before the colon ("" for a 155 # type parameter like `-A: Type`) 156 def param_name(pp: Tree.Node) -> String: 157 match pp: 158 case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TName{}, t, l, c}}, rest}: 159 t 160 case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TColon{}, t, l, c}}, rest}: 161 "" 162 case Tree.NCons{h, rest}: 163 param_name(rest) 164 case other: 165 "" 166 167 def names.go(ps: List<&2, Tree.Node>, acc: List<&2, String>) -> List<&2, String>: 168 match ps: 169 case Nil{}: 170 List.reverse(&2, String, acc) 171 case Con{p, rest}: 172 names.go(rest, param_name(p) <> acc) 173 174 # the names of a def's parameters 175 def names(sig: Tree.Node) -> List<&2, String>: 176 names.go(params(sig), []) 177 178 # does a chain hold a top-level colon? 179 def has_colon(pp: Tree.Node) -> Bool: 180 match pp: 181 case Tree.NCons{h, rest}: 182 Lazy.or_else(kind.leaf(~kind.colon, h), _u => has_colon(rest)) 183 case other: 184 False{} 185 186 # does the chain open with a `-` or `~` operator? 187 def is_live.sign(pp: Tree.Node) -> Bool: 188 match pp: 189 case Tree.NCons{+h, rest}: 190 +t = leaf.text(h) 191 Bool.and(kind.leaf(~kind.oper, h), Bool.or(String.eq(t, "-"), String.eq(t, "~"))) 192 case other: 193 False{} 194 195 # a live parameter: typed, and neither erased (`-A: Type`) nor a template 196 # (`~f: A -> B`); a bare quantity name (`a` in `List.get(a, ..)`) is not one 197 def is_live(+pp: Tree.Node) -> Bool: 198 Bool.and(Bool.not(is_live.sign(pp)), has_colon(pp)) 199 200 # the head of a parameter's type: the first capitalized name after the colon 201 # (`List` in `+xs: +List`), "" for none 202 def type_head(pp: Tree.Node, +after: Bool) -> String: 203 match pp: 204 case Tree.NCons{+h, rest}: 205 +more = type_head(rest, Bool.or(after, kind.leaf(~kind.colon, h))) 206 Bool.pick(String, Bool.and(after, kind.leaf(~kind.upper, h)), leaf.text(h), more) 207 case other: 208 "" 209 210 def walked.go(ps: List<&2, Tree.Node>, found: Maybe<&2, String>) -> String: 211 match ps found: 212 case Con{+p, rest} None{}: 213 walked.go(rest, Lazy.stop(Maybe<&2, String>, Bool.not(is_live(p)), None{}, 214 _u => Some{type_head(p, False{})})) 215 case Con{p, rest} Some{t}: 216 t 217 case Nil{} Some{t}: 218 t 219 case Nil{} None{}: 220 "" 221 222 # the type head of the first live parameter: the one a self-call must shrink 223 def walked(sig: Tree.Node) -> String: 224 walked.go(params(sig), None{}) 225 226 # does the def walk a list or a string (its first live parameter's type)? 227 def seq(sig: Tree.Node) -> Bool: 228 +h = walked(sig) 229 Bool.or(String.eq(h, "List"), String.eq(h, "String")) 230 231 # a `{` group after the arrow, or a proof further on 232 def returns_proof.or(+brace: Bool, more: Bool) -> Bool: 233 Bool.or(brace, more) 234 235 # does the signature return a proof, `-> {a == b : T}`? 236 def returns_proof(sig: Tree.Node) -> Bool: 237 match sig: 238 case Tree.NCons{h, +rest}: 239 match rest: 240 case Tree.NCons{+g, r2}: 241 Lazy.either(Bool, kind.leaf(~kind.arrow, h), _u => returns_proof.or(Tree.opens(g, "{"), returns_proof(r2)), 242 _v => returns_proof(rest)) 243 case _r: 244 returns_proof(rest) 245 case other: 246 False{} 247 248 # does a chain hold a top-level `->`? 249 def has_arrow(pp: Tree.Node) -> Bool: 250 match pp: 251 case Tree.NCons{h, rest}: 252 Lazy.or_else(kind.leaf(~kind.arrow, h), _u => has_arrow(rest)) 253 case other: 254 False{} 255 256 # does the signature write a type: a `:` among its parameters, or a `->`? 257 def typed(+sig: Tree.Node) -> Bool: 258 Lazy.or_else(has_colon(params.of(sig)), _u => has_arrow(sig)) 259 260 # is the def a proof? One that returns a proof (`-> {a == b : T}`), or one 261 # written with no type at all (`def f(x, y):`), which is how Bend fills the 262 # law named f 263 def proof(+sig: Tree.Node) -> Bool: 264 Bool.or(returns_proof(sig), Bool.not(typed(sig))) 265 266 # a law file, a proof file, or a def that is a proof: none of it runs, so its 267 # cost does not matter 268 def exempt(+path: String, sig: Tree.Node) -> Bool: 269 Bool.or(Paths.is_law_file(path), proof(sig))