Can load fields of parent types. ref #131

* dumping fields of parent types not working yet
This commit is contained in:
Felix Krause 2023-04-04 19:29:20 +02:00
parent 8e1b07975b
commit 576cf11d81
2 changed files with 117 additions and 55 deletions

View File

@ -61,6 +61,11 @@ type
WithIgnoredField {.ignore: ["z"].} = object WithIgnoredField {.ignore: ["z"].} = object
x, y: int x, y: int
Parent = object of RootObj
i*: int
Child = object of Parent
s*: string
proc `$`(v: BetterInt): string {.borrow.} proc `$`(v: BetterInt): string {.borrow.}
proc `==`(left, right: BetterInt): bool {.borrow.} proc `==`(left, right: BetterInt): bool {.borrow.}
@ -458,6 +463,14 @@ suite "Serialization":
assertStringEqual(yamlDirs & assertStringEqual(yamlDirs &
"!n!custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12\n", output) "!n!custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12\n", output)
test "Load object with inherited fields":
let input =
"i: 4\ns: hello"
var result: Child
load(input, result)
assert result.i == 4
assert result.s == "hello"
test "Load custom variant object": test "Load custom variant object":
let input = let input =
"---\n- - name: Bastet\n - kind: akCat\n - purringIntensity: 7\n" & "---\n- - name: Bastet\n - kind: akCat\n - purringIntensity: 7\n" &

View File

@ -598,41 +598,65 @@ proc recListNode(n: NimNode): NimNode {.compileTime.} =
if n.kind == nnkRecList: result = n[0] if n.kind == nnkRecList: result = n[0]
else: result = n else: result = n
proc fieldCount(t: NimNode): int {.compiletime.} = proc parentType(tDesc: NimNode): NimNode {.compileTime.} =
result = 0 var name: NimNode
let tDesc = getType(getType(t)[1]) case tDesc[1].kind
if tDesc.kind == nnkBracketExpr: of nnkEmpty: return nil
# tuple of nnkBracketExpr:
result = tDesc.len - 1 # happens when parent type is `ref X`
else: name = tDesc[1][1]
# object of nnkObjectTy, nnkSym:
for child in tDesc[2].children: name = tDesc[1]
inc(result) else:
if child.kind == nnkRecCase: return nil
for bIndex in 1..<len(child): result = newNimNode(nnkBracketExpr)
var increment = 0 result.add(bindSym("typeDesc"))
case child[bIndex].kind result.add(name)
of nnkOfBranch:
let content = child[bIndex][len(child[bIndex])-1]
# We cannot assume that child[bIndex][1] is a RecList due to
# a one-liner like 'of akDog: barkometer' not resulting in a
# RecList but in an Ident node.
case content.kind
of nnkRecList:
increment = len(content)
else:
increment = 1
of nnkElse:
# Same goes for the else branch.
case child[bIndex][0].kind
of nnkRecList:
increment = len(child[bIndex][0])
else:
increment = 1
else:
internalError("Unexpected child kind: " & $child[bIndex].kind)
inc(result, increment)
proc fieldCount(t: NimNode): int {.compiletime.} =
result = 0
var tTypedesc: NimNode
if t.kind == nnkSym:
tTypedesc = getType(t)
else:
tTypedesc = t
let tDesc = getType(tTypedesc[1])
if tDesc.kind == nnkBracketExpr:
# tuple
result = tDesc.len - 1
else:
# object
let tParent = parentType(tDesc)
if tParent != nil:
# inherited fields
result += fieldCount(tParent)
for child in tDesc[2].children:
inc(result)
if child.kind == nnkRecCase:
for bIndex in 1..<len(child):
var increment = 0
case child[bIndex].kind
of nnkOfBranch:
let content = child[bIndex][len(child[bIndex])-1]
# We cannot assume that child[bIndex][1] is a RecList due to
# a one-liner like 'of akDog: barkometer' not resulting in a
# RecList but in an Ident node.
case content.kind
of nnkRecList:
increment = len(content)
else:
increment = 1
of nnkElse:
# Same goes for the else branch.
case child[bIndex][0].kind
of nnkRecList:
increment = len(child[bIndex][0])
else:
increment = 1
else:
internalError("Unexpected child kind: " & $child[bIndex].kind)
inc(result, increment)
macro matchMatrix(t: typedesc): untyped = macro matchMatrix(t: typedesc): untyped =
let numFields = fieldCount(t) let numFields = fieldCount(t)
@ -703,18 +727,18 @@ proc ifNotTransient(o, field: NimNode,
when not `o`.`field`.hasCustomPragma(transient): when not `o`.`field`.hasCustomPragma(transient):
`stmts` `stmts`
macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed, proc recEnsureAllFieldsPresent(
matched: typed, m: Mark) = s: NimNode, tDecl: NimNode, o: NimNode, matched: NimNode, m: NimNode,
result = newStmtList() tName: string, field: var int, stmt: NimNode) {.compileTime.} =
let var
tDecl = getType(t)
tName = $tDecl[1]
tDesc = getType(tDecl[1]) tDesc = getType(tDecl[1])
var field = 0 tParent = parentType(tDesc)
if tParent != nil:
recEnsureAllFieldsPresent(s, tParent, o, matched, m, tName, field, stmt)
for child in tDesc[2].children: for child in tDesc[2].children:
if child.kind == nnkRecCase: if child.kind == nnkRecCase:
result.add(checkMissing( stmt.add(checkMissing(
s, t, tName, child[0], field, matched, o, m)) s, tDecl, tName, child[0], field, matched, o, m))
for bIndex in 1 .. len(child) - 1: for bIndex in 1 .. len(child) - 1:
let discChecks = newStmtList() let discChecks = newStmtList()
var var
@ -731,13 +755,22 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
for item in child[bIndex][recListIndex].recListItems: for item in child[bIndex][recListIndex].recListItems:
inc(field) inc(field)
discChecks.add(checkMissing( discChecks.add(checkMissing(
s, t, tName, item, field, matched, o, m)) s, tDecl, tName, item, field, matched, o, m))
result.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])), stmt.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
"in", curValues), discChecks))) "in", curValues), discChecks)))
else: else:
result.add(checkMissing(s, t, tName, child, field, matched, o, m)) stmt.add(checkMissing(s, tDecl, tName, child, field, matched, o, m))
inc(field) inc(field)
macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
matched: typed, m: Mark) =
result = newStmtList()
let
tDecl = getType(t)
tName = $tDecl[1]
var field = 0
recEnsureAllFieldsPresent(s, tDecl, o, matched, m, tName, field, result)
proc skipOverValue(s: var YamlStream) = proc skipOverValue(s: var YamlStream) =
var e = s.next() var e = s.next()
var depth = int(e.kind in {yamlStartMap, yamlStartSeq}) var depth = int(e.kind in {yamlStartMap, yamlStartSeq})
@ -748,16 +781,15 @@ proc skipOverValue(s: var YamlStream) =
of yamlScalar, yamlAlias: discard of yamlScalar, yamlAlias: discard
else: internalError("Unexpected event kind.") else: internalError("Unexpected event kind.")
macro constructFieldValue(t: typedesc, stream: untyped, proc addFieldCases(
context: untyped, name: untyped, o: untyped, tDecl: NimNode, stream: NimNode, context: NimNode, name: NimNode, o: NimNode,
matched: untyped, failOnUnknown: bool, m: untyped) = matched: NimNode, failOnUnknown: NimNode, m: NimNode, tName: string, caseStmt: NimNode,
let fieldIndex: var int) {.compileTime.} =
tDecl = getType(t) var
tName = $tDecl[1]
tDesc = getType(tDecl[1]) tDesc = getType(tDecl[1])
result = newStmtList() tParent = parentType(tDesc)
var caseStmt = newNimNode(nnkCaseStmt).add(name) if tParent != nil:
var fieldIndex = 0 addFieldCases(tParent, stream, context, name, o, matched, failOnUnknown, m, tName, caseStmt, fieldIndex)
for child in tDesc[2].children: for child in tDesc[2].children:
if child.kind == nnkRecCase: if child.kind == nnkRecCase:
let let
@ -828,6 +860,18 @@ macro constructFieldValue(t: typedesc, stream: untyped,
markAsFound(fieldIndex, matched)], true, stream, m, tName, $child)) markAsFound(fieldIndex, matched)], true, stream, m, tName, $child))
caseStmt.add(ob) caseStmt.add(ob)
inc(fieldIndex) inc(fieldIndex)
macro constructFieldValue(t: typedesc, stream: untyped,
context: untyped, name: untyped, o: untyped,
matched: untyped, failOnUnknown: bool, m: untyped) =
let
tDecl = getType(t)
tName = $tDecl[1]
tDesc = getType(tDecl[1])
result = newStmtList()
var caseStmt = newNimNode(nnkCaseStmt).add(name)
var fieldIndex = 0
addFieldCases(tDecl, stream, context, name, o, matched, failOnUnknown, m, tName, caseStmt, fieldIndex)
caseStmt.add(newNimNode(nnkElse).add(newNimNode(nnkWhenStmt).add( caseStmt.add(newNimNode(nnkElse).add(newNimNode(nnkWhenStmt).add(
newNimNode(nnkElifBranch).add(failOnUnknown, newNimNode(nnkElifBranch).add(failOnUnknown,
newNimNode(nnkRaiseStmt).add( newNimNode(nnkRaiseStmt).add(
@ -854,6 +898,11 @@ proc hasIgnore(t: typedesc): bool {.compileTime.} =
else: else:
return false return false
proc constructObjectDefault*(
s: var YamlStream, c: ConstructionContext, result: var RootObj) =
# specialization of generic proc for RootObj, doesn't do anything
return
proc constructObjectDefault*[O: object|tuple]( proc constructObjectDefault*[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].} =