nondescript/value.nim

142 lines
4.2 KiB
Nim

import strformat
type
KonType* = enum
ktNil, ktBool, ktFloat, ktString,
ktFunct,
ktTypeError,
const errorTypes = {ktTypeError}
type
KonValue* = object
case konType*: KonType:
of ktNil:
discard
of ktBool:
boolValue*: bool
of ktFloat:
floatValue*: float64
of ktString:
stringValue*: string
of ktFunct:
module*: string
entryII*: int # entry instruction index
arity*: int # number of arguments
of errorTypes:
message*: string
# KON VALUE HELPERS, MUST BE DEFINED FOR EVERY KONVALUE
proc `$`*(val: KonValue): string =
case val.konType:
of ktFloat:
return $val.floatValue
of ktBool:
return $val.boolValue
of ktNil:
return "nil"
of ktString:
return val.stringValue
of ktFunct:
return &"Function object: {val.entryII}"
of errorTypes:
let ename = $val.konType
return &"{ename[2..^1]}: {val.message}"
proc isError*(val: KonValue): bool =
val.konType in errorTypes
proc isFalsey*(val: KonValue): bool =
val.konType in {ktNil} or (val.konType == ktBool and not val.boolValue)
template isTruthy*(val: KonValue): bool =
not isFalsey(val)
proc equal*(val, right: KonValue): bool =
if val.konType != right.konType:
false
else:
case val.konType:
of ktFloat:
val.floatValue == right.floatValue
of ktBool:
val.boolValue == right.boolValue
of ktNil:
true
of ktString:
val.stringValue == right.stringValue
of ktFunct:
val.entryII == right.entryII
# same entry II/module but diff arity is a bug
of errorTypes:
false # error comparison is undefined
# NIM VALUE TO KON VALUE WRAPPERS
proc toKonValue*(val: float): KonValue =
KonValue(konType: ktFloat, floatValue: val)
proc toKonValue*(val: bool): KonValue =
KonValue(konType: ktBool, boolValue: val)
proc toKonValue*(val: string): KonValue =
KonValue(konType: ktString, stringValue: val)
proc newKonFunction*(ii: int, arity: int): KonValue =
KonValue(konType: ktFunct, entryII: ii, arity: arity)
proc toKonValue*: KonValue =
KonValue(konType: ktNil)
proc konTypeError*(msg: string): KonValue =
KonValue(konType: ktTypeError, message: msg)
# OPERATIONS
# NOTE: these operations can return ktTypeError with a message if types are invalid
proc negate*(val: KonValue): KonValue =
if (val.konType != ktFloat):
return konTypeError("Operand must be a number.")
else:
return toKonValue(-val.floatValue)
proc add*(val: KonValue, right: KonValue): KonValue =
if val.konType == ktFloat and right.konType == ktFloat:
return toKonValue(val.floatValue + right.floatValue)
elif val.konType == ktString and right.konType == ktString:
return toKonValue(val.stringValue & right.stringValue)
else:
return konTypeError(&"Attempt to add types {val.konType} and {right.konType}.")
proc subtract*(val: KonValue, right: KonValue): KonValue =
if val.konType == ktFloat and right.konType == ktFloat:
return toKonValue(val.floatValue - right.floatValue)
else:
return konTypeError(&"Attempt to subtract types {val.konType} and {right.konType}.")
proc multiply*(val: KonValue, right: KonValue): KonValue =
if val.konType == ktFloat and right.konType == ktFloat:
return toKonValue(val.floatValue * right.floatValue)
else:
return konTypeError(&"Attempt to multiply types {val.konType} and {right.konType}.")
proc divide*(val: KonValue, right: KonValue): KonValue =
if val.konType == ktFloat and right.konType == ktFloat:
return toKonValue(val.floatValue / right.floatValue)
else:
return konTypeError(&"Attempt to divide types {val.konType} and {right.konType}.")
proc `<`*(val: KonValue, right: KonValue): KonValue =
if val.konType == ktFloat and right.konType == ktFloat:
return toKonValue(val.floatValue < right.floatValue)
else:
return konTypeError(&"Attempt to compare types {val.konType} and {right.konType}.")
proc `>`*(val: KonValue, right: KonValue): KonValue =
if val.konType == ktFloat and right.konType == ktFloat:
return toKonValue(val.floatValue > right.floatValue)
else:
return konTypeError(&"Attempt to compare types {val.konType} and {right.konType}.")