import { test, expect } from "bun:test" import { toBytecode } from "#bytecode" import { VM } from "#vm" test("MAKE_FUNCTION - creates function with captured scope", async () => { const bytecode = toBytecode(` MAKE_FUNCTION () #999 `) const result = await new VM(bytecode).run() expect(result.type).toBe('function') if (result.type === 'function') { expect(result.body).toBe(999) expect(result.params).toEqual([]) } }) test("CALL and RETURN - basic function call", async () => { const bytecode = toBytecode(` MAKE_FUNCTION () #3 CALL #0 HALT PUSH 42 RETURN `) const result = await new VM(bytecode).run() expect(result).toEqual({ type: 'number', value: 42 }) }) test("CALL and RETURN - function with one parameter", async () => { const bytecode = toBytecode(` MAKE_FUNCTION (x) #4 PUSH 100 CALL #1 HALT LOAD x RETURN `) const result = await new VM(bytecode).run() expect(result).toEqual({ type: 'number', value: 100 }) }) test("CALL and RETURN - function with two parameters", async () => { const bytecode = toBytecode(` MAKE_FUNCTION (a b) #5 PUSH 10 PUSH 20 CALL #2 HALT LOAD a LOAD b ADD RETURN `) const result = await new VM(bytecode).run() expect(result).toEqual({ type: 'number', value: 30 }) }) test("CALL - variadic function with no fixed params", async () => { const bytecode = toBytecode(` MAKE_FUNCTION (...args) #6 PUSH 1 PUSH 2 PUSH 3 CALL #3 HALT LOAD args RETURN `) const result = await new VM(bytecode).run() expect(result).toEqual({ type: 'array', value: [ { type: 'number', value: 1 }, { type: 'number', value: 2 }, { type: 'number', value: 3 } ] }) }) test("CALL - variadic function with one fixed param", async () => { const bytecode = toBytecode(` MAKE_FUNCTION (x ...rest) #6 PUSH 10 PUSH 20 PUSH 30 CALL #3 HALT LOAD rest RETURN `) const result = await new VM(bytecode).run() // x should be 10, rest should be [20, 30] expect(result).toEqual({ type: 'array', value: [ { type: 'number', value: 20 }, { type: 'number', value: 30 } ] }) }) test("CALL - variadic function with two fixed params", async () => { const bytecode = toBytecode(` MAKE_FUNCTION (a b ...rest) #7 PUSH 1 PUSH 2 PUSH 3 PUSH 4 CALL #4 HALT LOAD rest RETURN `) const result = await new VM(bytecode).run() // a=1, b=2, rest=[3, 4] expect(result).toEqual({ type: 'array', value: [ { type: 'number', value: 3 }, { type: 'number', value: 4 } ] }) }) test("CALL - variadic function with no extra args", async () => { const bytecode = toBytecode(` MAKE_FUNCTION (x ...rest) #4 PUSH 10 CALL #1 HALT LOAD rest RETURN `) const result = await new VM(bytecode).run() // rest should be empty array expect(result).toEqual({ type: 'array', value: [] }) }) test("CALL - variadic function with defaults on fixed params", async () => { const bytecode = toBytecode(` MAKE_FUNCTION (x=5 ...rest) #3 CALL #0 HALT LOAD x RETURN `) const result = await new VM(bytecode).run() // x should use default value 5 expect(result).toEqual({ type: 'number', value: 5 }) }) test("TAIL_CALL - variadic function", async () => { const bytecode = toBytecode(` MAKE_FUNCTION (x ...rest) #6 PUSH 1 PUSH 2 PUSH 3 CALL #3 HALT LOAD rest RETURN `) const result = await new VM(bytecode).run() // Should return the rest array [2, 3] expect(result).toEqual({ type: 'array', value: [ { type: 'number', value: 2 }, { type: 'number', value: 3 } ] }) })