import { describe, expect, test } from "bun:test" import { toBytecode, run } from "#reef" describe("Programmatic Bytecode API", () => { test("basic stack operations", async () => { const bytecode = toBytecode([ ["PUSH", 42], ["DUP"], ["POP"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 42 }) }) test("arithmetic operations", async () => { const bytecode = toBytecode([ ["PUSH", 5], ["PUSH", 10], ["ADD"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 15 }) }) test("variables", async () => { const bytecode = toBytecode([ ["PUSH", 100], ["STORE", "x"], ["LOAD", "x"], ["PUSH", 50], ["SUB"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 50 }) }) test("labels and jumps", async () => { const bytecode = toBytecode([ ["PUSH", 5], ["PUSH", 10], ["GT"], ["JUMP_IF_TRUE", ".skip"], ["PUSH", 999], [".skip:"], ["PUSH", 42], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 42 }) }) test("loop with labels", async () => { const bytecode = toBytecode([ ["PUSH", 0], ["STORE", "i"], [".loop:"], ["LOAD", "i"], ["PUSH", 3], ["LT"], ["JUMP_IF_FALSE", ".end"], ["LOAD", "i"], ["PUSH", 1], ["ADD"], ["STORE", "i"], ["JUMP", ".loop"], [".end:"], ["LOAD", "i"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 3 }) }) test("simple function", async () => { const bytecode = toBytecode([ ["MAKE_FUNCTION", ["x", "y"], ".add_body"], ["STORE", "add"], ["JUMP", ".after_fn"], [".add_body:"], ["LOAD", "x"], ["LOAD", "y"], ["ADD"], ["RETURN"], [".after_fn:"], ["LOAD", "add"], ["PUSH", 5], ["PUSH", 10], ["PUSH", 2], ["PUSH", 0], ["CALL"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 15 }) }) test("function with defaults", async () => { const bytecode = toBytecode([ ["MAKE_FUNCTION", ["x", "y=10"], ".fn_body"], ["STORE", "fn"], ["JUMP", ".after"], [".fn_body:"], ["LOAD", "x"], ["LOAD", "y"], ["ADD"], ["RETURN"], [".after:"], ["LOAD", "fn"], ["PUSH", 5], ["PUSH", 1], ["PUSH", 0], ["CALL"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 15 }) }) test("variadic function", async () => { const bytecode = toBytecode([ ["MAKE_FUNCTION", ["...args"], ".sum_body"], ["STORE", "sum"], ["JUMP", ".after"], [".sum_body:"], ["PUSH", 0], ["STORE", "total"], ["LOAD", "args"], ["ARRAY_LEN"], ["STORE", "len"], ["PUSH", 0], ["STORE", "i"], [".loop:"], ["LOAD", "i"], ["LOAD", "len"], ["LT"], ["JUMP_IF_FALSE", ".done"], ["LOAD", "total"], ["LOAD", "args"], ["LOAD", "i"], ["ARRAY_GET"], ["ADD"], ["STORE", "total"], ["LOAD", "i"], ["PUSH", 1], ["ADD"], ["STORE", "i"], ["JUMP", ".loop"], [".done:"], ["LOAD", "total"], ["RETURN"], [".after:"], ["LOAD", "sum"], ["PUSH", 1], ["PUSH", 2], ["PUSH", 3], ["PUSH", 3], ["PUSH", 0], ["CALL"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 6 }) }) test("named parameters", async () => { const bytecode = toBytecode([ ["MAKE_FUNCTION", ["x", "@opts"], ".fn_body"], ["STORE", "fn"], ["JUMP", ".after"], [".fn_body:"], ["LOAD", "x"], ["RETURN"], [".after:"], ["LOAD", "fn"], ["PUSH", 42], ["PUSH", "verbose"], ["PUSH", true], ["PUSH", 1], ["PUSH", 1], ["CALL"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 42 }) }) test("arrays", async () => { const bytecode = toBytecode([ ["PUSH", 1], ["PUSH", 2], ["PUSH", 3], ["MAKE_ARRAY", 3], ["HALT"] ]) const result = await run(bytecode) expect(result.type).toBe("array") if (result.type === "array") { expect(result.value).toHaveLength(3) } }) test("dicts", async () => { const bytecode = toBytecode([ ["PUSH", "name"], ["PUSH", "Alice"], ["PUSH", "age"], ["PUSH", 30], ["MAKE_DICT", 2], ["HALT"] ]) const result = await run(bytecode) expect(result.type).toBe("dict") }) test("exception handling", async () => { const bytecode = toBytecode([ ["PUSH_TRY", ".catch"], ["PUSH", "error!"], ["THROW"], ["POP_TRY"], [".catch:"], ["STORE", "err"], ["LOAD", "err"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "string", value: "error!" }) }) test("string values", async () => { const bytecode = toBytecode([ ["PUSH", "hello"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "string", value: "hello" }) }) test("boolean values", async () => { const bytecode = toBytecode([ ["PUSH", true], ["NOT"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "boolean", value: false }) }) test("null values", async () => { const bytecode = toBytecode([ ["PUSH", null], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "null", value: null }) }) test("tail call", async () => { const bytecode = toBytecode([ ["MAKE_FUNCTION", ["n", "acc"], ".factorial_body"], ["STORE", "factorial"], ["JUMP", ".main"], [".factorial_body:"], ["LOAD", "n"], ["PUSH", 0], ["LTE"], ["JUMP_IF_FALSE", ".recurse"], ["LOAD", "acc"], ["RETURN"], [".recurse:"], ["LOAD", "factorial"], ["LOAD", "n"], ["PUSH", 1], ["SUB"], ["LOAD", "n"], ["LOAD", "acc"], ["MUL"], ["PUSH", 2], ["PUSH", 0], ["TAIL_CALL"], [".main:"], ["LOAD", "factorial"], ["PUSH", 5], ["PUSH", 1], ["PUSH", 2], ["PUSH", 0], ["CALL"], ["HALT"] ]) const result = await run(bytecode) expect(result).toEqual({ type: "number", value: 120 }) }) })