133 lines
2.5 KiB
TypeScript
133 lines
2.5 KiB
TypeScript
import { test, expect } from "bun:test"
|
|
import { toBytecode } from "#bytecode"
|
|
import { OpCode } from "#opcode"
|
|
import { VM } from "#vm"
|
|
|
|
test("string compilation", () => {
|
|
const str = `
|
|
PUSH 1
|
|
PUSH 5
|
|
ADD
|
|
`
|
|
expect(toBytecode(str)).toEqual({
|
|
instructions: [
|
|
{ op: OpCode.PUSH, operand: 0 },
|
|
{ op: OpCode.PUSH, operand: 1 },
|
|
{ op: OpCode.ADD },
|
|
],
|
|
constants: [
|
|
{ type: 'number', value: 1 },
|
|
{ type: 'number', value: 5 }
|
|
]
|
|
})
|
|
})
|
|
|
|
|
|
test("MAKE_FUNCTION - basic function", async () => {
|
|
const bytecode = toBytecode(`
|
|
MAKE_FUNCTION () #3
|
|
CALL #0
|
|
HALT
|
|
PUSH 42
|
|
RETURN
|
|
`)
|
|
|
|
const vm = new VM(bytecode)
|
|
const result = await vm.run()
|
|
expect(result).toEqual({ type: 'number', value: 42 })
|
|
})
|
|
|
|
test("MAKE_FUNCTION - function with parameters", async () => {
|
|
const bytecode = toBytecode(`
|
|
MAKE_FUNCTION (x y) #5
|
|
PUSH 10
|
|
PUSH 20
|
|
CALL #2
|
|
HALT
|
|
LOAD x
|
|
LOAD y
|
|
ADD
|
|
RETURN
|
|
`)
|
|
|
|
const vm = new VM(bytecode)
|
|
const result = await vm.run()
|
|
expect(result).toEqual({ type: 'number', value: 30 })
|
|
})
|
|
|
|
test("MAKE_FUNCTION - function with default parameters", async () => {
|
|
const bytecode = toBytecode(`
|
|
MAKE_FUNCTION (x y=100) #4
|
|
PUSH 10
|
|
CALL #1
|
|
HALT
|
|
LOAD x
|
|
LOAD y
|
|
ADD
|
|
RETURN
|
|
`)
|
|
|
|
const vm = new VM(bytecode)
|
|
const result = await vm.run()
|
|
expect(result).toEqual({ type: 'number', value: 110 })
|
|
})
|
|
|
|
test("MAKE_FUNCTION - tail recursive countdown", async () => {
|
|
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 vm = new VM(bytecode)
|
|
const result = await vm.run()
|
|
expect(result).toEqual({ type: 'string', value: 'done' })
|
|
})
|
|
|
|
test("MAKE_FUNCTION - multiple default values", async () => {
|
|
const bytecode = toBytecode(`
|
|
MAKE_FUNCTION (a=1 b=2 c=3) #3
|
|
CALL #0
|
|
HALT
|
|
LOAD a
|
|
LOAD b
|
|
LOAD c
|
|
ADD
|
|
ADD
|
|
RETURN
|
|
`)
|
|
|
|
const vm = new VM(bytecode)
|
|
const result = await vm.run()
|
|
expect(result).toEqual({ type: 'number', value: 6 })
|
|
})
|
|
|
|
test("MAKE_FUNCTION - default with string", async () => {
|
|
const bytecode = toBytecode(`
|
|
MAKE_FUNCTION (name="World") #3
|
|
CALL #0
|
|
HALT
|
|
LOAD name
|
|
RETURN
|
|
`)
|
|
|
|
const vm = new VM(bytecode)
|
|
const result = await vm.run()
|
|
expect(result).toEqual({ type: 'string', value: 'World' })
|
|
})
|
|
|