diff --git a/src/preserves.nim b/src/preserves.nim index 2171f47..8902718 100644 --- a/src/preserves.nim +++ b/src/preserves.nim @@ -229,7 +229,7 @@ proc toDictionary*(pairs: openArray[(string, Value)]): Value = for (key, val) in pairs: result[toSymbol(key)] = val proc embed*(pr: sink Value): Value = - ## Create a Preserves value that embeds ``e``. + ## Mark a Preserves value as embedded. result = pr result.embedded = true @@ -328,7 +328,7 @@ template preservesLiteral*(value: typed) {.pragma.} ## See ``toPreserves``. template preservesEmbedded*() {.pragma.} - ## Pragma to mark a value as embedded by `toPreserves`. + ## Pragma to mark a type or value as embedded. template unpreservable*() {.pragma.} ## Pragma to forbid a type from being converted by ``toPreserves``. @@ -375,11 +375,12 @@ proc toPreserves*[T](x: T): Value {.gcsafe.} = elif T is Ordinal: result = Value(kind: pkRegister, register: x.ord) assert result.register.T == x - elif T is EmbeddedRef: - result = embed(x) elif T is ptr | ref: - if system.`==`(x, nil): result = initRecord("null") - else: result = toPreserves(x[]) + when T.hasCustomPragma(preservesEmbedded): + result = embed(x) + else: + if system.`==`(x, nil): result = initRecord("null") + else: result = toPreserves(x[]) elif T is string: result = Value(kind: pkString, string: x) elif T is SomeInteger: @@ -390,15 +391,18 @@ proc toPreserves*[T](x: T): Value {.gcsafe.} = elif T is distinct: result = toPreserves(x.distinctBase) elif T is object: - template applyEmbed(key: string; v: var Value) {.used.} = - when x.dot(key).hasCustomPragma(preservesEmbedded): - v.embedded = true - template fieldToPreserve(key: string; val: typed): Value {.used.} = + template fieldToPreserve[F](key: string; field: F): Value {.used.} = when x.dot(key).hasCustomPragma(preservesLiteral): const atom = x.dot(key).getCustomPragmaVal(preservesLiteral).parsePreservesAtom - atom.toPreservesHook() + when x.dot(key).hasCustomPragma(preservesEmbedded): + atom.toPreservesHook().embed + else: + atom.toPreservesHook() + elif x.dot(key).hasCustomPragma(preservesEmbedded) and F is EmbeddedRef: + embed(field) else: - toPreserves(val) + field.toPreserves + # checking for the embedded pragma here yields false positives when T.hasCustomPragma(unpreservable): raiseAssert($T & " is unpreservable") elif T.hasCustomPragma(preservesOr): @@ -410,13 +414,11 @@ proc toPreserves*[T](x: T): Value {.gcsafe.} = else: assert(hasKind and not hasVariant) result = fieldToPreserve(k, v) - applyEmbed(k, result) hasVariant = true elif T.hasCustomPragma(preservesRecord): result = Value(kind: pkRecord) for k, v in x.fieldPairs: var pr = fieldToPreserve(k, v) - applyEmbed(k, pr) result.record.add(pr) result.record.add(toSymbol(T.getCustomPragmaVal(preservesRecord))) elif T.hasCustomPragma(preservesTuple): @@ -428,7 +430,6 @@ proc toPreserves*[T](x: T): Value {.gcsafe.} = # TODO: what if there are fields after the tail? else: var pr = fieldToPreserve(label, field) - applyEmbed(label, pr) result.sequence.add(pr) elif T.hasCustomPragma(preservesDictionary): result = initDictionary() @@ -436,11 +437,13 @@ proc toPreserves*[T](x: T): Value {.gcsafe.} = when val is Option: if val.isSome: var pr = fieldToPreserve(key, val.get) - applyEmbed(key, pr) + if x.dot(key).hasCustomPragma(preservesEmbedded): + pr.embedded = true result[key.toSymbol] = pr else: var pr = fieldToPreserve(key, val) - applyEmbed(key, pr) + if x.dot(key).hasCustomPragma(preservesEmbedded): + pr.embedded = true result[key.toSymbol] = pr sortDict(result) else: @@ -661,19 +664,27 @@ proc fromPreserves*[T](v: var T; pr: Value): bool {.gcsafe.} = else: false if not result: break else: discard - elif T is EmbeddedRef: - v = T(pr.embeddedRef) - result = true elif T is ref: - if isNil(v): new(v) - result = fromPreserves(v[], pr) + when T.hasCustomPragma(preservesEmbedded): + if (pr.kind == pkEmbedded) and (pr.embeddedRef of T): + v = T(pr.embeddedRef) + result = true + else: + if isNil(v): new(v) + result = fromPreserves(v[], pr) elif T is object: - template fieldFromPreserve(key: string; val: typed; pr: Value): bool {.used.} = + template fieldFromPreserve[T](key: string; val: T; pr: Value): bool {.used.} = when v.dot(key).hasCustomPragma(preservesLiteral): const atom = v.dot(key).getCustomPragmaVal(preservesLiteral).parsePreservesAtom pr == atom.toPreservesHook() - else: - fromPreserves(val, pr) + elif v.dot(key).hasCustomPragma(preservesEmbedded): + when T is EmbeddedRef: + if pr.kind == pkEmbedded and pr.embeddedRef of T: + val = T(pr.embeddedRef) + true + else: false + else: fromPreserves(val, pr) + else: fromPreserves(val, pr) when T.hasCustomPragma(unpreservable): raiseAssert($T & " is unpreservable") elif T.hasCustomPragma(preservesRecord): @@ -726,7 +737,8 @@ proc fromPreserves*[T](v: var T; pr: Value): bool {.gcsafe.} = inc i result = result and val.isSome if result: - result = result and fieldFromPreserve(key, v.dot(key), val.get) + var pr = val.get + result = result and fieldFromPreserve(key, v.dot(key), pr) result = result and (i <= pr.len) elif T.hasCustomPragma(preservesOr): for kind in typeof(T.orKind): diff --git a/tests/test_conversions.nim b/tests/test_conversions.nim index 00782cb..47ba47a 100644 --- a/tests/test_conversions.nim +++ b/tests/test_conversions.nim @@ -79,7 +79,7 @@ suite "conversions": type Foo {.preservesRecord: "foo".} = object n: int - bar: Bar + bar {.preservesEmbedded.}: Bar Bar = ref object of RootObj x: int Baz = ref object of RootObj