Move utilities for records to a seperate module

This commit is contained in:
Emery Hemingway 2021-06-28 15:47:14 +02:00
parent c7e8c301f9
commit 5aa5ccbd09
3 changed files with 86 additions and 39 deletions

View File

@ -236,19 +236,6 @@ proc symbol*(s: string): Preserve {.inline.} =
## Symbol constructor.
Preserve(kind: pkSymbol, symbol: s)
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)
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[prs.record.high]
@ -612,32 +599,6 @@ 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 `$`*(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)
result = initRecord(rec.label, fields)
proc isClassOf*(rec: RecordClass; val: Preserve): bool =
## Compare the label and arity of ``val`` to the record type ``rec``.
if val.kind == pkRecord:
assert(val.record.len > 0)
result = val.label == rec.label and rec.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``.
case prs.kind

52
src/preserves/records.nim Normal file
View File

@ -0,0 +1,52 @@
# SPDX-License-Identifier: ISC
import std/[macros, typetraits]
import ../preserves
proc initRecord*(label: Preserve; args: varargs[Preserve, toPreserve]): 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)
proc initRecord*(label: string; args: varargs[Preserve, toPreserve]): Preserve {.inline.} =
## Record constructor that converts ``label`` to a symbol.
initRecord(symbol(label), args)
type RecordClass* = object
## Type of a preserves record.
label*: Preserve
arity*: Natural
proc `$`*(rec: RecordClass): string =
$rec.label & "/" & $rec.arity
proc init*(rec: RecordClass; fields: varargs[Preserve, toPreserve]): Preserve =
## Initialize a new record value.
assert(fields.len == rec.arity)
result = initRecord(rec.label, fields)
proc isClassOf*(rec: RecordClass; val: Preserve): bool =
## Compare the label and arity of ``val`` to the record type ``rec``.
if val.kind == pkRecord:
assert(val.record.len > 0)
result = val.label == rec.label and rec.arity == val.arity
proc classOf*(val: Preserve): RecordClass =
## Derive the ``RecordClass`` of ``val``.
if val.kind != pkRecord:
raise newException(Defect, "cannot derive class of non-record value " & $val)
assert(val.record.len > 0)
RecordClass(label: val.label, arity: val.arity)
proc classOf*[T](x: T): RecordClass =
## Derive the ``RecordClass`` of ``T``.
# TODO: would be nice to get the class of a type without an instance.
when not T.hasCustomPragma(record):
raise newException(Defect, "{.record: \"\".} must be present to determine classOf")
else:
result.label = preserves.symbol(T.getCustomPragmaVal(record))
for k, v in x.fieldPairs: inc(result.arity)

View File

@ -0,0 +1,34 @@
# SPDX-License-Identifier: ISC
import streams, strutils, unittest
import bigints, preserves, preserves/records
suite "conversions":
test "dictionary":
type Bar = tuple
s: string
type Foobar = object
a, b: int
c: Bar
let
c = Foobar(a: 1, b: 2, c: (s: "ku",))
b = toPreserve(c)
a = preserveTo(b, Foobar)
check(a == c)
check(b.kind == pkDictionary)
expect Defect:
checkpoint $classOf(c)
test "records":
type Bar {.record: "bar".} = tuple
s: string
type Foobar {.record: "foo".} = object
a, b: int
c: Bar
let
c = Foobar(a: 1, b: 2, c: (s: "ku",))
b = toPreserve(c)
a = preserveTo(b, Foobar)
check(a == c)
check(b.kind == pkRecord)
check(classOf(c) == RecordClass(label: symbol"foo", arity: 3))