Record utilities
This commit is contained in:
parent
b5940cfe22
commit
b099475d25
|
@ -4,11 +4,13 @@ import base64, endians, json, hashes, tables, streams
|
||||||
import bigints
|
import bigints
|
||||||
|
|
||||||
type
|
type
|
||||||
PreserveKind = enum
|
PreserveKind* = enum
|
||||||
pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString,
|
pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString,
|
||||||
pkSymbol, pkRecord, pkSequence, pkSet, pkDictionary, pkEmbedded
|
pkSymbol, pkRecord, pkSequence, pkSet, pkDictionary, pkEmbedded
|
||||||
|
|
||||||
Preserve*[T] = object
|
Preserve*[T] {.acyclic.} = object
|
||||||
|
## Type that stores a Preserves value.
|
||||||
|
## ``T`` is the domain-specific type of "embedded" values, otherwise ``void``.
|
||||||
case kind*: PreserveKind
|
case kind*: PreserveKind
|
||||||
of pkBoolean:
|
of pkBoolean:
|
||||||
bool*: bool
|
bool*: bool
|
||||||
|
@ -37,17 +39,111 @@ type
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
embedded*: T
|
embedded*: T
|
||||||
|
|
||||||
|
proc `$`*[T](prs: Preserve[T]): string =
|
||||||
|
case prs.kind:
|
||||||
|
of pkBoolean:
|
||||||
|
case prs.bool
|
||||||
|
of false: result = "#f"
|
||||||
|
of true: result = "#t"
|
||||||
|
of pkFloat:
|
||||||
|
result = $prs.float & "f"
|
||||||
|
of pkDouble:
|
||||||
|
result = $prs.double
|
||||||
|
of pkSignedInteger:
|
||||||
|
result = $prs.int
|
||||||
|
of pkBigInteger:
|
||||||
|
result = $prs.bigint
|
||||||
|
of pkString:
|
||||||
|
result = escapeJson(prs.string)
|
||||||
|
of pkByteString:
|
||||||
|
result.add("#[")
|
||||||
|
result.add(base64.encode(prs.bytes))
|
||||||
|
result.add(']')
|
||||||
|
of pkSymbol:
|
||||||
|
result.add(escapeJsonUnquoted(prs.symbol))
|
||||||
|
of pkRecord:
|
||||||
|
result.add('<')
|
||||||
|
for val in prs.record:
|
||||||
|
result.add(' ')
|
||||||
|
result.add($val)
|
||||||
|
result.add('>')
|
||||||
|
of pkSequence:
|
||||||
|
result.add('[')
|
||||||
|
for i, val in prs.seq:
|
||||||
|
if i > 0:
|
||||||
|
result.add(' ')
|
||||||
|
result.add($val)
|
||||||
|
result.add(']')
|
||||||
|
of pkSet:
|
||||||
|
result.add("#{")
|
||||||
|
for val in prs.set.keys:
|
||||||
|
result.add($val)
|
||||||
|
result.add(' ')
|
||||||
|
if result.len > 1:
|
||||||
|
result.setLen(result.high)
|
||||||
|
result.add('}')
|
||||||
|
of pkDictionary:
|
||||||
|
result.add('{')
|
||||||
|
for (key, value) in prs.dict.pairs:
|
||||||
|
result.add($key)
|
||||||
|
result.add(" :")
|
||||||
|
result.add($value)
|
||||||
|
result.add(' ')
|
||||||
|
if result.len > 1:
|
||||||
|
result.setLen(result.high)
|
||||||
|
result.add('}')
|
||||||
|
of pkEmbedded:
|
||||||
|
when not T is void:
|
||||||
|
$prs.embedded
|
||||||
|
|
||||||
|
proc toPreserve*(b: bool): Preserve[void] =
|
||||||
|
Preserve[void](kind: pkBoolean, bool: b)
|
||||||
|
|
||||||
|
proc toPreserve*(n: SomeInteger): Preserve[void] =
|
||||||
|
Preserve[void](kind: pkSignedInteger, int: n.BiggestInt)
|
||||||
|
|
||||||
|
proc toPreserve*(n: BigInt): Preserve[void] =
|
||||||
|
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[void](kind: pkSignedInteger, int: cast[BiggestInt](tmp))
|
||||||
|
else:
|
||||||
|
result = Preserve[void](kind: pkBigInteger, bigint: n)
|
||||||
|
|
||||||
|
proc toPreserve*(s: string): Preserve[void] =
|
||||||
|
Preserve[void](kind: pkString, string: s)
|
||||||
|
|
||||||
proc symbol*[T](s: string): Preserve[void] {.inline.} =
|
proc symbol*[T](s: string): Preserve[void] {.inline.} =
|
||||||
## Symbol constructor.
|
## Symbol constructor.
|
||||||
Preserve[T](kind: pkSymbol, symbol: s)
|
Preserve[T](kind: pkSymbol, symbol: s)
|
||||||
|
|
||||||
|
proc record*[T](label: Preserve[T], args: varargs[Preserve[T]]): Preserve[T] =
|
||||||
|
## Record constructor.
|
||||||
|
result = Preserve[T](kind: pkRecord, record: newSeqOfCap(1+args.len))
|
||||||
|
result.record.add(label)
|
||||||
|
for arg in args: result.record.add(arg)
|
||||||
|
|
||||||
|
proc record*[T](label: string, args: varargs[Preserve[T]]): Preserve[T] {.inline.} =
|
||||||
|
## Record constructor that converts ``label`` to a symbol.
|
||||||
|
record(symbol[T](label), args)
|
||||||
|
|
||||||
proc label*[T](prs: Preserve[T]): Preserve[T] {.inline.} =
|
proc label*[T](prs: Preserve[T]): Preserve[T] {.inline.} =
|
||||||
|
## Return the label of a record value.
|
||||||
prs.record[0]
|
prs.record[0]
|
||||||
|
|
||||||
|
proc arity*[T](prs: Preserve[T]): int {.inline.} =
|
||||||
|
## Return the number of fields in a record value.
|
||||||
|
pred(prs.record.len)
|
||||||
|
|
||||||
proc fields*[T](prs: Preserve[T]): seq[Preserve[T]] {.inline.} =
|
proc fields*[T](prs: Preserve[T]): seq[Preserve[T]] {.inline.} =
|
||||||
|
## Return the fields of a record value.
|
||||||
prs.record[1..prs.record.high]
|
prs.record[1..prs.record.high]
|
||||||
|
|
||||||
iterator fields*[T](prs: Preserve[T]): seq[Preserve[T]] =
|
iterator fields*[T](prs: Preserve[T]): Preserve[T] =
|
||||||
|
## Iterate the fields of a record value.
|
||||||
for i in 1..prs.record.high:
|
for i in 1..prs.record.high:
|
||||||
yield prs.record[i]
|
yield prs.record[i]
|
||||||
|
|
||||||
|
@ -160,66 +256,6 @@ proc hash*[T](prs: Preserve[T]): Hash =
|
||||||
h = h !& hash(prs.embedded)
|
h = h !& hash(prs.embedded)
|
||||||
!$h
|
!$h
|
||||||
|
|
||||||
proc `$`*[T](prs: Preserve[T]): string =
|
|
||||||
case prs.kind:
|
|
||||||
of pkBoolean:
|
|
||||||
case prs.bool
|
|
||||||
of false: result = "#f"
|
|
||||||
of true: result = "#t"
|
|
||||||
of pkFloat:
|
|
||||||
result = $prs.float & "f"
|
|
||||||
of pkDouble:
|
|
||||||
result = $prs.double
|
|
||||||
of pkSignedInteger:
|
|
||||||
result = $prs.int
|
|
||||||
of pkBigInteger:
|
|
||||||
result = $prs.bigint
|
|
||||||
of pkString:
|
|
||||||
result = escapeJson(prs.string)
|
|
||||||
of pkByteString:
|
|
||||||
result.add("#[")
|
|
||||||
result.add(base64.encode(prs.bytes))
|
|
||||||
result.add(']')
|
|
||||||
of pkSymbol:
|
|
||||||
result.add('|')
|
|
||||||
result.add(escapeJsonUnquoted(prs.symbol))
|
|
||||||
result.add('|')
|
|
||||||
of pkRecord:
|
|
||||||
result.add('<')
|
|
||||||
result.add(prs.label)
|
|
||||||
for val in prs.fields:
|
|
||||||
result.add(' ')
|
|
||||||
result.add($val)
|
|
||||||
result.add('>')
|
|
||||||
of pkSequence:
|
|
||||||
result.add('[')
|
|
||||||
for i, val in prs.seq:
|
|
||||||
if i > 0:
|
|
||||||
result.add(' ')
|
|
||||||
result.add($val)
|
|
||||||
result.add(']')
|
|
||||||
of pkSet:
|
|
||||||
result.add("#{")
|
|
||||||
for val in prs.set.keys:
|
|
||||||
result.add($val)
|
|
||||||
result.add(' ')
|
|
||||||
if result.len > 1:
|
|
||||||
result.setLen(result.high)
|
|
||||||
result.add('}')
|
|
||||||
of pkDictionary:
|
|
||||||
result.add('{')
|
|
||||||
for (key, value) in prs.dict.pairs:
|
|
||||||
result.add($key)
|
|
||||||
result.add(" :")
|
|
||||||
result.add($value)
|
|
||||||
result.add(' ')
|
|
||||||
if result.len > 1:
|
|
||||||
result.setLen(result.high)
|
|
||||||
result.add('}')
|
|
||||||
of pkEmbedded:
|
|
||||||
when not T is void:
|
|
||||||
$prs.embedded
|
|
||||||
|
|
||||||
proc writeVarint(s: Stream; n: int) =
|
proc writeVarint(s: Stream; n: int) =
|
||||||
var n = n
|
var n = n
|
||||||
while true:
|
while true:
|
||||||
|
@ -420,26 +456,6 @@ proc parsePreserve*(s: Stream): Preserve[void] =
|
||||||
else:
|
else:
|
||||||
assertStream(false)
|
assertStream(false)
|
||||||
|
|
||||||
proc toPreserve*(b: bool): Preserve[void] =
|
|
||||||
Preserve[void](kind: pkBoolean, bool: b)
|
|
||||||
|
|
||||||
proc toPreserve*(n: SomeInteger): Preserve[void] =
|
|
||||||
Preserve[void](kind: pkSignedInteger, int: n.BiggestInt)
|
|
||||||
|
|
||||||
proc toPreserve*(n: BigInt): Preserve[void] =
|
|
||||||
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[void](kind: pkSignedInteger, int: cast[BiggestInt](tmp))
|
|
||||||
else:
|
|
||||||
result = Preserve[void](kind: pkBigInteger, bigint: n)
|
|
||||||
|
|
||||||
proc toPreserve*(s: string): Preserve[void] =
|
|
||||||
Preserve[void](kind: pkString, string: s)
|
|
||||||
|
|
||||||
proc toPreserve*(js: JsonNode): Preserve[void] =
|
proc toPreserve*(js: JsonNode): Preserve[void] =
|
||||||
case js.kind
|
case js.kind
|
||||||
of JString:
|
of JString:
|
||||||
|
@ -506,3 +522,26 @@ proc toJson*[T](prs: Preserve[T]): JsonNode =
|
||||||
result[key.string] = val.toJson
|
result[key.string] = val.toJson
|
||||||
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
|
||||||
|
## Type of a preserves record type.
|
||||||
|
label*: string
|
||||||
|
arity*: Natural
|
||||||
|
|
||||||
|
proc init*[T](rec: Record, fields: varargs[Preserve[T]]): Preserve[T] =
|
||||||
|
## Initialize a new record value.
|
||||||
|
assert(fields.len == rec.arity)
|
||||||
|
record(rec.label, fields)
|
||||||
|
|
||||||
|
proc isClassOf*[T](rec: Record, val: Preserve[T]): 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
|
||||||
|
|
||||||
|
proc classOf*[T](val: Preserve[T]): 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)
|
||||||
|
|
Loading…
Reference in New Issue