diff --git a/src/ndspkg/chunk.nim b/src/ndspkg/chunk.nim index cbbc007..fd4eb7a 100644 --- a/src/ndspkg/chunk.nim +++ b/src/ndspkg/chunk.nim @@ -118,6 +118,43 @@ const argInstructions = { opGetUpvalue, opSetUpvalue, } +proc disassembleInstruction*(ch: Chunk, c: var int, lastLine: var int) = + template instruction: uint8 = ch.code[c] + template line: int = ch.lines[c] + template double: DoubleUint8 = [ch.code[c+1], ch.code[c+2]] + template shortArg: uint8 = ch.code[c+1] + let cFmt = &"{c:04}" + let lineFmt = if lastLine == line: " | " else: &"{line:04}" + lastLine = line + try: + write stdout, &"[{cFmt}] {lineFmt} {instruction.OpCode} ({instruction.toHex(2)}" + case instruction.OpCode: + of simpleInstructions: + write stdout, ")\n" + of shortArgInstructions: + write stdout, &" {shortArg.toHex(2)})\n" + c += 1 + of argInstructions: + write stdout, &" {double[0].toHex(2)} {double[1].toHex(2)})\n" + c += 2 + of constantInstructions: + let i = double.toInt + write stdout, &" {double[0].toHex(2)} {double[1].toHex(2)})\n" + echo &" points to constant {ch.constants[i]} (i: {i})" + c += 2 + of opClosure: + let upvalCount = double.toInt + c += 2 + write stdout, &" length: {upvalCount} [ " + for i in countup(1, upvalCount): + let index = double.toInt + c += 2 + let local = shortArg.int + c += 1 + write stdout, &"(i: {index} l: {local}) " + write stdout, &"])\n" + except: + echo &"[{cFmt}] {lineFmt} Unknown opcode {instruction}" proc disassembleChunk*(ch: Chunk) = echo &"== Chunk {ch.name} begin ==" @@ -125,42 +162,7 @@ proc disassembleChunk*(ch: Chunk) = var c: int = 0 var lastLine = -1 while c < ch.code.len: - template instruction: uint8 = ch.code[c] - template line: int = ch.lines[c] - template double: DoubleUint8 = [ch.code[c+1], ch.code[c+2]] - template shortArg: uint8 = ch.code[c+1] - let cFmt = &"{c:04}" - let lineFmt = if lastLine == line: " | " else: &"{line:04}" - try: - write stdout, &"[{cFmt}] {lineFmt} {instruction.OpCode} ({instruction.toHex(2)}" - case instruction.OpCode: - of simpleInstructions: - write stdout, ")\n" - of shortArgInstructions: - write stdout, &" {shortArg.toHex(2)})\n" - c += 1 - of argInstructions: - write stdout, &" {double[0].toHex(2)} {double[1].toHex(2)})\n" - c += 2 - of constantInstructions: - let i = double.toInt - write stdout, &" {double[0].toHex(2)} {double[1].toHex(2)})\n" - echo &" points to constant {ch.constants[i]} (i: {i})" - c += 2 - of opClosure: - let upvalCount = double.toInt - c += 2 - write stdout, &" length: {upvalCount} [ " - for i in countup(1, upvalCount): - let index = double.toInt - c += 2 - let local = shortArg.int - c += 1 - write stdout, &"(i: {index} l: {local}) " - write stdout, &"])\n" - - except: - echo &"[{cFmt}] {lineFmt} Unknown opcode {instruction}" + disassembleInstruction(ch, c, lastLine) c.inc echo &"== Chunk {ch.name} end ==" diff --git a/src/ndspkg/types/closure.nim b/src/ndspkg/types/closure.nim index 1d1b2c1..21e5e28 100644 --- a/src/ndspkg/types/closure.nim +++ b/src/ndspkg/types/closure.nim @@ -17,4 +17,20 @@ proc newClosure*[T](start: ptr uint8, upvalueCount: int): Closure[T] = result.upvalueCount = upvalueCount proc getIp*[T](clos: Closure[T]): ptr uint8 {.inline.} = - clos.start \ No newline at end of file + clos.start + +proc set*[T](clos: Closure[T], index: int, val: Upvalue[T]) = + clos.upvalues[index] = val + +proc get*[T](clos: Closure[T], index: int): Upvalue[T] = + clos.upvalues[index] + +proc captureUpvalue*[T](location: ptr T): Upvalue[T] = + result = cast[Upvalue[T]](alloc0(sizeof(UpvalueObj[T]))) + result.location = location + +proc read*[T](upval: Upvalue[T]): T = + upval.location[] + +proc write*[T](upval: Upvalue[T], val: T) = + upval.location[]= val \ No newline at end of file diff --git a/src/ndspkg/types/value.nim b/src/ndspkg/types/value.nim index 44d5030..693e3ae 100644 --- a/src/ndspkg/types/value.nim +++ b/src/ndspkg/types/value.nim @@ -1,4 +1,5 @@ import strformat +import strutils import bitops import ndstring @@ -155,7 +156,9 @@ proc `$`*(val: NdValue): string = elif val.isFloat(): return $val.asFloat() elif val.isFunct(): - return &"Function object {cast[uint](val.asFunct())}" + return &"Function object {(val).uint.toHex()}" + elif val.isClosure(): + return &"Closure object {(val).uint.toHex()}" elif val.isString(): return $val.asString() elif val.isTable(): diff --git a/src/ndspkg/vm.nim b/src/ndspkg/vm.nim index 7e9c4ef..3473ae3 100644 --- a/src/ndspkg/vm.nim +++ b/src/ndspkg/vm.nim @@ -27,6 +27,7 @@ type Frame = object stackBottom: int # the absolute index of where 0 inside the frame is returnIp: ptr uint8 + closure: Closure[NdValue] InterpretResult* = enum irOK, irRuntimeError @@ -72,7 +73,7 @@ proc run*(chunk: Chunk): InterpretResult = frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip)) ip = funct.asFunct() # jump to the entry point elif funct.isClosure(): - frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip)) + frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip, closure: funct.asClosure())) ip = funct.asClosure().getIp() else: error @@ -84,9 +85,10 @@ proc run*(chunk: Chunk): InterpretResult = ip = ip.padd(1) when debugVM: - let ii = ip.pdiff(chunk.code[0].unsafeAddr) - 1 - let opname = ($ins) - var msg = &"[{ii:04}] {opname}" + var ii = ip.pdiff(chunk.code[0].unsafeAddr) - 1 + var ll = -1 + disassembleInstruction(chunk, ii, ll) + var msg = "" msg &= " Stack: [ " for i in 0 .. stack.high(): let e = stack[i] @@ -199,9 +201,11 @@ proc run*(chunk: Chunk): InterpretResult = let slot = ip.readDU8() stack[slot + frameBottom] = stack.peek() of opGetUpvalue: - discard + let slot = ip.readDU8() + stack.push(frames.peek().closure.get(slot).read()) of opSetUpvalue: - discard + let slot = ip.readDU8() + frames.peek().closure.get(slot).write(stack.peek()) of opJumpIfFalse: let offset = ip.readDU8() if stack.peek().isFalsey(): @@ -222,8 +226,18 @@ proc run*(chunk: Chunk): InterpretResult = ip = ip.padd(offset) stack.push(faddr.fromFunct()) of opClosure: - runtimeError("Closures are not implemented.") - break + let upvalCount = ip.readDU8() + let funct = stack.peek() + let closure = newClosure[NdValue](funct.asFunct(), upvalCount) + stack.settip(closure.fromClosure()) + for i in countup(0, upvalCount - 1): + let slot = ip.readDU8() + let isLocal = ip.readUI8() == 0 + if isLocal: + closure.set(i, stack[slot + frameBottom].addr.captureUpvalue()) + else: + closure.set(i, frames.peek().closure.get(slot)) + of opCheckArity: let arity = ip.readUI8() let argcount = stack.high() - frameBottom