import strformat import types/ndstring type NdType* = enum ntNil, ntBool, ntFloat, ntString, ntFunct, type NdValue* = object case ndType*: NdType: of ntNil: discard of ntBool: boolValue*: bool of ntFloat: floatValue*: float64 of ntString: stringValue*: NdString of ntFunct: entryII*: int # entry instruction index NatReturn* = object ok*: bool msg*: string # KON VALUE HELPERS, MUST BE DEFINED FOR EVERY KONVALUE proc `$`*(val: NdValue): string = case val.ndType: of ntFloat: return $val.floatValue of ntBool: return $val.boolValue of ntNil: return "nil" of ntString: return $val.stringValue of ntFunct: return &"Function object: {val.entryII}" proc isFalsey*(val: NdValue): bool = val.ndType in {ntNil} or (val.ndType == ntBool and not val.boolValue) template isTruthy*(val: NdValue): bool = not isFalsey(val) proc equal*(val, right: NdValue): bool = if val.ndType != right.ndType: false else: case val.ndType: of ntFloat: val.floatValue == right.floatValue of ntBool: val.boolValue == right.boolValue of ntNil: true of ntString: # TODO this was meant for nim strings, not ndStrings, FIXME! val.stringValue == right.stringValue of ntFunct: val.entryII == right.entryII # NIM VALUE TO KON VALUE WRAPPERS proc toNdValue*(val: float): NdValue = NdValue(ndType: ntFloat, floatValue: val) proc toNdValue*(val: bool): NdValue = NdValue(ndType: ntBool, boolValue: val) proc toNdValue*(val: string): NdValue = NdValue(ndType: ntString, stringValue: val.newString()) proc newNdFunction*(ii: int): NdValue = NdValue(ndType: ntFunct, entryII: ii) proc toNdValue*: NdValue = NdValue(ndType: ntNil) # NatReturn misc proc natError*(msg: string): NatReturn {.inline.} = NatReturn(ok: false, msg: msg) const natOk* = NatReturn(ok: true) # OPERATIONS # NOTE: these operations can return ktTypeError with a message if types are invalid proc negate*(val: var NdValue): NatReturn {.inline.} = 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 == ntFloat and right.ndType == ntFloat: val.floatValue += right.floatValue 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 == 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 == 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 == 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 == 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 == 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