vm.set(), new VM(bytecode, globalVars)

This commit is contained in:
Chris Wanstrath 2025-10-28 13:05:24 -07:00
parent 97b6722a11
commit e542070677
9 changed files with 120 additions and 96 deletions

View File

@ -137,37 +137,55 @@ Array format features:
- Function params as string arrays: `["MAKE_FUNCTION", ["x", "y=10"], ".body"]`
- See `tests/programmatic.test.ts` and `examples/programmatic.ts` for examples
### Native Function Registration
### Native Function Registration and Global Values
**Option 1**: Pass to `run()` or `VM` constructor (convenience)
```typescript
const result = await run(bytecode, {
add: (a: number, b: number) => a + b,
greet: (name: string) => `Hello, ${name}!`
greet: (name: string) => `Hello, ${name}!`,
pi: 3.14159,
config: { debug: true, port: 8080 }
})
// Or with VM constructor
const vm = new VM(bytecode, { add, greet })
const vm = new VM(bytecode, { add, greet, pi, config })
```
**Option 2**: Register with `vm.registerFunction()` (manual)
**Option 2**: Set values with `vm.set()` (manual)
```typescript
const vm = new VM(bytecode)
vm.registerFunction('add', (a: number, b: number) => a + b)
// Set functions (auto-wrapped to native functions)
vm.set('add', (a: number, b: number) => a + b)
// Set any other values (auto-converted to ReefVM Values)
vm.set('pi', 3.14159)
vm.set('config', { debug: true, port: 8080 })
await vm.run()
```
**Option 3**: Register Value-based functions (for direct Value access)
**Option 3**: Set Value-based functions with `vm.setValueFunction()` (advanced)
For functions that work directly with ReefVM Value types:
```typescript
vm.registerValueFunction('customOp', (a: Value, b: Value): Value => {
const vm = new VM(bytecode)
// Set Value-based function (no wrapping, works directly with Values)
vm.setValueFunction('customOp', (a: Value, b: Value): Value => {
return toValue(toNumber(a) + toNumber(b))
})
await vm.run()
```
Auto-wrapping handles:
- Value ↔ native type conversion (`fromValue`/`toValue`)
- Functions: wrapped as native functions with Value ↔ native type conversion
- Sync and async functions
- Arrays, objects, primitives, null, RegExp
- All values converted via `toValue()`
### Calling Functions from TypeScript

View File

@ -662,18 +662,18 @@ const vm = new VM(bytecode, { add, greet })
**Method 2**: Register after construction
```typescript
const vm = new VM(bytecode)
vm.registerFunction('add', (a: number, b: number) => a + b)
vm.set('add', (a: number, b: number) => a + b)
await vm.run()
```
**Method 3**: Value-based functions (for full control)
```typescript
vm.registerValueFunction('customOp', (a: Value, b: Value): Value => {
vm.setValueFunction('customOp', (a: Value, b: Value): Value => {
return { type: 'number', value: toNumber(a) + toNumber(b) }
})
```
**Auto-wrapping**: `registerFunction` automatically converts between native TypeScript types and ReefVM Value types. Both sync and async functions work.
**Auto-wrapping**: `vm.set()` automatically converts between native TypeScript types and ReefVM Value types. Both sync and async functions work.
**Usage in bytecode**:
```
@ -702,12 +702,12 @@ CALL ; → "Hi, Alice!"
```typescript
// Basic @named - collects all named args
vm.registerFunction('greet', (atNamed: any = {}) => {
vm.set('greet', (atNamed: any = {}) => {
return `Hello, ${atNamed.name || 'World'}!`
})
// Mixed positional and @named
vm.registerFunction('configure', (name: string, atOptions: any = {}) => {
vm.set('configure', (name: string, atOptions: any = {}) => {
return {
name,
debug: atOptions.debug || false,

24
SPEC.md
View File

@ -622,7 +622,7 @@ const vm = new VM(bytecode, {
})
// Or after construction:
vm.registerFunction('multiply', (a: number, b: number) => a * b)
vm.set('multiply', (a: number, b: number) => a * b)
```
**Usage in Bytecode**:
@ -637,9 +637,9 @@ CALL ; Call it like any other function
**Native Function Types**:
1. **Auto-wrapped functions** (via `registerFunction`): Accept and return native TypeScript types (number, string, boolean, array, object, etc.). The VM automatically converts between Value types and native types.
1. **Auto-wrapped functions** (via `vm.set()`): Accept and return native TypeScript types (number, string, boolean, array, object, etc.). The VM automatically converts between Value types and native types.
2. **Value-based functions** (via `registerValueFunction`): Accept and return `Value` types directly for full control over type handling.
2. **Value-based functions** (via `vm.setValueFunction()`): Accept and return `Value` types directly for full control over type handling.
**Auto-Wrapping Behavior**:
- Parameters: `Value` → native type (number, string, boolean, array, object, null, RegExp)
@ -655,27 +655,27 @@ CALL ; Call it like any other function
**Examples**:
```typescript
// Auto-wrapped native types
vm.registerFunction('add', (a: number, b: number) => a + b)
vm.registerFunction('greet', (name: string) => `Hello, ${name}!`)
vm.registerFunction('range', (n: number) => Array.from({ length: n }, (_, i) => i))
vm.set('add', (a: number, b: number) => a + b)
vm.set('greet', (name: string) => `Hello, ${name}!`)
vm.set('range', (n: number) => Array.from({ length: n }, (_, i) => i))
// With defaults
vm.registerFunction('greet', (name: string, greeting = 'Hello') => {
vm.set('greet', (name: string, greeting = 'Hello') => {
return `${greeting}, ${name}!`
})
// Variadic functions
vm.registerFunction('sum', (...nums: number[]) => {
vm.set('sum', (...nums: number[]) => {
return nums.reduce((acc, n) => acc + n, 0)
})
// Value-based for custom logic
vm.registerValueFunction('customOp', (a: Value, b: Value): Value => {
vm.setValueFunction('customOp', (a: Value, b: Value): Value => {
return { type: 'number', value: toNumber(a) + toNumber(b) }
})
// Async functions
vm.registerFunction('fetchData', async (url: string) => {
vm.set('fetchData', async (url: string) => {
const response = await fetch(url)
return response.json()
})
@ -912,10 +912,10 @@ const vm = new VM(bytecode, {
})
// Or register after construction
vm.registerFunction('multiply', (a: number, b: number) => a * b)
vm.set('multiply', (a: number, b: number) => a * b)
// Or use Value-based functions
vm.registerValueFunction('customOp', (a: Value, b: Value): Value => {
vm.setValueFunction('customOp', (a: Value, b: Value): Value => {
return { type: 'number', value: toNumber(a) + toNumber(b) }
})

View File

@ -9,7 +9,7 @@ const bytecode = toBytecode(`
const vm = new VM(bytecode)
vm.registerFunction('print', (...args: Value[]): Value => {
vm.set('print', (...args: Value[]): Value => {
console.log(...args.map(toString))
return toNull()
})

View File

@ -1,9 +1,9 @@
import type { Bytecode } from "./bytecode"
import type { Value, TypeScriptFunction } from "./value"
import type { Value } from "./value"
import { VM } from "./vm"
export async function run(bytecode: Bytecode, functions?: Record<string, TypeScriptFunction>): Promise<Value> {
const vm = new VM(bytecode, functions)
export async function run(bytecode: Bytecode, globals?: Record<string, any>): Promise<Value> {
const vm = new VM(bytecode, globals)
return await vm.run()
}

View File

@ -19,15 +19,15 @@ export class VM {
labels: Map<number, string> = new Map()
nativeFunctions: Map<string, NativeFunction> = new Map()
constructor(bytecode: Bytecode, globalFunctions?: Record<string, TypeScriptFunction>) {
constructor(bytecode: Bytecode, globals?: Record<string, any>) {
this.instructions = bytecode.instructions
this.constants = bytecode.constants
this.labels = bytecode.labels || new Map()
this.scope = new Scope()
if (globalFunctions) {
for (const name of Object.keys(globalFunctions))
this.registerFunction(name, globalFunctions[name]!)
if (globals) {
for (const name of Object.keys(globals ?? {}))
this.set(name, globals[name])
this.scope = new Scope(this.scope)
}
@ -48,12 +48,18 @@ export class VM {
}
}
registerFunction(name: string, fn: TypeScriptFunction) {
const wrapped = isWrapped(fn) ? fn as NativeFunction : wrapNative(this, fn)
this.scope.set(name, { type: 'native', fn: wrapped, value: '<function>' })
set(name: string, value: any) {
if (typeof value === 'function')
this.setFunction(name, value)
else
this.scope.set(name, toValue(value))
}
registerValueFunction(name: string, fn: NativeFunction) {
setFunction(name: string, fn: TypeScriptFunction) {
this.scope.set(name, { type: 'native', fn: wrapNative(this, fn), value: '<function>' })
}
setValueFunction(name: string, fn: NativeFunction) {
this.scope.set(name, { type: 'native', fn, value: '<function>' })
}

View File

@ -104,7 +104,7 @@ describe("functions parameter", () => {
expect(result).toEqual({ type: 'number', value: 200 })
})
test("can combine with manual registerFunction", async () => {
test("can combine with manual vm.set", async () => {
const bytecode = toBytecode(`
LOAD add
PUSH 5
@ -127,7 +127,7 @@ describe("functions parameter", () => {
})
// Register another function manually
vm.registerFunction('subtract', (a: number, b: number) => a - b)
vm.set('subtract', (a: number, b: number) => a - b)
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 6 })
@ -224,7 +224,7 @@ describe("functions parameter", () => {
})
// Override with manual registration
vm.registerFunction('getValue', () => 200)
vm.set('getValue', () => 200)
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 200 })

View File

@ -16,7 +16,7 @@ test("LOAD - basic function call", async () => {
const vm = new VM(bytecode)
// Register a Value-based function
vm.registerValueFunction('add', (a, b) => {
vm.setValueFunction('add', (a, b) => {
return toValue(toNumber(a) + toNumber(b))
})
@ -36,7 +36,7 @@ test("LOAD - function with string manipulation", async () => {
const vm = new VM(bytecode)
vm.registerValueFunction('concat', (a, b) => {
vm.setValueFunction('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)
@ -57,7 +57,7 @@ test("LOAD - async function", async () => {
const vm = new VM(bytecode)
vm.registerValueFunction('asyncDouble', async (a) => {
vm.setValueFunction('asyncDouble', async (a) => {
// Simulate async operation
await new Promise(resolve => setTimeout(resolve, 1))
return toValue(toNumber(a) * 2)
@ -77,7 +77,7 @@ test("LOAD - function with no arguments", async () => {
const vm = new VM(bytecode)
vm.registerValueFunction('getAnswer', () => {
vm.setValueFunction('getAnswer', () => {
return toValue(42)
})
@ -98,7 +98,7 @@ test("LOAD - function with multiple arguments", async () => {
const vm = new VM(bytecode)
vm.registerValueFunction('sum', (...args) => {
vm.setValueFunction('sum', (...args) => {
const total = args.reduce((acc, val) => acc + toNumber(val), 0)
return toValue(total)
})
@ -118,7 +118,7 @@ test("LOAD - function returns array", async () => {
const vm = new VM(bytecode)
vm.registerValueFunction('makeRange', (n) => {
vm.setValueFunction('makeRange', (n) => {
const count = toNumber(n)
const arr = []
for (let i = 0; i < count; i++) {
@ -165,7 +165,7 @@ test("LOAD - using result in subsequent operations", async () => {
const vm = new VM(bytecode)
vm.registerValueFunction('triple', (n) => {
vm.setValueFunction('triple', (n) => {
return toValue(toNumber(n) * 3)
})
@ -186,7 +186,7 @@ test("Native function wrapping - basic sync function with native types", async (
const vm = new VM(bytecode)
// Register with native TypeScript types - auto-wraps!
vm.registerFunction('add', (a: number, b: number) => {
vm.set('add', (a: number, b: number) => {
return a + b
})
@ -206,7 +206,7 @@ test("Native function wrapping - async function with native types", async () =>
const vm = new VM(bytecode)
// Async native function
vm.registerFunction('asyncDouble', async (n: number) => {
vm.set('asyncDouble', async (n: number) => {
await new Promise(resolve => setTimeout(resolve, 1))
return n * 2
})
@ -228,7 +228,7 @@ test("Native function wrapping - string manipulation", async () => {
const vm = new VM(bytecode)
// Native string function
vm.registerFunction('concat', (a: string, b: string) => {
vm.set('concat', (a: string, b: string) => {
return a + ' ' + b
})
@ -248,7 +248,7 @@ test("Native function wrapping - with default parameters", async () => {
const vm = new VM(bytecode)
// Function with default parameter (like NOSE commands)
vm.registerFunction('ls', (path: string, link = false) => {
vm.set('ls', (path: string, link = false) => {
return link ? `listing ${path} with links` : `listing ${path}`
})
@ -268,7 +268,7 @@ test("Native function wrapping - returns array", async () => {
const vm = new VM(bytecode)
// Return native array - auto-converts to Value array
vm.registerFunction('makeRange', (n: number) => {
vm.set('makeRange', (n: number) => {
return Array.from({ length: n }, (_, i) => i)
})
@ -297,7 +297,7 @@ test("Native function wrapping - returns object (becomes dict)", async () => {
const vm = new VM(bytecode)
// Return plain object - auto-converts to dict
vm.registerFunction('makeUser', (name: string, age: number) => {
vm.set('makeUser', (name: string, age: number) => {
return { name, age }
})
@ -326,11 +326,11 @@ test("Native function wrapping - mixed with manual Value functions", async () =>
const vm = new VM(bytecode)
// Native function (auto-wrapped by registerFunction)
vm.registerFunction('nativeAdd', (n: number) => n + 10)
// Native function (auto-wrapped by set)
vm.set('nativeAdd', (n: number) => n + 10)
// Manual Value function (use registerValueFunction)
vm.registerValueFunction('manualDouble', (v) => {
// Manual Value function (use setValueFunction)
vm.setValueFunction('manualDouble', (v) => {
return toValue(toNumber(v) * 2)
})
@ -349,7 +349,7 @@ test("Named arguments - basic named arg", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('greet', (name: string) => `Hello, ${name}!`)
vm.set('greet', (name: string) => `Hello, ${name}!`)
const result = await vm.run()
expect(result).toEqual({ type: 'string', value: 'Hello, Alice!' })
@ -367,7 +367,7 @@ test("Named arguments - mixed positional and named", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('makeUser', (name: string, age: number) => {
vm.set('makeUser', (name: string, age: number) => {
return { name, age }
})
@ -393,7 +393,7 @@ test("Named arguments - named takes priority over positional", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('add', (a: number, b: number) => a + b)
vm.set('add', (a: number, b: number) => a + b)
const result = await vm.run()
// Named args should be: a=5, b=10
@ -412,7 +412,7 @@ test("Named arguments - with defaults", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('greet', (name: string, greeting = 'Hello') => {
vm.set('greet', (name: string, greeting = 'Hello') => {
return `${greeting}, ${name}!`
})
@ -433,7 +433,7 @@ test("Named arguments - override defaults with named args", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('greet', (name: string, greeting = 'Hello') => {
vm.set('greet', (name: string, greeting = 'Hello') => {
return `${greeting}, ${name}!`
})
@ -455,7 +455,7 @@ test("Named arguments - with variadic function", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('sum', (multiplier: number, ...nums: number[]) => {
vm.set('sum', (multiplier: number, ...nums: number[]) => {
const total = nums.reduce((acc, n) => acc + n, 0)
return total * multiplier
})
@ -466,7 +466,7 @@ test("Named arguments - with variadic function", async () => {
test("Named arguments - works with both wrapped and non-wrapped functions", async () => {
const bytecode = toBytecode(`
; Test wrapped function (registerFunction)
; Test wrapped function (vm.set)
LOAD wrappedAdd
PUSH "a"
PUSH 5
@ -477,7 +477,7 @@ test("Named arguments - works with both wrapped and non-wrapped functions", asyn
CALL
STORE result1
; Test non-wrapped function (registerValueFunction)
; Test non-wrapped function (vm.setValueFunction)
LOAD valueAdd
PUSH "a"
PUSH 3
@ -497,10 +497,10 @@ test("Named arguments - works with both wrapped and non-wrapped functions", asyn
const vm = new VM(bytecode)
// Wrapped function - auto-converts types
vm.registerFunction('wrappedAdd', (a: number, b: number) => a + b)
vm.set('wrappedAdd', (a: number, b: number) => a + b)
// Non-wrapped function - works directly with Values
vm.registerValueFunction('valueAdd', (a, b) => {
vm.setValueFunction('valueAdd', (a, b) => {
return toValue(toNumber(a) + toNumber(b))
})
@ -523,7 +523,7 @@ test("@named pattern - basic atNamed parameter", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('greet', (atNamed: any = {}) => {
vm.set('greet', (atNamed: any = {}) => {
const name = atNamed.name || 'Unknown'
const greeting = atNamed.greeting || 'Hello'
const extra = atNamed.extra || ''
@ -548,7 +548,7 @@ test('@named pattern - atNamed parameters with varadic args', async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('cmd', (atNamed: any = {}, ...rest: string[]) => {
vm.set('cmd', (atNamed: any = {}, ...rest: string[]) => {
return { rest, namedArg: atNamed['named'] }
})
@ -573,7 +573,7 @@ test("@named pattern - mixed positional and atOptions", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('configure', (name: string, atOptions: any = {}) => {
vm.set('configure', (name: string, atOptions: any = {}) => {
return {
name,
debug: atOptions.debug || false,
@ -600,7 +600,7 @@ test("@named pattern - with default empty object", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('build', (name: string, atConfig: any = {}) => {
vm.set('build', (name: string, atConfig: any = {}) => {
return `Building ${name} with ${Object.keys(atConfig).length} options`
})
@ -625,7 +625,7 @@ test("@named pattern - collects only unmatched named args", async () => {
`)
const vm = new VM(bytecode)
vm.registerFunction('process', (file: string, mode: string, atOptions: any = {}) => {
vm.set('process', (file: string, mode: string, atOptions: any = {}) => {
// file and mode are matched, extra1 and extra2 go to atOptions
return {
file,
@ -675,7 +675,7 @@ test("Native function receives Reef function as callback - basic map", async ()
const vm = new VM(bytecode)
// Native function that takes an array and a callback
vm.registerFunction('map', async (array: any[], callback: Function) => {
vm.set('map', async (array: any[], callback: Function) => {
const results = []
for (const item of array) {
results.push(await callback(item))
@ -732,7 +732,7 @@ test("Native function receives Reef function - iterator pattern with each", asyn
const vm = new VM(bytecode)
// Native 'each' function (like Ruby's each)
vm.registerFunction('each', async (array: any[], callback: Function) => {
vm.set('each', async (array: any[], callback: Function) => {
for (const item of array) {
await callback(item)
}
@ -773,7 +773,7 @@ test("Native function receives Reef function - filter pattern", async () => {
const vm = new VM(bytecode)
// Native filter function
vm.registerFunction('filter', async (array: any[], predicate: Function) => {
vm.set('filter', async (array: any[], predicate: Function) => {
const results = []
for (const item of array) {
if (await predicate(item)) {
@ -824,7 +824,7 @@ test("Native function receives Reef function - closure capturing", async () => {
const vm = new VM(bytecode)
vm.registerFunction('map', async (array: any[], callback: Function) => {
vm.set('map', async (array: any[], callback: Function) => {
const results = []
for (const item of array) {
results.push(await callback(item))
@ -873,7 +873,7 @@ test("Native function receives Reef function - multiple arguments", async () =>
const vm = new VM(bytecode)
// Native reduce function (accumulator, array, callback)
vm.registerFunction('reduce', async (array: any[], initial: any, callback: Function) => {
vm.set('reduce', async (array: any[], initial: any, callback: Function) => {
let acc = initial
for (const item of array) {
acc = await callback(acc, item)
@ -913,7 +913,7 @@ test("Native function receives Reef function - returns non-primitive", async ()
const vm = new VM(bytecode)
vm.registerFunction('map', async (array: any[], callback: Function) => {
vm.set('map', async (array: any[], callback: Function) => {
const results = []
for (const item of array) {
results.push(await callback(item))
@ -965,7 +965,7 @@ test("Native function calls Reef function - basic", async () => {
const vm = new VM(bytecode)
vm.registerFunction('process', async function (n: number) {
vm.setFunction('process', async function (n: number) {
return await this.call('double', n)
})
@ -1006,7 +1006,7 @@ test("Native function calls multiple Reef functions", async () => {
const vm = new VM(bytecode)
vm.registerFunction('orchestrate', async function (n: number) {
vm.setFunction('orchestrate', async function (n: number) {
const doubled = await this.call('double', n)
const tripled = await this.call('triple', n)
@ -1064,7 +1064,7 @@ test("Native function conditionally calls Reef functions", async () => {
const vm = new VM(bytecode)
vm.registerFunction('validate', async function (n: number) {
vm.setFunction('validate', async function (n: number) {
const isPositive = await this.call('is_positive', n)
if (isPositive) {
@ -1105,7 +1105,7 @@ test("Native function calls Reef function with closure", async () => {
const vm = new VM(bytecode)
vm.registerFunction('transform', async function (n: number) {
vm.setFunction('transform', async function (n: number) {
return await this.call('multiply_by_ten', n)
})
@ -1143,7 +1143,7 @@ test("Native function uses Reef function as filter predicate", async () => {
const vm = new VM(bytecode)
vm.registerFunction('filter_evens', async function (array: any[]) {
vm.setFunction('filter_evens', async function (array: any[]) {
const results = []
for (const item of array) {
if (await this.call('is_even', item)) {
@ -1199,7 +1199,7 @@ test("Reef calls native calls Reef - roundtrip", async () => {
const vm = new VM(bytecode)
vm.registerFunction('native_helper', async function (n: number) {
vm.setFunction('native_helper', async function (n: number) {
const squared = await this.call('square', n)
return squared + 1 // Add 1 to the squared result
})
@ -1232,7 +1232,7 @@ test("Native function calls Reef function with multiple arguments", async () =>
const vm = new VM(bytecode)
vm.registerFunction('calculate', async function () {
vm.setFunction('calculate', async function () {
return await this.call('add_three', 10, 20, 30)
})
@ -1266,7 +1266,7 @@ test("Native function calls Reef function that returns complex type", async () =
const vm = new VM(bytecode)
vm.registerFunction('create_user', async function () {
vm.setFunction('create_user', async function () {
return await this.call('make_user', "Alice", 30)
})
@ -1289,7 +1289,7 @@ test("Native function calls non-existent Reef function - throws error", async ()
const vm = new VM(bytecode)
vm.registerFunction('bad_caller', async function () {
vm.setFunction('bad_caller', async function () {
return await this.call('nonexistent', 42)
})
@ -1387,7 +1387,7 @@ test("Native function receives Reef closure with default params - calls with nam
const vm = new VM(bytecode)
// Native function that calls the Reef closure with named arguments
vm.registerFunction('map', async (array: any[], callback: Function) => {
vm.set('map', async (array: any[], callback: Function) => {
const results = []
for (const item of array) {
// Call with named argument to override the default
@ -1457,7 +1457,7 @@ test("Native function receives Reef closure with variadic parameters", async ()
const vm = new VM(bytecode)
// Native function that calls the variadic Reef closure with multiple args
vm.registerFunction('process', async (callback: Function) => {
vm.set('process', async (callback: Function) => {
// Call with varying number of arguments
const result1 = await callback(1, 2, 3)
const result2 = await callback(10, 20)
@ -1508,7 +1508,7 @@ test("Native function receives Reef closure with @named parameter", async () =>
const vm = new VM(bytecode)
// Native function that calls Reef closure with named arguments
vm.registerFunction('format_messages', async (messages: any[], formatter: Function) => {
vm.set('format_messages', async (messages: any[], formatter: Function) => {
const results = []
for (const msg of messages) {
// Call with named arguments
@ -1553,7 +1553,7 @@ test("Native function receives Reef closure - calls with only named arguments",
const vm = new VM(bytecode)
// Native function that calls Reef closure with ONLY named arguments
vm.registerFunction('compute', async (callback: Function) => {
vm.set('compute', async (callback: Function) => {
// First call with all named args
const result1 = await callback({ x: 10, y: 20, z: 30 })
@ -1619,7 +1619,7 @@ test("Native function receives Reef closure with mixed positional and variadic",
const vm = new VM(bytecode)
// Native function that calls Reef closure with mixed args
vm.registerFunction('process_numbers', async (calculator: Function) => {
vm.set('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

View File

@ -399,7 +399,7 @@ describe("RegExp", () => {
const vm = new VM(bytecode)
// Register a native function that takes a string and regex
vm.registerFunction('match', (str: string, pattern: RegExp) => {
vm.set('match', (str: string, pattern: RegExp) => {
return pattern.test(str)
})
@ -422,7 +422,7 @@ describe("RegExp", () => {
const vm = new VM(bytecode)
vm.registerFunction('replace', (str: string, pattern: RegExp, replacement: string) => {
vm.set('replace', (str: string, pattern: RegExp, replacement: string) => {
return str.replace(pattern, replacement)
})
@ -444,7 +444,7 @@ describe("RegExp", () => {
const vm = new VM(bytecode)
vm.registerFunction('extractNumbers', (str: string, pattern: RegExp) => {
vm.set('extractNumbers', (str: string, pattern: RegExp) => {
return str.match(pattern) || []
})