Embed values for types with {.preservesEmbedded.}

This commit is contained in:
Emery Hemingway 2024-01-08 00:05:24 +02:00
parent 79ea25d1be
commit d2017228fb
2 changed files with 39 additions and 27 deletions

View File

@ -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):

View File

@ -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