forked from defunkt/ReefVM
fix nested globals
This commit is contained in:
parent
e542070677
commit
052f989e82
|
|
@ -14,7 +14,7 @@ export function wrapNative(vm: VM, fn: Function): (this: VM, ...args: Value[]) =
|
||||||
const wrapped = async function (this: VM, ...values: Value[]) {
|
const wrapped = async function (this: VM, ...values: Value[]) {
|
||||||
const nativeArgs = values.map(arg => fromValue(arg, vm))
|
const nativeArgs = values.map(arg => fromValue(arg, vm))
|
||||||
const result = await fn.call(this, ...nativeArgs)
|
const result = await fn.call(this, ...nativeArgs)
|
||||||
return toValue(result)
|
return toValue(result, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrappedObj = wrapped as any
|
const wrappedObj = wrapped as any
|
||||||
|
|
|
||||||
14
src/value.ts
14
src/value.ts
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { wrapNative } from "./function"
|
||||||
import { OpCode } from "./opcode"
|
import { OpCode } from "./opcode"
|
||||||
import { Scope } from "./scope"
|
import { Scope } from "./scope"
|
||||||
import { VM } from "./vm"
|
import { VM } from "./vm"
|
||||||
|
|
@ -41,7 +42,7 @@ export function isValue(v: any): boolean {
|
||||||
return !!(v && typeof v === 'object' && v.type && 'value' in v)
|
return !!(v && typeof v === 'object' && v.type && 'value' in v)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toValue(v: any): Value /* throws */ {
|
export function toValue(v: any, vm?: VM): Value /* throws */ {
|
||||||
if (v === null || v === undefined)
|
if (v === null || v === undefined)
|
||||||
return { type: 'null', value: null }
|
return { type: 'null', value: null }
|
||||||
|
|
||||||
|
|
@ -49,7 +50,7 @@ export function toValue(v: any): Value /* throws */ {
|
||||||
return v as Value
|
return v as Value
|
||||||
|
|
||||||
if (Array.isArray(v))
|
if (Array.isArray(v))
|
||||||
return { type: 'array', value: v.map(toValue) }
|
return { type: 'array', value: v.map(x => toValue(x, vm)) }
|
||||||
|
|
||||||
if (v instanceof RegExp)
|
if (v instanceof RegExp)
|
||||||
return { type: 'regex', value: v }
|
return { type: 'regex', value: v }
|
||||||
|
|
@ -64,11 +65,12 @@ export function toValue(v: any): Value /* throws */ {
|
||||||
case 'function':
|
case 'function':
|
||||||
if ((v as any)[REEF_FUNCTION])
|
if ((v as any)[REEF_FUNCTION])
|
||||||
return (v as any)[REEF_FUNCTION]
|
return (v as any)[REEF_FUNCTION]
|
||||||
throw new Error("can't toValue() a js function yet")
|
if (!vm) throw new Error("can't toValue() function without a vm")
|
||||||
|
return { type: 'native', fn: wrapNative(vm, v), value: '<function>' }
|
||||||
case 'object':
|
case 'object':
|
||||||
const dict: Dict = new Map()
|
const dict: Dict = new Map()
|
||||||
|
|
||||||
for (const key of Object.keys(v)) dict.set(key, toValue(v[key]))
|
for (const key of Object.keys(v)) dict.set(key, toValue(v[key], vm))
|
||||||
|
|
||||||
return { type: 'dict', value: dict }
|
return { type: 'dict', value: dict }
|
||||||
default:
|
default:
|
||||||
|
|
@ -210,10 +212,10 @@ export function fnFromValue(fn: Value, vm: VM): Function {
|
||||||
newVM.scope = fn.parentScope
|
newVM.scope = fn.parentScope
|
||||||
|
|
||||||
newVM.stack.push(fn)
|
newVM.stack.push(fn)
|
||||||
newVM.stack.push(...positional.map(toValue))
|
newVM.stack.push(...positional.map(x => toValue(x, vm)))
|
||||||
for (const [key, val] of Object.entries(named)) {
|
for (const [key, val] of Object.entries(named)) {
|
||||||
newVM.stack.push(toValue(key))
|
newVM.stack.push(toValue(key))
|
||||||
newVM.stack.push(toValue(val))
|
newVM.stack.push(toValue(val, vm))
|
||||||
}
|
}
|
||||||
newVM.stack.push(toValue(positional.length))
|
newVM.stack.push(toValue(positional.length))
|
||||||
newVM.stack.push(toValue(Object.keys(named).length))
|
newVM.stack.push(toValue(Object.keys(named).length))
|
||||||
|
|
|
||||||
|
|
@ -49,14 +49,11 @@ export class VM {
|
||||||
}
|
}
|
||||||
|
|
||||||
set(name: string, value: any) {
|
set(name: string, value: any) {
|
||||||
if (typeof value === 'function')
|
this.scope.set(name, toValue(value, this))
|
||||||
this.setFunction(name, value)
|
|
||||||
else
|
|
||||||
this.scope.set(name, toValue(value))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setFunction(name: string, fn: TypeScriptFunction) {
|
setFunction(name: string, fn: TypeScriptFunction) {
|
||||||
this.scope.set(name, { type: 'native', fn: wrapNative(this, fn), value: '<function>' })
|
this.scope.set(name, toValue(fn, this))
|
||||||
}
|
}
|
||||||
|
|
||||||
setValueFunction(name: string, fn: NativeFunction) {
|
setValueFunction(name: string, fn: NativeFunction) {
|
||||||
|
|
|
||||||
|
|
@ -1759,4 +1759,28 @@ test("builtin global functions are placed into a higher level scope", async () =
|
||||||
|
|
||||||
expect(globals).toEqual(['sum', 'greet'])
|
expect(globals).toEqual(['sum', 'greet'])
|
||||||
expect(locals).toEqual(['x'])
|
expect(locals).toEqual(['x'])
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test("builtin global scope can be values too", async () => {
|
||||||
|
const bytecode = toBytecode(`
|
||||||
|
PUSH 1
|
||||||
|
STORE x
|
||||||
|
HALT
|
||||||
|
`)
|
||||||
|
|
||||||
|
const vm = new VM(bytecode, {
|
||||||
|
pi: 3.14,
|
||||||
|
universe: true,
|
||||||
|
algorithms: {
|
||||||
|
bigOne: () => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await vm.run()
|
||||||
|
|
||||||
|
const locals = Array.from(vm.scope.locals.entries()).map(([name,]) => name)
|
||||||
|
const globals = Array.from(vm.scope.parent!.locals.entries()).map(([name,]) => name)
|
||||||
|
|
||||||
|
expect(globals).toEqual(['pi', 'universe', 'algorithms'])
|
||||||
|
expect(locals).toEqual(['x'])
|
||||||
})
|
})
|
||||||
Loading…
Reference in New Issue
Block a user