142 lines
4.2 KiB
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}.") |