Store record labels behind fields
This commit is contained in:
parent
6ebb752690
commit
dddf82eedd
|
@ -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..<prs.record.high:
|
||||
result.add(' ')
|
||||
result.add($val)
|
||||
result.add($prs.record[i])
|
||||
result.add('>')
|
||||
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..<prs.record.high: yield prs.record[i]
|
||||
|
||||
proc writeVarint(s: Stream; n: int) =
|
||||
var n = n
|
||||
|
@ -326,9 +355,11 @@ proc write*(str: Stream; prs: Preserve) =
|
|||
str.writeVarint(prs.symbol.len)
|
||||
str.write(prs.symbol)
|
||||
of pkRecord:
|
||||
assert(prs.record.len > 0)
|
||||
str.write(0xb4'u8)
|
||||
for val in prs.record:
|
||||
str.write(val)
|
||||
str.write(prs.record[prs.record.high])
|
||||
for i in 0..<prs.record.high:
|
||||
str.write(prs.record[i])
|
||||
str.write(0x84'u8)
|
||||
of pkSequence:
|
||||
str.write(0xb5'u8)
|
||||
|
@ -388,10 +419,11 @@ proc parsePreserve*(s: Stream): Preserve =
|
|||
result = symbol(s.readStr(len))
|
||||
of 0xb4:
|
||||
result = Preserve(kind: pkRecord)
|
||||
var label = s.parsePreserve()
|
||||
while s.peekUint8() != endMarker:
|
||||
result.record.add(s.parsePreserve())
|
||||
result.record.add(label)
|
||||
discard s.readUint8()
|
||||
assertStream(result.record.len > 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..<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
|
||||
|
||||
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.
|
||||
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``.
|
||||
if val.kind == pkRecord:
|
||||
let label = val.label
|
||||
if label.kind == pkSymbol:
|
||||
result = label.symbol == rec.label and rec.arity == val.arity
|
||||
assert(val.record.len > 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)
|
||||
|
|
Loading…
Reference in New Issue