parent
0f9440dfb0
commit
545322bf96
|
@ -16,7 +16,7 @@ requires "nim >= 1.6.2"
|
|||
|
||||
task test, "run tests":
|
||||
exec "nim c --gc:arc -d:release --skipProjCfg --skipParentCfg --out:bin/nds src/nds.nim"
|
||||
exec "nim c --gc:arc -d:release --skipProjCfg --skipParentCfg -r tests/test.nim"
|
||||
exec "nim c --gc:arc -d:debug --skipProjCfg --skipParentCfg -r tests/test.nim"
|
||||
exec "rm tests/test"
|
||||
|
||||
task debug, "build nds for debugging":
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import ../scanner
|
||||
import ../chunk
|
||||
import ../config
|
||||
import ../types/ndobject
|
||||
import ../memory
|
||||
|
||||
import types
|
||||
import utils
|
||||
|
@ -15,8 +13,6 @@ import functions
|
|||
import statement
|
||||
|
||||
proc compile*(comp: Compiler) =
|
||||
let enableGCOld = enableGC
|
||||
enableGC = false
|
||||
comp.scanner = newScanner(comp.source)
|
||||
comp.writeChunk(0, opNil)
|
||||
# the starting stackIndex is 0, which points to this nil
|
||||
|
@ -29,4 +25,3 @@ proc compile*(comp: Compiler) =
|
|||
when debugDumpChunk:
|
||||
if not comp.hadError:
|
||||
comp.chunk.disassembleChunk()
|
||||
enableGC = enableGCOld
|
||||
|
|
|
@ -14,8 +14,6 @@ const assertionsVM* = defined(debug) or defined(release) # sanity checks in the
|
|||
const boundsChecks* = defined(debug) or defined(release)
|
||||
const profileInstructions* = defined(ndsprofile) # if true, the time spent on every opcode is measured
|
||||
const debugClosures* = defined(debug) # specific closure debug switches
|
||||
const debugGC* = defined(debug) # debug GC
|
||||
const stressGC* = true # garbage collect on every allocation
|
||||
|
||||
# choose a line editor for the repl
|
||||
const lineEditor = leRdstdin
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
# marking for mark'n'sweep
|
||||
|
||||
import types/ndstack
|
||||
import types/ndobject
|
||||
import types/value
|
||||
import types/ndstring
|
||||
import types/hashtable
|
||||
import types/closure
|
||||
import types/ndlist
|
||||
|
||||
import memory
|
||||
import config
|
||||
import strformat
|
||||
import strutils
|
||||
import pointerutils
|
||||
|
||||
# globals
|
||||
var
|
||||
stack*: Stack[NdValue] = newStack[NdValue](256)
|
||||
globals*: Table[NdValue, NdValue]
|
||||
openUpvalues*: Upvalue[NdValue] = nil
|
||||
|
||||
var grayObjects: seq[ptr NdObject]
|
||||
|
||||
proc markObject*(obj: ptr NdObject) =
|
||||
if obj.isMarked:
|
||||
return
|
||||
|
||||
when debugGC:
|
||||
let niceAddr = cast[uint](obj).toHex()
|
||||
echo &"Marking object {niceAddr}."
|
||||
|
||||
obj.isMarked = true
|
||||
grayObjects.add(obj)
|
||||
|
||||
proc markValue*(val: NdValue) =
|
||||
if not val.isObject():
|
||||
return
|
||||
let obj: ptr NdObject = val.asObject()
|
||||
obj.markObject()
|
||||
|
||||
proc markRoots =
|
||||
var slot = 0
|
||||
var stacklen = stack.len()
|
||||
while slot < stacklen:
|
||||
stack.getIndex(slot).markValue()
|
||||
slot.inc
|
||||
cast[NdTable[NdValue, NdValue]](globals.addr).markObject()
|
||||
|
||||
var upvalue = openUpvalues
|
||||
while upvalue != nil:
|
||||
upvalue.markObject()
|
||||
upvalue = upvalue.next
|
||||
|
||||
proc blackenObject(obj: ptr NdObject) =
|
||||
when debugGC:
|
||||
let niceAddr = cast[uint](obj).toHex()
|
||||
echo &"blackening {niceAddr}."
|
||||
case obj.kind:
|
||||
of noInvalid:
|
||||
raise newException(Defect, "Somewhere an object with invalid object type was created.")
|
||||
of noString:
|
||||
discard
|
||||
of noClosure:
|
||||
let clos = cast[Closure[NdValue]](obj)
|
||||
let count = clos.upvalueCount
|
||||
var i = 0
|
||||
while i < count:
|
||||
let upval = clos.get(i)
|
||||
if upval != nil:
|
||||
markObject(upval)
|
||||
i.inc
|
||||
of noUpvalue:
|
||||
let upval = cast[Upvalue[NdValue]](obj)
|
||||
markValue(upval.location[])
|
||||
of noList:
|
||||
let list = cast[List[NdValue]](obj)
|
||||
let count = list.getLength()
|
||||
var i = 0
|
||||
while i < count:
|
||||
let elem = list.getIndex(i)
|
||||
markValue(elem)
|
||||
i.inc
|
||||
of noTable:
|
||||
let tbl = cast[NdTable[NdValue, NdValue]](obj)
|
||||
for elem in tbl[].iterate():
|
||||
markValue(elem)
|
||||
|
||||
proc traceReferences =
|
||||
while grayObjects.len() > 0:
|
||||
let obj = grayObjects.pop()
|
||||
obj.blackenObject()
|
||||
|
||||
proc freeObj(obj: ptr NdObject) =
|
||||
case obj.kind:
|
||||
of noInvalid:
|
||||
raise newException(Defect, "Somewhere an object with invalid object type was created.")
|
||||
of noString:
|
||||
let str = cast[NdString](obj)
|
||||
str.free()
|
||||
of noTable:
|
||||
let tbl = cast[NdTable[NdValue, NdValue]](obj)
|
||||
tbl.free()
|
||||
of noClosure:
|
||||
let clos = cast[Closure[NdValue]](obj)
|
||||
clos.free()
|
||||
of noList:
|
||||
let list = cast[List[NdValue]](obj)
|
||||
list.free()
|
||||
of noUpvalue:
|
||||
let upval = cast[Upvalue[NdValue]](obj)
|
||||
upval.free()
|
||||
|
||||
proc sweep =
|
||||
var previous: ptr NdObject = nil
|
||||
var obj = objects
|
||||
while obj != nil:
|
||||
if obj.isMarked:
|
||||
obj.isMarked = false
|
||||
previous = obj
|
||||
obj = obj.nextObj
|
||||
else:
|
||||
let unreached = obj
|
||||
obj = obj.nextObj
|
||||
if previous != nil:
|
||||
previous.nextObj = obj
|
||||
else:
|
||||
objects = obj
|
||||
|
||||
unreached.freeObj()
|
||||
|
||||
proc icollectGarbage =
|
||||
if not enableGC:
|
||||
return
|
||||
when debugGC:
|
||||
echo "-- gc begin"
|
||||
|
||||
markRoots()
|
||||
traceReferences()
|
||||
tableRemoveWhite() # defined in types/ndstring
|
||||
sweep()
|
||||
|
||||
when debugGC:
|
||||
echo "-- gc end"
|
||||
|
||||
collectGarbage = icollectGarbage
|
|
@ -1,41 +0,0 @@
|
|||
import config
|
||||
import strutils
|
||||
import strformat
|
||||
|
||||
var collectGarbage*: proc: void {.closure.}
|
||||
|
||||
var enableGC* = false
|
||||
# set by the compiler/VM as needed
|
||||
|
||||
proc ndAlloc*(size: int): pointer =
|
||||
when debugGC:
|
||||
echo &"Allocating {$size} bytes."
|
||||
when stressGC:
|
||||
if enableGC:
|
||||
collectGarbage()
|
||||
alloc(size)
|
||||
|
||||
proc ndAlloc0*(size: int): pointer =
|
||||
when debugGC:
|
||||
echo &"Allocating and zeroing {$size} bytes."
|
||||
when stressGC:
|
||||
if enableGC:
|
||||
collectGarbage()
|
||||
alloc0(size)
|
||||
|
||||
proc ndRealloc*[T](source: ptr T, oldsize: int, newsize: int): ptr T =
|
||||
when debugGC:
|
||||
let niceAddr = cast[uint](source).toHex()
|
||||
echo &"Reallocating {niceAddr} from size {$oldsize} to {$newsize}."
|
||||
if newsize > oldsize:
|
||||
when stressGC:
|
||||
if enableGC:
|
||||
collectGarbage()
|
||||
discard
|
||||
cast[ptr T](realloc(source, newsize))
|
||||
|
||||
proc ndDealloc*[T](mem: ptr T) =
|
||||
when debugGC:
|
||||
let niceAddr = cast[uint](mem).toHex()
|
||||
echo &"Freeing {niceAddr}"
|
||||
dealloc(mem)
|
|
@ -1,33 +1,23 @@
|
|||
import strformat
|
||||
import strutils
|
||||
import ndobject
|
||||
import ../memory
|
||||
|
||||
type
|
||||
UpvalueObj[T] = object of NdObject
|
||||
UpvalueObj[T] = object
|
||||
location*: ptr T
|
||||
next*: Upvalue[T]
|
||||
closed*: T
|
||||
|
||||
Upvalue*[T] = ptr UpvalueObj[T]
|
||||
|
||||
ClosureObj[T] = object of NdObject
|
||||
ClosureObj[T] = object
|
||||
start: ptr uint8
|
||||
upvalueCount*: int
|
||||
upvalues: UncheckedArray[Upvalue[T]]
|
||||
|
||||
Closure*[T] = ptr ClosureObj[T]
|
||||
|
||||
proc free*[T](clos: Closure[T]) =
|
||||
ndDealloc(clos)
|
||||
|
||||
proc free*[T](upval: Upvalue[T]) =
|
||||
ndDealloc(upval)
|
||||
|
||||
proc newClosure*[T](start: ptr uint8, upvalueCount: int): Closure[T] =
|
||||
result = cast[Closure[T]](ndAlloc0(8 * upvalueCount + sizeof(ClosureObj[T])))
|
||||
result.kind = noClosure
|
||||
result.newObject()
|
||||
result = cast[Closure[T]](alloc0(8 * upvalueCount + sizeof(ClosureObj[T])))
|
||||
result.start = start
|
||||
result.upvalueCount = upvalueCount
|
||||
for i in 0 .. upvalueCount:
|
||||
|
@ -57,9 +47,7 @@ proc debugStr*[T](clos: Closure[T]): string =
|
|||
result &= ")"
|
||||
|
||||
proc newUpvalue*[T](location: ptr T): Upvalue[T] =
|
||||
result = cast[Upvalue[T]](ndAlloc0(sizeof(UpvalueObj[T])))
|
||||
result.kind = noUpvalue
|
||||
result.newObject()
|
||||
result = cast[Upvalue[T]](alloc0(sizeof(UpvalueObj[T])))
|
||||
result.location = location
|
||||
result.next = nil
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# The hash table implementation for string interning and globals
|
||||
|
||||
import bitops
|
||||
import ndobject
|
||||
import ../memory
|
||||
|
||||
const tableMaxLoad = 0.75
|
||||
const tableInitSize = 8
|
||||
|
@ -16,7 +14,7 @@ type
|
|||
key: U
|
||||
value: V
|
||||
|
||||
Table*[U, V] = object of NdObject
|
||||
Table*[U, V] = object
|
||||
count: int
|
||||
cap: int
|
||||
entries: ptr UncheckedArray[Entry[U, V]]
|
||||
|
@ -26,24 +24,16 @@ type
|
|||
proc newTable*[U, V]: Table[U, V] =
|
||||
result.cap = 0
|
||||
result.count = 0
|
||||
result.kind = noTable
|
||||
# not calling result.newObject()
|
||||
|
||||
proc newNdTable*[U, V]: NdTable[U, V] =
|
||||
result = cast[NdTable[U, V]](ndAlloc(sizeof(Table[U, V])))
|
||||
result[].kind = noTable
|
||||
result.newObject()
|
||||
result = cast[NdTable[U, V]](alloc(sizeof(Table[U, V])))
|
||||
result[].cap = 0
|
||||
result[].count = 0
|
||||
result[].entries = nil # must be set, because dealloc will be ran on it otherwise
|
||||
|
||||
proc free*[U, V](tbl: Table[U, V]) =
|
||||
proc free*[U, V](tbl: var Table[U, V]) =
|
||||
if tbl.entries != nil:
|
||||
ndDealloc(tbl.entries)
|
||||
|
||||
proc free*[U, V](tbl: NdTable[U, V]) =
|
||||
tbl[].free()
|
||||
ndDealloc(tbl)
|
||||
dealloc(tbl.entries)
|
||||
|
||||
proc findEntry[U, V](entries: ptr UncheckedArray[Entry[U, V]], cap: int, key: U): ptr Entry[U, V] =
|
||||
mixin fnv1a, equal
|
||||
|
@ -70,7 +60,7 @@ proc grow[U, V](tbl: var Table[U, V]): int =
|
|||
tableInitSize
|
||||
|
||||
proc adjustCapacity[U, V](tbl: var Table[U, V], newcap: int) =
|
||||
let entries: ptr UncheckedArray[Entry[U, V]] = cast[ptr UncheckedArray[Entry[U, V]]](ndAlloc0(newcap * sizeof(Entry[U, V])))
|
||||
let entries: ptr UncheckedArray[Entry[U, V]] = cast[ptr UncheckedArray[Entry[U, V]]](alloc0(newcap * sizeof(Entry[U, V])))
|
||||
tbl.count = 0
|
||||
|
||||
if tbl.entries != nil:
|
||||
|
@ -83,7 +73,7 @@ proc adjustCapacity[U, V](tbl: var Table[U, V], newcap: int) =
|
|||
dest[].entryStatus = esAlive
|
||||
tbl.count.inc
|
||||
|
||||
ndDealloc(tbl.entries)
|
||||
dealloc(tbl.entries)
|
||||
|
||||
tbl.entries = entries
|
||||
tbl.cap = newcap
|
||||
|
@ -151,14 +141,6 @@ proc tableDelete*[U, V](tbl: Table[U, V], key: U): bool =
|
|||
proc getLength*[U, V](tbl: NdTable[U, V]): int =
|
||||
tbl.count.int
|
||||
|
||||
iterator iterate*[T](tbl: Table[T, T]): T =
|
||||
if tbl.entries != nil:
|
||||
for i in countup(0, tbl.cap-1):
|
||||
let entry = tbl.entries[i]
|
||||
if entry.entryStatus == esAlive:
|
||||
yield entry.key
|
||||
yield entry.value
|
||||
|
||||
proc `$`*[U, V](tbl: NdTable[U, V], tagged: V): string =
|
||||
if tbl[].count == 0:
|
||||
return "@{}"
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import strformat
|
||||
import ndobject
|
||||
import ../memory
|
||||
|
||||
# configure ndlist here
|
||||
import ../config
|
||||
|
@ -13,41 +11,37 @@ const growthFactor = 2
|
|||
const startCap = 8
|
||||
|
||||
type
|
||||
ListObj[T] = object of NdObject
|
||||
ListObj[T] = object
|
||||
len: int
|
||||
cap: int
|
||||
entries: ptr UncheckedArray[T]
|
||||
List*[T] = ptr ListObj[T]
|
||||
|
||||
proc newList*[T](): List[T] =
|
||||
result = cast[List[T]](ndAlloc(sizeof(ListObj[T])))
|
||||
result = cast[List[T]](alloc(sizeof(ListObj[T])))
|
||||
result.len = 0
|
||||
result.cap = 0
|
||||
result.kind = noList
|
||||
result.newObject()
|
||||
|
||||
proc newListCopymem*[T](start: ptr T, len: int): List[T] =
|
||||
result = newList[T]()
|
||||
result.len = len
|
||||
result.cap = len
|
||||
result.entries = cast[ptr UncheckedArray[T]](ndAlloc(sizeof(T) * len))
|
||||
result.entries = cast[ptr UncheckedArray[T]](alloc(sizeof(T) * len))
|
||||
copyMem(result.entries, start, len * sizeof(T))
|
||||
|
||||
proc free*[T](list: List[T]) =
|
||||
proc free*[T](list: var List[T]) =
|
||||
## dealloc's the list object
|
||||
list.ndDealloc()
|
||||
list.dealloc()
|
||||
|
||||
proc grow[T](list: var List[T]) =
|
||||
## growth the list's capacity
|
||||
let oldcap = list.cap
|
||||
let oldsize = oldcap * sizeof(T)
|
||||
let newcap = if oldcap == 0: startCap else: oldcap * growthFactor
|
||||
let newcap = if list.cap == 0: startCap else: list.cap * growthFactor
|
||||
let size = newcap * sizeof(T)
|
||||
if oldcap == 0:
|
||||
list.entries = cast[ptr UncheckedArray[T]](size.ndAlloc())
|
||||
if list.cap == 0:
|
||||
list.entries = cast[ptr UncheckedArray[T]](size.alloc())
|
||||
list.cap = newcap
|
||||
else:
|
||||
list.entries = cast[ptr UncheckedArray[T]](list.entries.ndRealloc(oldsize, size))
|
||||
list.entries = cast[ptr UncheckedArray[T]](list.entries.realloc(size))
|
||||
list.cap = newcap
|
||||
|
||||
proc add*[T](list: var List[T], item: T) =
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# generic object type
|
||||
|
||||
import ../config
|
||||
|
||||
type
|
||||
NdObjectKind* = enum
|
||||
noInvalid, noString, noClosure, noUpvalue, noList, noTable
|
||||
NdObject* = object of RootObj
|
||||
kind*: NdObjectKind
|
||||
isMarked*: bool
|
||||
nextObj*: ptr NdObject
|
||||
|
||||
var objects*: ptr NdObject
|
||||
|
||||
template newObject*(obj: ptr NdObject) =
|
||||
when assertionsVM:
|
||||
if obj.kind == noInvalid:
|
||||
raise newException(Defect, "Somewhere an object with invalid object type was created.")
|
||||
|
||||
obj.nextObj = objects
|
||||
objects = obj
|
||||
|
|
@ -1,16 +1,13 @@
|
|||
import hashtable
|
||||
import ndobject
|
||||
import ../memory
|
||||
|
||||
type
|
||||
NdStringObj* = object of NdObject
|
||||
NdString* = ptr object
|
||||
len*: uint32
|
||||
hash*: uint32
|
||||
chars*: UncheckedArray[char]
|
||||
NdString* = ptr NdStringObj
|
||||
|
||||
proc free*(ndStr: NdString) =
|
||||
ndDealloc(ndStr)
|
||||
proc free*(ndStr: var NdString) =
|
||||
dealloc(ndStr)
|
||||
|
||||
# hashes
|
||||
|
||||
|
@ -35,6 +32,7 @@ proc fnv1a*(str: char): int = # SHOULD RETURN THE SAME AS A 1 LENGTH STRING
|
|||
hash *= 16777619
|
||||
return hash.int
|
||||
|
||||
|
||||
# equals
|
||||
|
||||
proc equal*(left, right: NdString): bool =
|
||||
|
@ -42,13 +40,6 @@ proc equal*(left, right: NdString): bool =
|
|||
|
||||
var ndStrings = newTable[NdString, NdString]()
|
||||
|
||||
proc tableRemoveWhite* =
|
||||
for elem in ndStrings.iterate():
|
||||
if elem != nil:
|
||||
# vals are nil, keys are not
|
||||
if not elem.isMarked:
|
||||
discard tableDelete(ndStrings, elem)
|
||||
|
||||
proc newString*(str: string): NdString =
|
||||
let strlen = str.len()
|
||||
let hash = str.fnv1a()
|
||||
|
@ -62,12 +53,10 @@ proc newString*(str: string): NdString =
|
|||
if interned != nil:
|
||||
return interned
|
||||
|
||||
let len = sizeof(NdStringObj) + strlen
|
||||
result = cast[NdString](ndAlloc(len))
|
||||
let len = 8 + strlen
|
||||
result = cast[NdString](alloc(len))
|
||||
result.len = strlen.uint32
|
||||
result.hash = hash.uint32
|
||||
result.kind = noString
|
||||
result.newObject()
|
||||
if strlen > 0:
|
||||
copyMem(result.chars[0].unsafeAddr, str[0].unsafeAddr, strlen)
|
||||
|
||||
|
@ -80,16 +69,15 @@ proc newString*(str: char): NdString =
|
|||
if interned != nil:
|
||||
return interned
|
||||
|
||||
let len = sizeof(NdStringObj) + 1
|
||||
result = cast[NdString](ndAlloc(len))
|
||||
let len = 8 + 1
|
||||
result = cast[NdString](alloc(len))
|
||||
result.len = 1.uint32
|
||||
result.hash = hash.uint32
|
||||
result.chars[0] = str
|
||||
result.kind = noString
|
||||
result.newObject()
|
||||
|
||||
discard ndStrings.tableSet(result, nil)
|
||||
|
||||
|
||||
proc resetInternedStrings* =
|
||||
ndStrings.free()
|
||||
ndStrings = newTable[NdString, NdString]()
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import ../pointerutils
|
||||
|
||||
import ndobject
|
||||
import ../memory
|
||||
|
||||
# configure stacks here
|
||||
import ../config
|
||||
#const boundsChecks = defined(debug)
|
||||
|
@ -18,7 +15,7 @@ type
|
|||
cap*: int
|
||||
|
||||
proc newStack*[T](startingCap: int): Stack[T] =
|
||||
result.start = cast[ptr T](ndAlloc(startingCap * sizeof(T)))
|
||||
result.start = cast[ptr T](alloc(startingCap * sizeof(T)))
|
||||
result.top = result.start.psub(sizeof(T))
|
||||
result.cap = startingCap
|
||||
|
||||
|
@ -26,16 +23,14 @@ proc free*[T](stack: var Stack[T]) =
|
|||
## dealloc's the stack object
|
||||
## if the stack contains pointers, those should be freed before destroying the stack
|
||||
stack.cap = 0
|
||||
stack.start.ndDealloc()
|
||||
stack.start.dealloc()
|
||||
stack.start = nil
|
||||
stack.top = nil
|
||||
|
||||
proc grow[T](stack: var Stack[T], len: int) =
|
||||
## growth the stack's capacity and increments the top's index by one
|
||||
let oldcap = stack.cap
|
||||
let newcap = stack.cap * growthFactor
|
||||
stack.cap = newcap
|
||||
stack.start = cast[ptr T](ndRealloc(stack.start, oldcap * sizeof(T), newcap * sizeof(T)))
|
||||
stack.cap *= growthFactor
|
||||
stack.start = cast[ptr T](realloc(stack.start, stack.cap * sizeof(T)))
|
||||
stack.top = stack.start.padd(len * sizeof(T))
|
||||
|
||||
proc shrink[T](stack: var Stack[T]) =
|
|
@ -1,12 +1,7 @@
|
|||
import strformat
|
||||
import strutils
|
||||
import bitops
|
||||
export bitops
|
||||
|
||||
import ../config
|
||||
|
||||
import ndobject
|
||||
export ndobject
|
||||
import ndstring
|
||||
import ndlist
|
||||
import hashtable
|
||||
|
@ -26,12 +21,12 @@ type
|
|||
# bits 49-48 determine type:
|
||||
# if bit 63 is 0:
|
||||
# 00 -> nil or bool (singletons)
|
||||
# 01 -> object (string, closure, list, table, upvalue) (formerly string)
|
||||
# 01 -> string
|
||||
# 10 -> funct
|
||||
# 11 -> (formerly closure) free
|
||||
# 11 -> closure
|
||||
# if bit 63 is 1:
|
||||
# 00 -> (formerly list) free
|
||||
# 01 -> (formerly table) free
|
||||
# 00 -> list
|
||||
# 01 -> table
|
||||
# 10 -> native funct
|
||||
# 11 -> unused for now
|
||||
|
||||
|
@ -42,14 +37,13 @@ const ndTrue* = 0x7ffc000000000002'u
|
|||
const ndFalse* = 0x7ffc000000000003'u
|
||||
|
||||
# 0111 1111 1111 11*01* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
||||
#const tagString* = 0x7ffd000000000000'u
|
||||
const tagObject* = 0x7ffd000000000000'u
|
||||
const tagString* = 0x7ffd000000000000'u
|
||||
# 0111 1111 1111 11*10* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
||||
const tagFunct* = 0x7ffe000000000000'u
|
||||
# you can imagine these now...
|
||||
#const tagClosure* = 0x7fff000000000000'u
|
||||
#const tagList* = 0xfffc000000000000'u
|
||||
#const tagTable* = 0xfffd000000000000'u
|
||||
const tagClosure* = 0x7fff000000000000'u
|
||||
const tagList* = 0xfffc000000000000'u
|
||||
const tagTable* = 0xfffd000000000000'u
|
||||
const tagNative* = 0xfffe000000000000'u
|
||||
|
||||
const mask48* = 0xffff000000000000'u
|
||||
|
@ -65,26 +59,20 @@ template isBool*(val: NdValue): bool =
|
|||
template isFloat*(val: NdValue): bool =
|
||||
val.bitand(qNan) != qNan
|
||||
|
||||
template asObject*(val: NdValue): ptr NdObject =
|
||||
cast[ptr NdObject](val.bitand(mask48.bitnot))
|
||||
|
||||
template isObject*(val: NdValue): bool =
|
||||
val.bitand(mask48) == tagObject
|
||||
|
||||
template isString*(val: NdValue): bool =
|
||||
val.isObject() and val.asObject().kind == noString
|
||||
val.bitand(mask48) == tagString
|
||||
|
||||
template isFunct*(val: NdValue): bool =
|
||||
val.bitand(mask48) == tagFunct
|
||||
|
||||
template isClosure*(val: NdValue): bool =
|
||||
val.isObject() and val.asObject().kind == noClosure
|
||||
val.bitand(mask48) == tagClosure
|
||||
|
||||
template isList*(val: NdValue): bool =
|
||||
val.isObject() and val.asObject().kind == noList
|
||||
val.bitand(mask48) == tagList
|
||||
|
||||
template isTable*(val: NdValue): bool =
|
||||
val.isObject() and val.asObject().kind == noTable
|
||||
val.bitand(mask48) == tagTable
|
||||
|
||||
template isNative*(val: NdValue): bool =
|
||||
val.bitand(mask48) == tagNative
|
||||
|
@ -124,7 +112,7 @@ template fromFloat*(val: float): NdValue =
|
|||
cast[NdValue](val)
|
||||
|
||||
template fromNdString*(val: NdString): NdValue =
|
||||
cast[uint](val).bitor(tagObject)
|
||||
cast[uint](val).bitor(tagString)
|
||||
|
||||
template fromNimString*(sval: string): NdValue =
|
||||
fromNdString(newString(sval))
|
||||
|
@ -133,13 +121,13 @@ template fromFunct*(val: ptr uint8): NdValue =
|
|||
cast[uint](val).bitor(tagFunct)
|
||||
|
||||
template fromClosure*(val: Closure[NdValue]): NdValue =
|
||||
cast[uint](val).bitor(tagObject)
|
||||
cast[uint](val).bitor(tagClosure)
|
||||
|
||||
template fromList*(val: List[NdValue]): NdValue =
|
||||
cast[uint](val).bitor(tagObject)
|
||||
cast[uint](val).bitor(tagList)
|
||||
|
||||
template fromTable*(val: NdTable[NdValue, NdValue]): NdValue =
|
||||
cast[uint](val).bitor(tagObject)
|
||||
cast[uint](val).bitor(tagTable)
|
||||
|
||||
template fromNative*(val: Native): NdValue =
|
||||
cast[uint](val).bitor(tagNative)
|
||||
|
|
|
@ -4,8 +4,7 @@ import chunk
|
|||
import config
|
||||
import pointerutils
|
||||
|
||||
import types/ndstack
|
||||
import types/ndobject
|
||||
import types/stack
|
||||
import types/ndstring
|
||||
import bitops # needed for value's templates
|
||||
import types/value
|
||||
|
@ -16,9 +15,6 @@ import types/native
|
|||
|
||||
import lib/main
|
||||
|
||||
import memory
|
||||
import gc
|
||||
|
||||
when debugVM:
|
||||
import terminal
|
||||
|
||||
|
@ -36,17 +32,20 @@ type
|
|||
Frame = object
|
||||
stackBottom: int # the absolute index of where 0 inside the frame is
|
||||
returnIp: ptr uint8
|
||||
# closure: Closure[NdValue]
|
||||
|
||||
InterpretResult* = enum
|
||||
irOK, irRuntimeError
|
||||
|
||||
proc run*(chunk: Chunk): InterpretResult =
|
||||
enableGC = false
|
||||
|
||||
var
|
||||
ip: ptr uint8 = chunk.code[0].unsafeAddr
|
||||
stack: Stack[NdValue] = newStack[NdValue](256)
|
||||
hadError: bool
|
||||
globals: Table[NdValue, NdValue]
|
||||
frames: Stack[Frame] = newStack[Frame](4)
|
||||
objects: ptr NdObject
|
||||
openUpvalues: Upvalue[NdValue] = nil
|
||||
|
||||
proc runtimeError(msg: string) =
|
||||
let ii = ip.pdiff(chunk.code[0].unsafeAddr)
|
||||
|
@ -142,7 +141,6 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
let globalsVal = cast[NdTable[NdValue, NdValue]](globals.addr)
|
||||
discard globals.tableSet(globalsKey, globalsVal.fromTable())
|
||||
|
||||
enableGC = true
|
||||
while true:
|
||||
{.computedgoto.} # See https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma
|
||||
|
||||
|
@ -161,6 +159,15 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
msg &= "]"
|
||||
echo msg
|
||||
|
||||
when debugClosures:
|
||||
msg = " Closures: [ "
|
||||
#for i in 0 .. frames.high():
|
||||
#if frames[i].closure != nil:
|
||||
# msg &= debugStr(frames[i].closure) & " "
|
||||
msg &= "]"
|
||||
echo msg
|
||||
|
||||
|
||||
var ii = ip.pdiff(chunk.code[0].unsafeAddr) - 1
|
||||
var ll = -1
|
||||
setForegroundColor(fgYellow)
|
||||
|
@ -354,7 +361,7 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
of opSetUpvalue:
|
||||
let slot = ip.readDU8()
|
||||
when debugClosures:
|
||||
echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {cclosure.upvalueCount}"
|
||||
echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {frames.peek().closure.upvalueCount}"
|
||||
cclosure.get(slot).write(stack.peek())
|
||||
of opCloseUpvalue:
|
||||
let slot = ip.readDU8()
|
||||
|
@ -391,7 +398,9 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
let times = runcounts[op]
|
||||
echo &"OpCode: {op} total duration {dur} ms {times} times"
|
||||
|
||||
enableGC = false
|
||||
stack.free()
|
||||
frames.free()
|
||||
globals.free()
|
||||
|
||||
if hadError:
|
||||
irRuntimeError
|
||||
|
|
Loading…
Reference in New Issue