import { test, expect } from "bun:test" import { toBytecode } from "#bytecode" import { VM } from "#vm" test("TAIL_CALL - basic tail recursive countdown", async () => { // Tail recursive function that counts down to 0 // function countdown(n) { // if (n === 0) return "done" // return countdown(n - 1) // tail call // } const bytecode = toBytecode(` MAKE_FUNCTION (n) #6 STORE countdown LOAD countdown PUSH 5 CALL #1 HALT LOAD n PUSH 0 EQ JUMP_IF_FALSE #2 PUSH "done" RETURN LOAD countdown LOAD n PUSH 1 SUB TAIL_CALL #1 `) const result = await new VM(bytecode).run() expect(result).toEqual({ type: 'string', value: 'done' }) }) test("TAIL_CALL - tail recursive sum with accumulator", async () => { // function sum(n, acc = 0) { // if (n === 0) return acc // return sum(n - 1, acc + n) // tail call // } const bytecode = toBytecode(` MAKE_FUNCTION (n acc) #7 STORE sum LOAD sum PUSH 10 PUSH 0 CALL #2 HALT LOAD n PUSH 0 EQ JUMP_IF_FALSE #2 LOAD acc RETURN LOAD sum LOAD n PUSH 1 SUB LOAD acc LOAD n ADD TAIL_CALL #2 `) const result = await new VM(bytecode).run() expect(result).toEqual({ type: 'number', value: 55 }) // sum of 1..10 }) test("TAIL_CALL - doesn't overflow stack with deep recursion", async () => { // This would overflow the stack with regular CALL // but should work fine with TAIL_CALL const bytecode = toBytecode(` MAKE_FUNCTION (n) #6 STORE deep LOAD deep PUSH 10000 CALL #1 HALT LOAD n PUSH 0 LTE JUMP_IF_FALSE #2 PUSH "success" RETURN LOAD deep LOAD n PUSH 1 SUB TAIL_CALL #1 `) const result = await new VM(bytecode).run() expect(result).toEqual({ type: 'string', value: 'success' }) }) test("TAIL_CALL - tail call to different function", async () => { // TAIL_CALL can call a different function (mutual recursion) // function even(n) { return n === 0 ? true : odd(n - 1) } // function odd(n) { return n === 0 ? false : even(n - 1) } const bytecode = toBytecode(` MAKE_FUNCTION (n) #8 STORE even MAKE_FUNCTION (n) #19 STORE odd LOAD even PUSH 7 CALL #1 HALT LOAD n PUSH 0 EQ JUMP_IF_FALSE #2 PUSH true RETURN LOAD odd LOAD n PUSH 1 SUB TAIL_CALL #1 LOAD n PUSH 0 EQ JUMP_IF_FALSE #2 PUSH false RETURN LOAD even LOAD n PUSH 1 SUB TAIL_CALL #1 `) const result = await new VM(bytecode).run() expect(result).toEqual({ type: 'boolean', value: false }) // 7 is odd })