Stopped using ref objects and removed recursive dependency between gc and VM
This commit is contained in:
parent
e046981f4b
commit
c1deebf43b
|
@ -45,7 +45,33 @@ when debugVM:
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PeonVM* = ref object
|
ObjectKind* = enum
|
||||||
|
## A tag for heap-allocated
|
||||||
|
## peon objects
|
||||||
|
String, List,
|
||||||
|
Dict, Tuple,
|
||||||
|
CustomType,
|
||||||
|
HeapObject* = object
|
||||||
|
## A tagged box for a heap-allocated
|
||||||
|
## peon object
|
||||||
|
marked*: bool # Used in the GC phase
|
||||||
|
case kind*: ObjectKind
|
||||||
|
of String:
|
||||||
|
str*: ptr UncheckedArray[char]
|
||||||
|
len*: int
|
||||||
|
else:
|
||||||
|
discard # TODO
|
||||||
|
PeonGC* = object
|
||||||
|
## A simple Mark&Sweep collector
|
||||||
|
## to manage peon's heap space.
|
||||||
|
## All heap allocation goes through
|
||||||
|
## this system and is not handled
|
||||||
|
## manually by the VM
|
||||||
|
bytesAllocated: tuple[total, current: int]
|
||||||
|
nextGC: int
|
||||||
|
pointers: HashSet[uint64]
|
||||||
|
objects: seq[ptr HeapObject]
|
||||||
|
PeonVM* = object
|
||||||
## The Peon Virtual Machine.
|
## The Peon Virtual Machine.
|
||||||
## Note how the only data
|
## Note how the only data
|
||||||
## type we handle here is
|
## type we handle here is
|
||||||
|
@ -72,34 +98,6 @@ type
|
||||||
breakpoints: seq[uint64] # Breakpoints where we call our debugger
|
breakpoints: seq[uint64] # Breakpoints where we call our debugger
|
||||||
debugNext: bool # Whether to debug the next instruction
|
debugNext: bool # Whether to debug the next instruction
|
||||||
lastDebugCommand: string # The last debugging command input by the user
|
lastDebugCommand: string # The last debugging command input by the user
|
||||||
ObjectKind* = enum
|
|
||||||
## A tag for heap-allocated
|
|
||||||
## peon objects
|
|
||||||
String, List,
|
|
||||||
Dict, Tuple,
|
|
||||||
CustomType,
|
|
||||||
Closure
|
|
||||||
HeapObject* = object
|
|
||||||
## A tagged box for a heap-allocated
|
|
||||||
## peon object
|
|
||||||
marked*: bool # Used in the GC phase
|
|
||||||
case kind*: ObjectKind
|
|
||||||
of String:
|
|
||||||
str*: ptr UncheckedArray[char]
|
|
||||||
len*: int
|
|
||||||
else:
|
|
||||||
discard # TODO
|
|
||||||
PeonGC* = ref object
|
|
||||||
## A simple Mark&Sweep collector
|
|
||||||
## to manage peon's heap space.
|
|
||||||
## All heap allocation goes through
|
|
||||||
## this system and is not handled
|
|
||||||
## manually by the VM
|
|
||||||
vm: PeonVM
|
|
||||||
bytesAllocated: tuple[total, current: int]
|
|
||||||
nextGC: int
|
|
||||||
pointers: HashSet[uint64]
|
|
||||||
objects: seq[ptr HeapObject]
|
|
||||||
|
|
||||||
|
|
||||||
# Implementation of peon's memory manager
|
# Implementation of peon's memory manager
|
||||||
|
@ -107,19 +105,18 @@ type
|
||||||
proc newPeonGC*: PeonGC =
|
proc newPeonGC*: PeonGC =
|
||||||
## Initializes a new, blank
|
## Initializes a new, blank
|
||||||
## garbage collector
|
## garbage collector
|
||||||
new(result)
|
|
||||||
result.bytesAllocated = (0, 0)
|
result.bytesAllocated = (0, 0)
|
||||||
result.objects = @[]
|
result.objects = @[]
|
||||||
result.nextGC = FirstGC
|
result.nextGC = FirstGC
|
||||||
|
|
||||||
|
|
||||||
proc collect*(self: PeonGC)
|
proc collect*(self: var PeonVM)
|
||||||
|
|
||||||
|
|
||||||
proc reallocate*(self: PeonGC, p: pointer, oldSize: int, newSize: int): pointer =
|
proc reallocate*(self: var PeonVM, p: pointer, oldSize: int, newSize: int): pointer =
|
||||||
## Simple wrapper around realloc/dealloc with
|
## Simple wrapper around realloc/dealloc with
|
||||||
## built-in garbage collection
|
## built-in garbage collection
|
||||||
self.bytesAllocated.current += newSize - oldSize
|
self.gc.bytesAllocated.current += newSize - oldSize
|
||||||
try:
|
try:
|
||||||
if newSize == 0 and not p.isNil():
|
if newSize == 0 and not p.isNil():
|
||||||
when debugMem:
|
when debugMem:
|
||||||
|
@ -129,11 +126,11 @@ proc reallocate*(self: PeonGC, p: pointer, oldSize: int, newSize: int): pointer
|
||||||
echo "DEBUG - Memory manager: Deallocating 1 byte of memory"
|
echo "DEBUG - Memory manager: Deallocating 1 byte of memory"
|
||||||
dealloc(p)
|
dealloc(p)
|
||||||
elif (oldSize > 0 and not p.isNil() and newSize > oldSize) or oldSize == 0:
|
elif (oldSize > 0 and not p.isNil() and newSize > oldSize) or oldSize == 0:
|
||||||
self.bytesAllocated.total += newSize - oldSize
|
self.gc.bytesAllocated.total += newSize - oldSize
|
||||||
when debugStressGC:
|
when debugStressGC:
|
||||||
self.collect()
|
self.collect()
|
||||||
else:
|
else:
|
||||||
if self.bytesAllocated.current > self.nextGC:
|
if self.gc.bytesAllocated.current > self.gc.nextGC:
|
||||||
self.collect()
|
self.collect()
|
||||||
when debugMem:
|
when debugMem:
|
||||||
if oldSize == 0:
|
if oldSize == 0:
|
||||||
|
@ -154,37 +151,38 @@ proc reallocate*(self: PeonGC, p: pointer, oldSize: int, newSize: int): pointer
|
||||||
quit(139) # For now, there's not much we can do if we can't get the memory we need, so we exit
|
quit(139) # For now, there's not much we can do if we can't get the memory we need, so we exit
|
||||||
|
|
||||||
|
|
||||||
template resizeArray*(self: PeonGC, kind: untyped, p: pointer, oldCount, newCount: int): untyped =
|
|
||||||
|
template resizeArray*(self: var PeonVM, kind: untyped, p: pointer, oldCount, newCount: int): untyped =
|
||||||
## Handy template to resize a dynamic array
|
## Handy template to resize a dynamic array
|
||||||
cast[ptr UncheckedArray[kind]](reallocate(self, p, sizeof(kind) * oldCount, sizeof(kind) * newCount))
|
cast[ptr UncheckedArray[kind]](reallocate(self, p, sizeof(kind) * oldCount, sizeof(kind) * newCount))
|
||||||
|
|
||||||
|
|
||||||
template freeArray*(self: PeonGC, kind: untyped, p: pointer, size: int): untyped =
|
template freeArray*(self: var PeonVM, kind: untyped, p: pointer, size: int): untyped =
|
||||||
## Frees a dynamic array
|
## Frees a dynamic array
|
||||||
discard reallocate(self, p, sizeof(kind) * size, 0)
|
discard reallocate(self, p, sizeof(kind) * size, 0)
|
||||||
|
|
||||||
|
|
||||||
template free*(self: PeonGC, kind: typedesc, p: pointer): untyped =
|
template free*(self: var PeonVM, kind: typedesc, p: pointer): untyped =
|
||||||
## Frees a pointer by reallocating its
|
## Frees a pointer by reallocating its
|
||||||
## size to 0
|
## size to 0
|
||||||
discard reallocate(self, p, sizeof(kind), 0)
|
discard reallocate(self, p, sizeof(kind), 0)
|
||||||
|
|
||||||
|
|
||||||
proc allocate*(self: PeonGC, kind: ObjectKind, size: typedesc, count: int): ptr HeapObject {.inline.} =
|
proc allocate*(self: var PeonVM, kind: ObjectKind, size: typedesc, count: int): ptr HeapObject {.inline.} =
|
||||||
## Allocates aobject on the heap
|
## Allocates aobject on the heap
|
||||||
result = cast[ptr HeapObject](self.reallocate(nil, 0, sizeof(HeapObject)))
|
result = cast[ptr HeapObject](self.reallocate(nil, 0, sizeof(HeapObject)))
|
||||||
result.marked = false
|
result.marked = false
|
||||||
self.bytesAllocated.total += sizeof(result)
|
self.gc.bytesAllocated.total += sizeof(HeapObject)
|
||||||
self.bytesAllocated.current += sizeof(result)
|
self.gc.bytesAllocated.current += sizeof(HeapObject)
|
||||||
case kind:
|
case kind:
|
||||||
of String:
|
of String:
|
||||||
result.str = cast[ptr UncheckedArray[char]](self.reallocate(nil, 0, sizeof(size) * count))
|
result.str = cast[ptr UncheckedArray[char]](self.reallocate(nil, 0, sizeof(size) * count))
|
||||||
result.len = count
|
result.len = count
|
||||||
self.bytesAllocated.current += sizeof(size) * count
|
self.gc.bytesAllocated.current += sizeof(size) * count
|
||||||
else:
|
else:
|
||||||
discard # TODO
|
discard # TODO
|
||||||
self.objects.add(result)
|
self.gc.objects.add(result)
|
||||||
self.pointers.incl(cast[uint64](result))
|
self.gc.pointers.incl(cast[uint64](result))
|
||||||
|
|
||||||
|
|
||||||
proc mark(self: ptr HeapObject): bool =
|
proc mark(self: ptr HeapObject): bool =
|
||||||
|
@ -195,7 +193,7 @@ proc mark(self: ptr HeapObject): bool =
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
proc markRoots(self: PeonGC): seq[ptr HeapObject] =
|
proc markRoots(self: var PeonVM): seq[ptr HeapObject] =
|
||||||
## Marks root objects *not* to be
|
## Marks root objects *not* to be
|
||||||
## collected by the GC and returns
|
## collected by the GC and returns
|
||||||
## their addresses
|
## their addresses
|
||||||
|
@ -222,15 +220,15 @@ proc markRoots(self: PeonGC): seq[ptr HeapObject] =
|
||||||
# we allocated and that would cause a memory leak, but
|
# we allocated and that would cause a memory leak, but
|
||||||
# with a 64-bit address-space it probably hardly matters,
|
# with a 64-bit address-space it probably hardly matters,
|
||||||
# so I guess this is a mostly-precise Mark&Sweep collector
|
# so I guess this is a mostly-precise Mark&Sweep collector
|
||||||
var live = initHashSet[uint64](self.pointers.len())
|
var live = initHashSet[uint64](self.gc.pointers.len())
|
||||||
for obj in self.vm.calls:
|
for obj in self.calls:
|
||||||
if obj in self.pointers:
|
if obj in self.gc.pointers:
|
||||||
live.incl(obj)
|
live.incl(obj)
|
||||||
for obj in self.vm.operands:
|
for obj in self.operands:
|
||||||
if obj in self.pointers:
|
if obj in self.gc.pointers:
|
||||||
live.incl(obj)
|
live.incl(obj)
|
||||||
for obj in self.vm.envs:
|
for obj in self.envs:
|
||||||
if obj in self.pointers:
|
if obj in self.gc.pointers:
|
||||||
live.incl(obj)
|
live.incl(obj)
|
||||||
# We preallocate the space on the seq
|
# We preallocate the space on the seq
|
||||||
result = newSeqOfCap[ptr HeapObject](len(live))
|
result = newSeqOfCap[ptr HeapObject](len(live))
|
||||||
|
@ -245,7 +243,7 @@ proc markRoots(self: PeonGC): seq[ptr HeapObject] =
|
||||||
echo "DEBUG - GC: Mark phase complete"
|
echo "DEBUG - GC: Mark phase complete"
|
||||||
|
|
||||||
|
|
||||||
proc trace(self: PeonGC, roots: seq[ptr HeapObject]) =
|
proc trace(self: var PeonVM, roots: seq[ptr HeapObject]) =
|
||||||
## Traces references to other
|
## Traces references to other
|
||||||
## objects starting from the
|
## objects starting from the
|
||||||
## roots. The second argument
|
## roots. The second argument
|
||||||
|
@ -267,7 +265,7 @@ proc trace(self: PeonGC, roots: seq[ptr HeapObject]) =
|
||||||
echo &"DEBUG - GC: Traced {count} indirect references"
|
echo &"DEBUG - GC: Traced {count} indirect references"
|
||||||
|
|
||||||
|
|
||||||
proc free(self: PeonGC, obj: ptr HeapObject) =
|
proc free(self: var PeonVM, obj: ptr HeapObject) =
|
||||||
## Frees a single heap-allocated
|
## Frees a single heap-allocated
|
||||||
## peon object and all the memory
|
## peon object and all the memory
|
||||||
## it directly or indirectly owns
|
## it directly or indirectly owns
|
||||||
|
@ -282,10 +280,10 @@ proc free(self: PeonGC, obj: ptr HeapObject) =
|
||||||
else:
|
else:
|
||||||
discard # TODO
|
discard # TODO
|
||||||
self.free(HeapObject, obj)
|
self.free(HeapObject, obj)
|
||||||
self.pointers.excl(cast[uint64](obj))
|
self.gc.pointers.excl(cast[uint64](obj))
|
||||||
|
|
||||||
|
|
||||||
proc sweep(self: PeonGC) =
|
proc sweep(self: var PeonVM) =
|
||||||
## Sweeps unmarked objects
|
## Sweeps unmarked objects
|
||||||
## that have been left behind
|
## that have been left behind
|
||||||
## during the mark phase.
|
## during the mark phase.
|
||||||
|
@ -300,21 +298,21 @@ proc sweep(self: PeonGC) =
|
||||||
var idx = 0
|
var idx = 0
|
||||||
when debugGC:
|
when debugGC:
|
||||||
var count = 0
|
var count = 0
|
||||||
while j < self.objects.high():
|
while j < self.gc.objects.high():
|
||||||
inc(j)
|
inc(j)
|
||||||
if self.objects[j].marked:
|
if self.gc.objects[j].marked:
|
||||||
# Object is marked: don't touch it,
|
# Object is marked: don't touch it,
|
||||||
# but reset its mark so that it doesn't
|
# but reset its mark so that it doesn't
|
||||||
# stay alive forever
|
# stay alive forever
|
||||||
when debugGC:
|
when debugGC:
|
||||||
echo &"DEBUG - GC: Unmarking object: {self.objects[j][]}"
|
echo &"DEBUG - GC: Unmarking object: {self.gc.objects[j][]}"
|
||||||
self.objects[j].marked = false
|
self.gc.objects[j].marked = false
|
||||||
inc(idx)
|
inc(idx)
|
||||||
else:
|
else:
|
||||||
# Object is unmarked: its memory is
|
# Object is unmarked: its memory is
|
||||||
# fair game
|
# fair game
|
||||||
self.free(self.objects[idx])
|
self.free(self.gc.objects[idx])
|
||||||
self.objects.delete(idx)
|
self.gc.objects.delete(idx)
|
||||||
inc(idx)
|
inc(idx)
|
||||||
when debugGC:
|
when debugGC:
|
||||||
inc(count)
|
inc(count)
|
||||||
|
@ -322,25 +320,25 @@ proc sweep(self: PeonGC) =
|
||||||
echo &"DEBUG - GC: Swept {count} objects"
|
echo &"DEBUG - GC: Swept {count} objects"
|
||||||
|
|
||||||
|
|
||||||
proc collect(self: PeonGC) =
|
proc collect(self: var PeonVM) =
|
||||||
## Attempts to reclaim some
|
## Attempts to reclaim some
|
||||||
## memory from unreachable
|
## memory from unreachable
|
||||||
## objects onto the heap
|
## objects onto the heap
|
||||||
when debugGC:
|
when debugGC:
|
||||||
let before = self.bytesAllocated.current
|
let before = self.gc.bytesAllocated.current
|
||||||
let time = getMonoTime().ticks().float() / 1_000_000
|
let time = getMonoTime().ticks().float() / 1_000_000
|
||||||
echo &"DEBUG - GC: Starting collection cycle at heap size {self.bytesAllocated.current}"
|
echo &"DEBUG - GC: Starting collection cycle at heap size {self.gc.bytesAllocated.current}"
|
||||||
self.trace(self.markRoots())
|
self.trace(self.markRoots())
|
||||||
self.sweep()
|
self.sweep()
|
||||||
self.nextGC = self.bytesAllocated.current * HeapGrowFactor
|
self.gc.nextGC = self.gc.bytesAllocated.current * HeapGrowFactor
|
||||||
when debugGC:
|
when debugGC:
|
||||||
echo &"DEBUG - GC: Collection cycle has terminated in {getMonoTime().ticks().float() / 1_000_000 - time:.2f} ms, collected {before - self.bytesAllocated.current} bytes of memory in total"
|
echo &"DEBUG - GC: Collection cycle has terminated in {getMonoTime().ticks().float() / 1_000_000 - time:.2f} ms, collected {before - self.gc.bytesAllocated.current} bytes of memory in total"
|
||||||
echo &"DEBUG - GC: Next cycle at {self.nextGC} bytes"
|
echo &"DEBUG - GC: Next cycle at {self.gc.nextGC} bytes"
|
||||||
|
|
||||||
|
|
||||||
# Implementation of the peon VM
|
# Implementation of the peon VM
|
||||||
|
|
||||||
proc initCache*(self: PeonVM) =
|
proc initCache*(self: var PeonVM) =
|
||||||
## Initializes the VM's
|
## Initializes the VM's
|
||||||
## singletons cache
|
## singletons cache
|
||||||
self.cache[0] = 0x0 # False
|
self.cache[0] = 0x0 # False
|
||||||
|
@ -354,7 +352,6 @@ proc initCache*(self: PeonVM) =
|
||||||
proc newPeonVM*: PeonVM =
|
proc newPeonVM*: PeonVM =
|
||||||
## Initializes a new, blank VM
|
## Initializes a new, blank VM
|
||||||
## for executing Peon bytecode
|
## for executing Peon bytecode
|
||||||
new(result)
|
|
||||||
result.ip = 0
|
result.ip = 0
|
||||||
result.initCache()
|
result.initCache()
|
||||||
result.gc = newPeonGC()
|
result.gc = newPeonGC()
|
||||||
|
@ -363,25 +360,24 @@ proc newPeonVM*: PeonVM =
|
||||||
result.results = @[]
|
result.results = @[]
|
||||||
result.envs = @[]
|
result.envs = @[]
|
||||||
result.calls = @[]
|
result.calls = @[]
|
||||||
result.gc.vm = result
|
|
||||||
|
|
||||||
|
|
||||||
# Getters for singleton types
|
# Getters for singleton types
|
||||||
{.push inline.}
|
{.push inline.}
|
||||||
|
|
||||||
proc getNil*(self: PeonVM): uint64 = self.cache[2]
|
proc getNil*(self: var PeonVM): uint64 = self.cache[2]
|
||||||
|
|
||||||
proc getBool*(self: PeonVM, value: bool): uint64 =
|
proc getBool*(self: var PeonVM, value: bool): uint64 =
|
||||||
if value:
|
if value:
|
||||||
return self.cache[1]
|
return self.cache[1]
|
||||||
return self.cache[0]
|
return self.cache[0]
|
||||||
|
|
||||||
proc getInf*(self: PeonVM, positive: bool): uint64 =
|
proc getInf*(self: var PeonVM, positive: bool): uint64 =
|
||||||
if positive:
|
if positive:
|
||||||
return self.cache[3]
|
return self.cache[3]
|
||||||
return self.cache[4]
|
return self.cache[4]
|
||||||
|
|
||||||
proc getNan*(self: PeonVM): uint64 = self.cache[5]
|
proc getNan*(self: var PeonVM): uint64 = self.cache[5]
|
||||||
|
|
||||||
|
|
||||||
# Thanks to nim's *genius* idea of making x !> y a template
|
# Thanks to nim's *genius* idea of making x !> y a template
|
||||||
|
@ -403,13 +399,13 @@ proc `!>=`[T](a, b: T): auto {.inline, used.} =
|
||||||
# that go through the getc/setc wrappers are frame-relative,
|
# that go through the getc/setc wrappers are frame-relative,
|
||||||
# meaning that the index is added to the current stack frame's
|
# meaning that the index is added to the current stack frame's
|
||||||
# bottom to obtain an absolute stack index
|
# bottom to obtain an absolute stack index
|
||||||
proc push(self: PeonVM, obj: uint64) =
|
proc push(self: var PeonVM, obj: uint64) =
|
||||||
## Pushes a value object onto the
|
## Pushes a value object onto the
|
||||||
## operand stack
|
## operand stack
|
||||||
self.operands.add(obj)
|
self.operands.add(obj)
|
||||||
|
|
||||||
|
|
||||||
proc pop(self: PeonVM): uint64 =
|
proc pop(self: var PeonVM): uint64 =
|
||||||
## Pops a value off the operand
|
## Pops a value off the operand
|
||||||
## stack and returns it
|
## stack and returns it
|
||||||
return self.operands.pop()
|
return self.operands.pop()
|
||||||
|
@ -431,13 +427,13 @@ proc peek(self: PeonVM, distance: int = 0): uint64 =
|
||||||
return self.operands[self.operands.high() + distance]
|
return self.operands[self.operands.high() + distance]
|
||||||
|
|
||||||
|
|
||||||
proc pushc(self: PeonVM, val: uint64) =
|
proc pushc(self: var PeonVM, val: uint64) =
|
||||||
## Pushes a value onto the
|
## Pushes a value onto the
|
||||||
## call stack
|
## call stack
|
||||||
self.calls.add(val)
|
self.calls.add(val)
|
||||||
|
|
||||||
|
|
||||||
proc popc(self: PeonVM): uint64 =
|
proc popc(self: var PeonVM): uint64 =
|
||||||
## Pops a value off the call
|
## Pops a value off the call
|
||||||
## stack and returns it
|
## stack and returns it
|
||||||
return self.calls.pop()
|
return self.calls.pop()
|
||||||
|
@ -457,7 +453,7 @@ proc getc(self: PeonVM, idx: int): uint64 =
|
||||||
return self.calls[idx.uint64 + self.frames[^1]]
|
return self.calls[idx.uint64 + self.frames[^1]]
|
||||||
|
|
||||||
|
|
||||||
proc setc(self: PeonVM, idx: int, val: uint64) =
|
proc setc(self: var PeonVM, idx: int, val: uint64) =
|
||||||
## Setter method that abstracts
|
## Setter method that abstracts
|
||||||
## indexing our call stack through
|
## indexing our call stack through
|
||||||
## stack frames
|
## stack frames
|
||||||
|
@ -470,7 +466,7 @@ proc getClosure(self: PeonVM, idx: int): uint64 =
|
||||||
return self.envs[idx.uint + self.closures[^1]]
|
return self.envs[idx.uint + self.closures[^1]]
|
||||||
|
|
||||||
|
|
||||||
proc setClosure(self: PeonVM, idx: int, val: uint64) =
|
proc setClosure(self: var PeonVM, idx: int, val: uint64) =
|
||||||
## Setter method that abstracts
|
## Setter method that abstracts
|
||||||
## indexing closure environments
|
## indexing closure environments
|
||||||
if idx == self.envs.len():
|
if idx == self.envs.len():
|
||||||
|
@ -479,7 +475,7 @@ proc setClosure(self: PeonVM, idx: int, val: uint64) =
|
||||||
self.envs[idx.uint + self.closures[^1]] = val
|
self.envs[idx.uint + self.closures[^1]] = val
|
||||||
|
|
||||||
|
|
||||||
proc popClosure(self: PeonVM, idx: int): uint64 =
|
proc popClosure(self: var PeonVM, idx: int): uint64 =
|
||||||
## Pop method that abstracts
|
## Pop method that abstracts
|
||||||
## popping values off closure
|
## popping values off closure
|
||||||
## environments
|
## environments
|
||||||
|
@ -491,7 +487,7 @@ proc popClosure(self: PeonVM, idx: int): uint64 =
|
||||||
# Byte-level primitives to read and decode
|
# Byte-level primitives to read and decode
|
||||||
# bytecode
|
# bytecode
|
||||||
|
|
||||||
proc readByte(self: PeonVM): uint8 =
|
proc readByte(self: var PeonVM): uint8 =
|
||||||
## Reads a single byte from the
|
## Reads a single byte from the
|
||||||
## bytecode and returns it as an
|
## bytecode and returns it as an
|
||||||
## unsigned 8 bit integer
|
## unsigned 8 bit integer
|
||||||
|
@ -499,7 +495,7 @@ proc readByte(self: PeonVM): uint8 =
|
||||||
return self.chunk.code[self.ip - 1]
|
return self.chunk.code[self.ip - 1]
|
||||||
|
|
||||||
|
|
||||||
proc readShort(self: PeonVM): uint16 =
|
proc readShort(self: var PeonVM): uint16 =
|
||||||
## Reads two bytes from the
|
## Reads two bytes from the
|
||||||
## bytecode and returns them
|
## bytecode and returns them
|
||||||
## as an unsigned 16 bit
|
## as an unsigned 16 bit
|
||||||
|
@ -507,7 +503,7 @@ proc readShort(self: PeonVM): uint16 =
|
||||||
return [self.readByte(), self.readByte()].fromDouble()
|
return [self.readByte(), self.readByte()].fromDouble()
|
||||||
|
|
||||||
|
|
||||||
proc readLong(self: PeonVM): uint32 =
|
proc readLong(self: var PeonVM): uint32 =
|
||||||
## Reads three bytes from the
|
## Reads three bytes from the
|
||||||
## bytecode and returns them
|
## bytecode and returns them
|
||||||
## as an unsigned 32 bit
|
## as an unsigned 32 bit
|
||||||
|
@ -517,7 +513,7 @@ proc readLong(self: PeonVM): uint32 =
|
||||||
return uint32([self.readByte(), self.readByte(), self.readByte()].fromTriple())
|
return uint32([self.readByte(), self.readByte(), self.readByte()].fromTriple())
|
||||||
|
|
||||||
|
|
||||||
proc readUInt(self: PeonVM): uint32 {.used.} =
|
proc readUInt(self: var PeonVM): uint32 {.used.} =
|
||||||
## Reads three bytes from the
|
## Reads three bytes from the
|
||||||
## bytecode and returns them
|
## bytecode and returns them
|
||||||
## as an unsigned 32 bit
|
## as an unsigned 32 bit
|
||||||
|
@ -528,7 +524,7 @@ proc readUInt(self: PeonVM): uint32 {.used.} =
|
||||||
# Functions to read primitives from the chunk's
|
# Functions to read primitives from the chunk's
|
||||||
# constants table
|
# constants table
|
||||||
|
|
||||||
proc constReadInt64(self: PeonVM, idx: int): int64 =
|
proc constReadInt64(self: var PeonVM, idx: int): int64 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as an int64
|
## returns it as an int64
|
||||||
|
@ -540,7 +536,7 @@ proc constReadInt64(self: PeonVM, idx: int): int64 =
|
||||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc constReadUInt64(self: PeonVM, idx: int): uint64 =
|
proc constReadUInt64(self: var PeonVM, idx: int): uint64 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as an uint64
|
## returns it as an uint64
|
||||||
|
@ -552,7 +548,7 @@ proc constReadUInt64(self: PeonVM, idx: int): uint64 =
|
||||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc constReadUInt32(self: PeonVM, idx: int): uint32 =
|
proc constReadUInt32(self: var PeonVM, idx: int): uint32 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as an int32
|
## returns it as an int32
|
||||||
|
@ -561,7 +557,7 @@ proc constReadUInt32(self: PeonVM, idx: int): uint32 =
|
||||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc constReadInt32(self: PeonVM, idx: int): int32 =
|
proc constReadInt32(self: var PeonVM, idx: int): int32 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as an uint32
|
## returns it as an uint32
|
||||||
|
@ -570,7 +566,7 @@ proc constReadInt32(self: PeonVM, idx: int): int32 =
|
||||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc constReadInt16(self: PeonVM, idx: int): int16 =
|
proc constReadInt16(self: var PeonVM, idx: int): int16 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as an int16
|
## returns it as an int16
|
||||||
|
@ -578,7 +574,7 @@ proc constReadInt16(self: PeonVM, idx: int): int16 =
|
||||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc constReadUInt16(self: PeonVM, idx: int): uint16 =
|
proc constReadUInt16(self: var PeonVM, idx: int): uint16 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as an uint16
|
## returns it as an uint16
|
||||||
|
@ -586,21 +582,21 @@ proc constReadUInt16(self: PeonVM, idx: int): uint16 =
|
||||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc constReadInt8(self: PeonVM, idx: int): int8 =
|
proc constReadInt8(self: var PeonVM, idx: int): int8 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as an int8
|
## returns it as an int8
|
||||||
result = int8(self.chunk.consts[idx])
|
result = int8(self.chunk.consts[idx])
|
||||||
|
|
||||||
|
|
||||||
proc constReadUInt8(self: PeonVM, idx: int): uint8 =
|
proc constReadUInt8(self: var PeonVM, idx: int): uint8 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as an uint8
|
## returns it as an uint8
|
||||||
result = self.chunk.consts[idx]
|
result = self.chunk.consts[idx]
|
||||||
|
|
||||||
|
|
||||||
proc constReadFloat32(self: PeonVM, idx: int): float32 =
|
proc constReadFloat32(self: var PeonVM, idx: int): float32 =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as a float32
|
## returns it as a float32
|
||||||
|
@ -609,7 +605,7 @@ proc constReadFloat32(self: PeonVM, idx: int): float32 =
|
||||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc constReadFloat64(self: PeonVM, idx: int): float =
|
proc constReadFloat64(self: var PeonVM, idx: int): float =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as a float
|
## returns it as a float
|
||||||
|
@ -620,13 +616,13 @@ proc constReadFloat64(self: PeonVM, idx: int): float =
|
||||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
||||||
proc constReadString(self: PeonVM, size, idx: int): ptr HeapObject =
|
proc constReadString(self: var PeonVM, size, idx: int): ptr HeapObject =
|
||||||
## Reads a constant from the
|
## Reads a constant from the
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns it as a pointer to
|
## returns it as a pointer to
|
||||||
## a heap-allocated string
|
## a heap-allocated string
|
||||||
let str = self.chunk.consts[idx..<idx + size].fromBytes()
|
let str = self.chunk.consts[idx..<idx + size].fromBytes()
|
||||||
result = self.gc.allocate(String, char, len(str))
|
result = self.allocate(String, char, len(str))
|
||||||
for i, c in str:
|
for i, c in str:
|
||||||
result.str[i] = c
|
result.str[i] = c
|
||||||
when debugAlloc:
|
when debugAlloc:
|
||||||
|
@ -636,7 +632,7 @@ proc constReadString(self: PeonVM, size, idx: int): ptr HeapObject =
|
||||||
|
|
||||||
|
|
||||||
when debugVM: # So nim shuts up
|
when debugVM: # So nim shuts up
|
||||||
proc debug(self: PeonVM) =
|
proc debug(self: var PeonVM) =
|
||||||
## Implements the VM's runtime
|
## Implements the VM's runtime
|
||||||
## debugger
|
## debugger
|
||||||
styledEcho fgMagenta, "IP: ", fgYellow, &"{self.ip}"
|
styledEcho fgMagenta, "IP: ", fgYellow, &"{self.ip}"
|
||||||
|
@ -721,7 +717,7 @@ when debugVM: # So nim shuts up
|
||||||
styledEcho(fgRed, "Unknown command ", fgYellow, &"'{command}'")
|
styledEcho(fgRed, "Unknown command ", fgYellow, &"'{command}'")
|
||||||
|
|
||||||
|
|
||||||
proc dispatch*(self: PeonVM) =
|
proc dispatch*(self: var PeonVM) =
|
||||||
## Main bytecode dispatch loop
|
## Main bytecode dispatch loop
|
||||||
var instruction {.register.}: OpCode
|
var instruction {.register.}: OpCode
|
||||||
while true:
|
while true:
|
||||||
|
@ -1074,7 +1070,7 @@ proc dispatch*(self: PeonVM) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc run*(self: PeonVM, chunk: Chunk, breakpoints: seq[uint64] = @[]) =
|
proc run*(self: var PeonVM, chunk: Chunk, breakpoints: seq[uint64] = @[]) =
|
||||||
## Executes a piece of Peon bytecode
|
## Executes a piece of Peon bytecode
|
||||||
self.chunk = chunk
|
self.chunk = chunk
|
||||||
self.frames = @[]
|
self.frames = @[]
|
||||||
|
@ -1090,7 +1086,7 @@ proc run*(self: PeonVM, chunk: Chunk, breakpoints: seq[uint64] = @[]) =
|
||||||
stderr.writeLine("Memory Access Violation: SIGSEGV")
|
stderr.writeLine("Memory Access Violation: SIGSEGV")
|
||||||
quit(1)
|
quit(1)
|
||||||
# We clean up after ourselves!
|
# We clean up after ourselves!
|
||||||
self.gc.collect()
|
self.collect()
|
||||||
|
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -161,7 +161,7 @@ type
|
||||||
names: seq[Name]
|
names: seq[Name]
|
||||||
# The current scope depth. If > 0, we're
|
# The current scope depth. If > 0, we're
|
||||||
# in a local scope, otherwise it's global
|
# in a local scope, otherwise it's global
|
||||||
scopeDepth: int
|
depth: int
|
||||||
# Scope ownership data
|
# Scope ownership data
|
||||||
scopeOwners: seq[tuple[owner: Name, depth: int]]
|
scopeOwners: seq[tuple[owner: Name, depth: int]]
|
||||||
# The current function being compiled
|
# The current function being compiled
|
||||||
|
@ -254,7 +254,7 @@ proc newCompiler*(replMode: bool = false): Compiler =
|
||||||
result.current = 0
|
result.current = 0
|
||||||
result.file = ""
|
result.file = ""
|
||||||
result.names = @[]
|
result.names = @[]
|
||||||
result.scopeDepth = 0
|
result.depth = 0
|
||||||
result.lines = @[]
|
result.lines = @[]
|
||||||
result.jumps = @[]
|
result.jumps = @[]
|
||||||
result.currentFunction = nil
|
result.currentFunction = nil
|
||||||
|
@ -567,21 +567,38 @@ proc getStackPos(self: Compiler, name: Name): int =
|
||||||
result = 2
|
result = 2
|
||||||
for variable in self.names:
|
for variable in self.names:
|
||||||
if variable.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum, NameKind.Function, NameKind.None]:
|
if variable.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum, NameKind.Function, NameKind.None]:
|
||||||
|
# These names don't have a runtime representation on the call stack, so we skip them
|
||||||
continue
|
continue
|
||||||
elif variable.kind == NameKind.Argument and variable.depth > self.scopeDepth:
|
elif variable.kind == NameKind.Argument and variable.depth > self.depth:
|
||||||
|
# Argument of a function we haven't compiled yet (or one that we're
|
||||||
|
# not in). Ignore it, as it won't exist at runtime
|
||||||
continue
|
continue
|
||||||
elif not variable.belongsTo.isNil() and variable.belongsTo.valueType.isBuiltinFunction:
|
elif not variable.belongsTo.isNil() and variable.belongsTo.valueType.isBuiltinFunction:
|
||||||
|
# Builtin functions don't exist at runtime either, so variables belonging to them
|
||||||
|
# are not present in the stack
|
||||||
continue
|
continue
|
||||||
elif not variable.valueType.isNil() and variable.valueType.kind == Generic:
|
elif not variable.valueType.isNil() and variable.valueType.kind == Generic:
|
||||||
|
# Generics are also a purely compile-time construct and are therefore
|
||||||
|
# ignored as far as stack positioning goes
|
||||||
continue
|
continue
|
||||||
elif variable.owner != self.currentModule:
|
elif variable.owner != self.currentModule:
|
||||||
continue
|
# We don't own this variable, so we check
|
||||||
if name.ident == variable.ident:
|
# if the owner exported it to us. If not,
|
||||||
|
# we skip it and pretend it doesn't exist
|
||||||
|
if variable.isPrivate or not variable.exported:
|
||||||
|
continue
|
||||||
|
elif not variable.resolved:
|
||||||
|
dec(result)
|
||||||
|
elif name.ident == variable.ident:
|
||||||
|
# After all of these checks, we can
|
||||||
|
# finally check whether the name of
|
||||||
|
# the two variables match and if they
|
||||||
|
# do, bingo: We got our name object
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
inc(result)
|
inc(result)
|
||||||
if not found:
|
if not found:
|
||||||
return -1
|
result = -1
|
||||||
|
|
||||||
|
|
||||||
proc getClosurePos(self: Compiler, name: Name): int =
|
proc getClosurePos(self: Compiler, name: Name): int =
|
||||||
|
@ -1088,8 +1105,8 @@ proc handleBuiltinFunction(self: Compiler, fn: Type, args: seq[Expression], line
|
||||||
proc beginScope(self: Compiler) =
|
proc beginScope(self: Compiler) =
|
||||||
## Begins a new local scope by incrementing the current
|
## Begins a new local scope by incrementing the current
|
||||||
## scope's depth
|
## scope's depth
|
||||||
inc(self.scopeDepth)
|
inc(self.depth)
|
||||||
self.scopeOwners.add((self.currentFunction, self.scopeDepth))
|
self.scopeOwners.add((self.currentFunction, self.depth))
|
||||||
|
|
||||||
|
|
||||||
# Flattens our weird function tree into a linear
|
# Flattens our weird function tree into a linear
|
||||||
|
@ -1106,26 +1123,26 @@ proc flatten(self: Type): seq[Type] = flattenImpl(self, result)
|
||||||
|
|
||||||
proc endScope(self: Compiler) =
|
proc endScope(self: Compiler) =
|
||||||
## Ends the current local scope
|
## Ends the current local scope
|
||||||
if self.scopeDepth < 0:
|
if self.depth < 0:
|
||||||
self.error("cannot call endScope with scopeDepth < 0 (This is an internal error and most likely a bug)")
|
self.error("cannot call endScope with depth < 0 (This is an internal error and most likely a bug)")
|
||||||
discard self.scopeOwners.pop()
|
discard self.scopeOwners.pop()
|
||||||
dec(self.scopeDepth)
|
dec(self.depth)
|
||||||
var names: seq[Name] = @[]
|
var names: seq[Name] = @[]
|
||||||
var popCount = 0
|
var popCount = 0
|
||||||
if self.scopeDepth == -1 and not self.isMainModule:
|
if self.depth == -1 and not self.isMainModule:
|
||||||
# When we're compiling another module, we don't
|
# When we're compiling another module, we don't
|
||||||
# close its global scope because self.compileModule()
|
# close its global scope because self.compileModule()
|
||||||
# needs access to it
|
# needs access to it
|
||||||
return
|
return
|
||||||
for name in self.names:
|
for name in self.names:
|
||||||
if name.depth > self.scopeDepth:
|
if name.depth > self.depth:
|
||||||
if not name.belongsTo.isNil() and not name.belongsTo.resolved:
|
if not name.belongsTo.isNil() and not name.belongsTo.resolved:
|
||||||
continue
|
continue
|
||||||
names.add(name)
|
names.add(name)
|
||||||
#[if not name.resolved:
|
#[if not name.resolved:
|
||||||
# TODO: Emit a warning?
|
# TODO: Emit a warning?
|
||||||
continue]#
|
continue]#
|
||||||
if name.owner != self.currentModule and self.scopeDepth > -1:
|
if name.owner != self.currentModule and self.depth > -1:
|
||||||
# Names coming from other modules only go out of scope
|
# Names coming from other modules only go out of scope
|
||||||
# when the global scope is closed (i.e. at the end of
|
# when the global scope is closed (i.e. at the end of
|
||||||
# the module)
|
# the module)
|
||||||
|
@ -1229,7 +1246,7 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
# slap myself 100 times with a sign saying "I'm dumb". Mark my words
|
# slap myself 100 times with a sign saying "I'm dumb". Mark my words
|
||||||
self.error("cannot declare more than 16777215 variables at a time")
|
self.error("cannot declare more than 16777215 variables at a time")
|
||||||
declaredName = node.name.token.lexeme
|
declaredName = node.name.token.lexeme
|
||||||
self.names.add(Name(depth: self.scopeDepth,
|
self.names.add(Name(depth: self.depth,
|
||||||
ident: node.name,
|
ident: node.name,
|
||||||
isPrivate: node.isPrivate,
|
isPrivate: node.isPrivate,
|
||||||
owner: self.currentModule,
|
owner: self.currentModule,
|
||||||
|
@ -1246,7 +1263,7 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
result = self.names[^1]
|
result = self.names[^1]
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
var node = FunDecl(node)
|
var node = FunDecl(node)
|
||||||
result = Name(depth: self.scopeDepth,
|
result = Name(depth: self.depth,
|
||||||
isPrivate: node.isPrivate,
|
isPrivate: node.isPrivate,
|
||||||
isConst: false,
|
isConst: false,
|
||||||
owner: self.currentModule,
|
owner: self.currentModule,
|
||||||
|
@ -1304,7 +1321,7 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
var node = ImportStmt(node)
|
var node = ImportStmt(node)
|
||||||
var name = node.moduleName.token.lexeme.extractFilename().replace(".pn", "")
|
var name = node.moduleName.token.lexeme.extractFilename().replace(".pn", "")
|
||||||
declaredName = name
|
declaredName = name
|
||||||
self.names.add(Name(depth: self.scopeDepth,
|
self.names.add(Name(depth: self.depth,
|
||||||
owner: self.currentModule,
|
owner: self.currentModule,
|
||||||
ident: newIdentExpr(Token(kind: Identifier, lexeme: name, line: node.moduleName.token.line)),
|
ident: newIdentExpr(Token(kind: Identifier, lexeme: name, line: node.moduleName.token.line)),
|
||||||
line: node.moduleName.token.line,
|
line: node.moduleName.token.line,
|
||||||
|
@ -1317,7 +1334,7 @@ proc declareName(self: Compiler, node: ASTNode, mutable: bool = false): Name =
|
||||||
for name in self.findByName(declaredName):
|
for name in self.findByName(declaredName):
|
||||||
if name == result:
|
if name == result:
|
||||||
continue
|
continue
|
||||||
elif (name.kind == NameKind.Var and name.depth == self.scopeDepth) or name.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
elif (name.kind == NameKind.Var and name.depth == self.depth) or name.kind in [NameKind.Module, NameKind.CustomType, NameKind.Enum]:
|
||||||
self.error(&"attempt to redeclare '{name.ident.token.lexeme}', which was previously defined in '{name.owner}' at line {name.line}")
|
self.error(&"attempt to redeclare '{name.ident.token.lexeme}', which was previously defined in '{name.owner}' at line {name.line}")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1576,7 +1593,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
||||||
# they're referenced
|
# they're referenced
|
||||||
self.emitByte(LoadUInt64, node.token.line)
|
self.emitByte(LoadUInt64, node.token.line)
|
||||||
self.emitBytes(self.chunk.writeConstant(s.codePos.toLong()), node.token.line)
|
self.emitBytes(self.chunk.writeConstant(s.codePos.toLong()), node.token.line)
|
||||||
elif s.depth > 0 and self.scopeDepth > 0 and not self.currentFunction.isNil() and s.depth != self.scopeDepth and self.scopeOwners[s.depth].owner != self.currentFunction:
|
elif s.depth > 0 and self.depth > 0 and not self.currentFunction.isNil() and s.depth != self.depth and self.scopeOwners[s.depth].owner != self.currentFunction:
|
||||||
# Loads a closure variable. Stored in a separate "closure array" in the VM that does not
|
# Loads a closure variable. Stored in a separate "closure array" in the VM that does not
|
||||||
# align its semantics with the call stack. This makes closures work as expected and is
|
# align its semantics with the call stack. This makes closures work as expected and is
|
||||||
# not much slower than indexing our stack (since they're both dynamic arrays at runtime anyway)
|
# not much slower than indexing our stack (since they're both dynamic arrays at runtime anyway)
|
||||||
|
@ -1953,7 +1970,7 @@ proc breakStmt(self: Compiler, node: BreakStmt) =
|
||||||
## Compiles break statements. A break statement
|
## Compiles break statements. A break statement
|
||||||
## jumps to the end of the loop
|
## jumps to the end of the loop
|
||||||
self.currentLoop.breakJumps.add(self.emitJump(OpCode.JumpForwards, node.token.line))
|
self.currentLoop.breakJumps.add(self.emitJump(OpCode.JumpForwards, node.token.line))
|
||||||
if self.currentLoop.depth > self.scopeDepth:
|
if self.currentLoop.depth > self.depth:
|
||||||
# Breaking out of a loop closes its scope
|
# Breaking out of a loop closes its scope
|
||||||
self.endScope()
|
self.endScope()
|
||||||
|
|
||||||
|
@ -2076,7 +2093,7 @@ proc statement(self: Compiler, node: Statement) =
|
||||||
# for loops to while loops
|
# for loops to while loops
|
||||||
let loop = self.currentLoop
|
let loop = self.currentLoop
|
||||||
self.currentLoop = Loop(start: self.chunk.code.len(),
|
self.currentLoop = Loop(start: self.chunk.code.len(),
|
||||||
depth: self.scopeDepth, breakJumps: @[])
|
depth: self.depth, breakJumps: @[])
|
||||||
self.whileStmt(WhileStmt(node))
|
self.whileStmt(WhileStmt(node))
|
||||||
self.patchBreaks()
|
self.patchBreaks()
|
||||||
self.currentLoop = loop
|
self.currentLoop = loop
|
||||||
|
@ -2104,7 +2121,7 @@ proc varDecl(self: Compiler, node: VarDecl, name: Name) =
|
||||||
var typ: Type
|
var typ: Type
|
||||||
if node.value.isNil():
|
if node.value.isNil():
|
||||||
# Variable has no value: the type declaration
|
# Variable has no value: the type declaration
|
||||||
# takes over
|
# takes over
|
||||||
typ = self.inferOrError(node.valueType)
|
typ = self.inferOrError(node.valueType)
|
||||||
elif node.valueType.isNil:
|
elif node.valueType.isNil:
|
||||||
# Variable has no type declaration: the type
|
# Variable has no type declaration: the type
|
||||||
|
@ -2241,7 +2258,7 @@ proc compile*(self: Compiler, ast: seq[Declaration], file: string, lines: seq[tu
|
||||||
self.chunk = chunk
|
self.chunk = chunk
|
||||||
self.ast = ast
|
self.ast = ast
|
||||||
self.file = file
|
self.file = file
|
||||||
self.scopeDepth = 0
|
self.depth = 0
|
||||||
self.currentFunction = nil
|
self.currentFunction = nil
|
||||||
self.currentModule = self.file.extractFilename().replace(".pn", "")
|
self.currentModule = self.file.extractFilename().replace(".pn", "")
|
||||||
self.current = 0
|
self.current = 0
|
||||||
|
@ -2284,7 +2301,7 @@ proc compileModule(self: Compiler, moduleName: string) =
|
||||||
source, persist=true),
|
source, persist=true),
|
||||||
path, self.lexer.getLines(), source, chunk=self.chunk, incremental=true,
|
path, self.lexer.getLines(), source, chunk=self.chunk, incremental=true,
|
||||||
isMainModule=false)
|
isMainModule=false)
|
||||||
self.scopeDepth = 0
|
self.depth = 0
|
||||||
self.current = current
|
self.current = current
|
||||||
self.ast = ast
|
self.ast = ast
|
||||||
self.file = file
|
self.file = file
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
var x: uint64 = 1000000'u64;
|
|
||||||
|
var x: uint64 = 100000'u64;
|
||||||
var y = "just a test";
|
var y = "just a test";
|
||||||
print(y);
|
print(y);
|
||||||
print("Starting GC torture test");
|
print("Starting GC torture test");
|
||||||
|
|
Loading…
Reference in New Issue