diff --git a/src/preserves.nim b/src/preserves.nim index e1395d1..12ae622 100644 --- a/src/preserves.nim +++ b/src/preserves.nim @@ -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 diff --git a/src/preserves/records.nim b/src/preserves/records.nim new file mode 100644 index 0000000..fe594e0 --- /dev/null +++ b/src/preserves/records.nim @@ -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) diff --git a/tests/test_conversions.nim b/tests/test_conversions.nim new file mode 100644 index 0000000..6ac4034 --- /dev/null +++ b/tests/test_conversions.nim @@ -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))