Compare commits

..

2 Commits

Author SHA1 Message Date
Chris Wanstrath
4898a6bb5a failing function test 2025-10-14 12:23:24 -07:00
Chris Wanstrath
173fd28d6f simplify 2025-10-14 12:19:49 -07:00
2 changed files with 51 additions and 29 deletions

View File

@ -401,9 +401,8 @@ export class VM {
const key = this.stack.pop()! const key = this.stack.pop()!
namedPairs.unshift({ key: toString(key), value }) namedPairs.unshift({ key: toString(key), value })
} }
for (const pair of namedPairs) { for (const pair of namedPairs)
namedArgs.set(pair.key, pair.value) namedArgs.set(pair.key, pair.value)
}
// Pop positional arguments from stack // Pop positional arguments from stack
const positionalArgs: Value[] = [] const positionalArgs: Value[] = []

View File

@ -1,13 +1,13 @@
import { test, expect } from "bun:test" import { test, expect } from "bun:test"
import { toBytecode } from "#bytecode" import { toBytecode } from "#bytecode"
import { VM } from "#vm" import { toValue, run } from "#reef"
test("MAKE_FUNCTION - creates function with captured scope", async () => { test("MAKE_FUNCTION - creates function with captured scope", async () => {
const bytecode = toBytecode(` const bytecode = toBytecode(`
MAKE_FUNCTION () #999 MAKE_FUNCTION () #999
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result.type).toBe('function') expect(result.type).toBe('function')
if (result.type === 'function') { if (result.type === 'function') {
expect(result.body).toBe(999) expect(result.body).toBe(999)
@ -26,7 +26,7 @@ test("CALL and RETURN - basic function call", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ type: 'number', value: 42 }) expect(result).toEqual({ type: 'number', value: 42 })
}) })
@ -42,7 +42,7 @@ test("CALL and RETURN - function with one parameter", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ type: 'number', value: 100 }) expect(result).toEqual({ type: 'number', value: 100 })
}) })
@ -61,7 +61,7 @@ test("CALL and RETURN - function with two parameters", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ type: 'number', value: 30 }) expect(result).toEqual({ type: 'number', value: 30 })
}) })
@ -79,7 +79,7 @@ test("CALL - variadic function with no fixed params", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ expect(result).toEqual({
type: 'array', type: 'array',
value: [ value: [
@ -104,7 +104,7 @@ test("CALL - variadic function with one fixed param", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// x should be 10, rest should be [20, 30] // x should be 10, rest should be [20, 30]
expect(result).toEqual({ expect(result).toEqual({
type: 'array', type: 'array',
@ -130,7 +130,7 @@ test("CALL - variadic function with two fixed params", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// a=1, b=2, rest=[3, 4] // a=1, b=2, rest=[3, 4]
expect(result).toEqual({ expect(result).toEqual({
type: 'array', type: 'array',
@ -153,7 +153,7 @@ test("CALL - variadic function with no extra args", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// rest should be empty array // rest should be empty array
expect(result).toEqual({ type: 'array', value: [] }) expect(result).toEqual({ type: 'array', value: [] })
}) })
@ -169,7 +169,7 @@ test("CALL - variadic function with defaults on fixed params", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// x should use default value 5 // x should use default value 5
expect(result).toEqual({ type: 'number', value: 5 }) expect(result).toEqual({ type: 'number', value: 5 })
}) })
@ -188,7 +188,7 @@ test("TAIL_CALL - variadic function", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// Should return the rest array [2, 3] // Should return the rest array [2, 3]
expect(result).toEqual({ expect(result).toEqual({
type: 'array', type: 'array',
@ -214,7 +214,7 @@ test("CALL - named args function with no fixed params", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result.type).toBe('dict') expect(result.type).toBe('dict')
if (result.type === 'dict') { if (result.type === 'dict') {
expect(result.value.get('name')).toEqual({ type: 'string', value: 'Bob' }) expect(result.value.get('name')).toEqual({ type: 'string', value: 'Bob' })
@ -236,7 +236,7 @@ test("CALL - named args function with one fixed param", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result.type).toBe('dict') expect(result.type).toBe('dict')
if (result.type === 'dict') { if (result.type === 'dict') {
expect(result.value.get('name')).toEqual({ type: 'string', value: 'Alice' }) expect(result.value.get('name')).toEqual({ type: 'string', value: 'Alice' })
@ -258,7 +258,7 @@ test("CALL - named args with matching param name should bind to param not named"
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// name should be bound as regular param, not collected in named // name should be bound as regular param, not collected in named
expect(result).toEqual({ type: 'string', value: 'Bob' }) expect(result).toEqual({ type: 'string', value: 'Bob' })
}) })
@ -278,7 +278,7 @@ test("CALL - named args that match param names should not be in named", async ()
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result.type).toBe('dict') expect(result.type).toBe('dict')
if (result.type === 'dict') { if (result.type === 'dict') {
// Only city should be in named, name should be bound to param // Only city should be in named, name should be bound to param
@ -304,7 +304,7 @@ test("CALL - mixed variadic and named args", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// rest should have [2, 3] // rest should have [2, 3]
expect(result).toEqual({ expect(result).toEqual({
type: 'array', type: 'array',
@ -331,7 +331,7 @@ test("CALL - mixed variadic and named args, check named", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result.type).toBe('dict') expect(result.type).toBe('dict')
if (result.type === 'dict') { if (result.type === 'dict') {
expect(result.value.get('name')).toEqual({ type: 'string', value: 'Bob' }) expect(result.value.get('name')).toEqual({ type: 'string', value: 'Bob' })
@ -350,7 +350,7 @@ test("CALL - named args with no extra named args", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// named should be empty dict // named should be empty dict
expect(result.type).toBe('dict') expect(result.type).toBe('dict')
if (result.type === 'dict') { if (result.type === 'dict') {
@ -371,11 +371,34 @@ test("CALL - named args with defaults on fixed params", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
// x should use default value 5 // x should use default value 5
expect(result).toEqual({ type: 'number', value: 5 }) expect(result).toEqual({ type: 'number', value: 5 })
}) })
test("CALL - fixed params can be named", async () => {
const bytecode = toBytecode(`
MAKE_FUNCTION (a b) .func_0
STORE minus
TRY_LOAD minus
PUSH 200
PUSH 'a'
PUSH 900
PUSH 1
PUSH 1
CALL
HALT
.func_0:
TRY_LOAD a
TRY_LOAD b
SUB
RETURN
`)
const result = await run(bytecode)
expect(result).toEqual(toValue(700))
})
test("TRY_CALL - calls function if found", async () => { test("TRY_CALL - calls function if found", async () => {
const bytecode = toBytecode([ const bytecode = toBytecode([
["MAKE_FUNCTION", [], ".body"], ["MAKE_FUNCTION", [], ".body"],
@ -387,7 +410,7 @@ test("TRY_CALL - calls function if found", async () => {
["RETURN"] ["RETURN"]
]) ])
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ type: 'number', value: 42 }) expect(result).toEqual({ type: 'number', value: 42 })
}) })
@ -399,7 +422,7 @@ test("TRY_CALL - pushes value if variable exists but is not a function", async (
["HALT"] ["HALT"]
]) ])
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ type: 'number', value: 99 }) expect(result).toEqual({ type: 'number', value: 99 })
}) })
@ -409,7 +432,7 @@ test("TRY_CALL - pushes string if variable not found", async () => {
["HALT"] ["HALT"]
]) ])
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ type: 'string', value: 'unknownVar' }) expect(result).toEqual({ type: 'string', value: 'unknownVar' })
}) })
@ -423,7 +446,7 @@ test("TRY_CALL - handles arrays", async () => {
["HALT"] ["HALT"]
]) ])
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result.type).toBe('array') expect(result.type).toBe('array')
if (result.type === 'array') { if (result.type === 'array') {
expect(result.value).toEqual([ expect(result.value).toEqual([
@ -443,7 +466,7 @@ test("TRY_CALL - handles dicts", async () => {
["HALT"] ["HALT"]
]) ])
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result.type).toBe('dict') expect(result.type).toBe('dict')
if (result.type === 'dict') { if (result.type === 'dict') {
expect(result.value.get('key')).toEqual({ type: 'string', value: 'value' }) expect(result.value.get('key')).toEqual({ type: 'string', value: 'value' })
@ -458,7 +481,7 @@ test("TRY_CALL - handles null values", async () => {
["HALT"] ["HALT"]
]) ])
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ type: 'null', value: null }) expect(result).toEqual({ type: 'null', value: null })
}) })
@ -477,7 +500,7 @@ test("TRY_CALL - function can access its parameters", async () => {
["RETURN"] ["RETURN"]
]) ])
const result = await new VM(bytecode).run() const result = await run(bytecode)
// Function is called with 0 args, so x inside function should be null // Function is called with 0 args, so x inside function should be null
// Then we add 5 to null (which coerces to 0) // Then we add 5 to null (which coerces to 0)
expect(result).toEqual({ type: 'number', value: 5 }) expect(result).toEqual({ type: 'number', value: 5 })
@ -493,6 +516,6 @@ test("TRY_CALL - with string format", async () => {
RETURN RETURN
`) `)
const result = await new VM(bytecode).run() const result = await run(bytecode)
expect(result).toEqual({ type: 'number', value: 100 }) expect(result).toEqual({ type: 'number', value: 100 })
}) })