src/rules/calls.bend
bolt@1.21.0.0 · 269 lines · 0 definitions
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).
6import Base
7import bolt@1.21.0.0/src/paths.bend as Paths
8import bolt@1.21.0.0/src/syntax/lex.bend as Lex
9import bolt@1.21.0.0/src/syntax/tree.bend as Tree
10import bolt@1.21.0.0/src/syntax/bind.bend as Bind
11import bolt@1.21.0.0/src/lazy/lazy.bend as Lazy
12
13# a def: its name, its own tokens (the signature) and the statements under it
14type 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
18def 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
26def 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
36def 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
44def 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?
52def kind.leaf(~test: Lex.TokKind -> Bool, nn: Tree.Node) -> Bool:
53 kind.of(~test, leaf.kind(nn))
54
55# a `:`
56def 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
64def kind.upper(kk: Lex.TokKind) -> Bool:
65 match kk:
66 case Lex.TUpper{}:
67 True{}
68 case other:
69 False{}
70
71# `->`
72def 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 (`-`, `~`, `+`)
80def 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
88def 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)
96def 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
106def args(kids: Tree.Node) -> List<&2, Tree.Node>:
107 args.go(kids, Tree.NNil{}, [])
108
109# the nth argument, or nothing
110def 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?
120def 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
140def 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>`)
151def 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`)
156def 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
167def 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
175def names(sig: Tree.Node) -> List<&2, String>:
176 names.go(params(sig), [])
177
178# does a chain hold a top-level colon?
179def 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?
187def 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
197def 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<A>`), "" for none
202def 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
210def 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
223def 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)?
227def 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
232def returns_proof.or(+brace: Bool, more: Bool) -> Bool:
233 Bool.or(brace, more)
234
235# does the signature return a proof, `-> {a == b : T}`?
236def 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 `->`?
249def 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 `->`?
257def 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
263def 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
268def exempt(+path: String, sig: Tree.Node) -> Bool:
269 Bool.or(Paths.is_law_file(path), proof(sig))