more repl support
This commit is contained in:
parent
1fb5effb0a
commit
17d846b999
11
src/vm.ts
11
src/vm.ts
|
|
@ -83,8 +83,16 @@ export class VM {
|
||||||
// Helper for REPL mode: append new bytecode with proper constant index remapping
|
// Helper for REPL mode: append new bytecode with proper constant index remapping
|
||||||
appendBytecode(bytecode: Bytecode): void {
|
appendBytecode(bytecode: Bytecode): void {
|
||||||
const constantOffset = this.constants.length
|
const constantOffset = this.constants.length
|
||||||
|
const instructionOffset = this.instructions.length
|
||||||
|
|
||||||
this.constants.push(...bytecode.constants)
|
// Remap function body addresses in constants before adding them
|
||||||
|
for (const constant of bytecode.constants) {
|
||||||
|
if (constant.type === 'function_def') {
|
||||||
|
this.constants.push({ ...constant, body: constant.body + instructionOffset })
|
||||||
|
} else {
|
||||||
|
this.constants.push(constant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const instruction of bytecode.instructions) {
|
for (const instruction of bytecode.instructions) {
|
||||||
if (instruction.operand !== undefined && typeof instruction.operand === 'number') {
|
if (instruction.operand !== undefined && typeof instruction.operand === 'number') {
|
||||||
|
|
@ -103,7 +111,6 @@ export class VM {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytecode.labels) {
|
if (bytecode.labels) {
|
||||||
const instructionOffset = this.instructions.length - bytecode.instructions.length
|
|
||||||
for (const [addr, label] of bytecode.labels.entries()) {
|
for (const [addr, label] of bytecode.labels.entries()) {
|
||||||
this.labels.set(addr + instructionOffset, label)
|
this.labels.set(addr + instructionOffset, label)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,3 +110,40 @@ test("REPL mode - continue() executes only new bytecode", async () => {
|
||||||
expect(line2Count).toBe(1) // Ran once as expected
|
expect(line2Count).toBe(1) // Ran once as expected
|
||||||
expect(result).toEqual({ type: "number", value: 15 }) // 5 + 10
|
expect(result).toEqual({ type: "number", value: 15 }) // 5 + 10
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("REPL mode - function calls work across chunks", async () => {
|
||||||
|
const vm = new VM(toBytecode([]))
|
||||||
|
await vm.run()
|
||||||
|
|
||||||
|
// Line 1: Define a function
|
||||||
|
const line1 = toBytecode([
|
||||||
|
["MAKE_FUNCTION", ["x"], ".body"],
|
||||||
|
["STORE", "add1"],
|
||||||
|
["JUMP", ".end"],
|
||||||
|
[".body:"],
|
||||||
|
["LOAD", "x"],
|
||||||
|
["PUSH", 1],
|
||||||
|
["ADD"],
|
||||||
|
["RETURN"],
|
||||||
|
[".end:"]
|
||||||
|
])
|
||||||
|
|
||||||
|
vm.appendBytecode(line1)
|
||||||
|
await vm.continue()
|
||||||
|
|
||||||
|
expect(vm.scope.get("add1")?.type).toBe("function")
|
||||||
|
|
||||||
|
// Line 2: Call the function
|
||||||
|
const line2 = toBytecode([
|
||||||
|
["LOAD", "add1"],
|
||||||
|
["PUSH", 10],
|
||||||
|
["PUSH", 1],
|
||||||
|
["PUSH", 0],
|
||||||
|
["CALL"]
|
||||||
|
])
|
||||||
|
|
||||||
|
vm.appendBytecode(line2)
|
||||||
|
const result = await vm.continue()
|
||||||
|
|
||||||
|
expect(result).toEqual({ type: "number", value: 11 })
|
||||||
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user