|
| 1 | +import |
| 2 | + std/[macros, tables, hashes] |
| 3 | + |
| 4 | +export |
| 5 | + macros |
| 6 | + |
| 7 | +type |
| 8 | + FieldDescription* = object |
| 9 | + name*: NimNode |
| 10 | + isPublic*: bool |
| 11 | + isDiscriminator*: bool |
| 12 | + typ*: NimNode |
| 13 | + pragmas*: NimNode |
| 14 | + caseField*: NimNode |
| 15 | + caseBranch*: NimNode |
| 16 | + |
| 17 | +{.push raises: [].} |
| 18 | + |
| 19 | +func isTuple*(t: NimNode): bool = |
| 20 | + t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple") |
| 21 | + |
| 22 | +macro isTuple*(T: type): untyped = |
| 23 | + newLit(isTuple(getType(T)[1])) |
| 24 | + |
| 25 | +proc collectFieldsFromRecList(result: var seq[FieldDescription], |
| 26 | + n: NimNode, |
| 27 | + parentCaseField: NimNode = nil, |
| 28 | + parentCaseBranch: NimNode = nil, |
| 29 | + isDiscriminator = false) = |
| 30 | + case n.kind |
| 31 | + of nnkRecList: |
| 32 | + for entry in n: |
| 33 | + collectFieldsFromRecList result, entry, |
| 34 | + parentCaseField, parentCaseBranch |
| 35 | + of nnkRecWhen: |
| 36 | + for branch in n: |
| 37 | + case branch.kind: |
| 38 | + of nnkElifBranch: |
| 39 | + collectFieldsFromRecList result, branch[1], |
| 40 | + parentCaseField, parentCaseBranch |
| 41 | + of nnkElse: |
| 42 | + collectFieldsFromRecList result, branch[0], |
| 43 | + parentCaseField, parentCaseBranch |
| 44 | + else: |
| 45 | + doAssert false |
| 46 | + |
| 47 | + of nnkRecCase: |
| 48 | + collectFieldsFromRecList result, n[0], |
| 49 | + parentCaseField, |
| 50 | + parentCaseBranch, |
| 51 | + isDiscriminator = true |
| 52 | + |
| 53 | + for i in 1 ..< n.len: |
| 54 | + let branch = n[i] |
| 55 | + case branch.kind |
| 56 | + of nnkOfBranch: |
| 57 | + collectFieldsFromRecList result, branch[^1], n[0], branch |
| 58 | + of nnkElse: |
| 59 | + collectFieldsFromRecList result, branch[0], n[0], branch |
| 60 | + else: |
| 61 | + doAssert false |
| 62 | + |
| 63 | + of nnkIdentDefs: |
| 64 | + let fieldType = n[^2] |
| 65 | + for i in 0 ..< n.len - 2: |
| 66 | + var field: FieldDescription |
| 67 | + field.name = n[i] |
| 68 | + field.typ = fieldType |
| 69 | + field.caseField = parentCaseField |
| 70 | + field.caseBranch = parentCaseBranch |
| 71 | + field.isDiscriminator = isDiscriminator |
| 72 | + |
| 73 | + if field.name.kind == nnkPragmaExpr: |
| 74 | + field.pragmas = field.name[1] |
| 75 | + field.name = field.name[0] |
| 76 | + |
| 77 | + if field.name.kind == nnkPostfix: |
| 78 | + field.isPublic = true |
| 79 | + field.name = field.name[1] |
| 80 | + |
| 81 | + result.add field |
| 82 | + |
| 83 | + of nnkSym: |
| 84 | + result.add FieldDescription( |
| 85 | + name: n, |
| 86 | + typ: getType(n), |
| 87 | + caseField: parentCaseField, |
| 88 | + caseBranch: parentCaseBranch, |
| 89 | + isDiscriminator: isDiscriminator) |
| 90 | + |
| 91 | + of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty: |
| 92 | + discard |
| 93 | + |
| 94 | + else: |
| 95 | + doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr |
| 96 | + |
| 97 | +proc collectFieldsInHierarchy(result: var seq[FieldDescription], |
| 98 | + objectType: NimNode) = |
| 99 | + var objectType = objectType |
| 100 | + |
| 101 | + objectType.expectKind {nnkObjectTy, nnkRefTy} |
| 102 | + |
| 103 | + if objectType.kind == nnkRefTy: |
| 104 | + objectType = objectType[0] |
| 105 | + |
| 106 | + objectType.expectKind nnkObjectTy |
| 107 | + |
| 108 | + var baseType = objectType[1] |
| 109 | + if baseType.kind != nnkEmpty: |
| 110 | + baseType.expectKind nnkOfInherit |
| 111 | + baseType = baseType[0] |
| 112 | + baseType.expectKind nnkSym |
| 113 | + baseType = getImpl(baseType) |
| 114 | + baseType.expectKind nnkTypeDef |
| 115 | + baseType = baseType[2] |
| 116 | + baseType.expectKind {nnkObjectTy, nnkRefTy} |
| 117 | + collectFieldsInHierarchy result, baseType |
| 118 | + |
| 119 | + let recList = objectType[2] |
| 120 | + collectFieldsFromRecList result, recList |
| 121 | + |
| 122 | +proc recordFields*(typeImpl: NimNode): seq[FieldDescription] = |
| 123 | + if typeImpl.isTuple: |
| 124 | + for i in 1 ..< typeImpl.len: |
| 125 | + result.add FieldDescription(typ: typeImpl[i], name: ident("Field" & $(i - 1))) |
| 126 | + return |
| 127 | + |
| 128 | + let objectType = case typeImpl.kind |
| 129 | + of nnkObjectTy: typeImpl |
| 130 | + of nnkTypeDef: typeImpl[2] |
| 131 | + else: |
| 132 | + macros.error("object type expected", typeImpl) |
| 133 | + return |
| 134 | + |
| 135 | + collectFieldsInHierarchy(result, objectType) |
| 136 | + |
| 137 | +macro field*(obj: typed, fieldName: static string): untyped = |
| 138 | + newDotExpr(obj, ident fieldName) |
| 139 | + |
| 140 | +proc skipPragma*(n: NimNode): NimNode = |
| 141 | + if n.kind == nnkPragmaExpr: n[0] |
| 142 | + else: n |
| 143 | + |
| 144 | + |
| 145 | +{.pop.} |
0 commit comments