Record utilities
This commit is contained in:
parent
b5940cfe22
commit
b099475d25
|
@ -4,11 +4,13 @@ import base64, endians, json, hashes, tables, streams
|
|||
import bigints
|
||||
|
||||
type
|
||||
PreserveKind = enum
|
||||
PreserveKind* = enum
|
||||
pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString,
|
||||
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
|
||||
of pkBoolean:
|
||||
bool*: bool
|
||||
|
@ -37,17 +39,111 @@ type
|
|||
of pkEmbedded:
|
||||
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.} =
|
||||
## Symbol constructor.
|
||||
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.} =
|
||||
## Return the label of a record value.
|
||||
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.} =
|
||||
## Return the fields of a record value.
|
||||
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:
|
||||
yield prs.record[i]
|
||||
|
||||
|
@ -160,66 +256,6 @@ proc hash*[T](prs: Preserve[T]): Hash =
|
|||
h = h !& hash(prs.embedded)
|
||||
!$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) =
|
||||
var n = n
|
||||
while true:
|
||||
|
@ -420,26 +456,6 @@ proc parsePreserve*(s: Stream): Preserve[void] =
|
|||
else:
|
||||
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] =
|
||||
case js.kind
|
||||
of JString:
|
||||
|
@ -506,3 +522,26 @@ proc toJson*[T](prs: Preserve[T]): JsonNode =
|
|||
result[key.string] = val.toJson
|
||||
of pkEmbedded:
|
||||
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