199 lines
5.2 KiB
Nim
199 lines
5.2 KiB
Nim
import strformat
|
|
import bitops
|
|
|
|
import ndstring
|
|
import stringutils
|
|
|
|
type
|
|
NdValue* = uint
|
|
|
|
NatReturn* = object
|
|
ok*: bool
|
|
msg*: string
|
|
|
|
# NaN boxing constants
|
|
|
|
# see https://craftinginterpreters.com/optimization.html
|
|
# bit layout:
|
|
# bit 63 is unused (can be used for more types in the future)
|
|
# bits 62-50 are all 1 if it's not a float
|
|
# bits 49-48 determine type:
|
|
# 00 -> nil or bool (singletons)
|
|
# 01 -> string
|
|
# 10 -> funct
|
|
|
|
const qNan* = 0x7ffc000000000000'u
|
|
|
|
const ndNil* = 0x7ffc000000000001'u
|
|
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
|
|
|
|
# 0111 1111 1111 11*10* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
|
const tagFunct* = 0x7ffe000000000000'u
|
|
|
|
const mask48* = 0xffff000000000000'u
|
|
|
|
# converters
|
|
|
|
template isNil*(val: NdValue): bool =
|
|
val == ndNil
|
|
|
|
template isBool*(val: NdValue): bool =
|
|
(val.bitor(1'u)) == ndFalse
|
|
|
|
template isFloat*(val: NdValue): bool =
|
|
val.bitand(qNan) != qNan
|
|
|
|
template isString*(val: NdValue): bool =
|
|
val.bitand(mask48) == tagString
|
|
|
|
template isFunct*(val: NdValue): bool =
|
|
val.bitand(mask48) == tagFunct
|
|
|
|
# these assume that the type has been previously determined
|
|
template asBool*(val: NdValue): bool =
|
|
val == ndTrue
|
|
|
|
template asFloat*(val: NdValue): float =
|
|
cast[float64](val)
|
|
|
|
template asString*(val: NdValue): NdString =
|
|
cast[NdString](val.bitand(mask48.bitnot()))
|
|
|
|
template asFunct*(val: NdValue): ptr uint8 =
|
|
cast[ptr uint8](val.bitand(mask48.bitnot()))
|
|
|
|
template fromNil*(): NdValue =
|
|
ndNil
|
|
|
|
template fromBool*(val: bool): NdValue =
|
|
if val: ndTrue else: ndFalse
|
|
|
|
template fromFloat*(val: float): NdValue =
|
|
cast[NdValue](val)
|
|
|
|
template fromNdString*(val: NdString): NdValue =
|
|
cast[uint](val).bitor(tagString)
|
|
|
|
template fromNimString*(sval: string): NdValue =
|
|
fromNdString(newString(sval))
|
|
|
|
template fromFunct*(val: ptr uint8): NdValue =
|
|
cast[uint](val).bitor(tagFunct)
|
|
|
|
|
|
# KON VALUE HELPERS, MUST BE DEFINED FOR EVERY KONVALUE
|
|
|
|
proc `$`*(val: NdValue): string =
|
|
if val.isNil():
|
|
return "nil"
|
|
elif val == ndTrue:
|
|
return "true"
|
|
elif val == ndFalse:
|
|
return "false"
|
|
elif val.isFloat():
|
|
return $val.asFloat()
|
|
elif val.isFunct():
|
|
return &"Function object {cast[uint](val.asFunct())}"
|
|
elif val.isString():
|
|
return $val.asString()
|
|
|
|
proc isFalsey*(val: NdValue): bool =
|
|
val == ndNil or val == ndFalse
|
|
|
|
template isTruthy*(val: NdValue): bool =
|
|
not val.isFalsey()
|
|
|
|
proc friendlyType*(val: NdValue): string =
|
|
if val == ndNil:
|
|
"nil"
|
|
elif val.isBool():
|
|
"bool"
|
|
elif val.isFloat():
|
|
"number"
|
|
elif val.isString():
|
|
"string"
|
|
elif val.isFunct():
|
|
"function"
|
|
else:
|
|
"unknown"
|
|
|
|
# NatReturn misc
|
|
|
|
proc natError*(msg: string): NatReturn {.inline.} =
|
|
NatReturn(ok: false, msg: msg)
|
|
|
|
const natOk* = NatReturn(ok: true)
|
|
|
|
# OPERATIONS
|
|
|
|
proc negate*(val: var NdValue): NatReturn {.inline.} =
|
|
if not val.isFloat():
|
|
return natError("Operand must be a number.")
|
|
else:
|
|
val = fromFloat(-val.asFloat())
|
|
return natOk
|
|
|
|
proc add*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
|
if val.isFloat() and right.isFloat():
|
|
val = fromFloat(val.asFloat() + right.asFloat())
|
|
elif val.isString() and right.isString():
|
|
val = fromNdString(val.asString() & val.asString())
|
|
else:
|
|
return natError(&"Attempt to add types {val.friendlyType()} and {right.friendlyType()}.")
|
|
return natOk
|
|
|
|
proc subtract*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
|
if val.isFloat() and right.isFloat():
|
|
val = fromFloat(val.asFloat() - right.asFloat())
|
|
else:
|
|
return natError(&"Attempt to subtract types {val.friendlyType()} and {right.friendlyType()}.")
|
|
return natOk
|
|
|
|
proc multiply*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
|
if val.isFloat() and right.isFloat():
|
|
val = fromFloat(val.asFloat() * right.asFloat())
|
|
else:
|
|
return natError(&"Attempt to multiply types {val.friendlyType()} and {right.friendlyType()}.")
|
|
return natOk
|
|
|
|
proc divide*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
|
if val.isFloat() and right.isFloat():
|
|
val = fromFloat(val.asFloat() / right.asFloat())
|
|
else:
|
|
return natError(&"Attempt to divide types {val.friendlyType()} and {right.friendlyType()}.")
|
|
return natOk
|
|
|
|
proc less*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
|
if val.isFloat() and right.isFloat():
|
|
val = fromBool(val.asFloat() < right.asFloat())
|
|
else:
|
|
return natError(&"Attempt to compare types {val.friendlyType()} and {right.friendlyType()}.")
|
|
return natOk
|
|
|
|
proc greater*(val: var NdValue, right: NdValue): NatReturn {.inline.} =
|
|
if val.isFloat() and right.isFloat():
|
|
val = fromBool(val.asFloat() > right.asFloat())
|
|
else:
|
|
return natError(&"Attempt to compare types {val.friendlyType()} and {right.friendlyType()}.")
|
|
return natOk
|
|
|
|
# for hashtables
|
|
|
|
proc fnv1a*(ndval: NdValue): int =
|
|
var hash = 2166136261'u32
|
|
var val = ndval
|
|
for i in countup(0, 7):
|
|
hash = hash xor val.uint8.uint32
|
|
hash *= 16777619
|
|
val = val shr 8
|
|
return hash.int
|
|
|
|
proc equal*(val, right: NdValue): bool =
|
|
if val.isFloat() and right.isFloat():
|
|
val.asFloat() == right.asFloat()
|
|
else:
|
|
val == right |