control flow emitting
This commit is contained in:
parent
ef794ea881
commit
41e21733d3
|
@ -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
|
Loading…
Reference in New Issue