我想编写宏,它必须根据指定的元组或对象的字段执行某些逻辑。我认为这最好通过将元组/对象作为参数typed
传递给宏来完成。如何迭代宏中的元组/对象的字段(名称+类型)?
问题是,我如何遍历一个参数typed
的字段?我基本上在寻找宏的等价物fieldPairs
,即不是采用一个具体的元组/对象,它必须在NimNode
上操作,并且还返回字段名称/类型(用于进一步的AST生成)。
我想编写宏,它必须根据指定的元组或对象的字段执行某些逻辑。我认为这最好通过将元组/对象作为参数typed
传递给宏来完成。如何迭代宏中的元组/对象的字段(名称+类型)?
问题是,我如何遍历一个参数typed
的字段?我基本上在寻找宏的等价物fieldPairs
,即不是采用一个具体的元组/对象,它必须在NimNode
上操作,并且还返回字段名称/类型(用于进一步的AST生成)。
我找到了一个解决问题的方法,它似乎很好,但我不确定是否有更好的选择。该解决方案基于在typed
参数上使用getTypeImpl
。要看看它是如何工作的,它有助于查看t.getTypeImpl.treeRepr
的输出以获取简单的元组和对象。
元组:
TupleTy
IdentDefs
Sym "x"
Sym "int"
Empty
IdentDefs
Sym "y"
Sym "int"
Empty
IdentDefs
Sym "name"
Sym "string"
Empty
注:的getTypeImpl
的typeKind
是ntyTuple
对象内容:例如(x: 0, y: 1, name: "")
会是这个样子的类型IMPL AST的类型impl AST的结构相同的对象应该是:
ObjectTy
Empty
Empty
RecList
IdentDefs
Sym "x"
Sym "int"
Empty
IdentDefs
Sym "y"
Sym "int"
Empty
IdentDefs
Sym "name"
Sym "string"
Empty
注:getTypeImpl
的typeKind
为ntyObject
这表明,我们正在寻找的信息是在IdentDefs
可用。我们只需确保适当地处理元组和对象:对于元组IdentDefs
是NimNode
的直接子元素,而对于IdentDefs
存储在索引2上的子元素(索引0上的子元素包含杂注信息,孩子在索引1是父母的信息)。
总体宏可能看起来像(添加为说明一些调试输出):
macro iterateFields*(t: typed): untyped =
echo "--------------------------------"
# check type of t
var tTypeImpl = t.getTypeImpl
echo tTypeImpl.len
echo tTypeImpl.kind
echo tTypeImpl.typeKind
echo tTypeImpl.treeRepr
case tTypeImpl.typeKind:
of ntyTuple:
# For a tuple the IdentDefs are top level, no need to descent
discard
of ntyObject:
# For an object we have to descent to the nnkRecList
tTypeImpl = tTypeImpl[2]
else:
error "Not a tuple or object"
# iterate over fields
for child in tTypeImpl.children:
if child.kind == nnkIdentDefs:
let field = child[0] # first child of IdentDef is a Sym corresponding to field name
let ftype = child[1] # second child is type
echo "Iterating field: " & $field & " -> " & $ftype
else:
echo "Unexpected kind: " & child.kind.repr
# Note that this can happen for an object with a case
# fields, which would give a child of type nnkRecCase.
# How to handle them depends on the use case.
# small test
type
TestObj = object
x: int
y: int
name: string
let t = (x: 0, y: 1, name: "")
let o = TestObj(x: 0, y: 1, name: "")
iterateFields(t)
iterateFields(o)