WIP closures, fix compiler bug

This commit is contained in:
prod2 2022-02-06 03:49:36 +01:00
parent 2f790067f3
commit 086c6e3c2e
5 changed files with 73 additions and 15 deletions

View File

@ -113,7 +113,7 @@ const argInstructions = {
opPopA,
opGetLocal, opSetLocal,
opJumpIfFalse, opJump, opLoop, opJumpIfFalsePop,
opFunctionDef, opClosure,
opFunctionDef,
opCreateList, opCreateTable,
opGetUpvalue, opSetUpvalue,
}
@ -137,7 +137,7 @@ proc disassembleChunk*(ch: Chunk) =
of simpleInstructions:
write stdout, ")\n"
of shortArgInstructions:
write stdout, &" {shortArg.toHex(2)}"
write stdout, &" {shortArg.toHex(2)})\n"
c += 1
of argInstructions:
write stdout, &" {double[0].toHex(2)} {double[1].toHex(2)})\n"
@ -147,6 +147,18 @@ proc disassembleChunk*(ch: Chunk) =
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}"
c.inc

View File

@ -1,5 +1,6 @@
import strformat
import strutils
import sequtils
import sugar
import scanner
@ -17,6 +18,10 @@ type
# its depth will be set once its first ever value is determined
scope: Scope # the innermost scope of this local
Upvalue = ref object
index: int
isLocal: bool
Scope = ref object
labels: seq[string]
goalStackIndex: int # the stack count it started with plus 1
@ -24,6 +29,7 @@ type
function: bool # if true, it is a function
parentFunction: Scope # if not a function, which scope is the innermost function it's in (if it's a function this points to itself)
# if parentFunction is nil, the scope is not inside a function
upvalues: seq[Upvalue] # only needed for functions
Compiler = ref object
#
@ -299,12 +305,13 @@ proc jumpToEnd(comp: Compiler, scope: Scope) =
let jmp = comp.emitJump(delta, opJump)
scope.jumps.add(jmp)
proc endScope(comp: Compiler) =
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
# restore the stackIndex, emit pops
when debugCompiler:
debugEcho &"End scope called for depth {comp.scopes.len} function? {function}"
if function:
@ -316,6 +323,9 @@ proc endScope(comp: Compiler) =
comp.patchJump(jump)
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
# EXPRESSIONS
@ -397,8 +407,25 @@ proc addUpvalue(comp: Compiler, index: int): int =
## and creates an upvalue in every function up until the one
## including this local so that all of the function scopes in between
## have the right upvalue in them (compile time)
comp.error("addupvalue not implemented yet")
discard
template lenCheck(scope: Scope, blk: untyped) =
if scope.upvalues.len() >= argMax:
comp.error("Too many closure variables in function.")
blk
let local = comp.locals[index]
var scopeIndex = local.depth
var isLocal = true
var upvalIndex: int
while scopeIndex < comp.scopes.len():
let scope = comp.scopes[scopeIndex]
if scope.function:
scope.lenCheck():
return 0
scope.upvalues.add(Upvalue(index: if isLocal: index else: upvalIndex, isLocal: isLocal))
isLocal = false
upvalIndex = scope.upvalues.high()
scopeIndex.inc
proc resolveLocal(comp: Compiler, name: string): tuple[index: int, upvalue: bool] =
## the bool arg specifies whether it found an upvalue
@ -684,11 +711,19 @@ proc parseFunct(comp: Compiler) =
let shouldbeStackIndex = params.len + 1
if shouldbeStackIndex != comp.stackIndex:
comp.error(&"Assertion failed: wrong stackindex ({comp.stackIndex}) in function declaration (should be {shouldbeStackIndex}).")
comp.endScope()
let f = comp.endScope()
dec comp.stackIndex # the previous end scope did not put anything on the stack, it is jumped over
# end of function declaration:
comp.patchJump(jumpOverBody)
# closures are implemented in a way, where the vm pops the function from the stack
# and reads the upvalue details from the following bytes
if f.upvalues.len() > 0:
comp.writeChunk(0, opClosure)
comp.writeChunk(0, f.upvalues.len().toDU8())
for upval in f.upvalues:
comp.writeChunk(0, upval.index.toDU8())
comp.writeChunk(0, if upval.isLocal: 0'u8 else: 1'u8)
tkFunct.genRule(parseFunct, nop, pcNone)
@ -769,7 +804,7 @@ proc parseBlock(comp: Compiler) =
comp.beginScope()
while comp.current.tokenType != tkRightBrace and comp.current.tokenType != tkEof:
comp.statement()
comp.endScope()
discard comp.endScope()
comp.consume(tkRightBrace, "Expect '}' after block.")

View File

@ -222,10 +222,8 @@ proc run*(chunk: Chunk): InterpretResult =
ip = ip.padd(offset)
stack.push(faddr.fromFunct())
of opClosure:
let offset = ip.readDU8()
let faddr: ptr uint8 = ip
ip = ip.padd(offset)
stack.push(newClosure[NdValue](faddr, 0).fromClosure())
runtimeError("Closures are not implemented.")
break
of opCheckArity:
let arity = ip.readUI8()
let argcount = stack.high() - frameBottom

View File

@ -18,9 +18,9 @@ f()[0]();
// capturing the result of a function:
var f2 = funct() {
var x = { @f2
var x = { @ftwo
:result = funct() {
print :f2;
print :ftwo;
};
};
x = 5;
@ -31,8 +31,8 @@ f2()();
// oop: constructors, getters, setters
var newAnimal = funct(species, color) {
var species = species; // copy it so that it's mutable, if args ever get immutable
var newAnimal = funct(pspecies, color) {
var species = pspecies; // copy it so that it's mutable, if args ever get immutable
var animal = @{
"getSpecies" = funct() :result = species,
"setSpecies" = funct(newSpecies) species = newSpecies,

13
tests/shadowing.nds Normal file
View File

@ -0,0 +1,13 @@
var a = 5;
{
var a = 3;
{
var a = 2;
print a;
//expect:2.0
};
print a;
//expect:3.0
};
print a;
//expect:5.0