100 lines
3.0 KiB
Nim
100 lines
3.0 KiB
Nim
import strformat
|
|
import sequtils
|
|
import sugar
|
|
|
|
import ../scanner
|
|
import ../chunk
|
|
import ../config
|
|
|
|
import types
|
|
import utils
|
|
import jumps
|
|
|
|
|
|
# SCOPE HELPERS
|
|
proc beginScope*(comp: Compiler, function: bool = false) =
|
|
let scope = comp.newScope(function)
|
|
|
|
if not function:
|
|
while comp.match(tkLabel):
|
|
let label = comp.previous.text[1..^1]
|
|
scope.labels.add(label)
|
|
# only put the opNil if it's not a function scope, since
|
|
# function scopes are initialized by the caller
|
|
comp.writeChunk(1, opNil)
|
|
else:
|
|
# if it's a function scope, the frame will move
|
|
# access to outside locals is also limited to upvalues and closures
|
|
comp.stackIndex = 0
|
|
|
|
for label in scope.labels:
|
|
comp.addLocal(&":{label}", delta = 0)
|
|
|
|
proc scopeRetIndex*(comp: Compiler): int =
|
|
let scope = comp.scopes[comp.scopes.high()]
|
|
# this is an illegal operation for function scopes, as they work differently
|
|
if scope.function:
|
|
comp.error("Assertion failed, Internal error, scopeRetIndex calculation for a function scope.")
|
|
return scope.goalStackIndex
|
|
|
|
proc restore*(comp: Compiler, scope: Scope) =
|
|
let delta = comp.stackIndex - scope.goalStackIndex
|
|
comp.writePops(delta)
|
|
if not comp.stackIndex == scope.goalStackIndex:
|
|
comp.error("Assertion failed in restore")
|
|
|
|
proc restoreInFunct*(comp: Compiler, scope: Scope) =
|
|
#let pops = comp.stackIndex
|
|
#comp.writePops(pops)
|
|
|
|
comp.stackIndex = scope.goalStackIndex
|
|
#when debugCompiler:
|
|
# debugEcho &"Restored function scope: delta {pops}; new stackindex: {comp.stackIndex}"
|
|
|
|
proc jumpToEnd*(comp: Compiler, scope: Scope) =
|
|
## Jumps to the end of scope, does not affect stackIndex
|
|
var delta: int
|
|
if scope.function:
|
|
delta = comp.stackIndex
|
|
else:
|
|
delta = comp.stackIndex - scope.goalStackIndex
|
|
comp.writePops(delta)
|
|
var s = 0 # discard the saved stack length
|
|
let jmp = comp.emitJump(delta, opJump, s)
|
|
scope.jumps.add(jmp)
|
|
|
|
proc endScope*(comp: Compiler): Scope =
|
|
# remove locals
|
|
let popped = comp.scopes.pop()
|
|
if popped.parentFunction == popped:
|
|
popped.parentFunction = nil # cleanup cycles
|
|
let function = popped.function
|
|
# close upvalues
|
|
var i = comp.locals.high()
|
|
while i >= 0:
|
|
let local = comp.locals[i]
|
|
if local.scope == popped and local.captured:
|
|
comp.writeChunk(0, opCloseUpvalue)
|
|
comp.writeChunk(0, local.index.toDU8())
|
|
if local.depth < comp.scopes.len():
|
|
break
|
|
i.dec()
|
|
|
|
# restore the stackIndex, emit pops
|
|
if function:
|
|
comp.restoreInFunct(popped)
|
|
else:
|
|
comp.restore(popped)
|
|
# patch jumps to after the scope (such jumps from breaks emit the pops before jumping)
|
|
for jump in popped.jumps:
|
|
if function:
|
|
# all jumps should only happen to named block expressions
|
|
comp.error("Assertion failed: jump attempt to end function.")
|
|
comp.patchJump(jump, popped.goalStackIndex)
|
|
if function:
|
|
comp.writeChunk(0, opReturn)
|
|
# remove locals from the comp object that were of this scope
|
|
comp.locals.keepIf((it) => it.depth < comp.scopes.len())
|
|
popped
|
|
|