ReefVM/tests/native.test.ts
2025-10-05 18:38:22 -07:00

183 lines
4.9 KiB
TypeScript

import { test, expect } from "bun:test"
import { VM } from "#vm"
import { OpCode } from "#opcode"
import { toValue, toNumber, toString } from "#value"
test("CALL_NATIVE - basic function call", async () => {
const vm = new VM({
instructions: [
{ op: OpCode.PUSH, operand: 0 }, // push 5
{ op: OpCode.PUSH, operand: 1 }, // push 10
{ op: OpCode.CALL_NATIVE, operand: 'add' }, // call TypeScript 'add'
{ op: OpCode.HALT }
],
constants: [
toValue(5),
toValue(10)
]
})
// Register a TypeScript function
vm.registerFunction('add', (a, b) => {
return toValue(toNumber(a) + toNumber(b))
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 15 })
})
test("CALL_NATIVE - function with string manipulation", async () => {
const vm = new VM({
instructions: [
{ op: OpCode.PUSH, operand: 0 }, // push "hello"
{ op: OpCode.PUSH, operand: 1 }, // push "world"
{ op: OpCode.CALL_NATIVE, operand: 'concat' }, // call TypeScript 'concat'
{ op: OpCode.HALT }
],
constants: [
toValue("hello"),
toValue("world")
]
})
vm.registerFunction('concat', (a, b) => {
const aStr = a.type === 'string' ? a.value : toString(a)
const bStr = b.type === 'string' ? b.value : toString(b)
return toValue(aStr + ' ' + bStr)
})
const result = await vm.run()
expect(result).toEqual({ type: 'string', value: 'hello world' })
})
test("CALL_NATIVE - async function", async () => {
const vm = new VM({
instructions: [
{ op: OpCode.PUSH, operand: 0 }, // push 42
{ op: OpCode.CALL_NATIVE, operand: 'asyncDouble' }, // call async TypeScript function
{ op: OpCode.HALT }
],
constants: [
toValue(42)
]
})
vm.registerFunction('asyncDouble', async (a) => {
// Simulate async operation
await new Promise(resolve => setTimeout(resolve, 1))
return toValue(toNumber(a) * 2)
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 84 })
})
test("CALL_NATIVE - function with no arguments", async () => {
const vm = new VM({
instructions: [
{ op: OpCode.CALL_NATIVE, operand: 'getAnswer' }, // call with empty stack
{ op: OpCode.HALT }
],
constants: []
})
vm.registerFunction('getAnswer', () => {
return toValue(42)
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 42 })
})
test("CALL_NATIVE - function with multiple arguments", async () => {
const vm = new VM({
instructions: [
{ op: OpCode.PUSH, operand: 0 }, // push 2
{ op: OpCode.PUSH, operand: 1 }, // push 3
{ op: OpCode.PUSH, operand: 2 }, // push 4
{ op: OpCode.CALL_NATIVE, operand: 'sum' }, // call TypeScript 'sum'
{ op: OpCode.HALT }
],
constants: [
toValue(2),
toValue(3),
toValue(4)
]
})
vm.registerFunction('sum', (...args) => {
const total = args.reduce((acc, val) => acc + toNumber(val), 0)
return toValue(total)
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 9 })
})
test("CALL_NATIVE - function returns array", async () => {
const vm = new VM({
instructions: [
{ op: OpCode.PUSH, operand: 0 }, // push 3
{ op: OpCode.CALL_NATIVE, operand: 'makeRange' }, // call TypeScript 'makeRange'
{ op: OpCode.HALT }
],
constants: [
toValue(3)
]
})
vm.registerFunction('makeRange', (n) => {
const count = toNumber(n)
const arr = []
for (let i = 0; i < count; i++) {
arr.push(toValue(i))
}
return { type: 'array', value: arr }
})
const result = await vm.run()
expect(result.type).toBe('array')
if (result.type === 'array') {
expect(result.value.length).toBe(3)
expect(result.value).toEqual([
toValue(0),
toValue(1),
toValue(2)
])
}
})
test("CALL_NATIVE - function not found", async () => {
const vm = new VM({
instructions: [
{ op: OpCode.CALL_NATIVE, operand: 'nonexistent' }
],
constants: []
})
await expect(vm.run()).rejects.toThrow('CALL_NATIVE: function not found: nonexistent')
})
test("CALL_NATIVE - using result in subsequent operations", async () => {
const vm = new VM({
instructions: [
{ op: OpCode.PUSH, operand: 0 }, // push 5
{ op: OpCode.CALL_NATIVE, operand: 'triple' }, // call TypeScript 'triple' -> 15
{ op: OpCode.PUSH, operand: 1 }, // push 10
{ op: OpCode.ADD }, // 15 + 10 = 25
{ op: OpCode.HALT }
],
constants: [
toValue(5),
toValue(10)
]
})
vm.registerFunction('triple', (n) => {
return toValue(toNumber(n) * 3)
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 25 })
})