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"]` - Function params as string arrays: `["MAKE_FUNCTION", ["x", "y=10"], ".body"]`
- See `tests/programmatic.test.ts` and `examples/programmatic.ts` for examples - 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) **Option 1**: Pass to `run()` or `VM` constructor (convenience)
```typescript ```typescript
const result = await run(bytecode, { const result = await run(bytecode, {
add: (a: number, b: number) => a + b, 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 // 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 ```typescript
const vm = new VM(bytecode) 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() 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 ```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)) return toValue(toNumber(a) + toNumber(b))
}) })
await vm.run()
``` ```
Auto-wrapping handles: Auto-wrapping handles:
- Value ↔ native type conversion (`fromValue`/`toValue`) - Functions: wrapped as native functions with Value ↔ native type conversion
- Sync and async functions - Sync and async functions
- Arrays, objects, primitives, null, RegExp - Arrays, objects, primitives, null, RegExp
- All values converted via `toValue()`
### Calling Functions from TypeScript ### Calling Functions from TypeScript

View File

@ -662,18 +662,18 @@ const vm = new VM(bytecode, { add, greet })
**Method 2**: Register after construction **Method 2**: Register after construction
```typescript ```typescript
const vm = new VM(bytecode) 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() await vm.run()
``` ```
**Method 3**: Value-based functions (for full control) **Method 3**: Value-based functions (for full control)
```typescript ```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) } 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**: **Usage in bytecode**:
``` ```
@ -702,12 +702,12 @@ CALL ; → "Hi, Alice!"
```typescript ```typescript
// Basic @named - collects all named args // Basic @named - collects all named args
vm.registerFunction('greet', (atNamed: any = {}) => { vm.set('greet', (atNamed: any = {}) => {
return `Hello, ${atNamed.name || 'World'}!` return `Hello, ${atNamed.name || 'World'}!`
}) })
// Mixed positional and @named // Mixed positional and @named
vm.registerFunction('configure', (name: string, atOptions: any = {}) => { vm.set('configure', (name: string, atOptions: any = {}) => {
return { return {
name, name,
debug: atOptions.debug || false, debug: atOptions.debug || false,

24
SPEC.md
View File

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

View File

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

View File

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

View File

@ -19,15 +19,15 @@ export class VM {
labels: Map<number, string> = new Map() labels: Map<number, string> = new Map()
nativeFunctions: Map<string, NativeFunction> = 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.instructions = bytecode.instructions
this.constants = bytecode.constants this.constants = bytecode.constants
this.labels = bytecode.labels || new Map() this.labels = bytecode.labels || new Map()
this.scope = new Scope() this.scope = new Scope()
if (globalFunctions) { if (globals) {
for (const name of Object.keys(globalFunctions)) for (const name of Object.keys(globals ?? {}))
this.registerFunction(name, globalFunctions[name]!) this.set(name, globals[name])
this.scope = new Scope(this.scope) this.scope = new Scope(this.scope)
} }
@ -48,12 +48,18 @@ export class VM {
} }
} }
registerFunction(name: string, fn: TypeScriptFunction) { set(name: string, value: any) {
const wrapped = isWrapped(fn) ? fn as NativeFunction : wrapNative(this, fn) if (typeof value === 'function')
this.scope.set(name, { type: 'native', fn: wrapped, 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>' }) 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 }) 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(` const bytecode = toBytecode(`
LOAD add LOAD add
PUSH 5 PUSH 5
@ -127,7 +127,7 @@ describe("functions parameter", () => {
}) })
// Register another function manually // 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() const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 6 }) expect(result).toEqual({ type: 'number', value: 6 })
@ -224,7 +224,7 @@ describe("functions parameter", () => {
}) })
// Override with manual registration // Override with manual registration
vm.registerFunction('getValue', () => 200) vm.set('getValue', () => 200)
const result = await vm.run() const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 200 }) expect(result).toEqual({ type: 'number', value: 200 })

View File

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

View File

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