nondescript/src/ndspkg/compiler/scope.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