Merge
This commit is contained in:
parent
7bae3ad249
commit
20da594116
|
@ -68,7 +68,8 @@ type
|
||||||
## this system and is not handled
|
## this system and is not handled
|
||||||
## manually by the VM
|
## manually by the VM
|
||||||
bytesAllocated: tuple[total, current: int]
|
bytesAllocated: tuple[total, current: int]
|
||||||
cycles: int
|
when debugGC or debugAlloc:
|
||||||
|
cycles: int
|
||||||
nextGC: int
|
nextGC: int
|
||||||
pointers: HashSet[uint64]
|
pointers: HashSet[uint64]
|
||||||
PeonVM* = object
|
PeonVM* = object
|
||||||
|
@ -93,9 +94,10 @@ type
|
||||||
frames: seq[uint64] # Stores the bottom of stack frames
|
frames: seq[uint64] # Stores the bottom of stack frames
|
||||||
results: seq[uint64] # Stores function return values
|
results: seq[uint64] # Stores function return values
|
||||||
gc: PeonGC # A reference to the VM's garbage collector
|
gc: PeonGC # A reference to the VM's garbage collector
|
||||||
breakpoints: seq[uint64] # Breakpoints where we call our debugger
|
when debugVM:
|
||||||
debugNext: bool # Whether to debug the next instruction
|
breakpoints: seq[uint64] # Breakpoints where we call our debugger
|
||||||
lastDebugCommand: string # The last debugging command input by the user
|
debugNext: bool # Whether to debug the next instruction
|
||||||
|
lastDebugCommand: string # The last debugging command input by the user
|
||||||
|
|
||||||
|
|
||||||
# Implementation of peon's memory manager
|
# Implementation of peon's memory manager
|
||||||
|
@ -105,7 +107,8 @@ proc newPeonGC*: PeonGC =
|
||||||
## garbage collector
|
## garbage collector
|
||||||
result.bytesAllocated = (0, 0)
|
result.bytesAllocated = (0, 0)
|
||||||
result.nextGC = FirstGC
|
result.nextGC = FirstGC
|
||||||
result.cycles = 0
|
when debugGC or debugAlloc:
|
||||||
|
result.cycles = 0
|
||||||
|
|
||||||
|
|
||||||
proc collect*(self: var PeonVM)
|
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
|
# will mistakenly assume the object to be reachable, potentially
|
||||||
# leading to a nasty memory leak. Let's just hope a 48+ bit address
|
# 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
|
# 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())
|
var result = initHashSet[uint64](self.gc.pointers.len())
|
||||||
for obj in self.calls:
|
for obj in self.calls:
|
||||||
if obj in self.gc.pointers:
|
if obj in self.gc.pointers:
|
||||||
|
@ -285,7 +298,6 @@ proc sweep(self: var PeonVM) =
|
||||||
## during the mark phase.
|
## during the mark phase.
|
||||||
when debugGC:
|
when debugGC:
|
||||||
echo "DEBUG - GC: Beginning sweeping phase"
|
echo "DEBUG - GC: Beginning sweeping phase"
|
||||||
when debugGC:
|
|
||||||
var count = 0
|
var count = 0
|
||||||
var current: ptr HeapObject
|
var current: ptr HeapObject
|
||||||
var freed: HashSet[uint64]
|
var freed: HashSet[uint64]
|
||||||
|
@ -1050,10 +1062,11 @@ proc run*(self: var PeonVM, chunk: Chunk, breakpoints: seq[uint64] = @[], repl:
|
||||||
self.frames = @[]
|
self.frames = @[]
|
||||||
self.calls = @[]
|
self.calls = @[]
|
||||||
self.operands = @[]
|
self.operands = @[]
|
||||||
self.breakpoints = breakpoints
|
|
||||||
self.results = @[]
|
self.results = @[]
|
||||||
self.ip = 0
|
self.ip = 0
|
||||||
self.lastDebugCommand = ""
|
when debugVM:
|
||||||
|
self.breakpoints = breakpoints
|
||||||
|
self.lastDebugCommand = ""
|
||||||
try:
|
try:
|
||||||
self.dispatch()
|
self.dispatch()
|
||||||
except NilAccessDefect:
|
except NilAccessDefect:
|
||||||
|
|
|
@ -124,11 +124,11 @@ type
|
||||||
of Reference:
|
of Reference:
|
||||||
# A managed reference
|
# A managed reference
|
||||||
nullable*: bool # Is null a valid value for this type? (false by default)
|
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:
|
of Pointer:
|
||||||
# An unmanaged reference. Much
|
# An unmanaged reference. Much
|
||||||
# like a raw pointer in C
|
# like a raw pointer in C
|
||||||
data*: Type # The type we point to
|
data*: TypedNode # The type we point to
|
||||||
of TypeDecl:
|
of TypeDecl:
|
||||||
# A user-defined type
|
# A user-defined type
|
||||||
fields*: seq[TypedArgument] # List of fields in the object. May be empty
|
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
|
# Some forward declarations
|
||||||
proc compareUnions*(self: Compiler, a, b: seq[tuple[match: bool, kind: Type]]): bool
|
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 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): Type {.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): Type {.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): Type {.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): Type {.discardable.} = nil
|
proc unary*(self: Compiler, node: UnaryExpr, compile: bool = true): TypedNode {.discardable.} = nil
|
||||||
proc binary*(self: Compiler, node: BinaryExpr, compile: bool = true): Type {.discardable.} = nil
|
proc binary*(self: Compiler, node: BinaryExpr, compile: bool = true): TypedNode {.discardable.} = nil
|
||||||
proc lambdaExpr*(self: Compiler, node: LambdaExpr, compile: bool = true): Type {.discardable.} = nil
|
proc lambdaExpr*(self: Compiler, node: LambdaExpr, compile: bool = true): TypedNode {.discardable.} = nil
|
||||||
proc literal*(self: Compiler, node: ASTNode, compile: bool = true): Type {.discardable.} = nil
|
proc literal*(self: Compiler, node: ASTNode, compile: bool = true): TypedNode {.discardable.} = nil
|
||||||
proc infer*(self: Compiler, node: LiteralExpr): Type
|
proc infer*(self: Compiler, node: LiteralExpr): TypedNode
|
||||||
proc infer*(self: Compiler, node: Expression): Type
|
proc infer*(self: Compiler, node: Expression): TypedNode
|
||||||
proc inferOrError*(self: Compiler, node: Expression): Type
|
proc inferOrError*(self: Compiler, node: Expression): TypedNode
|
||||||
proc findByName*(self: Compiler, name: string): seq[Name]
|
proc findByName*(self: Compiler, name: string): seq[Name]
|
||||||
proc findInModule*(self: Compiler, name: string, module: Name): seq[Name]
|
proc findInModule*(self: Compiler, name: string, module: Name): seq[Name]
|
||||||
proc findByType*(self: Compiler, name: string, kind: Type): 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
|
# a and b are of either of the two
|
||||||
# types in this branch, so we just need
|
# types in this branch, so we just need
|
||||||
# to compare their values
|
# to compare their values
|
||||||
return self.compare(a.value, b.value)
|
return self.compare(a.value.value, b.value.value)
|
||||||
of Function:
|
of Function:
|
||||||
# Functions are a bit trickier to compare
|
# Functions are a bit trickier to compare
|
||||||
if a.arguments.len() != b.arguments.len():
|
if a.arguments.len() != b.arguments.len():
|
||||||
|
@ -569,7 +569,7 @@ proc toIntrinsic*(name: string): Type =
|
||||||
return Type(kind: String)
|
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
|
## Infers the type of a given literal expression
|
||||||
if node.isNil():
|
if node.isNil():
|
||||||
return nil
|
return nil
|
||||||
|
@ -577,32 +577,32 @@ proc infer*(self: Compiler, node: LiteralExpr): Type =
|
||||||
of intExpr, binExpr, octExpr, hexExpr:
|
of intExpr, binExpr, octExpr, hexExpr:
|
||||||
let size = node.token.lexeme.split("'")
|
let size = node.token.lexeme.split("'")
|
||||||
if size.len() == 1:
|
if size.len() == 1:
|
||||||
return Type(kind: Int64)
|
return TypedNode(node: node, value: Type(kind: Int64))
|
||||||
let typ = size[1].toIntrinsic()
|
let typ = size[1].toIntrinsic()
|
||||||
if not self.compare(typ, nil):
|
if not self.compare(typ, nil):
|
||||||
return typ
|
return TypedNode(node: node, value: typ)
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid type specifier '{size[1]}' for int", node)
|
self.error(&"invalid type specifier '{size[1]}' for int", node)
|
||||||
of floatExpr:
|
of floatExpr:
|
||||||
let size = node.token.lexeme.split("'")
|
let size = node.token.lexeme.split("'")
|
||||||
if size.len() == 1:
|
if size.len() == 1:
|
||||||
return Type(kind: Float64)
|
return TypedNode(node: node, value: Type(kind: Float64))
|
||||||
let typ = size[1].toIntrinsic()
|
let typ = size[1].toIntrinsic()
|
||||||
if not typ.isNil():
|
if not typ.isNil():
|
||||||
return typ
|
return TypedNode(node: node, value: typ)
|
||||||
else:
|
else:
|
||||||
self.error(&"invalid type specifier '{size[1]}' for float", node)
|
self.error(&"invalid type specifier '{size[1]}' for float", node)
|
||||||
of trueExpr:
|
of trueExpr:
|
||||||
return Type(kind: Bool)
|
return TypedNode(node: node, value: Type(kind: Bool))
|
||||||
of falseExpr:
|
of falseExpr:
|
||||||
return Type(kind: Bool)
|
return TypedNode(node: node, value: Type(kind: Bool))
|
||||||
of strExpr:
|
of strExpr:
|
||||||
return Type(kind: String)
|
return TypedNode(node: node, value: Type(kind: String))
|
||||||
else:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
|
|
||||||
|
|
||||||
proc infer*(self: Compiler, node: Expression): Type =
|
proc infer*(self: Compiler, node: Expression): TypedNode =
|
||||||
## Infers the type of a given expression and
|
## Infers the type of a given expression and
|
||||||
## returns it
|
## returns it
|
||||||
if node.isNil():
|
if node.isNil():
|
||||||
|
@ -621,9 +621,9 @@ proc infer*(self: Compiler, node: Expression): Type =
|
||||||
of NodeKind.callExpr:
|
of NodeKind.callExpr:
|
||||||
result = self.call(CallExpr(node), compile=false)
|
result = self.call(CallExpr(node), compile=false)
|
||||||
of NodeKind.refExpr:
|
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:
|
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:
|
of NodeKind.groupingExpr:
|
||||||
result = self.infer(GroupingExpr(node).expression)
|
result = self.infer(GroupingExpr(node).expression)
|
||||||
of NodeKind.getItemExpr:
|
of NodeKind.getItemExpr:
|
||||||
|
@ -634,7 +634,7 @@ proc infer*(self: Compiler, node: Expression): Type =
|
||||||
discard # TODO
|
discard # TODO
|
||||||
|
|
||||||
|
|
||||||
proc inferOrError*(self: Compiler, node: Expression): Type =
|
proc inferOrError*(self: Compiler, node: Expression): TypedNode =
|
||||||
## Attempts to infer the type of
|
## Attempts to infer the type of
|
||||||
## the given expression and raises an
|
## the given expression and raises an
|
||||||
## error if it fails
|
## error if it fails
|
||||||
|
@ -648,16 +648,16 @@ proc stringify*(self: Compiler, typ: Type): string =
|
||||||
## type object
|
## type object
|
||||||
if typ.isNil():
|
if typ.isNil():
|
||||||
return "nil"
|
return "nil"
|
||||||
case typ.value.kind:
|
case typ.kind:
|
||||||
of Int8, UInt8, Int16, UInt16, Int32,
|
of Int8, UInt8, Int16, UInt16, Int32,
|
||||||
UInt32, Int64, UInt64, Float32, Float64,
|
UInt32, Int64, UInt64, Float32, Float64,
|
||||||
Char, Byte, String, Nil, TypeKind.Nan, Bool,
|
Char, Byte, String, Nil, TypeKind.Nan, Bool,
|
||||||
TypeKind.Inf, Auto:
|
TypeKind.Inf, Auto:
|
||||||
result &= ($typ.value.kind).toLowerAscii()
|
result &= ($typ.kind).toLowerAscii()
|
||||||
of Pointer:
|
of Pointer:
|
||||||
result &= &"ptr {self.stringify(typ.value)}"
|
result &= &"ptr {self.stringify(typ)}"
|
||||||
of Reference:
|
of Reference:
|
||||||
result &= &"ref {self.stringify(typ.value)}"
|
result &= &"ref {self.stringify(typ)}"
|
||||||
of Any:
|
of Any:
|
||||||
return "any"
|
return "any"
|
||||||
of Union:
|
of Union:
|
||||||
|
@ -770,9 +770,9 @@ proc check*(self: Compiler, term: Expression, kind: Type) {.inline.} =
|
||||||
## Raises an error if appropriate and returns
|
## Raises an error if appropriate and returns
|
||||||
## otherwise
|
## otherwise
|
||||||
let k = self.inferOrError(term)
|
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)
|
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")
|
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
|
## Recursively unpacks a type constraint in a generic type
|
||||||
case condition.kind:
|
case condition.kind:
|
||||||
of identExpr:
|
of identExpr:
|
||||||
list.add((accept, self.inferOrError(condition)))
|
list.add((accept, self.inferOrError(condition).value))
|
||||||
if list[^1].kind.kind == Auto:
|
if list[^1].kind.kind == Auto:
|
||||||
self.error("automatic types cannot be used within generics", condition)
|
self.error("automatic types cannot be used within generics", condition)
|
||||||
of binaryExpr:
|
of binaryExpr:
|
||||||
|
@ -883,7 +883,7 @@ proc unpackUnion*(self: Compiler, condition: Expression, list: var seq[tuple[mat
|
||||||
## Recursively unpacks a type union
|
## Recursively unpacks a type union
|
||||||
case condition.kind:
|
case condition.kind:
|
||||||
of identExpr:
|
of identExpr:
|
||||||
list.add((accept, self.inferOrError(condition)))
|
list.add((accept, self.inferOrError(condition).value))
|
||||||
of binaryExpr:
|
of binaryExpr:
|
||||||
let condition = BinaryExpr(condition)
|
let condition = BinaryExpr(condition)
|
||||||
case condition.operator.lexeme:
|
case condition.operator.lexeme:
|
||||||
|
@ -966,13 +966,13 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
n.isGeneric = true
|
n.isGeneric = true
|
||||||
var typ: Type
|
var typ: Type
|
||||||
for argument in node.arguments:
|
for argument in node.arguments:
|
||||||
typ = self.infer(argument.valueType)
|
typ = self.infer(argument.valueType).value
|
||||||
if not typ.isNil() and typ.kind == Auto:
|
if not typ.isNil() and typ.kind == Auto:
|
||||||
n.obj.value.isAuto = true
|
n.obj.value.isAuto = true
|
||||||
if n.isGeneric:
|
if n.isGeneric:
|
||||||
self.error("automatic types cannot be used within generics", argument.valueType)
|
self.error("automatic types cannot be used within generics", argument.valueType)
|
||||||
break
|
break
|
||||||
typ = self.infer(node.returnType)
|
typ = self.infer(node.returnType).value
|
||||||
if not typ.isNil() and typ.kind == Auto:
|
if not typ.isNil() and typ.kind == Auto:
|
||||||
n.obj.value.isAuto = true
|
n.obj.value.isAuto = true
|
||||||
if n.isGeneric:
|
if n.isGeneric:
|
||||||
|
@ -1023,7 +1023,7 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
||||||
else:
|
else:
|
||||||
case node.value.kind:
|
case node.value.kind:
|
||||||
of identExpr:
|
of identExpr:
|
||||||
n.obj.value = self.inferOrError(node.value)
|
n.obj.value = self.inferOrError(node.value).value
|
||||||
of binaryExpr:
|
of binaryExpr:
|
||||||
# Type union
|
# Type union
|
||||||
n.obj.value = Type(kind: Union, types: @[])
|
n.obj.value = Type(kind: Union, types: @[])
|
||||||
|
|
Loading…
Reference in New Issue