diff --git a/src/backend/vm.nim b/src/backend/vm.nim index 213026f..59d1b86 100644 --- a/src/backend/vm.nim +++ b/src/backend/vm.nim @@ -208,19 +208,16 @@ proc markRoots(self: var PeonVM): HashSet[ptr HeapObject] = # Unlike what Bob does in his book, we keep track # of objects another way, mainly due to the difference # of our respective designs. Specifically, our VM only - # handles a single type (uint64), while Lox has a stack - # of heap-allocated structs (which is convenient, but slow). - # The previous implementation would just store all pointers - # allocated by us in a hash set and then check if any source - # of roots contained any of the integer values that it was - # keeping track of, but this meant that if a primitive object's - # value happened to collide with an active pointer the GC would - # mistakenly assume the object was reachable, potentially leading - # to a nasty memory leak. The current implementation uses pointer - # tagging: we know that modern CPUs never use bit 63 in addresses, - # so if it set we know it cannot be a pointer, and if it is set we - # just need to check if it's in our list of active addresses or not. - # This should resolve the potential memory leak (hopefully) + # handles a single type (uint64) while Lox stores all objects + # in heap-allocated structs (which is convenient, but slow). + # What we do is store the pointers to the objects we allocated in + # a hash set and then, at collection time, do a set difference + # between the reachable objects and the whole set and discard + # whatever is left; Unfortunately, this means that if a primitive + # object's value happens to collide with an active pointer the GC + # will mistakenly assume the object to be reachable, potentially + # leading to a nasty memory leak. Let's just hope a 48+ bit address + # space makes this occurrence rare enough not to be a problem var result = initHashSet[uint64](self.gc.pointers.len()) for obj in self.calls: if obj in self.gc.pointers: @@ -691,7 +688,7 @@ proc dispatch*(self: var PeonVM) = while true: {.computedgoto.} # https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma when debugVM: - if self.ip in self.breakpoints or self.breakpoints.len() == 0 or self.debugNext: + if self.ip in self.breakpoints or self.debugNext: self.debug() instruction = OpCode(self.readByte()) case instruction: diff --git a/src/frontend/compiler/compiler.nim b/src/frontend/compiler/compiler.nim index 3f082f5..c80e994 100644 --- a/src/frontend/compiler/compiler.nim +++ b/src/frontend/compiler/compiler.nim @@ -341,11 +341,9 @@ proc step*(self: Compiler): ASTNode {.inline.} = # and can be reused across multiple compilation backends proc resolve*(self: Compiler, name: string): Name = - ## Traverses all existing namespaces and returns - ## the first object with the given name. Returns - ## nil when the name can't be found. Note that - ## when a type or function declaration is first - ## resolved, it is also compiled on-the-fly + ## Traverses all existing namespaces in reverse order + ## and returns the first object with the given name. + ## Returns nil when the name can't be found for obj in reversed(self.names): if obj.ident.token.lexeme == name: if obj.owner.path != self.currentModule.path: @@ -361,6 +359,7 @@ proc resolve*(self: Compiler, name: string): Name = # module and said module has explicitly # exported it to us: we can use it result = obj + result.resolved = true break # If the name is public but not exported in # its owner module, then we act as if it's @@ -370,6 +369,7 @@ proc resolve*(self: Compiler, name: string): Name = # might not want to also have access to C's and D's # names as they might clash with its own stuff) continue + # We own this name, so we can definitely access it result = obj result.resolved = true break diff --git a/src/frontend/compiler/targets/bytecode/opcodes.nim b/src/frontend/compiler/targets/bytecode/opcodes.nim index 42855ce..53e564e 100644 --- a/src/frontend/compiler/targets/bytecode/opcodes.nim +++ b/src/frontend/compiler/targets/bytecode/opcodes.nim @@ -189,7 +189,7 @@ type SysClock64, # Pushes the output of a monotonic clock on the stack LoadTOS, # Pushes the top of the call stack onto the operand stack DupTop, # Duplicates the top of the operand stack onto the operand stack - ReplExit, # Exits the VM immediately, leaving its state intact. Used in the REPL + ReplExit, # Exits the VM immediately, leaving its state intact. Used in the REPL LoadGlobal # Loads a global variable diff --git a/src/frontend/compiler/targets/bytecode/target.nim b/src/frontend/compiler/targets/bytecode/target.nim index cbc39b5..b984658 100644 --- a/src/frontend/compiler/targets/bytecode/target.nim +++ b/src/frontend/compiler/targets/bytecode/target.nim @@ -95,7 +95,7 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines mode: CompileMode = Debug): Chunk proc statement(self: BytecodeCompiler, node: Statement) proc declaration(self: BytecodeCompiler, node: Declaration) -proc varDecl(self: BytecodeCompiler, node: VarDecl, hoist: bool = false) +proc varDecl(self: BytecodeCompiler, node: VarDecl) proc specialize(self: BytecodeCompiler, typ: Type, args: seq[Expression]): Type {.discardable.} proc patchReturnAddress(self: BytecodeCompiler, pos: int) proc handleMagicPragma(self: BytecodeCompiler, pragma: Pragma, name: Name) @@ -1864,10 +1864,8 @@ proc statement(self: BytecodeCompiler, node: Statement) = self.expression(Expression(node)) -proc varDecl(self: BytecodeCompiler, node: VarDecl, hoist: bool = false) = +proc varDecl(self: BytecodeCompiler, node: VarDecl) = ## Compiles variable declarations - if node.name.depth == 0 and not hoist: - return var typ: Type # Our parser guarantees that the variable declaration # will have a type declaration or a value (or both) @@ -2078,9 +2076,6 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines self.jumps = @[] let pos = self.beginProgram() let idx = self.stackIndex - for node in ast: - if node.kind == varDecl and VarDecl(node).name.depth == 0: - self.varDecl(VarDecl(node), hoist=true) self.stackIndex = idx while not self.done(): self.declaration(Declaration(self.step())) diff --git a/src/main.nim b/src/main.nim index 075fb51..e15f2a7 100644 --- a/src/main.nim +++ b/src/main.nim @@ -51,7 +51,7 @@ proc getLineEditor: LineEditor = result.bindHistory(history) -proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug) = +proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug, breakpoints: seq[uint64] = @[]) = styledEcho fgMagenta, "Welcome into the peon REPL!" var keep = true @@ -136,7 +136,7 @@ proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: Comp else: styledEcho fgRed, "Corrupted" if not first: - vm.run(serialized.chunk, repl=true) + vm.run(serialized.chunk, repl=true, breakpoints=breakpoints) first = true else: vm.resume(serialized.chunk) @@ -407,7 +407,9 @@ when isMainModule: else: echo "usage: peon [options] [filename.pn]" quit() + if breaks.len() == 0 and debugVM: + breaks.add(0) if file == "": - repl(warnings, mismatches, mode) + repl(warnings, mismatches, mode, breaks) else: runFile(file, fromString, dump, breaks, dis, warnings, mismatches, mode, run, backend, output)