Support type hierarchies in recordFields

This commit is contained in:
Zahary Karadjov 2020-05-26 18:42:33 +03:00
parent cf837b3fb6
commit 4ffd3e1f59
No known key found for this signature in database
GPG Key ID: C8936F8A3073D609
3 changed files with 87 additions and 24 deletions

View File

@ -74,45 +74,51 @@ func isTuple*(t: NimNode): bool =
macro isTuple*(T: type): untyped = macro isTuple*(T: type): untyped =
newLit(isTuple(getType(T)[1])) newLit(isTuple(getType(T)[1]))
proc skipRef*(T: NimNode): NimNode =
result = T
if T.kind == nnkBracketExpr:
if eqIdent(T[0], bindSym"ref"):
result = T[1]
template readPragma*(field: FieldDescription, pragmaName: static string): NimNode = template readPragma*(field: FieldDescription, pragmaName: static string): NimNode =
let p = findPragma(field.pragmas, bindSym(pragmaName)) let p = findPragma(field.pragmas, bindSym(pragmaName))
if p != nil and p.len == 2: p[1] else: p if p != nil and p.len == 2: p[1] else: p
proc recordFieldsAux(result: var seq[FieldDescription], proc collectFieldsFromRecList(result: var seq[FieldDescription],
n: NimNode, n: NimNode,
parentCaseField: NimNode = nil, parentCaseField: NimNode = nil,
parentCaseBranch: NimNode = nil, parentCaseBranch: NimNode = nil,
isDiscriminator = false) = isDiscriminator = false) =
case n.kind case n.kind
of nnkRecList: of nnkRecList:
for entry in n: for entry in n:
recordFieldsAux result, entry, collectFieldsFromRecList result, entry,
parentCaseField, parentCaseBranch parentCaseField, parentCaseBranch
of nnkRecWhen: of nnkRecWhen:
for branch in n: for branch in n:
case branch.kind: case branch.kind:
of nnkElifBranch: of nnkElifBranch:
recordFieldsAux result, branch[1], collectFieldsFromRecList result, branch[1],
parentCaseField, parentCaseBranch parentCaseField, parentCaseBranch
of nnkElse: of nnkElse:
recordFieldsAux result, branch[0], collectFieldsFromRecList result, branch[0],
parentCaseField, parentCaseBranch parentCaseField, parentCaseBranch
else: else:
doAssert false doAssert false
of nnkRecCase: of nnkRecCase:
recordFieldsAux result, n[0], collectFieldsFromRecList result, n[0],
parentCaseField, parentCaseField,
parentCaseBranch, parentCaseBranch,
isDiscriminator = true isDiscriminator = true
for i in 1 ..< n.len: for i in 1 ..< n.len:
let branch = n[i] let branch = n[i]
case branch.kind case branch.kind
of nnkOfBranch: of nnkOfBranch:
recordFieldsAux result, branch[^1], n[0], branch collectFieldsFromRecList result, branch[^1], n[0], branch
of nnkElse: of nnkElse:
recordFieldsAux result, branch[0], n[0], branch collectFieldsFromRecList result, branch[0], n[0], branch
else: else:
doAssert false doAssert false
@ -150,19 +156,37 @@ proc recordFieldsAux(result: var seq[FieldDescription],
else: else:
doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
proc collectFieldsInHierarchy(result: var seq[FieldDescription],
objectType: NimNode) =
var objectType = objectType
if objectType.kind == nnkRefTy:
objectType = objectType[0]
objectType.expectKind nnkObjectTy
var baseType = objectType[1]
if baseType.kind != nnkEmpty:
baseType.expectKind nnkOfInherit
baseType = baseType[0]
baseType.expectKind nnkSym
baseType = getImpl(baseType)
baseType.expectKind nnkTypeDef
baseType = baseType[2]
baseType.expectKind nnkObjectTy
collectFieldsInHierarchy result, baseType
let recList = objectType[2]
collectFieldsFromRecList result, recList
proc recordFields*(typeImpl: NimNode): seq[FieldDescription] = proc recordFields*(typeImpl: NimNode): seq[FieldDescription] =
# TODO: This doesn't support inheritance yet
if typeImpl.isTuple: if typeImpl.isTuple:
for i in 1 ..< typeImpl.len: for i in 1 ..< typeImpl.len:
result.add FieldDescription(typ: typeImpl[i], name: ident("Field" & $(i - 1))) result.add FieldDescription(typ: typeImpl[i], name: ident("Field" & $(i - 1)))
return return
var objectType = typeImpl[2] typeImpl.expectKind nnkTypeDef
if objectType.kind == nnkRefTy: collectFieldsInHierarchy(result, typeImpl[2])
objectType = objectType[0]
let recList = objectType[2]
recordFieldsAux result, recList
macro field*(obj: typed, fieldName: static string): untyped = macro field*(obj: typed, fieldName: static string): untyped =
newDotExpr(obj, ident fieldName) newDotExpr(obj, ident fieldName)

View File

@ -16,6 +16,7 @@ import
test_bitseqs, test_bitseqs,
test_byteutils, test_byteutils,
test_endians2, test_endians2,
test_macros,
test_objects, test_objects,
test_ptrops, test_ptrops,
test_results, test_results,

38
tests/test_macros.nim Normal file
View File

@ -0,0 +1,38 @@
import
../stew/shims/macros
type
FieldKind = enum
KindA
KindB
BaseType = object of RootObj
baseField: int
case baseCaseField: FieldKind
of KindA:
baseA: int
of KindB:
discard
DerivedType = ref object of BaseType
derivedField: int
macro getFieldsLists(T: type): untyped =
result = newTree(nnkBracket)
var resolvedType = skipRef getType(T)[1]
doAssert resolvedType.kind == nnkSym
var objectType = getImpl(resolvedType)
doAssert objectType.kind == nnkTypeDef
for f in recordFields(objectType):
result.add newLit($f.name)
static:
doAssert getFieldsLists(DerivedType) == [
"baseField",
"baseCaseField",
"baseA",
"derivedField"
]