diff --git a/src/backend/vm.nim b/src/backend/vm.nim index 4656ad6..585d57c 100644 --- a/src/backend/vm.nim +++ b/src/backend/vm.nim @@ -68,7 +68,8 @@ type ## this system and is not handled ## manually by the VM bytesAllocated: tuple[total, current: int] - cycles: int + when debugGC or debugAlloc: + cycles: int nextGC: int pointers: HashSet[uint64] PeonVM* = object @@ -93,9 +94,10 @@ type frames: seq[uint64] # Stores the bottom of stack frames results: seq[uint64] # Stores function return values gc: PeonGC # A reference to the VM's garbage collector - breakpoints: seq[uint64] # Breakpoints where we call our debugger - debugNext: bool # Whether to debug the next instruction - lastDebugCommand: string # The last debugging command input by the user + when debugVM: + breakpoints: seq[uint64] # Breakpoints where we call our debugger + debugNext: bool # Whether to debug the next instruction + lastDebugCommand: string # The last debugging command input by the user # Implementation of peon's memory manager @@ -105,7 +107,8 @@ proc newPeonGC*: PeonGC = ## garbage collector result.bytesAllocated = (0, 0) result.nextGC = FirstGC - result.cycles = 0 + when debugGC or debugAlloc: + result.cycles = 0 proc collect*(self: var PeonVM) @@ -214,6 +217,16 @@ proc markRoots(self: var PeonVM): HashSet[ptr HeapObject] = # 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 + # handles a single type (uint64), while Lox has a stack + # of heap-allocated structs (which is convenient, but slow). + # What we do instead is 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 we're keeping track of. Note + # that 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). Hopefully, in a 64-bit address space, this + # occurrence is rare enough for us to ignore var result = initHashSet[uint64](self.gc.pointers.len()) for obj in self.calls: if obj in self.gc.pointers: @@ -285,7 +298,6 @@ proc sweep(self: var PeonVM) = ## during the mark phase. when debugGC: echo "DEBUG - GC: Beginning sweeping phase" - when debugGC: var count = 0 var current: ptr HeapObject var freed: HashSet[uint64] @@ -1050,10 +1062,11 @@ proc run*(self: var PeonVM, chunk: Chunk, breakpoints: seq[uint64] = @[], repl: self.frames = @[] self.calls = @[] self.operands = @[] - self.breakpoints = breakpoints self.results = @[] self.ip = 0 - self.lastDebugCommand = "" + when debugVM: + self.breakpoints = breakpoints + self.lastDebugCommand = "" try: self.dispatch() except NilAccessDefect: diff --git a/src/frontend/compiler/newcompiler.nim b/src/frontend/compiler/newcompiler.nim index cc6b19c..430f635 100644 --- a/src/frontend/compiler/newcompiler.nim +++ b/src/frontend/compiler/newcompiler.nim @@ -124,11 +124,11 @@ type of Reference: # A managed reference nullable*: bool # Is null a valid value for this type? (false by default) - value*: Type # The type the reference points to + value*: TypedNode # The type the reference points to of Pointer: # An unmanaged reference. Much # like a raw pointer in C - data*: Type # The type we point to + data*: TypedNode # The type we point to of TypeDecl: # A user-defined type fields*: seq[TypedArgument] # List of fields in the object. May be empty @@ -317,17 +317,17 @@ proc step*(self: Compiler): ASTNode {.inline.} = # Some forward declarations proc compareUnions*(self: Compiler, a, b: seq[tuple[match: bool, kind: Type]]): bool -proc expression*(self: Compiler, node: Expression, compile: bool = true): Type {.discardable.} = nil -proc identifier*(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true, strict: bool = true): Type {.discardable.} = nil -proc call*(self: Compiler, node: CallExpr, compile: bool = true): Type {.discardable.} = nil -proc getItemExpr*(self: Compiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.} = nil -proc unary*(self: Compiler, node: UnaryExpr, compile: bool = true): Type {.discardable.} = nil -proc binary*(self: Compiler, node: BinaryExpr, compile: bool = true): Type {.discardable.} = nil -proc lambdaExpr*(self: Compiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} = nil -proc literal*(self: Compiler, node: ASTNode, compile: bool = true): Type {.discardable.} = nil -proc infer*(self: Compiler, node: LiteralExpr): Type -proc infer*(self: Compiler, node: Expression): Type -proc inferOrError*(self: Compiler, node: Expression): Type +proc expression*(self: Compiler, node: Expression, compile: bool = true): TypedNode {.discardable.} = nil +proc identifier*(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool = true, strict: bool = true): TypedNode {.discardable.} = nil +proc call*(self: Compiler, node: CallExpr, compile: bool = true): TypedNode {.discardable.} = nil +proc getItemExpr*(self: Compiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): TypedNode {.discardable.} = nil +proc unary*(self: Compiler, node: UnaryExpr, compile: bool = true): TypedNode {.discardable.} = nil +proc binary*(self: Compiler, node: BinaryExpr, compile: bool = true): TypedNode {.discardable.} = nil +proc lambdaExpr*(self: Compiler, node: LambdaExpr, compile: bool = true): TypedNode {.discardable.} = nil +proc literal*(self: Compiler, node: ASTNode, compile: bool = true): TypedNode {.discardable.} = nil +proc infer*(self: Compiler, node: LiteralExpr): TypedNode +proc infer*(self: Compiler, node: Expression): TypedNode +proc inferOrError*(self: Compiler, node: Expression): TypedNode proc findByName*(self: Compiler, name: string): seq[Name] proc findInModule*(self: Compiler, name: string, module: Name): seq[Name] proc findByType*(self: Compiler, name: string, kind: Type): seq[Name] @@ -420,7 +420,7 @@ proc compare*(self: Compiler, a, b: Type): bool = # a and b are of either of the two # types in this branch, so we just need # to compare their values - return self.compare(a.value, b.value) + return self.compare(a.value.value, b.value.value) of Function: # Functions are a bit trickier to compare if a.arguments.len() != b.arguments.len(): @@ -569,7 +569,7 @@ proc toIntrinsic*(name: string): Type = return Type(kind: String) -proc infer*(self: Compiler, node: LiteralExpr): Type = +proc infer*(self: Compiler, node: LiteralExpr): TypedNode = ## Infers the type of a given literal expression if node.isNil(): return nil @@ -577,32 +577,32 @@ proc infer*(self: Compiler, node: LiteralExpr): Type = of intExpr, binExpr, octExpr, hexExpr: let size = node.token.lexeme.split("'") if size.len() == 1: - return Type(kind: Int64) + return TypedNode(node: node, value: Type(kind: Int64)) let typ = size[1].toIntrinsic() if not self.compare(typ, nil): - return typ + return TypedNode(node: node, value: typ) else: self.error(&"invalid type specifier '{size[1]}' for int", node) of floatExpr: let size = node.token.lexeme.split("'") if size.len() == 1: - return Type(kind: Float64) + return TypedNode(node: node, value: Type(kind: Float64)) let typ = size[1].toIntrinsic() if not typ.isNil(): - return typ + return TypedNode(node: node, value: typ) else: self.error(&"invalid type specifier '{size[1]}' for float", node) of trueExpr: - return Type(kind: Bool) + return TypedNode(node: node, value: Type(kind: Bool)) of falseExpr: - return Type(kind: Bool) + return TypedNode(node: node, value: Type(kind: Bool)) of strExpr: - return Type(kind: String) + return TypedNode(node: node, value: Type(kind: String)) else: discard # Unreachable -proc infer*(self: Compiler, node: Expression): Type = +proc infer*(self: Compiler, node: Expression): TypedNode = ## Infers the type of a given expression and ## returns it if node.isNil(): @@ -621,9 +621,9 @@ proc infer*(self: Compiler, node: Expression): Type = of NodeKind.callExpr: result = self.call(CallExpr(node), compile=false) of NodeKind.refExpr: - result = Type(kind: Reference, value: self.infer(Ref(node).value)) + result = TypedNode(node: node, value: Type(kind: Reference, value: self.infer(Ref(node).value))) of NodeKind.ptrExpr: - result = Type(kind: Pointer, data: self.infer(Ptr(node).value)) + result = TypedNode(node: node, value: Type(kind: Pointer, data: self.infer(Ptr(node).value))) of NodeKind.groupingExpr: result = self.infer(GroupingExpr(node).expression) of NodeKind.getItemExpr: @@ -634,7 +634,7 @@ proc infer*(self: Compiler, node: Expression): Type = discard # TODO -proc inferOrError*(self: Compiler, node: Expression): Type = +proc inferOrError*(self: Compiler, node: Expression): TypedNode = ## Attempts to infer the type of ## the given expression and raises an ## error if it fails @@ -648,16 +648,16 @@ proc stringify*(self: Compiler, typ: Type): string = ## type object if typ.isNil(): return "nil" - case typ.value.kind: + case typ.kind: of Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float32, Float64, Char, Byte, String, Nil, TypeKind.Nan, Bool, TypeKind.Inf, Auto: - result &= ($typ.value.kind).toLowerAscii() + result &= ($typ.kind).toLowerAscii() of Pointer: - result &= &"ptr {self.stringify(typ.value)}" + result &= &"ptr {self.stringify(typ)}" of Reference: - result &= &"ref {self.stringify(typ.value)}" + result &= &"ref {self.stringify(typ)}" of Any: return "any" of Union: @@ -770,9 +770,9 @@ proc check*(self: Compiler, term: Expression, kind: Type) {.inline.} = ## Raises an error if appropriate and returns ## otherwise let k = self.inferOrError(term) - if not self.compare(k, kind): + if not self.compare(k.value, kind): self.error(&"expecting value of type {self.stringify(kind)}, got {self.stringify(k)}", term) - elif k.kind == Any and kind.kind != Any: + elif k.value.kind == Any and kind.kind != Any: self.error(&"any is not a valid type in this context") @@ -857,7 +857,7 @@ proc unpackGenerics*(self: Compiler, condition: Expression, list: var seq[tuple[ ## Recursively unpacks a type constraint in a generic type case condition.kind: of identExpr: - list.add((accept, self.inferOrError(condition))) + list.add((accept, self.inferOrError(condition).value)) if list[^1].kind.kind == Auto: self.error("automatic types cannot be used within generics", condition) of binaryExpr: @@ -883,7 +883,7 @@ proc unpackUnion*(self: Compiler, condition: Expression, list: var seq[tuple[mat ## Recursively unpacks a type union case condition.kind: of identExpr: - list.add((accept, self.inferOrError(condition))) + list.add((accept, self.inferOrError(condition).value)) of binaryExpr: let condition = BinaryExpr(condition) case condition.operator.lexeme: @@ -966,13 +966,13 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} = n.isGeneric = true var typ: Type for argument in node.arguments: - typ = self.infer(argument.valueType) + typ = self.infer(argument.valueType).value if not typ.isNil() and typ.kind == Auto: n.obj.value.isAuto = true if n.isGeneric: self.error("automatic types cannot be used within generics", argument.valueType) break - typ = self.infer(node.returnType) + typ = self.infer(node.returnType).value if not typ.isNil() and typ.kind == Auto: n.obj.value.isAuto = true if n.isGeneric: @@ -1023,7 +1023,7 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} = else: case node.value.kind: of identExpr: - n.obj.value = self.inferOrError(node.value) + n.obj.value = self.inferOrError(node.value).value of binaryExpr: # Type union n.obj.value = Type(kind: Union, types: @[])