2017-02-26 7 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)  
関連する問題