Initial work on closures (again?)
This commit is contained in:
parent
e3ab2fbdb6
commit
d241333047
|
@ -416,9 +416,11 @@ proc dispatch*(self: PeonVM) =
|
||||||
# variable
|
# variable
|
||||||
let idx = self.readLong().int
|
let idx = self.readLong().int
|
||||||
if idx > self.closedOver.high():
|
if idx > self.closedOver.high():
|
||||||
self.closedOver.add(self.pop())
|
# Note: we *peek* the stack, but we
|
||||||
|
# don't pop!
|
||||||
|
self.closedOver.add(self.peek())
|
||||||
else:
|
else:
|
||||||
self.closedOver[idx] = self.pop()
|
self.closedOver[idx] = self.peek()
|
||||||
of LoadClosure:
|
of LoadClosure:
|
||||||
# Loads a closed-over variable onto the
|
# Loads a closed-over variable onto the
|
||||||
# stack
|
# stack
|
||||||
|
|
|
@ -27,7 +27,7 @@ when len(PEON_COMMIT_HASH) != 40:
|
||||||
const PEON_BRANCH* = "master"
|
const PEON_BRANCH* = "master"
|
||||||
when len(PEON_BRANCH) > 255:
|
when len(PEON_BRANCH) > 255:
|
||||||
{.fatal: "The git branch name's length must be less than or equal to 255 characters".}
|
{.fatal: "The git branch name's length must be less than or equal to 255 characters".}
|
||||||
const DEBUG_TRACE_VM* = true # Traces VM execution
|
const DEBUG_TRACE_VM* = false # Traces VM execution
|
||||||
const DEBUG_TRACE_GC* = false # Traces the garbage collector (TODO)
|
const DEBUG_TRACE_GC* = false # Traces the garbage collector (TODO)
|
||||||
const DEBUG_TRACE_ALLOCATION* = false # Traces memory allocation/deallocation
|
const DEBUG_TRACE_ALLOCATION* = false # Traces memory allocation/deallocation
|
||||||
const DEBUG_TRACE_COMPILER* = false # Traces the compiler
|
const DEBUG_TRACE_COMPILER* = false # Traces the compiler
|
||||||
|
|
|
@ -1499,8 +1499,11 @@ proc varDecl(self: Compiler, node: VarDecl) =
|
||||||
let expected = self.inferType(node.valueType)
|
let expected = self.inferType(node.valueType)
|
||||||
let actual = self.inferType(node.value)
|
let actual = self.inferType(node.value)
|
||||||
if expected.isNil() and actual.isNil():
|
if expected.isNil() and actual.isNil():
|
||||||
if node.value.kind == identExpr:
|
if node.value.kind == identExpr or node.value.kind == callExpr and CallExpr(node.value).callee.kind == identExpr:
|
||||||
self.error(&"reference to undeclared identifier '{node.value.token.lexeme}'")
|
var name = node.value.token.lexeme
|
||||||
|
if node.value.kind == callExpr:
|
||||||
|
name = CallExpr(node.value).callee.token.lexeme
|
||||||
|
self.error(&"reference to undeclared identifier '{name}'")
|
||||||
self.error(&"'{node.name.token.lexeme}' has no type")
|
self.error(&"'{node.name.token.lexeme}' has no type")
|
||||||
elif not expected.isNil() and expected.kind == Mutable: # I mean, variables *are* already mutable (some of them anyway)
|
elif not expected.isNil() and expected.kind == Mutable: # I mean, variables *are* already mutable (some of them anyway)
|
||||||
self.error(&"invalid type '{self.typeToStr(expected)}' for var")
|
self.error(&"invalid type '{self.typeToStr(expected)}' for var")
|
||||||
|
@ -1510,35 +1513,42 @@ proc varDecl(self: Compiler, node: VarDecl) =
|
||||||
self.expression(node.value)
|
self.expression(node.value)
|
||||||
self.declareName(node, mutable=node.token.kind == Var)
|
self.declareName(node, mutable=node.token.kind == Var)
|
||||||
self.emitByte(StoreVar)
|
self.emitByte(StoreVar)
|
||||||
self.emitBytes((self.names.high() + 1).toTriple())
|
self.emitBytes(self.names.len().toTriple())
|
||||||
|
|
||||||
|
|
||||||
proc typeDecl(self: Compiler, node: TypeDecl) =
|
proc typeDecl(self: Compiler, node: TypeDecl) =
|
||||||
## Compiles type declarations
|
## Compiles type declarations
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
|
||||||
proc funDecl(self: Compiler, node: FunDecl) =
|
proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
## Compiles function declarations
|
## Compiles function declarations
|
||||||
# A function's code is just compiled linearly
|
|
||||||
# and then jumped over
|
|
||||||
var function = self.currentFunction
|
var function = self.currentFunction
|
||||||
self.declareName(node)
|
self.declareName(node)
|
||||||
self.frames.add(self.names.high())
|
self.frames.add(self.names.high())
|
||||||
let fn = self.names[^(node.arguments.len() + 1)]
|
let fn = self.names[^(node.arguments.len() + 1)]
|
||||||
|
# A function's code is just compiled linearly
|
||||||
|
# and then jumped over
|
||||||
let jmp = self.emitJump(JumpForwards)
|
let jmp = self.emitJump(JumpForwards)
|
||||||
|
# Function's code starts after the jump
|
||||||
fn.codePos = self.chunk.code.len()
|
fn.codePos = self.chunk.code.len()
|
||||||
for argument in node.arguments:
|
for argument in node.arguments:
|
||||||
|
# Pops off the operand stack onto the
|
||||||
|
# call stack
|
||||||
self.emitByte(LoadArgument)
|
self.emitByte(LoadArgument)
|
||||||
if not node.returnType.isNil() and self.inferType(node.returnType).isNil():
|
if not node.returnType.isNil() and self.inferType(node.returnType).isNil():
|
||||||
|
# Are we returning a generic type?
|
||||||
var isGeneric = false
|
var isGeneric = false
|
||||||
if node.returnType.kind == identExpr:
|
if node.returnType.kind == identExpr:
|
||||||
let name = IdentExpr(node.returnType)
|
let name = IdentExpr(node.returnType)
|
||||||
for g in node.generics:
|
for g in node.generics:
|
||||||
if name == g.name:
|
if name == g.name:
|
||||||
|
# Yep!
|
||||||
isGeneric = true
|
isGeneric = true
|
||||||
break
|
break
|
||||||
if not isGeneric:
|
if not isGeneric:
|
||||||
|
# Nope
|
||||||
self.error(&"cannot infer the type of '{node.returnType.token.lexeme}'")
|
self.error(&"cannot infer the type of '{node.returnType.token.lexeme}'")
|
||||||
# TODO: Forward declarations
|
# TODO: Forward declarations
|
||||||
if not node.body.isNil():
|
if not node.body.isNil():
|
||||||
|
@ -1589,6 +1599,7 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
self.endFunctionBeforeReturn()
|
self.endFunctionBeforeReturn()
|
||||||
hasVal = hasVal and not typ.returnType.isNil()
|
hasVal = hasVal and not typ.returnType.isNil()
|
||||||
self.endScope(deleteNames=true, fromFunc=true)
|
self.endScope(deleteNames=true, fromFunc=true)
|
||||||
|
# Terminates the function's context
|
||||||
self.emitByte(OpCode.Return)
|
self.emitByte(OpCode.Return)
|
||||||
if hasVal:
|
if hasVal:
|
||||||
self.emitByte(1)
|
self.emitByte(1)
|
||||||
|
|
|
@ -125,10 +125,10 @@ type
|
||||||
## Misc
|
## Misc
|
||||||
Assert, # Raises an AssertionFailed exception if x is false
|
Assert, # Raises an AssertionFailed exception if x is false
|
||||||
NoOp, # Just a no-op
|
NoOp, # Just a no-op
|
||||||
LoadArgument,
|
LoadArgument, # The caller uses this to pop off the argument from the operand stack onto the call stack
|
||||||
LoadFunctionObj,
|
LoadFunctionObj, # Creates and pushes a function object onto the stack with ip X
|
||||||
PopC,
|
PopC, # Pop off the call stack onto the operand stack
|
||||||
PushC
|
PushC # Pop off the operand stack onto the call stack
|
||||||
|
|
||||||
|
|
||||||
# We group instructions by their operation/operand types for easier handling when debugging
|
# We group instructions by their operation/operand types for easier handling when debugging
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
fn outer {
|
fn makeClosure(n: int): fn: int {
|
||||||
var x = 5;
|
var x = n;
|
||||||
fn inner {
|
fn inner: int {
|
||||||
var y = x;
|
return x;
|
||||||
}
|
}
|
||||||
inner();
|
return inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
outer();
|
|
||||||
|
makeClosure(1)();
|
||||||
|
|
Loading…
Reference in New Issue