forked from defunkt/ReefVM
support roundtrip value conversions
This commit is contained in:
parent
d50b143c9d
commit
97b6722a11
|
|
@ -13,5 +13,5 @@ export { wrapNative, isWrapped, type ParamInfo, extractParamInfo, getOriginalFun
|
||||||
export { OpCode } from "./opcode"
|
export { OpCode } from "./opcode"
|
||||||
export { Scope } from "./scope"
|
export { Scope } from "./scope"
|
||||||
export type { Value, TypeScriptFunction, NativeFunction } from "./value"
|
export type { Value, TypeScriptFunction, NativeFunction } from "./value"
|
||||||
export { isValue, toValue, toString, toNumber, fromValue, toNull } from "./value"
|
export { isValue, toValue, toString, toNumber, fromValue, toNull, fnFromValue } from "./value"
|
||||||
export { VM } from "./vm"
|
export { VM } from "./vm"
|
||||||
10
src/value.ts
10
src/value.ts
|
|
@ -4,6 +4,7 @@ import { VM } from "./vm"
|
||||||
|
|
||||||
export type NativeFunction = (...args: Value[]) => Promise<Value> | Value
|
export type NativeFunction = (...args: Value[]) => Promise<Value> | Value
|
||||||
export type TypeScriptFunction = (this: VM, ...args: any[]) => any
|
export type TypeScriptFunction = (this: VM, ...args: any[]) => any
|
||||||
|
const REEF_FUNCTION = Symbol('__reefFunction')
|
||||||
|
|
||||||
export type Value =
|
export type Value =
|
||||||
| { type: 'null', value: null }
|
| { type: 'null', value: null }
|
||||||
|
|
@ -61,6 +62,8 @@ export function toValue(v: any): Value /* throws */ {
|
||||||
case 'string':
|
case 'string':
|
||||||
return { type: 'string', value: v }
|
return { type: 'string', value: v }
|
||||||
case 'function':
|
case 'function':
|
||||||
|
if ((v as any)[REEF_FUNCTION])
|
||||||
|
return (v as any)[REEF_FUNCTION]
|
||||||
throw new Error("can't toValue() a js function yet")
|
throw new Error("can't toValue() a js function yet")
|
||||||
case 'object':
|
case 'object':
|
||||||
const dict: Dict = new Map()
|
const dict: Dict = new Map()
|
||||||
|
|
@ -190,7 +193,7 @@ export function fnFromValue(fn: Value, vm: VM): Function {
|
||||||
if (fn.type !== 'function')
|
if (fn.type !== 'function')
|
||||||
throw new Error('Value is not a function')
|
throw new Error('Value is not a function')
|
||||||
|
|
||||||
return async function (...args: any[]) {
|
const wrapper = async function (...args: any[]) {
|
||||||
let positional: any[] = args
|
let positional: any[] = args
|
||||||
let named: Record<string, any> = {}
|
let named: Record<string, any> = {}
|
||||||
|
|
||||||
|
|
@ -226,4 +229,9 @@ export function fnFromValue(fn: Value, vm: VM): Function {
|
||||||
|
|
||||||
return fromValue(newVM.stack.pop() || toNull(), vm)
|
return fromValue(newVM.stack.pop() || toNull(), vm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// support roundtrips, eg fromValue(toValue(fn))
|
||||||
|
; (wrapper as any)[REEF_FUNCTION] = fn
|
||||||
|
|
||||||
|
return wrapper
|
||||||
}
|
}
|
||||||
|
|
@ -80,3 +80,36 @@ test("isValue - edge cases with type and value properties", () => {
|
||||||
expect(isValue({ type: 'number', val: 42 })).toBe(false)
|
expect(isValue({ type: 'number', val: 42 })).toBe(false)
|
||||||
expect(isValue({ typ: 'number', value: 42 })).toBe(false)
|
expect(isValue({ typ: 'number', value: 42 })).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("toValue - converts wrapped Reef functions back to original Value", async () => {
|
||||||
|
const { VM, toBytecode, fnFromValue } = await import("#reef")
|
||||||
|
|
||||||
|
// Create a Reef function
|
||||||
|
const bytecode = toBytecode([
|
||||||
|
["MAKE_FUNCTION", ["x"], ".body"],
|
||||||
|
["STORE", "add1"],
|
||||||
|
["JUMP", ".end"],
|
||||||
|
[".body:"],
|
||||||
|
["LOAD", "x"],
|
||||||
|
["PUSH", 1],
|
||||||
|
["ADD"],
|
||||||
|
["RETURN"],
|
||||||
|
[".end:"],
|
||||||
|
["HALT"]
|
||||||
|
])
|
||||||
|
|
||||||
|
const vm = new VM(bytecode)
|
||||||
|
await vm.run()
|
||||||
|
|
||||||
|
const reefFunction = vm.scope.get("add1")!
|
||||||
|
expect(reefFunction.type).toBe("function")
|
||||||
|
|
||||||
|
// Convert to JS function
|
||||||
|
const jsFunction = fnFromValue(reefFunction, vm)
|
||||||
|
expect(typeof jsFunction).toBe("function")
|
||||||
|
|
||||||
|
// Convert back to Value - should return the original Reef function
|
||||||
|
const backToValue = toValue(jsFunction)
|
||||||
|
expect(backToValue).toBe(reefFunction) // Same reference
|
||||||
|
expect(backToValue.type).toBe("function")
|
||||||
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user