# SPDX-FileCopyrightText: ☭ Emery Hemingway # SPDX-License-Identifier: Unlicense import std/[algorithm, hashes, math, options, sets, sequtils, tables] import bigints type PreserveKind* = enum pkBoolean, pkFloat, pkRegister, pkBigInt, pkString, pkByteString, pkSymbol, pkRecord, pkSequence, pkSet, pkDictionary, pkEmbedded const atomKinds* = {pkBoolean, pkFloat, pkRegister, pkBigInt, pkString, pkByteString, pkSymbol} compoundKinds* = {pkRecord, pkSequence, pkSet, pkDictionary} type Symbol* = distinct string proc `<`*(x, y: Symbol): bool {.borrow.} proc `==`*(x, y: Symbol): bool {.borrow.} proc hash*(s: Symbol): Hash {.borrow.} proc len*(s: Symbol): int {.borrow.} type Atom* = object ## Atomic Preserves value. ## Useful when a `const Value` is required. case kind*: PreserveKind of pkBoolean: bool*: bool of pkFloat: float*: float of pkRegister: register*: int of pkBigInt: bigint*: BigInt of pkString: string*: string of pkByteString: bytes*: seq[byte] of pkSymbol: symbol*: Symbol else: discard Value* = object case kind*: PreserveKind of pkBoolean: bool*: bool of pkFloat: float*: float of pkRegister: register*: int of pkBigInt: bigint*: BigInt of pkString: string*: string of pkByteString: bytes*: seq[byte] of pkSymbol: symbol*: Symbol of pkRecord: record*: seq[Value] # label is last of pkSequence: sequence*: seq[Value] of pkSet: set*: seq[Value] # TODO: HashSet of pkDictionary: dict*: seq[DictEntry] # TODO: Tables of pkEmbedded: embeddedRef*: EmbeddedRef embedded*: bool ## Flag to mark embedded Preserves value DictEntry* = tuple[key: Value, val: Value] EmbeddedRef* = ref RootObj EmbeddedObj* = RootObj ## Object refs embedded in Preserves `Value`s must inherit from `EmbeddedObj`. ## At the moment this is just an alias to `RootObj` but this may change in the future. func `===`[T: SomeFloat](a, b: T): bool = ## Compare where Nan == NaN. let class = a.classify (class == b.classify) and ((class notin {fcNormal,fcSubnormal}) or (a == b)) func `==`*(x, y: Value): bool = ## Check `x` and `y` for equivalence. if x.kind == y.kind and x.embedded == y.embedded: case x.kind of pkBoolean: result = x.bool == y.bool of pkFloat: result = x.float === y.float of pkRegister: result = x.register == y.register of pkBigInt: result = x.bigint == y.bigint of pkString: result = x.string == y.string of pkByteString: result = x.bytes == y.bytes of pkSymbol: result = x.symbol == y.symbol of pkRecord: result = x.record.len == y.record.len for i in 0..x.record.high: if not result: break result = result and (x.record[i] == y.record[i]) of pkSequence: for i, val in x.sequence: if y.sequence[i] != val: return false result = true of pkSet: result = x.set.len == y.set.len for i in 0..x.set.high: if not result: break result = result and (x.set[i] == y.set[i]) of pkDictionary: result = x.dict.len == y.dict.len for i in 0..x.dict.high: if not result: break result = result and (x.dict[i].key == y.dict[i].key) and (x.dict[i].val == y.dict[i].val) of pkEmbedded: result = x.embeddedRef == y.embeddedRef proc `<`(x, y: string | seq[byte]): bool = for i in 0 .. min(x.high, y.high): if x[i] < y[i]: return true if x[i] != y[i]: return false x.len < y.len proc `<`*(x, y: Value): bool = ## Preserves have a total order over values. Check if `x` is ordered before `y`. if x.embedded != y.embedded: result = y.embedded elif x.kind != y.kind: result = x.kind < y.kind else: case x.kind of pkBoolean: result = (not x.bool) and y.bool of pkFloat: result = x.float < y.float of pkRegister: result = x.register < y.register of pkBigInt: result = x.bigint < y.bigint of pkString: result = x.string < y.string of pkByteString: result = x.bytes < y.bytes of pkSymbol: result = x.symbol < y.symbol of pkRecord: if x.record[x.record.high] < y.record[y.record.high]: return true for i in 0..