2017-02-26 25 views
1

我想编写宏,它必须根据指定的元组或对象的字段执行某些逻辑。我认为这最好通过将元组/对象作为参数typed传递给宏来完成。如何迭代宏中的元组/对象的字段(名称+类型)?

问题是,我如何遍历一个参数typed的字段?我基本上在寻找宏的等价物fieldPairs,即不是采用一个具体的元组/对象,它必须在NimNode上操作,并且还返回字段名称/类型(用于进一步的AST生成)。

回答

1

我找到了一个解决问题的方法,它似乎很好,但我不确定是否有更好的选择。该解决方案基于在typed参数上使用getTypeImpl。要看看它是如何工作的,它有助于查看t.getTypeImpl.treeRepr的输出以获取简单的元组和对象。

  • 元组

    TupleTy 
        IdentDefs 
        Sym "x" 
        Sym "int" 
        Empty 
        IdentDefs 
        Sym "y" 
        Sym "int" 
        Empty 
        IdentDefs 
        Sym "name" 
        Sym "string" 
        Empty 
    

    注:的getTypeImpltypeKindntyTuple

  • 对象内容:例如(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 
    

    注:getTypeImpltypeKindntyObject

这表明,我们正在寻找的信息是在IdentDefs可用。我们只需确保适当地处理元组和对象:对于元组IdentDefsNimNode的直接子元素,而对于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)  
相关问题