mapEmbeds for Preserve[void] to Preserve[E]

This commit is contained in:
Emery Hemingway 2021-10-20 22:15:11 +02:00
parent b6275a241b
commit cca512c9df
3 changed files with 75 additions and 29 deletions

View File

@ -282,34 +282,15 @@ proc initSet*[E](): Preserve[E] = Preserve[E](kind: pkSet)
proc initDictionary*[E](): Preserve[E] = Preserve[E](kind: pkDictionary)
## Create a Preserves dictionary value.
proc embed*[E](e: E): Preserve[E] =
proc embed*[E](pr: sink Preserve[E]): Preserve[E] =
## Create a Preserves value that embeds ``e``.
result = pr
result.embedded = true
proc embed*[E](e: sink E): Preserve[E] =
## Create a Preserves value that embeds ``e``.
Preserve[E](kind: pkEmbedded, embed: e)
proc mapEmbeds*[A, B](pr: Preserve[A]; op: proc (v: A): B): Preserve[B] =
## Convert `Preserve[A]` to `Preserve[B]` using an `A -> B` procedure.
case pr.kind
of pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString, pkSymbol:
result = cast[Preserve[B]](pr)
of pkRecord:
result = Preserve[B](kind: pr.kind)
result.record = map(pr.record) do (x: Preserve[A]) -> Preserve[B]:
mapEmbeds(x, op)
of pkSequence:
result = Preserve[B](kind: pr.kind)
result.sequence = map(pr.sequence) do (x: Preserve[A]) -> Preserve[B]:
mapEmbeds(x, op)
of pkSet:
result = Preserve[B](kind: pr.kind)
result.set = map(pr.set) do (x: Preserve[A]) -> Preserve[B]:
mapEmbeds(x, op)
of pkDictionary:
result = Preserve[B](kind: pr.kind)
result.dict = map(pr.dict) do (e: DictEntry[A]) -> DictEntry[B]:
(mapEmbeds(e.key, op), mapEmbeds(e.val, op))
of pkEmbedded:
result = embed op(pr.embed)
proc len*(pr: Preserve): int =
## Return the shallow count of values in ``pr``, that is the number of
## fields in a record, items in a sequence, items in a set, or key-value pairs
@ -699,7 +680,10 @@ proc toPreserve*[T](x: T; E = void): Preserve[E] =
when x.dot(key).hasCustomPragma(preservesSymbol):
toSymbol(val, E)
elif x.dot(key).hasCustomPragma(preservesLiteral):
const lit = parsePreserves x.dot(key).getCustomPragmaVal(preservesLiteral)
when E is void:
const lit = parsePreserves(x.dot(key).getCustomPragmaVal(preservesLiteral))
else:
let lit = parsePreserves(x.dot(key).getCustomPragmaVal(preservesLiteral), E)
lit
else:
toPreserve(val, E)
@ -883,7 +867,10 @@ proc fromPreserve*[T, E](v: var T; pr: Preserve[E]): bool =
else:
false
elif v.dot(key).hasCustomPragma(preservesLiteral):
const lit = parsePreserves v.dot(key).getCustomPragmaVal(preservesLiteral)
when E is void:
const lit = parsePreserves(v.dot(key).getCustomPragmaVal(preservesLiteral))
else:
let lit = parsePreserves(v.dot(key).getCustomPragmaVal(preservesLiteral), E)
pr == lit
else:
fromPreserve(val, pr)
@ -988,6 +975,61 @@ when isMainModule:
var pr = t.toPreserveHook(void)
assert fromPreserveHook(t, pr)
proc mapEmbeds*(pr: sink Preserve[void]; E: typedesc): Preserve[E] =
## Convert `Preserve[void]` to `Preserve[E]` using `fromPreserve` for `E`.
when E is void: {.error: "E cannot be void".}
if pr.embedded:
pr.embedded = false
result = Preserve[E](kind: pkEmbedded)
if not fromPreserve(result.embed, pr):
raise newException(ValueError, "failed to convert " & $E & " from " & $pr)
else:
case pr.kind
of pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString, pkSymbol:
result = cast[Preserve[E]](pr)
of pkRecord:
result = Preserve[E](kind: pr.kind)
result.record = map(pr.record) do (x: Preserve[void]) -> Preserve[E]:
mapEmbeds(x, E)
of pkSequence:
result = Preserve[E](kind: pr.kind)
result.sequence = map(pr.sequence) do (x: Preserve[void]) -> Preserve[E]:
mapEmbeds(x, E)
of pkSet:
result = Preserve[E](kind: pr.kind)
result.set = map(pr.set) do (x: Preserve[void]) -> Preserve[E]:
mapEmbeds(x, E)
of pkDictionary:
result = Preserve[E](kind: pr.kind)
result.dict = map(pr.dict) do (e: DictEntry[void]) -> DictEntry[E]:
(mapEmbeds(e.key, E), mapEmbeds(e.val, E))
of pkEmbedded:
assert false
proc mapEmbeds*[A, B](pr: sink Preserve[A]; op: proc (v: A): B): Preserve[B] =
## Convert `Preserve[A]` to `Preserve[B]` using an `A → B` procedure.
case pr.kind
of pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString, pkSymbol:
result = cast[Preserve[B]](pr)
of pkRecord:
result = Preserve[B](kind: pr.kind)
result.record = map(pr.record) do (x: Preserve[A]) -> Preserve[B]:
mapEmbeds(x, op)
of pkSequence:
result = Preserve[B](kind: pr.kind)
result.sequence = map(pr.sequence) do (x: Preserve[A]) -> Preserve[B]:
mapEmbeds(x, op)
of pkSet:
result = Preserve[B](kind: pr.kind)
result.set = map(pr.set) do (x: Preserve[A]) -> Preserve[B]:
mapEmbeds(x, op)
of pkDictionary:
result = Preserve[B](kind: pr.kind)
result.dict = map(pr.dict) do (e: DictEntry[A]) -> DictEntry[B]:
(mapEmbeds(e.key, op), mapEmbeds(e.val, op))
of pkEmbedded:
result = embed op(pr.embed)
proc concat[E](result: var string; pr: Preserve[E]) =
if pr.embedded: result.add("#!")
case pr.kind:

View File

@ -104,5 +104,9 @@ proc parsePreserves*(text: string): Preserve[void] {.gcsafe.} =
assert(stack.len == 1)
stack.pop.value
proc parsePreserves*(text: string; E: typedesc): Preserve[E] {.gcsafe.} =
when E is void: parsePreserves(text)
else: mapEmbeds(parsePreserves(text), E)
when isMainModule:
assert(parsePreserves("#f") == Preserve())

View File

@ -26,12 +26,12 @@ suite "parse":
for (txt, bin) in examples:
test txt:
checkpoint(txt)
let test = parsePreserves(txt)
let test = parsePreserves(txt, int)
checkpoint($test)
block:
let
a = test
b = decodePreserves(bin)
b = decodePreserves(bin, int)
check(a == b)
block:
let