diff --git a/src/ndspkg/compv2/emitter.nim b/src/ndspkg/compv2/emitter.nim index 496c528..9aaaf98 100644 --- a/src/ndspkg/compv2/emitter.nim +++ b/src/ndspkg/compv2/emitter.nim @@ -104,6 +104,43 @@ proc addLocal(em: Emitter, name: string, delta: int) = proc markInitialized(em: Emitter) = em.locals[em.locals.high()].depth = em.scopes.high() +# jump helpers + +proc emitJump(em: Emitter, delta: int, op: OpCode): (int, int) = + # delta = 0 - jump does not pop + # delta = -1 - jump pops the condition from the stack + em.writeChunk(delta, op) + em.writeChunk(0, 0xffffff.toDU8()) + # return where the jump target coordinate is and the starting stackindex + return (em.chunk.len() - argSize, em.stackIndex) + +proc patchJump(em: Emitter, target: int, stackLen: int) = + # target points to the DU8 specifying jump size + # stackLen is the original stack length - for compiler assertions only + if em.stackIndex != stackLen: + em.error("Assertion failed: jump doesn't preserve stackindex.") + let jump = em.chunk.len - target - 2 + if jump > argMax: + em.error("Too much code to jump over.") + let jumpDU8 = jump.toDU8() + em.chunk.code[target] = jumpDU8[0] + em.chunk.code[target+1] = jumpDU8[1] + +proc emitLoop(em: Emitter, loopStart: int, stackLen: int) = + # loopStart is the place to jump to, + # loopStackLen is the stack len at the place it is jumping to. + # opLoop is the only looping op for now, so it is assumed + if em.stackIndex != stackLen: + em.error("Assertion failed: loop doesn't preserve stackindex.") + em.writeChunk(0, opLoop) + + let offset = em.chunk.len - loopStart + argSize + if offset > argMax: + em.error("Loop body too large.") + + em.writeChunk(0, offset.toDU8) + + # node compilers proc emit(em: Emitter, node: Node) = em.line = node.line @@ -158,9 +195,65 @@ proc emit(em: Emitter, node: Node) = em.writeChunk(0, opNot) else: raise newException(Defect, "Misaligned case list.") # unreachable + of nkIf: + # condition + em.emit(node.ifCondition) + let (thenJump, thenStackLen) = em.emitJump(0, opJumpIfFalse) + # jumped over conditional code must leave stack size intact + # if body: + em.writePops(1) # pop condition + em.emit(node.ifBody) # if body expression + # if body: jump over else, if there is an else body + let (elseJump, elseStackLen) = em.emitJump(0, opJump) + + # start of else, so jumps here if condition is falsy + em.patchJump(thenJump, thenStackLen) + if node.elseBody != nil: + # else body: + em.writePops(1) # pop condition + em.emit(node.elseBody) # else body expression + + em.patchJump(elseJump, elseStackLen) # jumps over the else end up here + of nkAnd: + em.emit(node.left) + let (andJump, andStackLen) = em.emitJump(0, opJumpIfFalse) + # condition: if left is truthy, pop left and do the right expression + em.writePops(1) + em.emit(node.right) + # jump here if falsey + em.patchJump(andJump, andStackLen) + of nkOr: + em.emit(node.left) + # if falsey, jump over the jump that jumps over the right one (that's a mouthful) + let (orJump, orStackLen) = em.emitJump(0, opJumpIfFalse) + let (endJump, endStackLen) = em.emitJump(0, opJump) + em.patchJump(orJump, orStackLen) + + # right side, only executed if left side is truthy + em.writePops(1) # pop left side + em.emit(node.right) # right side + + # jumps to the end point here + em.patchJump(endJump, endStackLen) + of nkWhile: + em.writeChunk(1, opNil) # default return value + + # looping will go here + let loopStart = em.chunk.len() + let loopStackLen = em.stackIndex + + em.emit(node.whileCondition) # condition - the default return value of while + let (exitJump, exitStackLen) = em.emitJump(-1, opJumpIfFalsePop) # pop condition + + # while body: + em.writePops(1) # pop old return value + em.emit(node.whileBody) # body + + em.emitLoop(loopStart, loopStackLen) + + em.patchJump(exitJump, exitStackLen) else: raise newException(Defect, &"Unsupported node kind: {$node.kind}") - proc emit*(em: Emitter) = em.emit(em.root) - em.writeChunk(0, opReturn) # required for the vm to exit gracefully \ No newline at end of file + em.writeChunk(0, opReturn) # required for the vm to exit gracefully \ No newline at end of file