It is now possible to close over function arguments
This commit is contained in:
parent
885d6e3ea8
commit
85de75a50a
|
@ -31,7 +31,7 @@ when debugVM or debugMem or debugGC:
|
||||||
import std/terminal
|
import std/terminal
|
||||||
|
|
||||||
|
|
||||||
{.push checks:off.} # The VM is a critical point where checks are deleterious
|
{.push checks:on.} # The VM is a critical point where checks are deleterious
|
||||||
|
|
||||||
type
|
type
|
||||||
PeonVM* = ref object
|
PeonVM* = ref object
|
||||||
|
@ -54,6 +54,7 @@ type
|
||||||
operands: seq[uint64] # Our operand stack
|
operands: seq[uint64] # Our operand stack
|
||||||
cache: array[6, uint64] # Singletons cache
|
cache: array[6, uint64] # Singletons cache
|
||||||
frames: seq[uint64] # Stores the bottom of stack frames
|
frames: seq[uint64] # Stores the bottom of stack frames
|
||||||
|
closures: seq[uint64] # Stores closure offsets
|
||||||
closedOver: seq[uint64] # Stores variables that do not have stack semantics
|
closedOver: seq[uint64] # Stores variables that do not have stack semantics
|
||||||
results: seq[uint64] # Stores function's results (return values)
|
results: seq[uint64] # Stores function's results (return values)
|
||||||
gc: PeonGC
|
gc: PeonGC
|
||||||
|
@ -62,7 +63,8 @@ type
|
||||||
## peon objects
|
## peon objects
|
||||||
String, List,
|
String, List,
|
||||||
Dict, Tuple,
|
Dict, Tuple,
|
||||||
CustomType
|
CustomType,
|
||||||
|
Closure
|
||||||
HeapObject* = object
|
HeapObject* = object
|
||||||
## A tagged box for a heap-allocated
|
## A tagged box for a heap-allocated
|
||||||
## peon object
|
## peon object
|
||||||
|
@ -429,11 +431,11 @@ proc peekc(self: PeonVM, distance: int = 0): uint64 {.used.} =
|
||||||
return self.calls[self.calls.high() + distance]
|
return self.calls[self.calls.high() + distance]
|
||||||
|
|
||||||
|
|
||||||
proc getc(self: PeonVM, idx: uint64): uint64 =
|
proc getc(self: PeonVM, idx: int): uint64 =
|
||||||
## Accessor method that abstracts
|
## Accessor method that abstracts
|
||||||
## indexing our call stack through stack
|
## indexing our call stack through stack
|
||||||
## frames
|
## frames
|
||||||
return self.calls[idx + self.frames[^1]]
|
return self.calls[idx.uint64 + self.frames[^1]]
|
||||||
|
|
||||||
|
|
||||||
proc setc(self: PeonVM, idx: uint, val: uint64) =
|
proc setc(self: PeonVM, idx: uint, val: uint64) =
|
||||||
|
@ -636,6 +638,13 @@ when debugVM: # So nim shuts up
|
||||||
if i < self.results.high():
|
if i < self.results.high():
|
||||||
stdout.styledWrite(fgYellow, ", ")
|
stdout.styledWrite(fgYellow, ", ")
|
||||||
styledEcho fgMagenta, "]"
|
styledEcho fgMagenta, "]"
|
||||||
|
if self.closures.len() !> 0:
|
||||||
|
stdout.styledWrite(fgBlue, "Closure offsets: ", fgMagenta, "[")
|
||||||
|
for i, e in self.closures:
|
||||||
|
stdout.styledWrite(fgYellow, $e)
|
||||||
|
if i < self.closures.high():
|
||||||
|
stdout.styledWrite(fgYellow, ", ")
|
||||||
|
styledEcho fgMagenta, "]"
|
||||||
discard readLine stdin
|
discard readLine stdin
|
||||||
|
|
||||||
|
|
||||||
|
@ -713,6 +722,7 @@ proc dispatch*(self: PeonVM) =
|
||||||
self.results.add(self.getNil())
|
self.results.add(self.getNil())
|
||||||
# Creates a new call frame
|
# Creates a new call frame
|
||||||
self.frames.add(uint64(self.calls.len() - 2))
|
self.frames.add(uint64(self.calls.len() - 2))
|
||||||
|
self.closures.add(self.closedOver.len().uint64)
|
||||||
# Loads the arguments onto the stack
|
# Loads the arguments onto the stack
|
||||||
for _ in 0..<argc:
|
for _ in 0..<argc:
|
||||||
self.pushc(self.pop())
|
self.pushc(self.pop())
|
||||||
|
@ -726,7 +736,7 @@ proc dispatch*(self: PeonVM) =
|
||||||
# Every peon program is wrapped
|
# Every peon program is wrapped
|
||||||
# in a hidden function, so this
|
# in a hidden function, so this
|
||||||
# will also exit the VM if we're
|
# will also exit the VM if we're
|
||||||
# at the end of the program
|
# at the end of the program
|
||||||
while self.calls.len().uint64 !> self.frames[^1] + 2'u64:
|
while self.calls.len().uint64 !> self.frames[^1] + 2'u64:
|
||||||
# Discards the function's local variables,
|
# Discards the function's local variables,
|
||||||
# if there is any
|
# if there is any
|
||||||
|
@ -740,6 +750,7 @@ proc dispatch*(self: PeonVM) =
|
||||||
discard self.results.pop()
|
discard self.results.pop()
|
||||||
# Discard the topmost stack frame
|
# Discard the topmost stack frame
|
||||||
discard self.frames.pop()
|
discard self.frames.pop()
|
||||||
|
discard self.closures.pop()
|
||||||
if self.frames.len() == 0:
|
if self.frames.len() == 0:
|
||||||
# End of the program!
|
# End of the program!
|
||||||
return
|
return
|
||||||
|
@ -757,7 +768,7 @@ proc dispatch*(self: PeonVM) =
|
||||||
# into the given call stack index
|
# into the given call stack index
|
||||||
let idx = self.readLong()
|
let idx = self.readLong()
|
||||||
when debugVM:
|
when debugVM:
|
||||||
assert idx - self.calls.high() <= 1, "StoreVar index is bigger than the length of the call stack"
|
assert idx.int - self.calls.high() <= 1, "StoreVar index is bigger than the length of the call stack"
|
||||||
if idx + self.frames[^1] <= self.calls.high().uint:
|
if idx + self.frames[^1] <= self.calls.high().uint:
|
||||||
self.setc(idx, self.pop())
|
self.setc(idx, self.pop())
|
||||||
else:
|
else:
|
||||||
|
@ -775,11 +786,16 @@ proc dispatch*(self: PeonVM) =
|
||||||
of LoadClosure:
|
of LoadClosure:
|
||||||
# Loads a closed-over variable onto the
|
# Loads a closed-over variable onto the
|
||||||
# stack
|
# stack
|
||||||
self.push(self.closedOver[self.readLong()])
|
self.push(self.closedOver[self.readLong() + self.closures[^1] - 1])
|
||||||
|
of PopClosure:
|
||||||
|
self.closedOver.delete(self.readLong())
|
||||||
|
of LiftArgument:
|
||||||
|
# Lifts a function argument onto the stack
|
||||||
|
self.closedOver.add(self.getc(self.readLong().int))
|
||||||
of LoadVar:
|
of LoadVar:
|
||||||
# Pushes a variable onto the operand
|
# Pushes a variable onto the operand
|
||||||
# stack
|
# stack
|
||||||
self.push(self.getc(self.readLong()))
|
self.push(self.getc(self.readLong().int))
|
||||||
of NoOp:
|
of NoOp:
|
||||||
# Does nothing
|
# Does nothing
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
import meta/token
|
import meta/token
|
||||||
import meta/ast
|
import meta/ast
|
||||||
import meta/errors
|
import meta/errors
|
||||||
import ../config
|
|
||||||
import ../util/multibyte
|
import ../util/multibyte
|
||||||
import ../util/symbols
|
import ../util/symbols
|
||||||
import lexer as l
|
import lexer as l
|
||||||
|
@ -63,6 +62,9 @@ type
|
||||||
isBuiltinFunction: bool
|
isBuiltinFunction: bool
|
||||||
builtinOp: string
|
builtinOp: string
|
||||||
fun: FunDecl
|
fun: FunDecl
|
||||||
|
isClosure: bool
|
||||||
|
closureBounds: tuple[start, stop: int]
|
||||||
|
childFunc: Type
|
||||||
of Reference, Pointer:
|
of Reference, Pointer:
|
||||||
value: Type
|
value: Type
|
||||||
of Generic:
|
of Generic:
|
||||||
|
@ -100,6 +102,8 @@ type
|
||||||
codePos: int
|
codePos: int
|
||||||
# Is the name closed over (i.e. used in a closure)?
|
# Is the name closed over (i.e. used in a closure)?
|
||||||
isClosedOver: bool
|
isClosedOver: bool
|
||||||
|
# The function that owns this variable (may be nil!)
|
||||||
|
belongsTo: Name
|
||||||
# Is this a function argument?
|
# Is this a function argument?
|
||||||
isFunctionArgument: bool
|
isFunctionArgument: bool
|
||||||
# Where is this node declared in the file?
|
# Where is this node declared in the file?
|
||||||
|
@ -143,7 +147,7 @@ type
|
||||||
# in a local scope, otherwise it's global
|
# in a local scope, otherwise it's global
|
||||||
scopeDepth: int
|
scopeDepth: int
|
||||||
# The current function being compiled
|
# The current function being compiled
|
||||||
currentFunction: Type
|
currentFunction: Name
|
||||||
# Are optimizations turned on?
|
# Are optimizations turned on?
|
||||||
enableOptimizations: bool
|
enableOptimizations: bool
|
||||||
# The current loop being compiled (used to
|
# The current loop being compiled (used to
|
||||||
|
@ -172,7 +176,7 @@ type
|
||||||
# be empty)
|
# be empty)
|
||||||
deferred: seq[uint8]
|
deferred: seq[uint8]
|
||||||
# List of closed-over variables
|
# List of closed-over variables
|
||||||
closedOver: seq[Name]
|
closedOver: seq[tuple[name: Name, count: int]]
|
||||||
# Keeps track of stack frames
|
# Keeps track of stack frames
|
||||||
frames: seq[int]
|
frames: seq[int]
|
||||||
# Compiler procedures called by pragmas
|
# Compiler procedures called by pragmas
|
||||||
|
@ -239,7 +243,7 @@ proc compileModule(self: Compiler, filename: string)
|
||||||
## Public getter for nicer error formatting
|
## Public getter for nicer error formatting
|
||||||
proc getCurrentNode*(self: Compiler): ASTNode = (if self.current >=
|
proc getCurrentNode*(self: Compiler): ASTNode = (if self.current >=
|
||||||
self.ast.len(): self.ast[^1] else: self.ast[self.current - 1])
|
self.ast.len(): self.ast[^1] else: self.ast[self.current - 1])
|
||||||
proc getCurrentFunction*(self: Compiler): Declaration {.inline.} = (if self.currentFunction.isNil(): nil else: self.currentFunction.fun)
|
proc getCurrentFunction*(self: Compiler): Declaration {.inline.} = (if self.currentFunction.valueType.isNil(): nil else: self.currentFunction.valueType.fun)
|
||||||
proc getFile*(self: Compiler): string {.inline.} = self.file
|
proc getFile*(self: Compiler): string {.inline.} = self.file
|
||||||
proc getModule*(self: Compiler): string {.inline.} = self.currentModule
|
proc getModule*(self: Compiler): string {.inline.} = self.currentModule
|
||||||
proc getLines*(self: Compiler): seq[tuple[start, stop: int]] = self.lines
|
proc getLines*(self: Compiler): seq[tuple[start, stop: int]] = self.lines
|
||||||
|
@ -428,7 +432,7 @@ proc getClosurePos(self: Compiler, name: Name): int =
|
||||||
result = self.closedOver.high()
|
result = self.closedOver.high()
|
||||||
var found = false
|
var found = false
|
||||||
for variable in reversed(self.closedOver):
|
for variable in reversed(self.closedOver):
|
||||||
if name == variable:
|
if name == variable.name:
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
dec(result)
|
dec(result)
|
||||||
|
@ -464,23 +468,29 @@ proc detectClosureVariable(self: Compiler, name: var Name, depth: int = self.sco
|
||||||
## unpredictably or crash
|
## unpredictably or crash
|
||||||
if name.isNil() or name.depth == 0 or name.isClosedOver:
|
if name.isNil() or name.depth == 0 or name.isClosedOver:
|
||||||
return
|
return
|
||||||
elif name.depth < depth and self.scopes[name.depth - 1] != self.scopes[depth - 1]:
|
elif name.depth < depth and self.scopes[name.depth - 1] != self.scopes[depth - 1]:
|
||||||
# Ding! The given name is closed over in another function:
|
# Ding! The given name is closed over in another function:
|
||||||
# we need to change the Jump instruction that self.declareName
|
# we need to change the Jump instruction that self.declareName
|
||||||
# put in place for us into a StoreClosure. We also update
|
# put in place for us into a StoreClosure. We also update
|
||||||
# the name's isClosedOver field so that self.identifier()
|
# the name's isClosedOver field so that self.identifier()
|
||||||
# can emit a LoadClosure instruction instead of a LoadVar
|
# can emit a LoadClosure instruction instead of a LoadVar
|
||||||
# once this name is referenced in the future
|
# once this name is referenced in the future
|
||||||
|
self.closedOver.add((name, 0))
|
||||||
|
name.isClosedOver = true
|
||||||
|
if not self.currentFunction.valueType.isClosure:
|
||||||
|
self.currentFunction.valueType.isClosure = true
|
||||||
|
self.currentFunction.valueType.closureBounds.start = self.closedOver.high()
|
||||||
|
self.currentFunction.valueType.closureBounds.stop = self.closedOver.high()
|
||||||
|
if self.closedOver.len() >= 16777216:
|
||||||
|
self.error("too many consecutive closed-over variables (max is 16777215)")
|
||||||
if not name.isFunctionArgument:
|
if not name.isFunctionArgument:
|
||||||
# We handle closed-over function arguments later
|
|
||||||
self.closedOver.add(name)
|
|
||||||
if self.closedOver.len() >= 16777216:
|
|
||||||
self.error("too many consecutive closed-over variables (max is 16777215)")
|
|
||||||
name.isClosedOver = true
|
|
||||||
self.chunk.code[name.codePos] = StoreClosure.uint8()
|
self.chunk.code[name.codePos] = StoreClosure.uint8()
|
||||||
for i, b in self.closedOver.high().toTriple():
|
for i, b in self.closedOver.high().toTriple():
|
||||||
self.chunk.code[name.codePos + i + 1] = b
|
self.chunk.code[name.codePos + i + 1] = b
|
||||||
|
else:
|
||||||
|
self.chunk.code[name.codePos] = LiftArgument.uint8()
|
||||||
|
for i, b in self.getStackPos(name).toTriple():
|
||||||
|
self.chunk.code[name.codePos + i + 1] = b
|
||||||
|
|
||||||
|
|
||||||
proc compareTypes(self: Compiler, a, b: Type): bool =
|
proc compareTypes(self: Compiler, a, b: Type): bool =
|
||||||
|
@ -920,7 +930,7 @@ proc literal(self: Compiler, node: ASTNode) =
|
||||||
var x: float
|
var x: float
|
||||||
var y = FloatExpr(node)
|
var y = FloatExpr(node)
|
||||||
try:
|
try:
|
||||||
discard parseFloat(y.literal.lexeme)
|
discard parseFloat(y.literal.lexeme, x)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.error("floating point value out of range")
|
self.error("floating point value out of range")
|
||||||
self.emitConstant(y, self.inferType(y))
|
self.emitConstant(y, self.inferType(y))
|
||||||
|
@ -1036,6 +1046,12 @@ proc generateCall(self: Compiler, fn: Name, args: seq[Expression], onStack: bool
|
||||||
# because of how stack semantics
|
# because of how stack semantics
|
||||||
# work. They'll be fixed at runtime
|
# work. They'll be fixed at runtime
|
||||||
self.expression(argument)
|
self.expression(argument)
|
||||||
|
var f = fn.valueType
|
||||||
|
while not f.isNil():
|
||||||
|
if f.isClosure:
|
||||||
|
for i in f.closureBounds.start..f.closureBounds.stop:
|
||||||
|
self.closedOver[i].count += 1
|
||||||
|
f = f.childFunc
|
||||||
# Creates a new call frame and jumps
|
# Creates a new call frame and jumps
|
||||||
# to the function's first instruction
|
# to the function's first instruction
|
||||||
# in the code
|
# in the code
|
||||||
|
@ -1100,7 +1116,9 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) =
|
||||||
codePos: self.chunk.code.len(),
|
codePos: self.chunk.code.len(),
|
||||||
isLet: node.isLet,
|
isLet: node.isLet,
|
||||||
isClosedOver: false,
|
isClosedOver: false,
|
||||||
line: node.token.line))
|
line: node.token.line,
|
||||||
|
belongsTo: self.currentFunction
|
||||||
|
))
|
||||||
if mutable:
|
if mutable:
|
||||||
self.names[^1].valueType.mutable = true
|
self.names[^1].valueType.mutable = true
|
||||||
# We emit a jump of 0 because this may become a
|
# We emit a jump of 0 because this may become a
|
||||||
|
@ -1163,7 +1181,9 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) =
|
||||||
isLet: false,
|
isLet: false,
|
||||||
isClosedOver: false,
|
isClosedOver: false,
|
||||||
line: argument.name.token.line,
|
line: argument.name.token.line,
|
||||||
isFunctionArgument: true)
|
isFunctionArgument: true,
|
||||||
|
belongsTo: fn
|
||||||
|
)
|
||||||
self.names.add(name)
|
self.names.add(name)
|
||||||
name.valueType = self.inferType(argument.valueType)
|
name.valueType = self.inferType(argument.valueType)
|
||||||
# If it's still nil, it's an error!
|
# If it's still nil, it's an error!
|
||||||
|
@ -1201,7 +1221,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
||||||
# align its semantics with the call stack. This makes closures work as expected and is
|
# align its semantics with the call stack. This makes closures work as expected and is
|
||||||
# not much slower than indexing our stack (since they're both dynamic arrays at runtime anyway)
|
# not much slower than indexing our stack (since they're both dynamic arrays at runtime anyway)
|
||||||
self.emitByte(LoadClosure)
|
self.emitByte(LoadClosure)
|
||||||
self.emitBytes(self.getClosurePos(s).toTriple())
|
self.emitBytes((self.getClosurePos(s)).toTriple())
|
||||||
|
|
||||||
|
|
||||||
proc assignment(self: Compiler, node: ASTNode) =
|
proc assignment(self: Compiler, node: ASTNode) =
|
||||||
|
@ -1242,7 +1262,7 @@ proc beginScope(self: Compiler) =
|
||||||
## Begins a new local scope by incrementing the current
|
## Begins a new local scope by incrementing the current
|
||||||
## scope's depth
|
## scope's depth
|
||||||
inc(self.scopeDepth)
|
inc(self.scopeDepth)
|
||||||
self.scopes.add(self.currentFunction)
|
self.scopes.add(self.currentFunction.valueType)
|
||||||
|
|
||||||
|
|
||||||
proc endScope(self: Compiler) =
|
proc endScope(self: Compiler) =
|
||||||
|
@ -1261,6 +1281,16 @@ proc endScope(self: Compiler) =
|
||||||
# We don't increase the pop count for these kinds of objects
|
# We don't increase the pop count for these kinds of objects
|
||||||
# because they're not stored the same way as regular variables
|
# because they're not stored the same way as regular variables
|
||||||
inc(popCount)
|
inc(popCount)
|
||||||
|
if name.isFunDecl and not name.valueType.childFunc.isNil() and name.valueType.childFunc.isClosure:
|
||||||
|
var i = 0
|
||||||
|
var closure: tuple[name: Name, count: int]
|
||||||
|
for y in name.valueType.childFunc.closureBounds.start..name.valueType.childFunc.closureBounds.stop:
|
||||||
|
closure = self.closedOver[y + i]
|
||||||
|
self.closedOver.delete(y + i)
|
||||||
|
for _ in 0..<closure.count:
|
||||||
|
self.emitByte(PopClosure)
|
||||||
|
self.emitBytes((y + i).toTriple())
|
||||||
|
inc(i)
|
||||||
if popCount > 1:
|
if popCount > 1:
|
||||||
# If we're popping less than 65535 variables, then
|
# If we're popping less than 65535 variables, then
|
||||||
# we can emit a PopN instruction. This is true for
|
# we can emit a PopN instruction. This is true for
|
||||||
|
@ -1379,8 +1409,8 @@ proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} =
|
||||||
result = funct
|
result = funct
|
||||||
self.generateCall(funct, argExpr, onStack)
|
self.generateCall(funct, argExpr, onStack)
|
||||||
if not self.checkCallIsPure(node.callee):
|
if not self.checkCallIsPure(node.callee):
|
||||||
if self.currentFunction.name != "":
|
if self.currentFunction.valueType.name != "":
|
||||||
self.error(&"cannot make sure that calls to '{self.currentFunction.name}' are side-effect free")
|
self.error(&"cannot make sure that calls to '{self.currentFunction.valueType.name}' are side-effect free")
|
||||||
else:
|
else:
|
||||||
self.error(&"cannot make sure that call is side-effect free")
|
self.error(&"cannot make sure that call is side-effect free")
|
||||||
|
|
||||||
|
@ -1451,7 +1481,7 @@ proc deferStmt(self: Compiler, node: DeferStmt) =
|
||||||
|
|
||||||
proc returnStmt(self: Compiler, node: ReturnStmt) =
|
proc returnStmt(self: Compiler, node: ReturnStmt) =
|
||||||
## Compiles return statements
|
## Compiles return statements
|
||||||
var expected = self.currentFunction.returnType
|
var expected = self.currentFunction.valueType.returnType
|
||||||
self.check(node.value, expected)
|
self.check(node.value, expected)
|
||||||
if not node.value.isNil():
|
if not node.value.isNil():
|
||||||
self.expression(node.value)
|
self.expression(node.value)
|
||||||
|
@ -1683,21 +1713,20 @@ proc dispatchPragmas(self: Compiler, node: ASTnode) =
|
||||||
self.compilerProcs[pragma.name.token.lexeme](self, pragma, node)
|
self.compilerProcs[pragma.name.token.lexeme](self, pragma, node)
|
||||||
|
|
||||||
|
|
||||||
proc fixGenericFunc(self: Compiler, name: Name, args: seq[Expression]): Type =
|
proc fixGenericFunc(self: Compiler, name: Name, args: seq[Expression]): Name =
|
||||||
## Specializes generic arguments in functions
|
## Specializes generic arguments in functions
|
||||||
var fn = name.valueType.deepCopy()
|
var fn = name.deepCopy()
|
||||||
result = fn
|
result = fn
|
||||||
var typ: Type
|
var typ: Type
|
||||||
for i in 0..args.high():
|
for i in 0..args.high():
|
||||||
if fn.args[i].kind.kind == Generic:
|
if fn.valueType.args[i].kind.kind == Generic:
|
||||||
typ = self.inferType(args[i])
|
typ = self.inferType(args[i])
|
||||||
fn.args[i].kind = typ
|
fn.valueType.args[i].kind = typ
|
||||||
self.resolve(fn.args[i].name).valueType = typ
|
self.resolve(fn.valueType.args[i].name).valueType = typ
|
||||||
if fn.args[i].kind.isNil():
|
if fn.valueType.args[i].kind.isNil():
|
||||||
self.error(&"cannot specialize generic function: argument {i + 1} has no type")
|
self.error(&"cannot specialize generic function: argument {i + 1} has no type")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc funDecl(self: Compiler, node: FunDecl, fn: Name = nil, args: seq[Expression] = @[]) =
|
proc funDecl(self: Compiler, node: FunDecl, fn: Name = nil, args: seq[Expression] = @[]) =
|
||||||
## Compiles function declarations
|
## Compiles function declarations
|
||||||
#[if not node.isNil():
|
#[if not node.isNil():
|
||||||
|
@ -1710,6 +1739,7 @@ proc funDecl(self: Compiler, node: FunDecl, fn: Name = nil, args: seq[Expression
|
||||||
self.dispatchPragmas(node)
|
self.dispatchPragmas(node)
|
||||||
var node = node
|
var node = node
|
||||||
var fn = if fn.isNil(): self.names[^(node.arguments.len() + 1)] else: fn
|
var fn = if fn.isNil(): self.names[^(node.arguments.len() + 1)] else: fn
|
||||||
|
var names = self.names[^(node.arguments.len())..^1]
|
||||||
if fn.valueType.isBuiltinFunction:
|
if fn.valueType.isBuiltinFunction:
|
||||||
# We take the arguments off of our name list
|
# We take the arguments off of our name list
|
||||||
# because they become temporaries on the stack.
|
# because they become temporaries on the stack.
|
||||||
|
@ -1727,13 +1757,20 @@ proc funDecl(self: Compiler, node: FunDecl, fn: Name = nil, args: seq[Expression
|
||||||
jmp = self.emitJump(JumpForwards)
|
jmp = self.emitJump(JumpForwards)
|
||||||
# Function's code starts after the jump
|
# Function's code starts after the jump
|
||||||
fn.codePos = self.chunk.code.len()
|
fn.codePos = self.chunk.code.len()
|
||||||
|
# We let our debugger know a function is starting
|
||||||
|
let start = self.chunk.code.high()
|
||||||
|
for name in names:
|
||||||
|
self.emitBytes([NoOp, NoOp, NoOp, NoOp])
|
||||||
|
name.codePos = self.chunk.code.len() - 4
|
||||||
# We store the current function
|
# We store the current function
|
||||||
self.currentFunction = fn.valueType
|
if not self.currentFunction.isNil():
|
||||||
|
self.currentFunction.valueType.childFunc = fn.valueType
|
||||||
|
self.currentFunction = fn
|
||||||
if node.isNil():
|
if node.isNil():
|
||||||
# We got called back with more specific type
|
# We got called back with more specific type
|
||||||
# arguments: time to fix them!
|
# arguments: time to fix them!
|
||||||
self.currentFunction = self.fixGenericFunc(fn, args)
|
self.currentFunction = self.fixGenericFunc(fn, args)
|
||||||
node = self.currentFunction.fun
|
node = self.currentFunction.valueType.fun
|
||||||
elif not node.body.isNil():
|
elif not node.body.isNil():
|
||||||
if BlockStmt(node.body).code.len() == 0:
|
if BlockStmt(node.body).code.len() == 0:
|
||||||
self.error("cannot declare function with empty body")
|
self.error("cannot declare function with empty body")
|
||||||
|
@ -1758,18 +1795,16 @@ proc funDecl(self: Compiler, node: FunDecl, fn: Name = nil, args: seq[Expression
|
||||||
# the try/finally block with the deferred
|
# the try/finally block with the deferred
|
||||||
# code
|
# code
|
||||||
var deferStart = self.deferred.len()
|
var deferStart = self.deferred.len()
|
||||||
# We let our debugger know a function is starting
|
|
||||||
let start = self.chunk.code.high()
|
|
||||||
self.beginScope()
|
self.beginScope()
|
||||||
for decl in BlockStmt(node.body).code:
|
for decl in BlockStmt(node.body).code:
|
||||||
self.declaration(decl)
|
self.declaration(decl)
|
||||||
let typ = self.currentFunction.returnType
|
let typ = self.currentFunction.valueType.returnType
|
||||||
var hasVal: bool = false
|
var hasVal: bool = false
|
||||||
case self.currentFunction.fun.kind:
|
case self.currentFunction.valueType.fun.kind:
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
hasVal = self.currentFunction.fun.hasExplicitReturn
|
hasVal = self.currentFunction.valueType.fun.hasExplicitReturn
|
||||||
of NodeKind.lambdaExpr:
|
of NodeKind.lambdaExpr:
|
||||||
hasVal = LambdaExpr(Declaration(self.currentFunction.fun)).hasExplicitReturn
|
hasVal = LambdaExpr(Declaration(self.currentFunction.valueType.fun)).hasExplicitReturn
|
||||||
else:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
if not hasVal and not typ.isNil():
|
if not hasVal and not typ.isNil():
|
||||||
|
|
|
@ -152,6 +152,8 @@ type
|
||||||
StoreVar, # Stores the value of b at position a in the stack
|
StoreVar, # Stores the value of b at position a in the stack
|
||||||
LoadClosure, # Pushes the object position x in the closure array onto the stack
|
LoadClosure, # Pushes the object position x in the closure array onto the stack
|
||||||
StoreClosure, # Stores the value of b at position a in the closure array
|
StoreClosure, # Stores the value of b at position a in the closure array
|
||||||
|
LiftArgument, # Closes over a function argument
|
||||||
|
PopClosure,
|
||||||
## Looping and jumping
|
## Looping and jumping
|
||||||
Jump, # Absolute, unconditional jump into the bytecode
|
Jump, # Absolute, unconditional jump into the bytecode
|
||||||
JumpForwards, # Relative, unconditional, positive jump in the bytecode
|
JumpForwards, # Relative, unconditional, positive jump in the bytecode
|
||||||
|
@ -253,7 +255,7 @@ const constantInstructions* = {LoadInt64, LoadUInt64,
|
||||||
|
|
||||||
# Stack triple instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
# Stack triple instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
||||||
# of 24 bit integers
|
# of 24 bit integers
|
||||||
const stackTripleInstructions* = {StoreVar, LoadVar, LoadCLosure, }
|
const stackTripleInstructions* = {StoreVar, LoadVar, LoadCLosure, LiftArgument, PopClosure}
|
||||||
|
|
||||||
# Stack double instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
# Stack double instructions operate on the stack at arbitrary offsets and pop arguments off of it in the form
|
||||||
# of 16 bit integers
|
# of 16 bit integers
|
||||||
|
@ -265,9 +267,6 @@ const argumentDoubleInstructions* = {PopN, }
|
||||||
# Argument double argument instructions take hardcoded arguments as 24 bit integers
|
# Argument double argument instructions take hardcoded arguments as 24 bit integers
|
||||||
const argumentTripleInstructions* = {StoreClosure}
|
const argumentTripleInstructions* = {StoreClosure}
|
||||||
|
|
||||||
# Instructions that call functions
|
|
||||||
const callInstructions* = {Call, }
|
|
||||||
|
|
||||||
# Jump instructions jump at relative or absolute bytecode offsets
|
# Jump instructions jump at relative or absolute bytecode offsets
|
||||||
const jumpInstructions* = {Jump, JumpIfFalse, JumpIfFalsePop,
|
const jumpInstructions* = {Jump, JumpIfFalse, JumpIfFalsePop,
|
||||||
JumpForwards, JumpBackwards,
|
JumpForwards, JumpBackwards,
|
||||||
|
|
|
@ -99,7 +99,6 @@ proc simpleInstruction(self: Debugger, instruction: OpCode) =
|
||||||
self.checkFrameEnd(self.current)
|
self.checkFrameEnd(self.current)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc stackTripleInstruction(self: Debugger, instruction: OpCode) =
|
proc stackTripleInstruction(self: Debugger, instruction: OpCode) =
|
||||||
## Debugs instructions that operate on a single value on the stack using a 24-bit operand
|
## Debugs instructions that operate on a single value on the stack using a 24-bit operand
|
||||||
var slot = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
var slot = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
||||||
|
@ -136,9 +135,10 @@ proc argumentTripleInstruction(self: Debugger, instruction: OpCode) =
|
||||||
proc callInstruction(self: Debugger, instruction: OpCode) =
|
proc callInstruction(self: Debugger, instruction: OpCode) =
|
||||||
## Debugs function calls
|
## Debugs function calls
|
||||||
var size = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
var size = [self.chunk.code[self.current + 1], self.chunk.code[self.current + 2], self.chunk.code[self.current + 3]].fromTriple()
|
||||||
|
self.current += 3
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
styledEcho fgGreen, &", creates frame of size ", fgYellow, $(size + 2)
|
styledEcho fgGreen, &", creates frame of size ", fgYellow, $(size + 2), fgGreen
|
||||||
self.current += 4
|
self.current += 1
|
||||||
|
|
||||||
|
|
||||||
proc functionInstruction(self: Debugger, instruction: OpCode) =
|
proc functionInstruction(self: Debugger, instruction: OpCode) =
|
||||||
|
@ -183,6 +183,8 @@ proc jumpInstruction(self: Debugger, instruction: OpCode) =
|
||||||
stdout.styledWrite(fgYellow, $jump)
|
stdout.styledWrite(fgYellow, $jump)
|
||||||
nl()
|
nl()
|
||||||
self.current += 4
|
self.current += 4
|
||||||
|
while self.chunk.code[self.current] == NoOp.uint8:
|
||||||
|
inc(self.current)
|
||||||
for i in countup(orig, self.current + 1):
|
for i in countup(orig, self.current + 1):
|
||||||
self.checkFrameStart(i)
|
self.checkFrameStart(i)
|
||||||
|
|
||||||
|
@ -207,7 +209,7 @@ proc disassembleInstruction*(self: Debugger) =
|
||||||
self.argumentDoubleInstruction(opcode)
|
self.argumentDoubleInstruction(opcode)
|
||||||
of argumentTripleInstructions:
|
of argumentTripleInstructions:
|
||||||
self.argumentTripleInstruction(opcode)
|
self.argumentTripleInstruction(opcode)
|
||||||
of callInstructions:
|
of Call:
|
||||||
self.callInstruction(opcode)
|
self.callInstruction(opcode)
|
||||||
of jumpInstructions:
|
of jumpInstructions:
|
||||||
self.jumpInstruction(opcode)
|
self.jumpInstruction(opcode)
|
||||||
|
|
|
@ -3,14 +3,15 @@ import std;
|
||||||
|
|
||||||
|
|
||||||
fn makeClosure(n: int): fn: int {
|
fn makeClosure(n: int): fn: int {
|
||||||
let n = n; # Workaround
|
|
||||||
fn inner: int {
|
fn inner: int {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
return inner;
|
return inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var closed = makeClosure(1)();
|
||||||
var closure = makeClosure(1)();
|
print(closed); # 1
|
||||||
print(closure); # 1
|
print(makeClosure(2)()); # 2
|
||||||
print(makeClosure(2)()); # 2
|
var closure = makeClosure(3);
|
||||||
|
print(closure()); # 3
|
||||||
|
print(closure()); # 3
|
||||||
|
|
Loading…
Reference in New Issue