• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/tests/custom/02_runtime/09_tail_calls

  1 Testing tail call optimization of function invocations in return position.
  2 
  3 
  4 1. Testing unbounded direct tail recursion.
  5 
  6 -- Expect stdout --
  7 done
  8 -- End --
  9 
 10 -- Testcase --
 11 {%
 12         function count(n) {
 13                 if (n === 0)
 14                         return "done";
 15 
 16                 return count(n - 1);
 17         }
 18 
 19         print(count(200000), "\n");
 20 %}
 21 -- End --
 22 
 23 
 24 2. Testing tail recursive accumulator and mutual recursion.
 25 
 26 -- Expect stdout --
 27 125250
 28 even
 29 even
 30 -- End --
 31 
 32 -- Testcase --
 33 {%
 34         function sum(n, acc) {
 35                 return n === 0 ? acc : sum(n - 1, acc + n);
 36         }
 37 
 38         let isEven, isOdd;
 39 
 40         isEven = (n) => n === 0 ? true : isOdd(n - 1);
 41         isOdd = (n) => n === 0 ? false : isEven(n - 1);
 42 
 43         print(sum(500, 0), "\n");
 44         print(isEven(100000) ? "even" : "odd", "\n");
 45         print(isOdd(100000) ? "odd" : "even", "\n");
 46 %}
 47 -- End --
 48 
 49 
 50 3. Testing method invocation tail calls preserving the receiver.
 51 
 52 -- Expect stdout --
 53 called 4 times, counter=12
 54 -- End --
 55 
 56 -- Testcase --
 57 {%
 58         let o = {
 59                 count: 0,
 60                 counter: 0,
 61                 inc(n) {
 62                         this.count++;
 63                         this.counter += n;
 64 
 65                         if (this.count < 4)
 66                                 return this.inc(3);
 67 
 68                         return `called ${this.count} times, counter=${this.counter}`;
 69                 }
 70         };
 71 
 72         print(o.inc(3), "\n");
 73 %}
 74 -- End --
 75 
 76 
 77 4. Testing tail calls in conditional and logical expressions.
 78 
 79 -- Expect stdout --
 80 yes no or and nullish y g -
 81 -- End --
 82 
 83 -- Testcase --
 84 {%
 85         function id(x) {
 86                 return x;
 87         }
 88 
 89         function pick(n) {
 90                 return n ? id("yes") : id("no");
 91         }
 92 
 93         function logical(n) {
 94                 return n || id("or");
 95         }
 96 
 97         function coalesce(n) {
 98                 return n ?? id("nullish");
 99         }
100 
101         function optional(fn) {
102                 return fn?.("g");
103         }
104 
105         print(pick(1), " ", pick(0), " ",
106               logical(null), " ", logical("and"), " ",
107               coalesce(null), " ", coalesce("y"), " ",
108               optional(id), " ", optional(null) ?? "-",
109         "\n");
110 %}
111 -- End --
112 
113 
114 5. Testing tail calls with spread, missing and excess arguments.
115 
116 -- Expect stdout --
117 6 12 0 nob 5
118 -- End --
119 
120 -- Testcase --
121 {%
122         function total(...vals) {
123                 let r = 0;
124 
125                 for (let v in vals)
126                         r += v;
127 
128                 return r;
129         }
130 
131         function defaults(a, b, c) {
132                 return b ?? "nob";
133         }
134 
135         function nargs(...vals) {
136                 return length(vals);
137         }
138 
139         function spread(arr, n) {
140                 return n === 0 ? total(...arr) : spread(arr, n - 1);
141         }
142 
143         function mixed(arr, n) {
144                 return n === 0 ? total(1, ...arr, 6) : mixed(arr, n - 1);
145         }
146 
147         function empty(n) {
148                 return n === 0 ? total() : empty(n - 1);
149         }
150 
151         function missing(n) {
152                 return n === 0 ? defaults() : missing(n - 1);
153         }
154 
155         function excess(n) {
156                 return n === 0 ? nargs(1, 2, 3, 4, 5) : excess(n - 1);
157         }
158 
159         print(spread([1, 2, 3], 10000), " ",
160               mixed([2, 3], 10000), " ",
161               empty(10000), " ",
162               missing(10000), " ",
163               excess(10000),
164         "\n");
165 %}
166 -- End --
167 
168 
169 6. Testing that tail calls do not disturb enclosing local variables.
170 
171 -- Expect stdout --
172 T1000:1 { "tag": "T1000" }
173 -- End --
174 
175 -- Testcase --
176 {%
177         function inner(o, arr, n) {
178                 if (n === 0)
179                         return `${o.tag}:${arr[1]} ${o}`;
180 
181                 arr[1] = n;
182 
183                 return inner(o, arr, n - 1);
184         }
185 
186         function outer(n) {
187                 let obj = { tag: `T${n}` };
188                 let list = [n];
189 
190                 return inner(obj, list, 5);
191         }
192 
193         print(outer(1000), "\n");
194 %}
195 -- End --
196 
197 
198 7. Testing tail calls of closures over locals of the replaced frame.
199 
200 -- Expect stdout --
201 #7 #100000 done
202 -- End --
203 
204 -- Testcase --
205 {%
206         function mk(n) {
207                 let label = `#${n}`;
208                 let walk;
209 
210                 walk = (k) => k === 0 ? label : walk(k - 1);
211 
212                 return n === 0 ? "done" : walk(n);
213         }
214 
215         print(mk(7), " ", mk(100000), " ", mk(0), "\n");
216 %}
217 -- End --
218 
219 
220 8. Testing tail calls through callback invocation.
221 
222 -- Expect stdout --
223 applied deep-done
224 -- End --
225 
226 -- Testcase --
227 {%
228         function apply(cb, n) {
229                 return cb(n);
230         }
231 
232         function repeater(n) {
233                 return apply((x) => x === 0 ? "applied" : repeater(x - 1), n);
234         }
235 
236         function tramp(n, f) {
237                 return n === 0 ? f() : tramp(n - 1, f);
238         }
239 
240         print(repeater(50000), " ", tramp(50000, () => "deep-done"), "\n");
241 %}
242 -- End --
243 
244 
245 9. Testing exceptions raised by tail called functions.
246 
247 -- Expect stdout --
248 caught: boom in-try: recovered
249 -- End --
250 
251 -- Testcase --
252 {%
253         function fail() {
254                 die("boom");
255         }
256 
257         function deep(n) {
258                 return n === 0 ? fail() : deep(n - 1);
259         }
260 
261         function direct(n) {
262                 return n === 0 ? die("nope") : direct(n - 1);
263         }
264 
265         function recover(n) {
266                 try {
267                         return direct(n);
268                 }
269                 catch (e) {
270                         return "recovered";
271                 }
272         }
273 
274         function intry(n) {
275                 try {
276                         return deep(n);
277                 }
278                 catch (e) {
279                         return `caught: ${e.message} in-try: ${recover(n)}`;
280                 }
281         }
282 
283         print(intry(20000), "\n");
284 %}
285 -- End --
286 
287 
288 10. Testing that non-tail invocations still hit the recursion limit.
289 
290 -- Expect stderr --
291 Runtime error: Too much recursion
292 In tailrec(), line 3, byte 11:
293   called from anonymous function ([stdin]:6:10)
294 
295  `        tailrec();`
296   Near here ------^
297 
298 
299 -- End --
300 
301 -- Testcase --
302 {%
303         function tailrec() {
304                 tailrec();
305         }
306 
307         tailrec();
308 %}
309 -- End --
310 
311 
312 11. Testing that calls contributing to an expression still hit the recursion limit.
313 
314 -- Expect stderr --
315 Runtime error: Too much recursion
316 In tailrec(), line 3, byte 18:
317   called from anonymous function ([stdin]:6:10)
318 
319  `        return tailrec() + 1;`
320   Near here -------------^
321 
322 
323 -- End --
324 
325 -- Testcase --
326 {%
327         function tailrec() {
328                 return tailrec() + 1;
329         }
330 
331         tailrec();
332 %}
333 -- End --

This page was automatically generated by LXR 0.3.1.  •  OpenWrt