Store record labels behind fields
This commit is contained in:
parent
6ebb752690
commit
dddf82eedd
|
@ -5,8 +5,8 @@ import bigints
|
||||||
|
|
||||||
type
|
type
|
||||||
PreserveKind* = enum
|
PreserveKind* = enum
|
||||||
pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString,
|
pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString,
|
||||||
pkSymbol, pkRecord, pkSequence, pkSet, pkDictionary, pkEmbedded
|
pkByteString, pkSymbol, pkRecord, pkSequence, pkSet, pkDictionary, pkEmbedded
|
||||||
|
|
||||||
Preserve* {.acyclic.} = object
|
Preserve* {.acyclic.} = object
|
||||||
## Type that stores a Preserves value.
|
## Type that stores a Preserves value.
|
||||||
|
@ -28,7 +28,7 @@ type
|
||||||
of pkSymbol:
|
of pkSymbol:
|
||||||
symbol*: string
|
symbol*: string
|
||||||
of pkRecord:
|
of pkRecord:
|
||||||
record*: seq[Preserve] # record[0] is the label
|
record*: seq[Preserve] # label is last
|
||||||
of pkSequence:
|
of pkSequence:
|
||||||
sequence*: seq[Preserve]
|
sequence*: seq[Preserve]
|
||||||
of pkSet:
|
of pkSet:
|
||||||
|
@ -38,6 +38,26 @@ type
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
embedded*: pointer
|
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 =
|
proc `<`(x, y: string | seq[byte]): bool =
|
||||||
for i in 0 .. min(x.high, y.high):
|
for i in 0 .. min(x.high, y.high):
|
||||||
if x[i] < y[i]:
|
if x[i] < y[i]:
|
||||||
|
@ -168,10 +188,12 @@ proc `$`*(prs: Preserve): string =
|
||||||
of pkSymbol:
|
of pkSymbol:
|
||||||
result.add(escapeJsonUnquoted(prs.symbol))
|
result.add(escapeJsonUnquoted(prs.symbol))
|
||||||
of pkRecord:
|
of pkRecord:
|
||||||
|
assert(prs.record.len > 0)
|
||||||
result.add('<')
|
result.add('<')
|
||||||
for val in prs.record:
|
result.add($prs.record[prs.record.high])
|
||||||
|
for i in 0..<prs.record.high:
|
||||||
result.add(' ')
|
result.add(' ')
|
||||||
result.add($val)
|
result.add($prs.record[i])
|
||||||
result.add('>')
|
result.add('>')
|
||||||
of pkSequence:
|
of pkSequence:
|
||||||
result.add('[')
|
result.add('[')
|
||||||
|
@ -201,6 +223,11 @@ proc `$`*(prs: Preserve): string =
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
result.add(prs.embedded.repr)
|
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 isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
|
||||||
proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".}
|
proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".}
|
||||||
template distinctBase[T](a: T): untyped = distinctBase(type(a))(a)
|
template distinctBase[T](a: T): untyped = distinctBase(type(a))(a)
|
||||||
|
@ -209,20 +236,22 @@ proc symbol*(s: string): Preserve {.inline.} =
|
||||||
## Symbol constructor.
|
## Symbol constructor.
|
||||||
Preserve(kind: pkSymbol, symbol: s)
|
Preserve(kind: pkSymbol, symbol: s)
|
||||||
|
|
||||||
proc initRecord*(label: Preserve, args: varargs[Preserve]): Preserve =
|
proc initRecord*(label: Preserve; args: varargs[Preserve]): Preserve =
|
||||||
## Record constructor.
|
## Record constructor.
|
||||||
result = Preserve(kind: pkRecord,
|
result = Preserve(kind: pkRecord,
|
||||||
record: newSeqOfCap[Preserve](1+args.len))
|
record: newSeqOfCap[Preserve](1+args.len))
|
||||||
|
for arg in args:
|
||||||
|
assertValid(arg)
|
||||||
|
result.record.add(arg)
|
||||||
result.record.add(label)
|
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.
|
## Record constructor that converts ``label`` to a symbol.
|
||||||
initRecord(symbol(label), args)
|
initRecord(symbol(label), args)
|
||||||
|
|
||||||
proc label*(prs: Preserve): Preserve {.inline.} =
|
proc label*(prs: Preserve): Preserve {.inline.} =
|
||||||
## Return the label of a record value.
|
## Return the label of a record value.
|
||||||
prs.record[0]
|
prs.record[prs.record.high]
|
||||||
|
|
||||||
proc arity*(prs: Preserve): int {.inline.} =
|
proc arity*(prs: Preserve): int {.inline.} =
|
||||||
## Return the number of fields in a record value.
|
## 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.} =
|
proc fields*(prs: Preserve): seq[Preserve] {.inline.} =
|
||||||
## Return the fields of a record value.
|
## 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 =
|
iterator fields*(prs: Preserve): Preserve =
|
||||||
## Iterate the fields of a record value.
|
## Iterate the fields of a record value.
|
||||||
for i in 1..prs.record.high: yield prs.record[i]
|
for i in 0..<prs.record.high: yield prs.record[i]
|
||||||
|
|
||||||
proc writeVarint(s: Stream; n: int) =
|
proc writeVarint(s: Stream; n: int) =
|
||||||
var n = n
|
var n = n
|
||||||
|
@ -326,9 +355,11 @@ proc write*(str: Stream; prs: Preserve) =
|
||||||
str.writeVarint(prs.symbol.len)
|
str.writeVarint(prs.symbol.len)
|
||||||
str.write(prs.symbol)
|
str.write(prs.symbol)
|
||||||
of pkRecord:
|
of pkRecord:
|
||||||
|
assert(prs.record.len > 0)
|
||||||
str.write(0xb4'u8)
|
str.write(0xb4'u8)
|
||||||
for val in prs.record:
|
str.write(prs.record[prs.record.high])
|
||||||
str.write(val)
|
for i in 0..<prs.record.high:
|
||||||
|
str.write(prs.record[i])
|
||||||
str.write(0x84'u8)
|
str.write(0x84'u8)
|
||||||
of pkSequence:
|
of pkSequence:
|
||||||
str.write(0xb5'u8)
|
str.write(0xb5'u8)
|
||||||
|
@ -388,10 +419,11 @@ proc parsePreserve*(s: Stream): Preserve =
|
||||||
result = symbol(s.readStr(len))
|
result = symbol(s.readStr(len))
|
||||||
of 0xb4:
|
of 0xb4:
|
||||||
result = Preserve(kind: pkRecord)
|
result = Preserve(kind: pkRecord)
|
||||||
|
var label = s.parsePreserve()
|
||||||
while s.peekUint8() != endMarker:
|
while s.peekUint8() != endMarker:
|
||||||
result.record.add(s.parsePreserve())
|
result.record.add(s.parsePreserve())
|
||||||
|
result.record.add(label)
|
||||||
discard s.readUint8()
|
discard s.readUint8()
|
||||||
assertStream(result.record.len > 0)
|
|
||||||
of 0xb5:
|
of 0xb5:
|
||||||
result = Preserve(kind: pkSequence)
|
result = Preserve(kind: pkSequence)
|
||||||
while s.peekUint8() != endMarker:
|
while s.peekUint8() != endMarker:
|
||||||
|
@ -434,16 +466,42 @@ proc parsePreserve*(s: Stream): Preserve =
|
||||||
else:
|
else:
|
||||||
assertStream(false)
|
assertStream(false)
|
||||||
|
|
||||||
proc toPreserveHook*(n: BigInt): Preserve =
|
proc toPreserve*[T](x: T): Preserve =
|
||||||
if initBigInt(low(BiggestInt)) < n and n < initBigInt(high(BiggestInt)):
|
## Serializes `x` to Preserves; uses `toPreserveHook(x: A)` if it's in scope to
|
||||||
var tmp: BiggestUint
|
## customize serialization.
|
||||||
for limb in n.limbs:
|
when T is Preserve: result = x
|
||||||
tmp = (tmp shl 32) or limb
|
elif T is Bigint:
|
||||||
if Negative in n.flags:
|
result = Preserve(kind: pkBigInteger, bigint: x)
|
||||||
tmp = (not tmp) + 1
|
elif compiles(toPreserveHook(x)):
|
||||||
result = Preserve(kind: pkSignedInteger, int: cast[BiggestInt](tmp))
|
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:
|
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 =
|
proc toPreserveHook*(js: JsonNode): Preserve =
|
||||||
case js.kind
|
case js.kind
|
||||||
|
@ -469,41 +527,6 @@ proc toPreserveHook*(js: JsonNode): Preserve =
|
||||||
for i, e in js.elems:
|
for i, e in js.elems:
|
||||||
result.sequence[i] = toPreserveHook(e)
|
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 =
|
proc toJsonHook*(prs: Preserve): JsonNode =
|
||||||
case prs.kind:
|
case prs.kind:
|
||||||
of pkBoolean:
|
of pkBoolean:
|
||||||
|
@ -547,28 +570,73 @@ proc toJsonHook*(prs: Preserve): JsonNode =
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
raise newException(ValueError, "cannot convert embedded value to JSON")
|
raise newException(ValueError, "cannot convert embedded value to JSON")
|
||||||
|
|
||||||
type Record* = object
|
proc fromPreserve*[T](result: var T; prs: Preserve) =
|
||||||
## Type of a preserves record type.
|
# Inplace version of `preserveTo`.
|
||||||
label*: string
|
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..<prs.record.high:
|
||||||
|
fromPreserve(result[i], prs.record[i])
|
||||||
|
else:
|
||||||
|
raiseAssert("cannot convert from Preserves: " & $T)
|
||||||
|
|
||||||
|
proc preserveTo*(prs: Preserve; T: typedesc): T =
|
||||||
|
## Reverse of `toPreserve`.
|
||||||
|
fromPreserve(result, prs)
|
||||||
|
|
||||||
|
type RecordClass* = object
|
||||||
|
## Type of a preserves record.
|
||||||
|
label*: Preserve
|
||||||
arity*: Natural
|
arity*: Natural
|
||||||
|
|
||||||
proc init*(rec: Record, fields: varargs[Preserve]): Preserve =
|
proc `$`*(rec: RecordClass): string =
|
||||||
|
$rec.label & "/" & $rec.arity
|
||||||
|
|
||||||
|
proc init*(rec: RecordClass; fields: varargs[Preserve]): Preserve =
|
||||||
## Initialize a new record value.
|
## Initialize a new record value.
|
||||||
assert(fields.len == rec.arity)
|
assert(fields.len == rec.arity)
|
||||||
initRecord(rec.label, fields)
|
result = initRecord(rec.label, fields)
|
||||||
|
|
||||||
proc isClassOf*(rec: Record, val: Preserve): bool =
|
proc isClassOf*(rec: RecordClass; val: Preserve): bool =
|
||||||
## Compare the label and arity of ``val`` to the record type ``rec``.
|
## Compare the label and arity of ``val`` to the record type ``rec``.
|
||||||
if val.kind == pkRecord:
|
if val.kind == pkRecord:
|
||||||
let label = val.label
|
assert(val.record.len > 0)
|
||||||
if label.kind == pkSymbol:
|
result = val.label == rec.label and rec.arity == val.arity
|
||||||
result = label.symbol == rec.label and rec.arity == val.arity
|
|
||||||
|
|
||||||
proc classOf*(val: Preserve): Record =
|
proc classOf*(val: Preserve): RecordClass =
|
||||||
## Derive the ``Record`` type of ``val``.
|
## Derive the ``RecordClass`` of ``val``.
|
||||||
if val.kind != pkRecord or val.label.kind == pkSymbol:
|
if val.kind != pkRecord:
|
||||||
raise newException(ValueError, "cannot derive class of non-record value")
|
raise newException(ValueError, "cannot derive class of non-record value " & $val)
|
||||||
Record(label: val.label.symbol, arity: val.arity)
|
assert(val.record.len > 0)
|
||||||
|
RecordClass(label: val.label, arity: val.arity)
|
||||||
|
|
||||||
proc len*(prs: Preserve): int =
|
proc len*(prs: Preserve): int =
|
||||||
## Return the number of values one level below ``prs``.
|
## Return the number of values one level below ``prs``.
|
||||||
|
@ -581,7 +649,7 @@ proc len*(prs: Preserve): int =
|
||||||
|
|
||||||
proc `[]`*(prs: Preserve; i: int): Preserve =
|
proc `[]`*(prs: Preserve; i: int): Preserve =
|
||||||
case prs.kind
|
case prs.kind
|
||||||
of pkRecord: prs.record[succ(i)]
|
of pkRecord: prs.record[i]
|
||||||
of pkSequence: prs.sequence[i]
|
of pkSequence: prs.sequence[i]
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "`[]` is not valid for " & $prs.kind)
|
raise newException(ValueError, "`[]` is not valid for " & $prs.kind)
|
||||||
|
|
Loading…
Reference in New Issue