import strformat type KonType* = enum ktNil, ktBool, ktFloat, ktString, 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 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 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 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 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}.")