more native named arg tests

This commit is contained in:
Chris Wanstrath 2025-10-24 11:00:02 -07:00
parent 797eb281cb
commit f79fea33c5

View File

@ -1011,7 +1011,7 @@ test("Native function calls multiple Reef functions", async () => {
const doubled = await this.call('double', n) const doubled = await this.call('double', n)
const tripled = await this.call('triple', n) const tripled = await this.call('triple', n)
return doubled + tripled // 10 + 15 = 25 return doubled + tripled
}) })
const result = await vm.run() const result = await vm.run()
@ -1328,3 +1328,305 @@ test("Native function calls Reef function with named arguments", async () => {
const result = await vm.run() const result = await vm.run()
expect(result).toEqual({ type: 'string', value: "Hi Alice!" }) expect(result).toEqual({ type: 'string', value: "Hi Alice!" })
}) })
test("vm.call() with only named arguments", async () => {
const bytecode = toBytecode(`
; Reef function with default parameters
MAKE_FUNCTION (name greeting='Hello') .greet_body
STORE greet
JUMP .skip_greet
.greet_body:
LOAD greeting
PUSH " "
LOAD name
PUSH "!"
STR_CONCAT #4
RETURN
.skip_greet:
LOAD call_greet
PUSH 0
PUSH 0
CALL
`)
const vm = new VM(bytecode, {
call_greet: async function () {
// Call with ONLY named arguments (no positional)
return await this.call('greet', { name: "Bob", greeting: "Hey" })
}
})
const result = await vm.run()
expect(result).toEqual({ type: 'string', value: "Hey Bob!" })
})
test("Native function receives Reef closure with default params - calls with named args", async () => {
const bytecode = toBytecode(`
; Reef function with default parameters
MAKE_FUNCTION (x multiplier=2) .transform_body
STORE transform
JUMP .skip_transform
.transform_body:
LOAD x
LOAD multiplier
MUL
RETURN
.skip_transform:
; Pass to native map function
LOAD map
PUSH 1
PUSH 2
PUSH 3
MAKE_ARRAY #3
LOAD transform
PUSH 2
PUSH 0
CALL
`)
const vm = new VM(bytecode)
// Native function that calls the Reef closure with named arguments
vm.registerFunction('map', async (array: any[], callback: Function) => {
const results = []
for (const item of array) {
// Call with named argument to override the default
results.push(await callback(item, { multiplier: 10 }))
}
return results
})
const result = await vm.run()
expect(result.type).toBe('array')
if (result.type === 'array') {
expect(result.value).toEqual([
toValue(10), // 1 * 10
toValue(20), // 2 * 10
toValue(30) // 3 * 10
])
}
})
test("Native function receives Reef closure with variadic parameters", async () => {
const bytecode = toBytecode(`
; Reef function with variadic params
MAKE_FUNCTION (...nums) .sum_body
STORE sum_all
JUMP .skip_sum
.sum_body:
PUSH 0
STORE total
LOAD nums
STORE items
PUSH 0
STORE idx
.loop:
LOAD idx
LOAD items
ARRAY_LEN
LT
JUMP_IF_FALSE .done
LOAD total
LOAD items
LOAD idx
ARRAY_GET
ADD
STORE total
LOAD idx
PUSH 1
ADD
STORE idx
JUMP .loop
.done:
LOAD total
RETURN
.skip_sum:
; Pass to native function
LOAD process
LOAD sum_all
PUSH 1
PUSH 0
CALL
`)
const vm = new VM(bytecode)
// Native function that calls the variadic Reef closure with multiple args
vm.registerFunction('process', async (callback: Function) => {
// Call with varying number of arguments
const result1 = await callback(1, 2, 3)
const result2 = await callback(10, 20)
return result1 + result2
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 36 }) // (1+2+3) + (10+20) = 6 + 30
})
test("Native function receives Reef closure with @named parameter", async () => {
const bytecode = toBytecode(`
; Reef function with @named parameter
MAKE_FUNCTION (message @options) .format_body
STORE format_message
JUMP .skip_format
.format_body:
; Get uppercase option (default false)
LOAD options
PUSH "uppercase"
DICT_GET
STORE use_upper
; Get prefix option (default empty)
LOAD options
PUSH "prefix"
DICT_GET
STORE prefix_val
; Build result
LOAD prefix_val
LOAD message
STR_CONCAT #2
RETURN
.skip_format:
; Pass to native function
LOAD format_messages
PUSH "hello"
PUSH "world"
MAKE_ARRAY #2
LOAD format_message
PUSH 2
PUSH 0
CALL
`)
const vm = new VM(bytecode)
// Native function that calls Reef closure with named arguments
vm.registerFunction('format_messages', async (messages: any[], formatter: Function) => {
const results = []
for (const msg of messages) {
// Call with named arguments
results.push(await formatter(msg, { prefix: "[LOG] ", uppercase: true }))
}
return results
})
const result = await vm.run()
expect(result.type).toBe('array')
if (result.type === 'array') {
expect(result.value).toEqual([
toValue('[LOG] hello'),
toValue('[LOG] world')
])
}
})
test("Native function receives Reef closure - calls with only named arguments", async () => {
const bytecode = toBytecode(`
; Reef function expecting named arguments
MAKE_FUNCTION (x=0 y=0 z=0) .add_body
STORE add_coords
JUMP .skip_add
.add_body:
LOAD x
LOAD y
ADD
LOAD z
ADD
RETURN
.skip_add:
; Pass to native function
LOAD compute
LOAD add_coords
PUSH 1
PUSH 0
CALL
`)
const vm = new VM(bytecode)
// Native function that calls Reef closure with ONLY named arguments
vm.registerFunction('compute', async (callback: Function) => {
// First call with all named args
const result1 = await callback({ x: 10, y: 20, z: 30 })
// Second call with partial named args (rest use defaults)
const result2 = await callback({ x: 5, z: 15 })
return result1 + result2
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 80 }) // (10+20+30) + (5+0+15) = 60 + 20
})
test("Native function receives Reef closure with mixed positional and variadic", async () => {
const bytecode = toBytecode(`
; Reef function with fixed param and variadic
MAKE_FUNCTION (multiplier ...nums) .scale_body
STORE scale_numbers
JUMP .skip_scale
.scale_body:
PUSH 0
STORE total
LOAD nums
STORE items
PUSH 0
STORE idx
.loop:
LOAD idx
LOAD items
ARRAY_LEN
LT
JUMP_IF_FALSE .done
LOAD total
LOAD items
LOAD idx
ARRAY_GET
LOAD multiplier
MUL
ADD
STORE total
LOAD idx
PUSH 1
ADD
STORE idx
JUMP .loop
.done:
LOAD total
RETURN
.skip_scale:
; Pass to native function
LOAD process_numbers
LOAD scale_numbers
PUSH 1
PUSH 0
CALL
`)
const vm = new VM(bytecode)
// Native function that calls Reef closure with mixed args
vm.registerFunction('process_numbers', async (calculator: Function) => {
// Call with fixed multiplier and variadic numbers
const result1 = await calculator(10, 1, 2, 3) // 10 * (1+2+3) = 60
const result2 = await calculator(5, 4, 6) // 5 * (4+6) = 50
return result1 + result2
})
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 110 }) // 60 + 50
})