如何迭代宏中 tuple/object 的字段(名称+类型)?
How to iterate over fields (names + types) of a tuple/object in a macro?
我想编写宏,它必须根据命名元组或对象的字段执行一些逻辑。我认为最好通过将 tuple/object 作为 typed
参数传递给宏来实现。
问题是,通常如何遍历 typed
参数的字段?我基本上是在为宏寻找 fieldPairs
的等价物,即,不是采用具体的 tuple/object,而是必须对 NimNode
和 return 字段进行操作 names/types 如此(用于进一步生成 AST)。
我找到了解决问题的办法,似乎工作正常,但我不确定是否有更好的选择。该解决方案基于在 typed
参数上使用 getTypeImpl
。要了解它是如何工作的,查看 t.getTypeImpl.treeRepr
的简单元组和对象的输出会有所帮助。
Tuple:例如 (x: 0, y: 1, name: "")
的类型 impl AST 看起来像这样:
TupleTy
IdentDefs
Sym "x"
Sym "int"
Empty
IdentDefs
Sym "y"
Sym "int"
Empty
IdentDefs
Sym "name"
Sym "string"
Empty
注:getTypeImpl
的typeKind
为ntyTuple
Object:具有相同结构的对象的类型 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 上的子项包含 pragma 信息,索引 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)
我想编写宏,它必须根据命名元组或对象的字段执行一些逻辑。我认为最好通过将 tuple/object 作为 typed
参数传递给宏来实现。
问题是,通常如何遍历 typed
参数的字段?我基本上是在为宏寻找 fieldPairs
的等价物,即,不是采用具体的 tuple/object,而是必须对 NimNode
和 return 字段进行操作 names/types 如此(用于进一步生成 AST)。
我找到了解决问题的办法,似乎工作正常,但我不确定是否有更好的选择。该解决方案基于在 typed
参数上使用 getTypeImpl
。要了解它是如何工作的,查看 t.getTypeImpl.treeRepr
的简单元组和对象的输出会有所帮助。
Tuple:例如
(x: 0, y: 1, name: "")
的类型 impl AST 看起来像这样:TupleTy IdentDefs Sym "x" Sym "int" Empty IdentDefs Sym "y" Sym "int" Empty IdentDefs Sym "name" Sym "string" Empty
注:
getTypeImpl
的typeKind
为ntyTuple
Object:具有相同结构的对象的类型 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 上的子项包含 pragma 信息,索引 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)