custom string object
This commit is contained in:
parent
90d04f13f6
commit
8031c7720d
|
@ -1,9 +1,9 @@
|
|||
local function fib(n)
|
||||
if n < 2 then
|
||||
return 1
|
||||
return 1.5
|
||||
else
|
||||
return fib(n-1) + fib(n-2)
|
||||
end
|
||||
end
|
||||
|
||||
print(fib(37))
|
||||
print(fib(37.2))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var fib = funct(n)
|
||||
if (n < 2) ^result = 1
|
||||
if (n < 2) ^result = 1.5
|
||||
else ^result = fib(n-1) + fib(n-2)
|
||||
;
|
||||
|
||||
print fib(37);
|
||||
print fib(37.2);
|
|
@ -1,7 +1,7 @@
|
|||
def fib(n):
|
||||
if n < 2:
|
||||
return 1
|
||||
return 1.5
|
||||
else:
|
||||
return fib(n-1) + fib(n-2)
|
||||
|
||||
print(fib(37))
|
||||
print(fib(37.2))
|
|
@ -648,12 +648,12 @@ proc parseFunct(comp: Compiler) =
|
|||
dec comp.stackIndex # the previous end scope did not put anything on the stack, it is jumped over
|
||||
|
||||
# get ndvalue functions:
|
||||
let ndFunct = newNdFunction(functII)
|
||||
let ntFunct = newNdFunction(functII)
|
||||
|
||||
# end of function declaration:
|
||||
comp.patchJump(jumpOverBody)
|
||||
# put the fn object on the stack
|
||||
comp.writeConstant(ndFunct)
|
||||
comp.writeConstant(ntFunct)
|
||||
|
||||
tkFunct.genRule(parseFunct, nop, pcNone)
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ proc skipWhitespace(scanner: Scanner) =
|
|||
discard scanner.advance()
|
||||
of '/':
|
||||
if scanner.peekNext() == '/':
|
||||
while scanner.peek != '\n' and not scanner.isAtEnd():
|
||||
while not scanner.isAtEnd() and scanner.peek != '\n' :
|
||||
discard scanner.advance()
|
||||
else:
|
||||
return
|
||||
|
@ -85,7 +85,7 @@ proc skipWhitespace(scanner: Scanner) =
|
|||
return
|
||||
|
||||
proc scanString(scanner: Scanner): Token =
|
||||
while scanner.peek() != '\"' and not scanner.isAtEnd():
|
||||
while not scanner.isAtEnd() and scanner.peek() != '\"' :
|
||||
if scanner.peek() == '\n':
|
||||
scanner.line.inc
|
||||
discard scanner.advance()
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
type
|
||||
NdString* = ptr object
|
||||
len: uint32
|
||||
chars: UncheckedArray[char]
|
||||
|
||||
proc newString*(str: string): NdString =
|
||||
let strlen = str.len()
|
||||
let len = 4 + strlen
|
||||
result = cast[NdString](alloc(len))
|
||||
result.len = strlen.uint32
|
||||
copyMem(result.chars[0].unsafeAddr, str[0].unsafeAddr, strlen)
|
||||
|
||||
proc `$`*(ndStr: NdString): string =
|
||||
result = newString(ndStr.len.int)
|
||||
copyMem(result[0].unsafeAddr, ndStr.chars[0].unsafeAddr, ndStr.len.int)
|
||||
|
||||
proc `&`*(left, right: NdString): NdString =
|
||||
# TODO optimize this later when strings will be benchmarked
|
||||
newString($left & $right)
|
|
@ -2,13 +2,16 @@ import ../pointerutils
|
|||
|
||||
# configure stacks here
|
||||
const boundsChecks = false
|
||||
# boundsChecks default: false, true has a large performance impact, and emitting correct code is on the compiler's job
|
||||
# boundsChecking is only meant for debugging
|
||||
const growthFactor = 2
|
||||
# should be a natural number larger than 1
|
||||
|
||||
type
|
||||
Stack*[T] = object
|
||||
top: ptr T
|
||||
start: ptr T
|
||||
cap: int
|
||||
cap*: int
|
||||
|
||||
proc newStack*[T](startingCap: int): Stack[T] =
|
||||
result.start = cast[ptr T](alloc(startingCap * sizeof(T)))
|
||||
|
@ -25,8 +28,8 @@ proc destroyStack*[T](stack: var Stack[T]) =
|
|||
|
||||
proc grow[T](stack: var Stack[T], len: int) {.inline.} =
|
||||
## growth the stack's capacity and increments the top's index by one
|
||||
stack.start = cast[ptr T](realloc(stack.start, stack.cap * growthFactor))
|
||||
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]) {.inline.} =
|
||||
|
@ -57,6 +60,9 @@ proc pop*[T](stack: var Stack[T]): T {.inline.} =
|
|||
stack.top = stack.top.psub(sizeof(T))
|
||||
|
||||
proc peek*[T](stack: Stack[T]): var T {.inline.} =
|
||||
when boundsChecks:
|
||||
if stack.top == nil or stack.top.pless(stack.start):
|
||||
raise newException(Defect, "Stacktop is nil or smaller than start.")
|
||||
stack.top[]
|
||||
|
||||
proc deleteTopN*[T](stack: var Stack[T], n: Natural) =
|
||||
|
@ -71,6 +77,12 @@ proc getIndex*[T](stack: Stack[T], index: int): T =
|
|||
raise newException(Defect, "Attempt to getIndex with an index out of bounds.")
|
||||
stack.start.padd(index * sizeof(T))[]
|
||||
|
||||
proc getIndexNeg*[T](stack: Stack[T], index: int): T =
|
||||
when boundsChecks:
|
||||
if index < 0 or index >= stack.len():
|
||||
raise newException(Defect, "Attempt to getIndexNeg with an index out of bounds.")
|
||||
stack.top.psub(index * sizeof(T))[]
|
||||
|
||||
template `[]`*[T](stack: Stack[T], index: int): T =
|
||||
stack.getIndex(index)
|
||||
|
||||
|
@ -80,5 +92,12 @@ proc setIndex*[T](stack: var Stack[T], index: int, item: T) =
|
|||
raise newException(Defect, "Attempt to getIndex with an index out of bounds.")
|
||||
stack.start.padd(index * sizeof(T))[]= item
|
||||
|
||||
proc setIndexNeg*[T](stack: Stack[T], index: int, item: T) =
|
||||
when boundsChecks:
|
||||
if index < 0 or index >= stack.len():
|
||||
raise newException(Defect, "Attempt to setIndexNeg with an index out of bounds.")
|
||||
stack.top.psub(index * sizeof(T))[] = item
|
||||
|
||||
|
||||
template `[]=`*[T](stack: var Stack[T], index: int, item: T) =
|
||||
stack.setIndex(index, item)
|
||||
|
|
70
value.nim
70
value.nim
|
@ -1,23 +1,23 @@
|
|||
import strformat
|
||||
import types/ndstring
|
||||
|
||||
type
|
||||
NdType* = enum
|
||||
ndNil, ndBool, ndFloat, ndString,
|
||||
ndFunct,
|
||||
ntNil, ntBool, ntFloat, ntString,
|
||||
ntFunct,
|
||||
|
||||
type
|
||||
NdValue* = object
|
||||
case ndType*: NdType:
|
||||
of ndNil:
|
||||
of ntNil:
|
||||
discard
|
||||
of ndBool:
|
||||
of ntBool:
|
||||
boolValue*: bool
|
||||
of ndFloat:
|
||||
of ntFloat:
|
||||
floatValue*: float64
|
||||
of ndString:
|
||||
stringValue*: string
|
||||
of ndFunct:
|
||||
placeholder*: string
|
||||
of ntString:
|
||||
stringValue*: NdString
|
||||
of ntFunct:
|
||||
entryII*: int # entry instruction index
|
||||
|
||||
NatReturn* = object
|
||||
|
@ -28,19 +28,19 @@ type
|
|||
|
||||
proc `$`*(val: NdValue): string =
|
||||
case val.ndType:
|
||||
of ndFloat:
|
||||
of ntFloat:
|
||||
return $val.floatValue
|
||||
of ndBool:
|
||||
of ntBool:
|
||||
return $val.boolValue
|
||||
of ndNil:
|
||||
of ntNil:
|
||||
return "nil"
|
||||
of ndString:
|
||||
return val.stringValue
|
||||
of ndFunct:
|
||||
of ntString:
|
||||
return $val.stringValue
|
||||
of ntFunct:
|
||||
return &"Function object: {val.entryII}"
|
||||
|
||||
proc isFalsey*(val: NdValue): bool =
|
||||
val.ndType in {ndNil} or (val.ndType == ndBool and not val.boolValue)
|
||||
val.ndType in {ntNil} or (val.ndType == ntBool and not val.boolValue)
|
||||
|
||||
template isTruthy*(val: NdValue): bool =
|
||||
not isFalsey(val)
|
||||
|
@ -50,33 +50,33 @@ proc equal*(val, right: NdValue): bool =
|
|||
false
|
||||
else:
|
||||
case val.ndType:
|
||||
of ndFloat:
|
||||
of ntFloat:
|
||||
val.floatValue == right.floatValue
|
||||
of ndBool:
|
||||
of ntBool:
|
||||
val.boolValue == right.boolValue
|
||||
of ndNil:
|
||||
of ntNil:
|
||||
true
|
||||
of ndString:
|
||||
of ntString:
|
||||
val.stringValue == right.stringValue
|
||||
of ndFunct:
|
||||
of ntFunct:
|
||||
val.entryII == right.entryII
|
||||
|
||||
# NIM VALUE TO KON VALUE WRAPPERS
|
||||
|
||||
proc toNdValue*(val: float): NdValue =
|
||||
NdValue(ndType: ndFloat, floatValue: val)
|
||||
NdValue(ndType: ntFloat, floatValue: val)
|
||||
|
||||
proc toNdValue*(val: bool): NdValue =
|
||||
NdValue(ndType: ndBool, boolValue: val)
|
||||
NdValue(ndType: ntBool, boolValue: val)
|
||||
|
||||
proc toNdValue*(val: string): NdValue =
|
||||
NdValue(ndType: ndString, stringValue: val)
|
||||
NdValue(ndType: ntString, stringValue: val.newString())
|
||||
|
||||
proc newNdFunction*(ii: int): NdValue =
|
||||
NdValue(ndType: ndFunct, entryII: ii)
|
||||
NdValue(ndType: ntFunct, entryII: ii)
|
||||
|
||||
proc toNdValue*: NdValue =
|
||||
NdValue(ndType: ndNil)
|
||||
NdValue(ndType: ntNil)
|
||||
|
||||
# NatReturn misc
|
||||
|
||||
|
@ -89,51 +89,51 @@ const natOk* = NatReturn(ok: true)
|
|||
# NOTE: these operations can return ktTypeError with a message if types are invalid
|
||||
|
||||
proc negate*(val: var NdValue): NatReturn {.inline.} =
|
||||
if (val.ndType != ndFloat):
|
||||
if (val.ndType != ntFloat):
|
||||
return natError("Operand must be a number.")
|
||||
else:
|
||||
val.floatValue = -val.floatValue
|
||||
return natOk
|
||||
|
||||
proc add*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
||||
if val.ndType == ndFloat and right.ndType == ndFloat:
|
||||
if val.ndType == ntFloat and right.ndType == ntFloat:
|
||||
val.floatValue += right.floatValue
|
||||
elif val.ndType == ndString and right.ndType == ndString:
|
||||
val.stringValue &= right.stringValue
|
||||
elif val.ndType == ntString and right.ndType == ntString:
|
||||
val.stringValue = val.stringValue & right.stringValue
|
||||
else:
|
||||
return natError(&"Attempt to add types {val.ndType} and {right.ndType}.")
|
||||
return natOk
|
||||
|
||||
proc subtract*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
||||
if val.ndType == ndFloat and right.ndType == ndFloat:
|
||||
if val.ndType == ntFloat and right.ndType == ntFloat:
|
||||
val.floatValue -= right.floatValue
|
||||
else:
|
||||
return natError(&"Attempt to subtract types {val.ndType} and {right.ndType}.")
|
||||
return natOk
|
||||
|
||||
proc multiply*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
||||
if val.ndType == ndFloat and right.ndType == ndFloat:
|
||||
if val.ndType == ntFloat and right.ndType == ntFloat:
|
||||
val.floatValue *= right.floatValue
|
||||
else:
|
||||
return natError(&"Attempt to multiply types {val.ndType} and {right.ndType}.")
|
||||
return natOk
|
||||
|
||||
proc divide*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
||||
if val.ndType == ndFloat and right.ndType == ndFloat:
|
||||
if val.ndType == ntFloat and right.ndType == ntFloat:
|
||||
val.floatValue /= right.floatValue
|
||||
else:
|
||||
return natError(&"Attempt to divide types {val.ndType} and {right.ndType}.")
|
||||
return natOk
|
||||
|
||||
proc less*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
||||
if val.ndType == ndFloat and right.ndType == ndFloat:
|
||||
if val.ndType == ntFloat and right.ndType == ntFloat:
|
||||
val = toNdValue(val.floatValue < right.floatValue)
|
||||
else:
|
||||
return natError(&"Attempt to compare types {val.ndType} and {right.ndType}.")
|
||||
return natOk
|
||||
|
||||
proc greater*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
||||
if val.ndType == ndFloat and right.ndType == ndFloat:
|
||||
if val.ndType == ntFloat and right.ndType == ntFloat:
|
||||
val = toNdValue(val.floatValue > right.floatValue)
|
||||
else:
|
||||
return natError(&"Attempt to compare types {val.ndType} and {right.ndType}.")
|
||||
|
|
40
vm.nim
40
vm.nim
|
@ -7,11 +7,18 @@ import config
|
|||
import pointerutils
|
||||
|
||||
import types/stack
|
||||
import types/ndstring
|
||||
|
||||
when profileInstructions:
|
||||
import times
|
||||
import std/monotimes
|
||||
|
||||
# not a perfect profiling approach, doing random stacktrace dumps
|
||||
# x amount of times per second to get the current source line
|
||||
# would be better
|
||||
var durations: array[OpCode, Duration]
|
||||
var runcounts: array[OpCode, float64]
|
||||
|
||||
type
|
||||
Frame = object
|
||||
stackBottom: int # the absolute index of where 0 inside the frame is
|
||||
|
@ -20,21 +27,14 @@ type
|
|||
InterpretResult* = enum
|
||||
irOK, irRuntimeError
|
||||
|
||||
when profileInstructions:
|
||||
# not a perfect profiling approach, doing random stacktrace dumps
|
||||
# x amount of times per second to get the current source line
|
||||
# would be better
|
||||
var durations: array[OpCode, Duration]
|
||||
|
||||
proc run*(chunk: Chunk): InterpretResult =
|
||||
|
||||
var
|
||||
ip: ptr uint8 = chunk.code[0].unsafeAddr
|
||||
#stack: seq[NdValue] = newSeqOfCap[NdValue](256)
|
||||
stack: Stack[NdValue] = newStack[NdValue](256)
|
||||
hadError: bool
|
||||
globals: Table[string, NdValue]
|
||||
frames: seq[Frame] = newSeqOfCap[Frame](4)
|
||||
frames: Stack[Frame] = newStack[Frame](4)
|
||||
|
||||
proc runtimeError(msg: string) =
|
||||
let ii = ip.pdiff(chunk.code[0].unsafeAddr)
|
||||
|
@ -42,6 +42,8 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
write stderr, &"[line: {line}] {msg}\n"
|
||||
hadError = true
|
||||
|
||||
frames.add(Frame(stackBottom: 0))
|
||||
|
||||
#template peek(stack: seq[NdValue]): NdValue =
|
||||
# stack[stack.high]
|
||||
|
||||
|
@ -63,6 +65,7 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
let index = readDU8()
|
||||
chunk.constants[index]
|
||||
|
||||
#template frameBottom: int = frames.peek().stackBottom
|
||||
template frameBottom: int = frames[frames.high].stackBottom
|
||||
|
||||
while true:
|
||||
|
@ -87,6 +90,7 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
|
||||
when profileInstructions:
|
||||
let startTime = getMonoTime()
|
||||
runtimes[ins].inc
|
||||
|
||||
case ins:
|
||||
of opPop:
|
||||
|
@ -130,7 +134,7 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
runtimeError(res.msg)
|
||||
break
|
||||
of opReturn:
|
||||
if frames.len == 0:
|
||||
if frames.len() == 1:
|
||||
break
|
||||
else:
|
||||
ip = frames.pop().returnIp # remove frame that's over
|
||||
|
@ -159,21 +163,21 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
of opPrint:
|
||||
echo $stack.peek()
|
||||
of opDefineGlobal:
|
||||
let name = readConstant().stringValue
|
||||
let name = $readConstant().stringValue
|
||||
if not globals.hasKey(name):
|
||||
globals[name] = stack.pop()
|
||||
else:
|
||||
runtimeError("Attempt to redefine an existing global variable.")
|
||||
break
|
||||
of opGetGlobal:
|
||||
let name = readConstant().stringValue
|
||||
let name = $readConstant().stringValue
|
||||
if globals.hasKey(name):
|
||||
stack.add(globals[name])
|
||||
else:
|
||||
runtimeError(&"Undefined global variable {name}.")
|
||||
break
|
||||
of opSetGlobal:
|
||||
let name = readConstant().stringValue
|
||||
let name = $readConstant().stringValue
|
||||
if globals.hasKey(name):
|
||||
globals[name] = stack.peek()
|
||||
else:
|
||||
|
@ -212,12 +216,12 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
# opCall converts it to this
|
||||
# ... <ret val> <arg1> <arg2> <arg3>
|
||||
let argcount = readUI8()
|
||||
let funct = stack[stack.high - argcount]
|
||||
if funct.ndType != ndFunct:
|
||||
let funct = stack.getIndexNeg(argcount)
|
||||
if funct.ndType != ntFunct:
|
||||
runtimeError("Attempt to call a non-funct (a defunct?).") # here is a bad defunct joke
|
||||
break
|
||||
|
||||
stack[stack.high - argcount] = toNdValue() # replace the function with nil: this is the return value slot
|
||||
stack.setIndexNeg(argcount, toNdValue()) # replace the function with nil: this is the return value slot
|
||||
|
||||
# create new frame
|
||||
frames.add(Frame(stackBottom: stack.high - argcount, returnIp: ip))
|
||||
|
@ -234,7 +238,11 @@ proc run*(chunk: Chunk): InterpretResult =
|
|||
when profileInstructions:
|
||||
for op in OpCode:
|
||||
let dur = durations[op].inMilliseconds
|
||||
echo &"OpCode: {op} total duration {dur} ms"
|
||||
let times = runcounts[op]
|
||||
echo &"OpCode: {op} total duration {dur} ms {times} times"
|
||||
|
||||
stack.destroyStack()
|
||||
frames.destroyStack()
|
||||
|
||||
if hadError:
|
||||
irRuntimeError
|
||||
|
|
Loading…
Reference in New Issue