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