From dddf82eedd58a25b1d01f77c3b215bdeca29cd8f Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Thu, 24 Jun 2021 17:31:30 +0200 Subject: [PATCH] Store record labels behind fields --- src/preserves.nim | 214 ++++++++++++++++++++++++++++++---------------- 1 file changed, 141 insertions(+), 73 deletions(-) diff --git a/src/preserves.nim b/src/preserves.nim index 0627724..e1395d1 100644 --- a/src/preserves.nim +++ b/src/preserves.nim @@ -5,8 +5,8 @@ import bigints type PreserveKind* = enum - pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString, - pkSymbol, pkRecord, pkSequence, pkSet, pkDictionary, pkEmbedded + pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, + pkByteString, pkSymbol, pkRecord, pkSequence, pkSet, pkDictionary, pkEmbedded Preserve* {.acyclic.} = object ## Type that stores a Preserves value. @@ -28,7 +28,7 @@ type of pkSymbol: symbol*: string of pkRecord: - record*: seq[Preserve] # record[0] is the label + record*: seq[Preserve] # label is last of pkSequence: sequence*: seq[Preserve] of pkSet: @@ -38,6 +38,26 @@ type of pkEmbedded: embedded*: pointer +proc assertValid*(prs: Preserve) = + case prs.kind + of pkBigInteger: + assert(BiggestInt.low.initBigInt < prs.bigint and prs.bigint < BiggestInt.high.initBigInt) + of pkRecord: + assert(prs.record.len > 0, "invalid Preserves record " & prs.repr) + assert(prs.record[prs.record.high].kind < pkRecord) + for v in prs.record: assertValid(v) + of pkSequence: + for v in prs.sequence: assertValid(v) + of pkSet: + for v in prs.set: assertValid(v) + of pkDictionary: + for key, val in prs.dict.pairs: + assert(key.kind < pkRecord) + assertValid(key) + assertValid(val) + else: + discard + proc `<`(x, y: string | seq[byte]): bool = for i in 0 .. min(x.high, y.high): if x[i] < y[i]: @@ -168,10 +188,12 @@ proc `$`*(prs: Preserve): string = of pkSymbol: result.add(escapeJsonUnquoted(prs.symbol)) of pkRecord: + assert(prs.record.len > 0) result.add('<') - for val in prs.record: + result.add($prs.record[prs.record.high]) + for i in 0..') of pkSequence: result.add('[') @@ -201,6 +223,11 @@ proc `$`*(prs: Preserve): string = of pkEmbedded: result.add(prs.embedded.repr) +func isRecord*(prs: Preserve): bool = + if prs.kind == pkRecord: + result = true + assert(prs.record.len > 0) + proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} template distinctBase[T](a: T): untyped = distinctBase(type(a))(a) @@ -209,20 +236,22 @@ proc symbol*(s: string): Preserve {.inline.} = ## Symbol constructor. Preserve(kind: pkSymbol, symbol: s) -proc initRecord*(label: Preserve, args: varargs[Preserve]): Preserve = +proc initRecord*(label: Preserve; args: varargs[Preserve]): Preserve = ## Record constructor. result = Preserve(kind: pkRecord, record: newSeqOfCap[Preserve](1+args.len)) + for arg in args: + assertValid(arg) + result.record.add(arg) result.record.add(label) - for arg in args: result.record.add(arg) -proc initRecord*(label: string, args: varargs[Preserve]): Preserve {.inline.} = +proc initRecord*(label: string; args: varargs[Preserve]): Preserve {.inline.} = ## Record constructor that converts ``label`` to a symbol. initRecord(symbol(label), args) proc label*(prs: Preserve): Preserve {.inline.} = ## Return the label of a record value. - prs.record[0] + prs.record[prs.record.high] proc arity*(prs: Preserve): int {.inline.} = ## Return the number of fields in a record value. @@ -230,11 +259,11 @@ proc arity*(prs: Preserve): int {.inline.} = proc fields*(prs: Preserve): seq[Preserve] {.inline.} = ## Return the fields of a record value. - prs.record[1..prs.record.high] + prs.record[0..prs.record.high.pred] iterator fields*(prs: Preserve): Preserve = ## Iterate the fields of a record value. - for i in 1..prs.record.high: yield prs.record[i] + for i in 0.. 0) str.write(0xb4'u8) - for val in prs.record: - str.write(val) + str.write(prs.record[prs.record.high]) + for i in 0.. 0) of 0xb5: result = Preserve(kind: pkSequence) while s.peekUint8() != endMarker: @@ -434,16 +466,42 @@ proc parsePreserve*(s: Stream): Preserve = else: assertStream(false) -proc toPreserveHook*(n: BigInt): Preserve = - if initBigInt(low(BiggestInt)) < n and n < initBigInt(high(BiggestInt)): - var tmp: BiggestUint - for limb in n.limbs: - tmp = (tmp shl 32) or limb - if Negative in n.flags: - tmp = (not tmp) + 1 - result = Preserve(kind: pkSignedInteger, int: cast[BiggestInt](tmp)) +proc toPreserve*[T](x: T): Preserve = + ## Serializes `x` to Preserves; uses `toPreserveHook(x: A)` if it's in scope to + ## customize serialization. + when T is Preserve: result = x + elif T is Bigint: + result = Preserve(kind: pkBigInteger, bigint: x) + elif compiles(toPreserveHook(x)): + result = toPreserveHook(x) + elif T is array | seq: + result = Preserve(kind: pkSequence) + for v in x.items: result.sequence.add(toPreserve(v)) + elif T is bool: + result = Preserve(kind: pkBoolean, bool: x) + elif T is distinct: + result = toPreserve(x.distinctBase) + elif T is float: + result = Preserve(kind: pkFloat, float: x) + elif T is float64: + result = Preserve(kind: pkDouble, double: x) + elif T is object | tuple: + result = Preserve(kind: pkDictionary) + for k, v in x.fieldPairs: result.dict[symbol(k)] = toPreserve(v) + elif T is Ordinal: + result = Preserve(kind: pkSignedInteger, int: x.ord.BiggestInt) + elif T is ptr | ref: + if system.`==`(x, nil): result = symbol"null" + else: result = toPreserve(x[]) + elif T is string: + result = Preserve(kind: pkString, string: x) + elif T is SomeInteger: + result = Preserve(kind: pkSignedInteger, int: x.BiggestInt) else: - result = Preserve(kind: pkBigInteger, bigint: n) + raiseAssert("cannot convert to Preserves: " & $T) + +proc toPreserveHook*[T](set: HashSet[T]): Preserve = + Preserve(kind: pkSet, set: set.map(toPreserve)) proc toPreserveHook*(js: JsonNode): Preserve = case js.kind @@ -469,41 +527,6 @@ proc toPreserveHook*(js: JsonNode): Preserve = for i, e in js.elems: result.sequence[i] = toPreserveHook(e) -proc toPreserve*[T](x: T): Preserve = - ## Serializes `x` to Preserves; uses `toPreserveHook(x: A)` if it's in scope to - ## customize serialization. - when T is Preserve: result = x - elif compiles(toPreserveHook(x)): - result = toPreserveHook(x) - elif T is array | seq: - result = Preserve(kind: pkSequence) - for v in x.items: result.sequence.add(toPreserve(v)) - elif T is bool: - result = Preserve(kind: pkBoolean, bool: x) - elif T is distinct: - result = toPreserve(x.distinctBase) - elif T is float: - result = Preserve(kind: pkFloat, float: x) - elif T is float64: - result = Preserve(kind: pkDouble, double: x) - elif T is object: - result = Preserve(kind: pkDictionary) - for k, v in x.fieldPairs: result.dict[symbol(k)] = toPreserve(v) - elif T is Ordinal: - result = Preserve(kind: pkSignedInteger, int: x.ord.BiggestInt) - elif T is ptr | ref: - if system.`==`(x, nil): result = symbol"null" - else: result = toPreserve(x[]) - elif T is string: - result = Preserve(kind: pkString, string: x) - elif T is SomeInteger: - result = Preserve(kind: pkSignedInteger, int: x.BiggestInt) - elif T is tuple: - result = Preserve(kind: pkRecord, record: @[symbol("")]) - for v in x.fields: result.record.add(toPreserve(v)) - else: - raiseAssert("cannot convert to Preserves: " & $T) - proc toJsonHook*(prs: Preserve): JsonNode = case prs.kind: of pkBoolean: @@ -547,28 +570,73 @@ proc toJsonHook*(prs: Preserve): JsonNode = of pkEmbedded: raise newException(ValueError, "cannot convert embedded value to JSON") -type Record* = object - ## Type of a preserves record type. - label*: string +proc fromPreserve*[T](result: var T; prs: Preserve) = + # Inplace version of `preserveTo`. + when compiles(fromPreserveHook(result, prs)): + fromPreserveHook(result, prs) + elif T is Preserve: + result = prs + elif T is Bigint: + result = prs.bigint + elif T is bool: + result = prs.bool + elif T is SomeInteger: + result = T(prs.int) + elif T is float: + result = prs.float + elif T is seq: + result.setLen(prs.sequence.len) + for i, val in prs.sequence: + fromPreserve(result[i], val) + elif T is float64: + result = prs.double + elif T is object: + result = Preserve(kind: pkDictionary) + for k, v in x.fieldPairs: result.dict[symbol(k)] = toPreserve(v) + elif T is Ordinal: + result = Preserve(kind: pkSignedInteger, int: x.ord.BiggestInt) + elif T is ptr | ref: + if system.`==`(x, nil): result = symbol"null" + else: result = toPreserve(x[]) + elif T is string: + result = Preserve(kind: pkString, string: x) + elif T is SomeInteger: + result = Preserve(kind: pkSignedInteger, int: x.BiggestInt) + elif T is tuple: + for i in 0.. 0) + result = val.label == rec.label and rec.arity == val.arity -proc classOf*(val: Preserve): Record = - ## Derive the ``Record`` type of ``val``. - if val.kind != pkRecord or val.label.kind == pkSymbol: - raise newException(ValueError, "cannot derive class of non-record value") - Record(label: val.label.symbol, arity: val.arity) +proc classOf*(val: Preserve): RecordClass = + ## Derive the ``RecordClass`` of ``val``. + if val.kind != pkRecord: + raise newException(ValueError, "cannot derive class of non-record value " & $val) + assert(val.record.len > 0) + RecordClass(label: val.label, arity: val.arity) proc len*(prs: Preserve): int = ## Return the number of values one level below ``prs``. @@ -581,7 +649,7 @@ proc len*(prs: Preserve): int = proc `[]`*(prs: Preserve; i: int): Preserve = case prs.kind - of pkRecord: prs.record[succ(i)] + of pkRecord: prs.record[i] of pkSequence: prs.sequence[i] else: raise newException(ValueError, "`[]` is not valid for " & $prs.kind)