closures work - test passes now
This commit is contained in:
parent
85aa396054
commit
d5479a0be7
|
@ -1,17 +1,5 @@
|
||||||
outer = funct() {
|
var argcap = funct(n)
|
||||||
var a = 1;
|
:result = funct() print n
|
||||||
var b = 2;
|
;
|
||||||
var result;
|
|
||||||
var middle = funct() {
|
|
||||||
var c = 3;
|
|
||||||
var d = 4;
|
|
||||||
var inner = funct() {
|
|
||||||
print a + c + b + d;
|
|
||||||
};
|
|
||||||
result = inner;
|
|
||||||
};
|
|
||||||
:result = result;
|
|
||||||
};
|
|
||||||
|
|
||||||
//expect:10.0
|
argcap(5)();
|
||||||
outer()();
|
|
||||||
|
|
|
@ -414,7 +414,8 @@ proc expString(comp: Compiler) =
|
||||||
|
|
||||||
tkString.genRule(expString, nop, pcNone)
|
tkString.genRule(expString, nop, pcNone)
|
||||||
|
|
||||||
proc addUpvalue(comp: Compiler, index: int): int =
|
proc addUpvalue(comp: Compiler, local: Local): int =
|
||||||
|
## argument: local
|
||||||
## This proc takes an index to a local in the locals table
|
## This proc takes an index to a local in the locals table
|
||||||
## and creates an upvalue in every function up until the one
|
## and creates an upvalue in every function up until the one
|
||||||
## including this local so that all of the function scopes in between
|
## including this local so that all of the function scopes in between
|
||||||
|
@ -428,9 +429,11 @@ proc addUpvalue(comp: Compiler, index: int): int =
|
||||||
comp.error("Too many closure variables in function.")
|
comp.error("Too many closure variables in function.")
|
||||||
blk
|
blk
|
||||||
|
|
||||||
var scopeIndex = comp.locals[index].depth
|
var scopeIndex = local.depth + 1
|
||||||
var isLocal = true
|
# +1 must be here, because if scope the local is in is a function
|
||||||
var upvalIndex: int = index
|
# then the upvalues should only be in child functions
|
||||||
|
var isLocal = true # local means that it's the outermost function that's closing it
|
||||||
|
var upvalIndex: int = local.index
|
||||||
while scopeIndex < comp.scopes.len():
|
while scopeIndex < comp.scopes.len():
|
||||||
let scope = comp.scopes[scopeIndex]
|
let scope = comp.scopes[scopeIndex]
|
||||||
if scope.function:
|
if scope.function:
|
||||||
|
@ -443,9 +446,10 @@ proc addUpvalue(comp: Compiler, index: int): int =
|
||||||
upvalIndex = i
|
upvalIndex = i
|
||||||
break ensure
|
break ensure
|
||||||
scope.upvalues.add(Upvalue(index: upvalIndex, isLocal: isLocal))
|
scope.upvalues.add(Upvalue(index: upvalIndex, isLocal: isLocal))
|
||||||
|
upvalIndex = scope.upvalues.high()
|
||||||
isLocal = false
|
isLocal = false
|
||||||
upvalIndex = scope.upvalues.high()
|
|
||||||
scopeIndex.inc
|
scopeIndex.inc
|
||||||
|
return upvalIndex
|
||||||
|
|
||||||
proc resolveLocal(comp: Compiler, name: string): tuple[index: int, upvalue: bool] =
|
proc resolveLocal(comp: Compiler, name: string): tuple[index: int, upvalue: bool] =
|
||||||
## the bool arg specifies whether it found an upvalue
|
## the bool arg specifies whether it found an upvalue
|
||||||
|
@ -465,7 +469,7 @@ proc resolveLocal(comp: Compiler, name: string): tuple[index: int, upvalue: bool
|
||||||
else:
|
else:
|
||||||
# resolveUpvalue
|
# resolveUpvalue
|
||||||
local.captured = true
|
local.captured = true
|
||||||
return (comp.addUpvalue(i), true)
|
return (comp.addUpvalue(local), true)
|
||||||
i.dec
|
i.dec
|
||||||
return (index: -1, upvalue: false)
|
return (index: -1, upvalue: false)
|
||||||
|
|
||||||
|
@ -756,7 +760,7 @@ proc parseList(comp: Compiler) =
|
||||||
while comp.current.tokenType != tkRightBracket:
|
while comp.current.tokenType != tkRightBracket:
|
||||||
comp.expression()
|
comp.expression()
|
||||||
count.inc()
|
count.inc()
|
||||||
if comp.current.tokenType != tkRightBracket or comp.current.tokenType == tkComma:
|
if comp.current.tokenType != tkRightBracket:
|
||||||
comp.consume(tkComma, "Comma expected after list member.")
|
comp.consume(tkComma, "Comma expected after list member.")
|
||||||
comp.consume(tkRightBracket, "Right bracket expected after list members.")
|
comp.consume(tkRightBracket, "Right bracket expected after list members.")
|
||||||
if count > argMax:
|
if count > argMax:
|
||||||
|
@ -776,9 +780,10 @@ proc parseTable(comp: Compiler) =
|
||||||
comp.consume(tkEqual, "Equal sign expected after key.")
|
comp.consume(tkEqual, "Equal sign expected after key.")
|
||||||
comp.expression()
|
comp.expression()
|
||||||
count.inc()
|
count.inc()
|
||||||
if comp.current.tokenType != tkRightBrace or comp.current.tokenType == tkComma:
|
if comp.current.tokenType != tkRightBrace:
|
||||||
comp.consume(tkComma, "Comma expected after key-value pair.")
|
comp.consume(tkComma, "Comma expected after key-value pair.")
|
||||||
comp.consume(tkRightBrace, "Right brace expected after list members.")
|
|
||||||
|
comp.consume(tkRightBrace, "Right brace expected after table members.")
|
||||||
if count > argMax:
|
if count > argMax:
|
||||||
comp.error("Maximum table length exceeded.")
|
comp.error("Maximum table length exceeded.")
|
||||||
comp.writeChunk(1 - 2 * count, opCreateTable)
|
comp.writeChunk(1 - 2 * count, opCreateTable)
|
||||||
|
|
|
@ -38,9 +38,10 @@ proc debugStr*[T](clos: Closure[T]): string =
|
||||||
let upvalCountStr: string = $clos.upvalueCount
|
let upvalCountStr: string = $clos.upvalueCount
|
||||||
result = &"Closure(start: {addrStr}, length: {upvalCountStr}, upvalues: "
|
result = &"Closure(start: {addrStr}, length: {upvalCountStr}, upvalues: "
|
||||||
mixin `$`
|
mixin `$`
|
||||||
for i in 0 .. clos.upvalueCount:
|
for i in 0 .. clos.upvalueCount-1:
|
||||||
if clos.upvalues[i] != nil:
|
if clos.upvalues[i] != nil:
|
||||||
result &= &"{$(clos.upvalues[i].location[])}, "
|
let upvalStr = $(clos.upvalues[i].location[])
|
||||||
|
result &= &"{upvalStr}, "
|
||||||
else:
|
else:
|
||||||
result &= "<unset>, "
|
result &= "<unset>, "
|
||||||
result &= ")"
|
result &= ")"
|
||||||
|
|
|
@ -29,6 +29,7 @@ type
|
||||||
Frame = object
|
Frame = object
|
||||||
stackBottom: int # the absolute index of where 0 inside the frame is
|
stackBottom: int # the absolute index of where 0 inside the frame is
|
||||||
returnIp: ptr uint8
|
returnIp: ptr uint8
|
||||||
|
closure: Closure[NdValue]
|
||||||
|
|
||||||
InterpretResult* = enum
|
InterpretResult* = enum
|
||||||
irOK, irRuntimeError
|
irOK, irRuntimeError
|
||||||
|
@ -41,7 +42,6 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
hadError: bool
|
hadError: bool
|
||||||
globals: Table[NdValue, NdValue]
|
globals: Table[NdValue, NdValue]
|
||||||
frames: Stack[Frame] = newStack[Frame](4)
|
frames: Stack[Frame] = newStack[Frame](4)
|
||||||
closures: Stack[Closure[NdValue]] = newStack[Closure[NdValue]](4)
|
|
||||||
openUpvalues: Upvalue[NdValue] = nil
|
openUpvalues: Upvalue[NdValue] = nil
|
||||||
|
|
||||||
proc runtimeError(msg: string) =
|
proc runtimeError(msg: string) =
|
||||||
|
@ -76,13 +76,14 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip))
|
frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip))
|
||||||
ip = funct.asFunct() # jump to the entry point
|
ip = funct.asFunct() # jump to the entry point
|
||||||
elif funct.isClosure():
|
elif funct.isClosure():
|
||||||
frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip))
|
frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip, closure: funct.asClosure()))
|
||||||
closures.add(funct.asClosure())
|
|
||||||
ip = funct.asClosure().getIp()
|
ip = funct.asClosure().getIp()
|
||||||
else:
|
else:
|
||||||
error
|
error
|
||||||
|
|
||||||
proc captureUpvalue(location: ptr NdValue): Upvalue[NdValue] =
|
proc captureUpvalue(location: ptr NdValue): Upvalue[NdValue] =
|
||||||
|
when debugClosures:
|
||||||
|
write stdout, "CLOSURES - captureUpvalue: "
|
||||||
var prev: Upvalue[NdValue]
|
var prev: Upvalue[NdValue]
|
||||||
var upvalue = openUpvalues
|
var upvalue = openUpvalues
|
||||||
while upvalue != nil and upvalue.location.pgreater(location):
|
while upvalue != nil and upvalue.location.pgreater(location):
|
||||||
|
@ -91,9 +92,13 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
|
|
||||||
# existing upvalue
|
# existing upvalue
|
||||||
if upvalue != nil and upvalue.location == location:
|
if upvalue != nil and upvalue.location == location:
|
||||||
|
when debugClosures:
|
||||||
|
write stdout, "found existing, returning that.\n"
|
||||||
return upvalue
|
return upvalue
|
||||||
|
|
||||||
# new upvalue
|
# new upvalue
|
||||||
|
when debugClosures:
|
||||||
|
write stdout, "creating new.\n"
|
||||||
result = newUpvalue(location)
|
result = newUpvalue(location)
|
||||||
result.next = upvalue
|
result.next = upvalue
|
||||||
|
|
||||||
|
@ -130,12 +135,12 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
echo msg
|
echo msg
|
||||||
|
|
||||||
when debugClosures:
|
when debugClosures:
|
||||||
if closures.len() > 0:
|
msg = " Closures: [ "
|
||||||
msg = " Closures: [ "
|
for i in 0 .. frames.high():
|
||||||
for i in 0 .. closures.high():
|
if frames[i].closure != nil:
|
||||||
msg &= debugStr(closures[i]) & " "
|
msg &= debugStr(frames[i].closure) & " "
|
||||||
msg &= "]"
|
msg &= "]"
|
||||||
echo msg
|
echo msg
|
||||||
|
|
||||||
|
|
||||||
var ii = ip.pdiff(chunk.code[0].unsafeAddr) - 1
|
var ii = ip.pdiff(chunk.code[0].unsafeAddr) - 1
|
||||||
|
@ -248,15 +253,15 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
stack[slot + frameBottom] = stack.peek()
|
stack[slot + frameBottom] = stack.peek()
|
||||||
of opGetUpvalue:
|
of opGetUpvalue:
|
||||||
let slot = ip.readDU8()
|
let slot = ip.readDU8()
|
||||||
let val = closures.peek().get(slot).read()
|
let val = frames.peek().closure.get(slot).read()
|
||||||
when debugClosures:
|
when debugClosures:
|
||||||
echo &"CLOSURES - getupvalue got {val} from slot {slot}"
|
echo &"CLOSURES - getupvalue got {val} from slot {slot}"
|
||||||
stack.push(val)
|
stack.push(val)
|
||||||
of opSetUpvalue:
|
of opSetUpvalue:
|
||||||
let slot = ip.readDU8()
|
let slot = ip.readDU8()
|
||||||
when debugClosures:
|
when debugClosures:
|
||||||
echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {closures.peek().upvalueCount}"
|
echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {frames.peek().closure.upvalueCount}"
|
||||||
closures.peek().get(slot).write(stack.peek())
|
frames.peek().closure.get(slot).write(stack.peek())
|
||||||
of opCloseUpvalue:
|
of opCloseUpvalue:
|
||||||
let slot = ip.readDU8()
|
let slot = ip.readDU8()
|
||||||
stack[slot + frameBottom].addr.closeUpvalues()
|
stack[slot + frameBottom].addr.closeUpvalues()
|
||||||
|
@ -293,7 +298,7 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
echo &"CLOSURES - opClosure: local upvalue {loc[]} from local slot {slot} to slot {i}"
|
echo &"CLOSURES - opClosure: local upvalue {loc[]} from local slot {slot} to slot {i}"
|
||||||
closure.set(i, loc.captureUpvalue())
|
closure.set(i, loc.captureUpvalue())
|
||||||
else:
|
else:
|
||||||
let val = closures.peek().get(slot)
|
let val = frames.peek().closure.get(slot)
|
||||||
when debugClosures:
|
when debugClosures:
|
||||||
echo &"CLOSURES - opClosure: non local upvalue {val.location[]} from slot {slot} to slot {i}"
|
echo &"CLOSURES - opClosure: non local upvalue {val.location[]} from slot {slot} to slot {i}"
|
||||||
closure.set(i, val)
|
closure.set(i, val)
|
||||||
|
@ -396,7 +401,6 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
stack.free()
|
stack.free()
|
||||||
frames.free()
|
frames.free()
|
||||||
globals.free()
|
globals.free()
|
||||||
closures.free()
|
|
||||||
|
|
||||||
if hadError:
|
if hadError:
|
||||||
irRuntimeError
|
irRuntimeError
|
||||||
|
|
|
@ -42,25 +42,43 @@ var f = funct() {
|
||||||
:result = x;
|
:result = x;
|
||||||
};
|
};
|
||||||
|
|
||||||
//expect:5.0
|
var inst = f();
|
||||||
f()[0]();
|
|
||||||
f()[1]();
|
|
||||||
//expect:6.0
|
|
||||||
f()[0]();
|
|
||||||
|
|
||||||
// capturing the result of a function:
|
//expect:5.0
|
||||||
|
inst[0]();
|
||||||
|
inst[1]();
|
||||||
|
//expect:6.0
|
||||||
|
inst[0]();
|
||||||
|
|
||||||
|
// multiple different labels
|
||||||
|
|
||||||
var f2 = funct() {
|
var f2 = funct() {
|
||||||
var x = { @ftwo
|
var x = { @a @b
|
||||||
:result = funct() {
|
// this captures the internal value, not whatever it returns is assigned to
|
||||||
print :ftwo;
|
:result = @{
|
||||||
|
"get" = funct() print :a,
|
||||||
|
"set" = funct(n) :b = n,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
x = 5;
|
x = 5;
|
||||||
};
|
};
|
||||||
|
|
||||||
//expect:5.0
|
var inst2 = f2();
|
||||||
f2()();
|
inst2["get"]();
|
||||||
|
//expect:nil
|
||||||
|
inst2["set"](5.2);
|
||||||
|
inst2["get"]();
|
||||||
|
//expect:5.2
|
||||||
|
|
||||||
|
// capturing args
|
||||||
|
|
||||||
|
var argcap = funct(n)
|
||||||
|
:result = funct() print n
|
||||||
|
;
|
||||||
|
|
||||||
|
//expect:8.1
|
||||||
|
argcap(8.1)();
|
||||||
|
|
||||||
|
|
||||||
// oop: constructors, getters, setters
|
// oop: constructors, getters, setters
|
||||||
|
|
||||||
|
@ -151,11 +169,12 @@ outer = funct() {
|
||||||
};
|
};
|
||||||
result = inner;
|
result = inner;
|
||||||
};
|
};
|
||||||
:result = result;
|
middle();
|
||||||
|
:result = funct() :result = result;
|
||||||
};
|
};
|
||||||
|
|
||||||
//expect:10.0
|
//expect:10.0
|
||||||
outer()();
|
outer()()();
|
||||||
|
|
||||||
// 4: manipulation of vals from closures
|
// 4: manipulation of vals from closures
|
||||||
|
|
||||||
|
@ -209,12 +228,12 @@ globalGet();
|
||||||
print c;
|
print c;
|
||||||
};
|
};
|
||||||
|
|
||||||
print f();
|
f();
|
||||||
print g();
|
g();
|
||||||
print h();
|
h();
|
||||||
//expect:1
|
//expect:1.0
|
||||||
//expect:2
|
//expect:2.0
|
||||||
//expect:3
|
//expect:3.0
|
||||||
};
|
};
|
||||||
|
|
||||||
// bonus: the last one with a list twist
|
// bonus: the last one with a list twist
|
||||||
|
@ -240,6 +259,6 @@ bonus = bonus();
|
||||||
bonus[2]();
|
bonus[2]();
|
||||||
bonus[0]();
|
bonus[0]();
|
||||||
bonus[1]();
|
bonus[1]();
|
||||||
//expect:3
|
//expect:3.0
|
||||||
//expect:1
|
//expect:1.0
|
||||||
//expect:2
|
//expect:2.0
|
||||||
|
|
|
@ -5,6 +5,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import strutils
|
import strutils
|
||||||
import osproc
|
import osproc
|
||||||
|
import terminal
|
||||||
|
|
||||||
testHashtables()
|
testHashtables()
|
||||||
|
|
||||||
|
@ -24,10 +25,24 @@ proc runTest(path: string) =
|
||||||
let success = output == expoutput
|
let success = output == expoutput
|
||||||
if not success:
|
if not success:
|
||||||
echo "Nds test failed: " & path
|
echo "Nds test failed: " & path
|
||||||
echo "expected output:"
|
|
||||||
echo expoutput
|
let oupLines = output.split('\n')
|
||||||
echo "got output:"
|
let expLines = expoutput.split('\n')
|
||||||
echo output
|
for i in 0 .. oupLines.high():
|
||||||
|
let oupLine = oupLines[i]
|
||||||
|
var expLine = ""
|
||||||
|
if expLines.len() > i:
|
||||||
|
expLine = expLines[i]
|
||||||
|
if oupLine == expLine:
|
||||||
|
setForegroundColor(fgGreen)
|
||||||
|
echo oupLine
|
||||||
|
else:
|
||||||
|
setForegroundColor(fgRed)
|
||||||
|
write stdout, oupLine
|
||||||
|
setForegroundColor(fgDefault)
|
||||||
|
write stdout, " (expected: " & expLine & ")\n"
|
||||||
|
|
||||||
|
setForegroundColor(fgDefault)
|
||||||
else:
|
else:
|
||||||
echo "Test success: " & path
|
echo "Test success: " & path
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue