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