mirror of https://github.com/status-im/NimYAML.git
Dump variant objects with transient fields
* tuples not working yet * loading not woring yet * added tests
This commit is contained in:
parent
b3d83025f7
commit
3ab3dc7ad0
|
@ -38,6 +38,31 @@ type
|
||||||
of akDog:
|
of akDog:
|
||||||
barkometer: int
|
barkometer: int
|
||||||
|
|
||||||
|
DumbEnum = enum
|
||||||
|
deA, deB, deC, deD
|
||||||
|
|
||||||
|
NonVariantWithTransient = object
|
||||||
|
a, b, c, d: string
|
||||||
|
|
||||||
|
VariantWithTransient = object
|
||||||
|
gStorable, gTemporary: string
|
||||||
|
case kind: DumbEnum
|
||||||
|
of deA, deB:
|
||||||
|
cStorable, cTemporary: string
|
||||||
|
of deC:
|
||||||
|
alwaysThere: int
|
||||||
|
of deD:
|
||||||
|
neverThere: int
|
||||||
|
|
||||||
|
markAsTransient(NonVariantWithTransient, a)
|
||||||
|
markAsTransient(NonVariantWithTransient, c)
|
||||||
|
|
||||||
|
markAsTransient(VariantWithTransient, gTemporary)
|
||||||
|
markAsTransient(VariantWithTransient, cTemporary)
|
||||||
|
markAsTransient(VariantWithTransient, neverThere)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc `$`(v: BetterInt): string {.borrow.}
|
proc `$`(v: BetterInt): string {.borrow.}
|
||||||
proc `==`(left, right: BetterInt): bool {.borrow.}
|
proc `==`(left, right: BetterInt): bool {.borrow.}
|
||||||
|
|
||||||
|
@ -451,6 +476,32 @@ suite "Serialization":
|
||||||
expectConstructionError(1, 32, "While constructing Animal: Missing field: \"purringIntensity\""):
|
expectConstructionError(1, 32, "While constructing Animal: Missing field: \"purringIntensity\""):
|
||||||
load(input, result)
|
load(input, result)
|
||||||
|
|
||||||
|
test "Dump non-variant object with transient fields":
|
||||||
|
let input = NonVariantWithTransient(a: "a", b: "b", c: "c", d: "d")
|
||||||
|
let output = dump(input, tsNone, asTidy, blockOnly)
|
||||||
|
assertStringEqual yamlDirs & "\nb: b\nd: d", output
|
||||||
|
|
||||||
|
test "Dump variant object with transient fields":
|
||||||
|
let input = @[VariantWithTransient(kind: deB, gStorable: "gs",
|
||||||
|
gTemporary: "gt", cStorable: "cs", cTemporary: "ct"),
|
||||||
|
VariantWithTransient(kind: deD, gStorable: "a", gTemporary: "b",
|
||||||
|
neverThere: 42)]
|
||||||
|
let output = dump(input, tsNone, asTidy, blockOnly)
|
||||||
|
assertStringEqual yamlDirs & """
|
||||||
|
|
||||||
|
-
|
||||||
|
-
|
||||||
|
gStorable: gs
|
||||||
|
-
|
||||||
|
kind: deB
|
||||||
|
-
|
||||||
|
cStorable: cs
|
||||||
|
-
|
||||||
|
-
|
||||||
|
gStorable: a
|
||||||
|
-
|
||||||
|
kind: deD""", output
|
||||||
|
|
||||||
test "Dump cyclic data structure":
|
test "Dump cyclic data structure":
|
||||||
var
|
var
|
||||||
a = newNode("a")
|
a = newNode("a")
|
||||||
|
|
|
@ -616,8 +616,10 @@ macro constructFieldValue(t: typedesc, stream: untyped, context: untyped,
|
||||||
newCall(bindSym("escape"), name))))))
|
newCall(bindSym("escape"), name))))))
|
||||||
|
|
||||||
proc isVariantObject(t: typedesc): bool {.compileTime.} =
|
proc isVariantObject(t: typedesc): bool {.compileTime.} =
|
||||||
let tDesc = getType(t)
|
var tDesc = getType(t)
|
||||||
if tDesc.kind != nnkObjectTy: return false
|
if tDesc.kind == nnkBracketExpr: tDesc = getType(tDesc[1])
|
||||||
|
if tDesc.kind != nnkObjectTy:
|
||||||
|
return false
|
||||||
for child in tDesc[2].children:
|
for child in tDesc[2].children:
|
||||||
if child.kind == nnkRecCase: return true
|
if child.kind == nnkRecCase: return true
|
||||||
return false
|
return false
|
||||||
|
@ -628,6 +630,8 @@ let
|
||||||
transientBitvectorProc {.compileTime.} =
|
transientBitvectorProc {.compileTime.} =
|
||||||
newIdentNode(":transientObject")
|
newIdentNode(":transientObject")
|
||||||
|
|
||||||
|
var transientVectors {.compileTime.} = newSeq[set[int16]]()
|
||||||
|
|
||||||
proc constructObject*[O: object|tuple](
|
proc constructObject*[O: object|tuple](
|
||||||
s: var YamlStream, c: ConstructionContext, result: var O)
|
s: var YamlStream, c: ConstructionContext, result: var O)
|
||||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||||
|
@ -685,30 +689,92 @@ proc constructObject*[O: object|tuple](
|
||||||
inc(i)
|
inc(i)
|
||||||
else: ensureAllFieldsPresent(s, O, result, matched)
|
else: ensureAllFieldsPresent(s, O, result, matched)
|
||||||
|
|
||||||
macro genRepresentObject(t: typedesc, value, childTagStyle: typed): typed =
|
macro getTransientBitvector(t: typedesc): untyped =
|
||||||
echo "here"
|
echo "getTransientBitvector"
|
||||||
result = quote do:
|
result = quote do:
|
||||||
when compiles(`transientBitvectorProc`(`t`)):
|
when compiles(`transientBitvectorProc`(`t`)):
|
||||||
const bitvector = `transientBitvectorProc`(`t`)
|
transientVectors[`transientBitvectorProc`(`t`)]
|
||||||
var fieldIndex = 0'i16
|
else:
|
||||||
for name, value in fieldPairs(value):
|
set[int16]({})
|
||||||
when compiles(`transientBitvectorProc`(`t`)):
|
echo result.repr
|
||||||
if fieldIndex notin bitvector:
|
|
||||||
when isVariantObject(O): c.put(startMapEvent(yTagQuestionMark, yAnchorNone))
|
|
||||||
c.put(scalarEvent(name, if childTagStyle == tsNone: yTagQuestionMark else:
|
|
||||||
yTagNimField, yAnchorNone))
|
|
||||||
representChild(value, childTagStyle, c)
|
|
||||||
when isVariantObject(O): c.put(endMapEvent())
|
|
||||||
fieldIndex.inc()
|
|
||||||
else:
|
|
||||||
when isVariantObject(O): c.put(startMapEvent(yTagQuestionMark, yAnchorNone))
|
|
||||||
c.put(scalarEvent(name, if childTagStyle == tsNone: yTagQuestionMark else:
|
|
||||||
yTagNimField, yAnchorNone))
|
|
||||||
representChild(value, childTagStyle, c)
|
|
||||||
when isVariantObject(O): c.put(endMapEvent())
|
|
||||||
echo "did it"
|
|
||||||
|
|
||||||
proc representObject*[O: object|tuple](value: O, ts: TagStyle,
|
macro genRepresentObject(t: typedesc, value, childTagStyle: typed): typed =
|
||||||
|
result = newStmtList()
|
||||||
|
let tSym = genSym(nskConst, ":tSym")
|
||||||
|
result.add(quote do:
|
||||||
|
when compiles(`transientBitvectorProc`(`t`)):
|
||||||
|
const `tSym` = `transientBitvectorProc`(`t`)
|
||||||
|
else:
|
||||||
|
const `tSym` = -1
|
||||||
|
)
|
||||||
|
let
|
||||||
|
tDecl = getType(t)
|
||||||
|
tDesc = getType(tDecl[1])
|
||||||
|
isVO = isVariantObject(t)
|
||||||
|
var fieldIndex = 0'i16
|
||||||
|
for child in tDesc[2].children:
|
||||||
|
if child.kind == nnkRecCase:
|
||||||
|
let
|
||||||
|
fieldName = $child[0]
|
||||||
|
fieldAccessor = newDotExpr(value, newIdentNode(fieldName))
|
||||||
|
result.add(quote do:
|
||||||
|
c.put(startMapEvent(yTagQuestionMark, yAnchorNone))
|
||||||
|
c.put(scalarEvent(`fieldName`, if `childTagStyle` == tsNone:
|
||||||
|
yTagQuestionMark else: yTagNimField, yAnchorNone))
|
||||||
|
representChild(`fieldAccessor`, `childTagStyle`, c)
|
||||||
|
c.put(endMapEvent())
|
||||||
|
)
|
||||||
|
let enumName = $getTypeInst(child[0])
|
||||||
|
var caseStmt = newNimNode(nnkCaseStmt).add(fieldAccessor)
|
||||||
|
for bIndex in 1 .. len(child) - 1:
|
||||||
|
var curBranch: NimNode
|
||||||
|
var recListIndex = 0
|
||||||
|
case child[bIndex].kind
|
||||||
|
of nnkOfBranch:
|
||||||
|
curBranch = newNimNode(nnkOfBranch)
|
||||||
|
while child[bIndex][recListIndex].kind == nnkIntLit:
|
||||||
|
curBranch.add(newCall(enumName, newLit(child[bIndex][recListIndex].intVal)))
|
||||||
|
inc(recListIndex)
|
||||||
|
of nnkElse:
|
||||||
|
curBranch = newNimNode(nnkElse)
|
||||||
|
else:
|
||||||
|
internalError("Unexpected child kind: " & $child[bIndex].kind)
|
||||||
|
doAssert child[bIndex][recListIndex].kind == nnkRecList
|
||||||
|
var curStmtList = newStmtList()
|
||||||
|
if child[bIndex][recListIndex].len > 0:
|
||||||
|
for item in child[bIndex][recListIndex].children:
|
||||||
|
inc(fieldIndex)
|
||||||
|
let
|
||||||
|
name = $item
|
||||||
|
itemAccessor = newDotExpr(value, newIdentNode(name))
|
||||||
|
curStmtList.add(quote do:
|
||||||
|
when `tSym` == -1 or `fieldIndex` notin transientVectors[`tSym`]:
|
||||||
|
c.put(startMapEvent(yTagQuestionMark, yAnchorNone))
|
||||||
|
c.put(scalarEvent(`name`, if `childTagStyle` == tsNone:
|
||||||
|
yTagQuestionMark else: yTagNimField, yAnchorNone))
|
||||||
|
representChild(`itemAccessor`, `childTagStyle`, c)
|
||||||
|
c.put(endMapEvent())
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
curStmtList.add(newNimNode(nnkDiscardStmt).add(newEmptyNode()))
|
||||||
|
curBranch.add(curStmtList)
|
||||||
|
caseStmt.add(curBranch)
|
||||||
|
result.add(caseStmt)
|
||||||
|
else:
|
||||||
|
let
|
||||||
|
name = $child
|
||||||
|
childAccessor = newDotExpr(value, newIdentNode(name))
|
||||||
|
result.add(quote do:
|
||||||
|
when `tSym` == -1 or `fieldIndex` notin transientVectors[`tSym`]:
|
||||||
|
when `isVO`: c.put(startMapEvent(yTagQuestionMark, yAnchorNone))
|
||||||
|
c.put(scalarEvent(`name`, if `childTagStyle` == tsNone:
|
||||||
|
yTagQuestionMark else: yTagNimField, yAnchorNone))
|
||||||
|
representChild(`childAccessor`, `childTagStyle`, c)
|
||||||
|
when `isVO`: c.put(endMapEvent())
|
||||||
|
)
|
||||||
|
inc(fieldIndex)
|
||||||
|
|
||||||
|
proc representObject*[O: object](value: O, ts: TagStyle,
|
||||||
c: SerializationContext, tag: TagId) =
|
c: SerializationContext, tag: TagId) =
|
||||||
## represents a Nim object or tuple as YAML mapping
|
## represents a Nim object or tuple as YAML mapping
|
||||||
let childTagStyle = if ts == tsRootOnly: tsNone else: ts
|
let childTagStyle = if ts == tsRootOnly: tsNone else: ts
|
||||||
|
@ -717,7 +783,20 @@ proc representObject*[O: object|tuple](value: O, ts: TagStyle,
|
||||||
genRepresentObject(O, value, childTagStyle)
|
genRepresentObject(O, value, childTagStyle)
|
||||||
when isVariantObject(O): c.put(endSeqEvent())
|
when isVariantObject(O): c.put(endSeqEvent())
|
||||||
else: c.put(endMapEvent())
|
else: c.put(endMapEvent())
|
||||||
static: echo "represented " & typetraits.name(O)
|
|
||||||
|
proc representObject*[O: tuple](value: O, ts: TagStyle,
|
||||||
|
c: SerializationContext, tag: TagId) =
|
||||||
|
let childTagStyle = if ts == tsRootOnly: tsNone else: ts
|
||||||
|
const bitvector = getTransientBitvector(O)
|
||||||
|
var fieldIndex = 0'i16
|
||||||
|
c.put(startMapEvent(tag, yAnchorNone))
|
||||||
|
for name, fvalue in fieldPairs(value):
|
||||||
|
#if fieldIndex notin bitvector: TODO: fix!
|
||||||
|
c.put(scalarEvent(name, if childTagStyle == tsNone:
|
||||||
|
yTagQuestionMark else: yTagNimField, yAnchorNone))
|
||||||
|
representChild(fvalue, childTagStyle, c)
|
||||||
|
inc(fieldIndex)
|
||||||
|
c.put(endMapEvent())
|
||||||
|
|
||||||
proc constructObject*[O: enum](s: var YamlStream, c: ConstructionContext,
|
proc constructObject*[O: enum](s: var YamlStream, c: ConstructionContext,
|
||||||
result: var O)
|
result: var O)
|
||||||
|
@ -963,10 +1042,8 @@ proc representChild*[O](value: O, ts: TagStyle,
|
||||||
inc(count)
|
inc(count)
|
||||||
if count == 1: c.put(scalarEvent("~", yTagNull))
|
if count == 1: c.put(scalarEvent("~", yTagNull))
|
||||||
else:
|
else:
|
||||||
static: echo "repchild " & typetraits.name(O)
|
|
||||||
representObject(value, ts, c,
|
representObject(value, ts, c,
|
||||||
if ts == tsNone: yTagQuestionMark else: yamlTag(O))
|
if ts == tsNone: yTagQuestionMark else: yamlTag(O))
|
||||||
static: echo "/repchild " & typetraits.name(O)
|
|
||||||
|
|
||||||
proc construct*[T](s: var YamlStream, target: var T)
|
proc construct*[T](s: var YamlStream, target: var T)
|
||||||
{.raises: [YamlStreamError].} =
|
{.raises: [YamlStreamError].} =
|
||||||
|
@ -1119,7 +1196,16 @@ macro getFieldIndex(t: typedesc, field: untyped): untyped =
|
||||||
for child in tDesc[2].children:
|
for child in tDesc[2].children:
|
||||||
if child.kind == nnkRecCase:
|
if child.kind == nnkRecCase:
|
||||||
for bIndex in 1 .. len(child) - 1:
|
for bIndex in 1 .. len(child) - 1:
|
||||||
for item in child[bIndex][1].children:
|
var bChildIndex = 0
|
||||||
|
case child[bIndex].kind
|
||||||
|
of nnkOfBranch:
|
||||||
|
while child[bIndex][bChildIndex].kind == nnkIntLit: inc(bChildIndex)
|
||||||
|
of nnkElse: discard
|
||||||
|
else:
|
||||||
|
internalError("Unexpected child kind: " &
|
||||||
|
$child[bIndex][bChildIndex].kind)
|
||||||
|
yAssert child[bIndex][bChildIndex].kind == nnkRecList
|
||||||
|
for item in child[bIndex][bChildIndex].children:
|
||||||
inc(fieldIndex)
|
inc(fieldIndex)
|
||||||
yAssert item.kind == nnkSym
|
yAssert item.kind == nnkSym
|
||||||
if $item == fieldName:
|
if $item == fieldName:
|
||||||
|
@ -1137,15 +1223,19 @@ macro getFieldIndex(t: typedesc, field: untyped): untyped =
|
||||||
else:
|
else:
|
||||||
result = newNimNode(nnkInt16Lit)
|
result = newNimNode(nnkInt16Lit)
|
||||||
result.intVal = fieldIndex
|
result.intVal = fieldIndex
|
||||||
|
echo "found fieldIndex of \"", fieldName, "\": ", result.intVal
|
||||||
|
|
||||||
macro markAsTransient*(t: typedesc, field: untyped): typed =
|
macro markAsTransient*(t: typedesc, field: untyped): typed =
|
||||||
let mySet = genSym(nskVar, ident = ":mySym")
|
let nextBitvectorIndex = transientVectors.len
|
||||||
quote do:
|
result = quote do:
|
||||||
when compiles(`implicitVariantObjectMarker`(`t`)):
|
when compiles(`implicitVariantObjectMarker`(`t`)):
|
||||||
{.fatal: "Cannot mark fields of implicit variant objects as transient." .}
|
{.fatal: "Cannot mark fields of implicit variant objects as transient." .}
|
||||||
when not compiles(`transientBitvectorProc`(`t`)):
|
when not compiles(`transientBitvectorProc`(`t`)):
|
||||||
var `mySet` {.compileTime.}: set[int16] = {}
|
proc `transientBitvectorProc`*(myType: typedesc[`t`]): int
|
||||||
proc `transientBitvectorProc`*(myType: typedesc[`t`]): var set[int16]
|
|
||||||
{.compileTime.} =
|
{.compileTime.} =
|
||||||
return `mySet`
|
`nextBitvectorIndex`
|
||||||
static: `transientBitvectorProc`(`t`).incl(getFieldIndex(`t`, `field`))
|
static: transientVectors.add({})
|
||||||
|
static:
|
||||||
|
echo "setting transientBitvector at ", `transientBitvectorProc`(`t`)
|
||||||
|
transientVectors[`transientBitvectorProc`(`t`)].incl(getFieldIndex(`t`, `field`))
|
||||||
|
echo "transient bitvector is now ", transientVectors[`transientBitvectorProc`(`t`)]
|
Loading…
Reference in New Issue