Initial work on closures (again?)

This commit is contained in:
Mattia Giambirtone 2022-06-13 17:28:05 +02:00
parent e3ab2fbdb6
commit d241333047
5 changed files with 33 additions and 19 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)();