forked from defunkt/ReefVM
tmp change - LOAD_NATIVE
This commit is contained in:
parent
62f890e59d
commit
4d2ae1c9fe
|
|
@ -74,7 +74,7 @@ type InstructionTuple =
|
||||||
| ["STR_CONCAT", number]
|
| ["STR_CONCAT", number]
|
||||||
|
|
||||||
// Native
|
// Native
|
||||||
| ["CALL_NATIVE", string]
|
| ["LOAD_NATIVE", string]
|
||||||
|
|
||||||
// Special
|
// Special
|
||||||
| ["HALT"]
|
| ["HALT"]
|
||||||
|
|
@ -88,7 +88,7 @@ export type ProgramItem = InstructionTuple | LabelDefinition
|
||||||
// Operand types are determined by prefix/literal:
|
// Operand types are determined by prefix/literal:
|
||||||
// #42 -> immediate number (e.g., JUMP #5, MAKE_ARRAY #3)
|
// #42 -> immediate number (e.g., JUMP #5, MAKE_ARRAY #3)
|
||||||
// .label -> label reference (e.g., JUMP .loop_start, MAKE_FUNCTION (x y) .body)
|
// .label -> label reference (e.g., JUMP .loop_start, MAKE_FUNCTION (x y) .body)
|
||||||
// name -> variable/function name (e.g., LOAD x, CALL_NATIVE add)
|
// name -> variable/function name (e.g., LOAD x, LOAD_NATIVE add)
|
||||||
// 42 -> number constant (e.g., PUSH 42)
|
// 42 -> number constant (e.g., PUSH 42)
|
||||||
// "str" -> string constant (e.g., PUSH "hello")
|
// "str" -> string constant (e.g., PUSH "hello")
|
||||||
// 'str' -> string constant (e.g., PUSH 'hello')
|
// 'str' -> string constant (e.g., PUSH 'hello')
|
||||||
|
|
@ -336,7 +336,7 @@ function toBytecodeFromArray(program: ProgramItem[]): Bytecode /* throws */ {
|
||||||
case "STORE":
|
case "STORE":
|
||||||
case "TRY_LOAD":
|
case "TRY_LOAD":
|
||||||
case "TRY_CALL":
|
case "TRY_CALL":
|
||||||
case "CALL_NATIVE":
|
case "LOAD_NATIVE":
|
||||||
operandValue = operand as string
|
operandValue = operand as string
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ export enum OpCode {
|
||||||
STR_CONCAT, // operand: value count (number) | stack: [val1, ..., valN] → [string] | concatenate N values
|
STR_CONCAT, // operand: value count (number) | stack: [val1, ..., valN] → [string] | concatenate N values
|
||||||
|
|
||||||
// typescript interop
|
// typescript interop
|
||||||
CALL_NATIVE, // operand: function name (identifier) | stack: [...args] → [result] | consumes entire stack
|
LOAD_NATIVE, // operand: function name (identifier) | stack: [] → [function] | load native function
|
||||||
|
|
||||||
// special
|
// special
|
||||||
HALT // operand: none | stop execution
|
HALT // operand: none | stop execution
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ const OPCODES_WITH_OPERANDS = new Set([
|
||||||
OpCode.MAKE_DICT,
|
OpCode.MAKE_DICT,
|
||||||
OpCode.STR_CONCAT,
|
OpCode.STR_CONCAT,
|
||||||
OpCode.MAKE_FUNCTION,
|
OpCode.MAKE_FUNCTION,
|
||||||
OpCode.CALL_NATIVE,
|
OpCode.LOAD_NATIVE,
|
||||||
])
|
])
|
||||||
|
|
||||||
const OPCODES_WITHOUT_OPERANDS = new Set([
|
const OPCODES_WITHOUT_OPERANDS = new Set([
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { Scope } from "./scope"
|
import { Scope } from "./scope"
|
||||||
|
|
||||||
|
type NativeFunction = (...args: Value[]) => Promise<Value> | Value
|
||||||
|
|
||||||
export type Value =
|
export type Value =
|
||||||
| { type: 'null', value: null }
|
| { type: 'null', value: null }
|
||||||
| { type: 'boolean', value: boolean }
|
| { type: 'boolean', value: boolean }
|
||||||
|
|
@ -18,6 +20,7 @@ export type Value =
|
||||||
named: boolean,
|
named: boolean,
|
||||||
value: '<function>'
|
value: '<function>'
|
||||||
}
|
}
|
||||||
|
| { type: 'native_function', fn: NativeFunction, value: '<native>' }
|
||||||
|
|
||||||
export type Dict = Map<string, Value>
|
export type Dict = Map<string, Value>
|
||||||
|
|
||||||
|
|
@ -101,6 +104,8 @@ export function toString(v: Value): string {
|
||||||
return 'null'
|
return 'null'
|
||||||
case 'function':
|
case 'function':
|
||||||
return '<function>'
|
return '<function>'
|
||||||
|
case 'native_function':
|
||||||
|
return '<native>'
|
||||||
case 'array':
|
case 'array':
|
||||||
return `[${v.value.map(toString).join(', ')}]`
|
return `[${v.value.map(toString).join(', ')}]`
|
||||||
case 'dict': {
|
case 'dict': {
|
||||||
|
|
@ -145,6 +150,8 @@ export function isEqual(a: Value, b: Value): boolean {
|
||||||
}
|
}
|
||||||
case 'function':
|
case 'function':
|
||||||
return false // functions never equal
|
return false // functions never equal
|
||||||
|
case 'native_function':
|
||||||
|
return false // native functions never equal
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -168,6 +175,8 @@ export function fromValue(v: Value): any {
|
||||||
return v.value
|
return v.value
|
||||||
case 'function':
|
case 'function':
|
||||||
return '<function>'
|
return '<function>'
|
||||||
|
case 'native_function':
|
||||||
|
return '<native>'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
38
src/vm.ts
38
src/vm.ts
|
|
@ -430,6 +430,21 @@ export class VM {
|
||||||
|
|
||||||
const fn = this.stack.pop()!
|
const fn = this.stack.pop()!
|
||||||
|
|
||||||
|
// Handle native functions
|
||||||
|
if (fn.type === 'native_function') {
|
||||||
|
if (namedCount > 0)
|
||||||
|
throw new Error('CALL: native functions do not support named arguments')
|
||||||
|
|
||||||
|
// Mark current frame as break target (like regular CALL does)
|
||||||
|
if (this.callStack.length > 0)
|
||||||
|
this.callStack[this.callStack.length - 1]!.isBreakTarget = true
|
||||||
|
|
||||||
|
// Call the native function with positional args
|
||||||
|
const result = await fn.fn(...positionalArgs)
|
||||||
|
this.stack.push(result)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if (fn.type !== 'function')
|
if (fn.type !== 'function')
|
||||||
throw new Error('CALL: not a function')
|
throw new Error('CALL: not a function')
|
||||||
|
|
||||||
|
|
@ -591,27 +606,16 @@ export class VM {
|
||||||
this.stack.push(returnValue)
|
this.stack.push(returnValue)
|
||||||
break
|
break
|
||||||
|
|
||||||
case OpCode.CALL_NATIVE:
|
case OpCode.LOAD_NATIVE: {
|
||||||
const functionName = instruction.operand as string
|
const functionName = instruction.operand as string
|
||||||
const tsFunction = this.nativeFunctions.get(functionName)
|
const nativeFunc = this.nativeFunctions.get(functionName)
|
||||||
|
|
||||||
if (!tsFunction)
|
if (!nativeFunc)
|
||||||
throw new Error(`CALL_NATIVE: function not found: ${functionName}`)
|
throw new Error(`LOAD_NATIVE: function not found: ${functionName}`)
|
||||||
|
|
||||||
// Mark current frame as break target (like CALL does)
|
this.stack.push({ type: 'native_function', fn: nativeFunc, value: '<native>' })
|
||||||
if (this.callStack.length > 0)
|
|
||||||
this.callStack[this.callStack.length - 1]!.isBreakTarget = true
|
|
||||||
|
|
||||||
// Pop all arguments from stack (TypeScript function consumes entire stack)
|
|
||||||
const tsArgs = [...this.stack]
|
|
||||||
this.stack = []
|
|
||||||
|
|
||||||
// Call the TypeScript function and await if necessary
|
|
||||||
const tsResult = await tsFunction(...tsArgs)
|
|
||||||
|
|
||||||
// Push result back onto stack
|
|
||||||
this.stack.push(tsResult)
|
|
||||||
break
|
break
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw `Unknown op: ${instruction.op}`
|
throw `Unknown op: ${instruction.op}`
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,12 @@ import { toBytecode } from "#bytecode"
|
||||||
describe("functions parameter", () => {
|
describe("functions parameter", () => {
|
||||||
test("pass functions to run()", async () => {
|
test("pass functions to run()", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE add
|
||||||
PUSH 5
|
PUSH 5
|
||||||
PUSH 3
|
PUSH 3
|
||||||
CALL_NATIVE add
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -20,9 +23,12 @@ describe("functions parameter", () => {
|
||||||
|
|
||||||
test("pass functions to VM constructor", async () => {
|
test("pass functions to VM constructor", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE multiply
|
||||||
PUSH 10
|
PUSH 10
|
||||||
PUSH 2
|
PUSH 2
|
||||||
CALL_NATIVE multiply
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -36,11 +42,19 @@ describe("functions parameter", () => {
|
||||||
|
|
||||||
test("pass multiple functions", async () => {
|
test("pass multiple functions", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE add
|
||||||
PUSH 10
|
PUSH 10
|
||||||
PUSH 5
|
PUSH 5
|
||||||
CALL_NATIVE add
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
|
STORE sum
|
||||||
|
LOAD_NATIVE multiply
|
||||||
|
LOAD sum
|
||||||
PUSH 3
|
PUSH 3
|
||||||
CALL_NATIVE multiply
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -54,9 +68,12 @@ describe("functions parameter", () => {
|
||||||
|
|
||||||
test("auto-wraps native functions", async () => {
|
test("auto-wraps native functions", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE concat
|
||||||
PUSH "hello"
|
PUSH "hello"
|
||||||
PUSH "world"
|
PUSH "world"
|
||||||
CALL_NATIVE concat
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -69,8 +86,11 @@ describe("functions parameter", () => {
|
||||||
|
|
||||||
test("works with async functions", async () => {
|
test("works with async functions", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE delay
|
||||||
PUSH 100
|
PUSH 100
|
||||||
CALL_NATIVE delay
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -86,11 +106,19 @@ describe("functions parameter", () => {
|
||||||
|
|
||||||
test("can combine with manual registerFunction", async () => {
|
test("can combine with manual registerFunction", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE add
|
||||||
PUSH 5
|
PUSH 5
|
||||||
PUSH 3
|
PUSH 3
|
||||||
CALL_NATIVE add
|
|
||||||
PUSH 2
|
PUSH 2
|
||||||
CALL_NATIVE subtract
|
PUSH 0
|
||||||
|
CALL
|
||||||
|
STORE sum
|
||||||
|
LOAD_NATIVE subtract
|
||||||
|
LOAD sum
|
||||||
|
PUSH 2
|
||||||
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -127,8 +155,11 @@ describe("functions parameter", () => {
|
||||||
|
|
||||||
test("function throws error", async () => {
|
test("function throws error", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE divide
|
||||||
PUSH 0
|
PUSH 0
|
||||||
CALL_NATIVE divide
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -147,16 +178,25 @@ describe("functions parameter", () => {
|
||||||
|
|
||||||
test("complex workflow with multiple function calls", async () => {
|
test("complex workflow with multiple function calls", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE add
|
||||||
PUSH 5
|
PUSH 5
|
||||||
PUSH 3
|
PUSH 3
|
||||||
CALL_NATIVE add
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
STORE result
|
STORE result
|
||||||
|
LOAD_NATIVE multiply
|
||||||
LOAD result
|
LOAD result
|
||||||
PUSH 2
|
PUSH 2
|
||||||
CALL_NATIVE multiply
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
STORE final
|
STORE final
|
||||||
|
LOAD_NATIVE format
|
||||||
LOAD final
|
LOAD final
|
||||||
CALL_NATIVE format
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -171,8 +211,11 @@ describe("functions parameter", () => {
|
||||||
|
|
||||||
test("function overriding - later registration wins", async () => {
|
test("function overriding - later registration wins", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE getValue
|
||||||
PUSH 5
|
PUSH 5
|
||||||
CALL_NATIVE getValue
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,14 @@ import { VM } from "#vm"
|
||||||
import { toBytecode } from "#bytecode"
|
import { toBytecode } from "#bytecode"
|
||||||
import { toValue, toNumber, toString } from "#value"
|
import { toValue, toNumber, toString } from "#value"
|
||||||
|
|
||||||
test("CALL_NATIVE - basic function call", async () => {
|
test("LOAD_NATIVE - basic function call", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE add
|
||||||
PUSH 5
|
PUSH 5
|
||||||
PUSH 10
|
PUSH 10
|
||||||
CALL_NATIVE add
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -21,11 +24,14 @@ test("CALL_NATIVE - basic function call", async () => {
|
||||||
expect(result).toEqual({ type: 'number', value: 15 })
|
expect(result).toEqual({ type: 'number', value: 15 })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL_NATIVE - function with string manipulation", async () => {
|
test("LOAD_NATIVE - function with string manipulation", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE concat
|
||||||
PUSH "hello"
|
PUSH "hello"
|
||||||
PUSH "world"
|
PUSH "world"
|
||||||
CALL_NATIVE concat
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -40,10 +46,13 @@ test("CALL_NATIVE - function with string manipulation", async () => {
|
||||||
expect(result).toEqual({ type: 'string', value: 'hello world' })
|
expect(result).toEqual({ type: 'string', value: 'hello world' })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL_NATIVE - async function", async () => {
|
test("LOAD_NATIVE - async function", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE asyncDouble
|
||||||
PUSH 42
|
PUSH 42
|
||||||
CALL_NATIVE asyncDouble
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -58,9 +67,12 @@ test("CALL_NATIVE - async function", async () => {
|
||||||
expect(result).toEqual({ type: 'number', value: 84 })
|
expect(result).toEqual({ type: 'number', value: 84 })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL_NATIVE - function with no arguments", async () => {
|
test("LOAD_NATIVE - function with no arguments", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
CALL_NATIVE getAnswer
|
LOAD_NATIVE getAnswer
|
||||||
|
PUSH 0
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -73,12 +85,15 @@ test("CALL_NATIVE - function with no arguments", async () => {
|
||||||
expect(result).toEqual({ type: 'number', value: 42 })
|
expect(result).toEqual({ type: 'number', value: 42 })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL_NATIVE - function with multiple arguments", async () => {
|
test("LOAD_NATIVE - function with multiple arguments", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE sum
|
||||||
PUSH 2
|
PUSH 2
|
||||||
PUSH 3
|
PUSH 3
|
||||||
PUSH 4
|
PUSH 4
|
||||||
CALL_NATIVE sum
|
PUSH 3
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -92,10 +107,13 @@ test("CALL_NATIVE - function with multiple arguments", async () => {
|
||||||
expect(result).toEqual({ type: 'number', value: 9 })
|
expect(result).toEqual({ type: 'number', value: 9 })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL_NATIVE - function returns array", async () => {
|
test("LOAD_NATIVE - function returns array", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE makeRange
|
||||||
PUSH 3
|
PUSH 3
|
||||||
CALL_NATIVE makeRange
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -121,20 +139,26 @@ test("CALL_NATIVE - function returns array", async () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL_NATIVE - function not found", async () => {
|
test("LOAD_NATIVE - function not found", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
CALL_NATIVE nonexistent
|
LOAD_NATIVE nonexistent
|
||||||
|
PUSH 0
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
||||||
expect(vm.run()).rejects.toThrow('CALL_NATIVE: function not found: nonexistent')
|
expect(vm.run()).rejects.toThrow('LOAD_NATIVE: function not found: nonexistent')
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CALL_NATIVE - using result in subsequent operations", async () => {
|
test("LOAD_NATIVE - using result in subsequent operations", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE triple
|
||||||
PUSH 5
|
PUSH 5
|
||||||
CALL_NATIVE triple
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
PUSH 10
|
PUSH 10
|
||||||
ADD
|
ADD
|
||||||
`)
|
`)
|
||||||
|
|
@ -151,9 +175,12 @@ test("CALL_NATIVE - using result in subsequent operations", async () => {
|
||||||
|
|
||||||
test("Native function wrapping - basic sync function with native types", async () => {
|
test("Native function wrapping - basic sync function with native types", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE add
|
||||||
PUSH 5
|
PUSH 5
|
||||||
PUSH 10
|
PUSH 10
|
||||||
CALL_NATIVE add
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -169,8 +196,11 @@ test("Native function wrapping - basic sync function with native types", async (
|
||||||
|
|
||||||
test("Native function wrapping - async function with native types", async () => {
|
test("Native function wrapping - async function with native types", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE asyncDouble
|
||||||
PUSH 42
|
PUSH 42
|
||||||
CALL_NATIVE asyncDouble
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -187,9 +217,12 @@ test("Native function wrapping - async function with native types", async () =>
|
||||||
|
|
||||||
test("Native function wrapping - string manipulation", async () => {
|
test("Native function wrapping - string manipulation", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE concat
|
||||||
PUSH "hello"
|
PUSH "hello"
|
||||||
PUSH "world"
|
PUSH "world"
|
||||||
CALL_NATIVE concat
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -205,8 +238,11 @@ test("Native function wrapping - string manipulation", async () => {
|
||||||
|
|
||||||
test("Native function wrapping - with default parameters", async () => {
|
test("Native function wrapping - with default parameters", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE ls
|
||||||
PUSH "/home/user"
|
PUSH "/home/user"
|
||||||
CALL_NATIVE ls
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -222,8 +258,11 @@ test("Native function wrapping - with default parameters", async () => {
|
||||||
|
|
||||||
test("Native function wrapping - returns array", async () => {
|
test("Native function wrapping - returns array", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE makeRange
|
||||||
PUSH 3
|
PUSH 3
|
||||||
CALL_NATIVE makeRange
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -247,9 +286,12 @@ test("Native function wrapping - returns array", async () => {
|
||||||
|
|
||||||
test("Native function wrapping - returns object (becomes dict)", async () => {
|
test("Native function wrapping - returns object (becomes dict)", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE makeUser
|
||||||
PUSH "Alice"
|
PUSH "Alice"
|
||||||
PUSH 30
|
PUSH 30
|
||||||
CALL_NATIVE makeUser
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
@ -269,9 +311,17 @@ test("Native function wrapping - returns object (becomes dict)", async () => {
|
||||||
|
|
||||||
test("Native function wrapping - mixed with manual Value functions", async () => {
|
test("Native function wrapping - mixed with manual Value functions", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE nativeAdd
|
||||||
PUSH 5
|
PUSH 5
|
||||||
CALL_NATIVE nativeAdd
|
PUSH 1
|
||||||
CALL_NATIVE manualDouble
|
PUSH 0
|
||||||
|
CALL
|
||||||
|
STORE sum
|
||||||
|
LOAD_NATIVE manualDouble
|
||||||
|
LOAD sum
|
||||||
|
PUSH 1
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const vm = new VM(bytecode)
|
const vm = new VM(bytecode)
|
||||||
|
|
|
||||||
|
|
@ -387,9 +387,12 @@ describe("RegExp", () => {
|
||||||
test("with native functions", async () => {
|
test("with native functions", async () => {
|
||||||
const { VM } = await import("#vm")
|
const { VM } = await import("#vm")
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE match
|
||||||
PUSH "hello world"
|
PUSH "hello world"
|
||||||
PUSH /world/
|
PUSH /world/
|
||||||
CALL_NATIVE match
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -407,10 +410,13 @@ describe("RegExp", () => {
|
||||||
test("native function with regex replacement", async () => {
|
test("native function with regex replacement", async () => {
|
||||||
const { VM } = await import("#vm")
|
const { VM } = await import("#vm")
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE replace
|
||||||
PUSH "hello world"
|
PUSH "hello world"
|
||||||
PUSH /o/g
|
PUSH /o/g
|
||||||
PUSH "0"
|
PUSH "0"
|
||||||
CALL_NATIVE replace
|
PUSH 3
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -427,9 +433,12 @@ describe("RegExp", () => {
|
||||||
test("native function extracting matches", async () => {
|
test("native function extracting matches", async () => {
|
||||||
const { VM } = await import("#vm")
|
const { VM } = await import("#vm")
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
|
LOAD_NATIVE extractNumbers
|
||||||
PUSH "test123abc456"
|
PUSH "test123abc456"
|
||||||
PUSH /\\d+/g
|
PUSH /\\d+/g
|
||||||
CALL_NATIVE extractNumbers
|
PUSH 2
|
||||||
|
PUSH 0
|
||||||
|
CALL
|
||||||
HALT
|
HALT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user