2016-04-01 9 views
5

私は基本的にすべての条件に共通の1つの結果文を提供してif/else ifチェーンを自動的に生成するマクロを作成しようとしています。Expr(式)の配列をHaxeマクロに渡すには?

これは私が(ちょうど例として表示するようにコードを修正)これまでに試したものです:

import haxe.macro.Expr; 

class LazyUtils { 

    public macro static function tryUntilFalse(xBool:Expr, xConds:Array<Expr>) { 
     var con1, con2, con3, con4, con5; 

     /* 
     * Here's a switch to handle specifiC# of conditions, because for-loops 
     * don't seem to be allowed here (at least in the ways I've tried so far). 
     * 
     * If you know how to use for-loop for this, PLEASE do tell! 
     */ 
     switch(xConds.length) { 
      case 1: { 
       con1 = conds[0]; 
       return macro { 
        if (!$con1) $xBool; 
       } 
      } 
      case 2: { 
       con1 = conds[0]; 
       con2 = conds[1]; 
       return macro { 
        if (!$con1) $xBool; 
        else if (!$con2) $xBool; 
       } 
      } 
      case 3: { 
       con1 = conds[0]; 
       con2 = conds[1]; 
       con3 = conds[2]; 
       return macro { 
        if (!$con1) $xBool; 
        else if (!$con2) $xBool; 
        else if (!$con3) $xBool; 
       } 
      } 
      // ... so on and so forth 
     } 

     return macro { trace("Unhandled length of conditions :("); }; 
    } 
} 

次に、理論的にはこのように使用することができます:

class Main { 
    static function main() { 
     var isOK = true; 
     LazyUtils.tryUntilFalse(isOK = false, [ 
      doSomething(), 
      doSomethingElse(), //Returns false, so should stop here. 
      doFinalThing() 
     ]); 
    } 

    static function doSomething():Bool { 
     // ??? 
     return true; 
    } 

    static function doSomethingElse():Bool { 
     // ??? 
     return false; 
    } 

    static function doFinalThing():Bool { 
     return true; 
    } 
} 

この条件ツリーを生成するもの:

if (!doSomething()) isOK = false; 
else if (!doSomethingElse()) isOK = false; 
else if (!doFinalThing()) isOK = false; 

代わりに、これを代わりに出力できるとします。

if(!doSomething() || !doSomethingElse() || !doFinalThing()) isOK = false; 

は真、すぐに戻ってこれで見て - それは、生の形式だそれで出て入力する方が簡単だろうコードを生成するために、全体のマクロを書くことはあまり意味がないかもしれません。

マクロについて学ぶために、上記のコードサンプルで試したように、複数の式をArray<Expr>に渡すことができるかどうかは誰にも分かりますか?

答えて

4

Array<Expr>の式マクロの最後の引数が暗黙的にrest argumentであるため、xConds引数が期待どおりに動作しない可能性があります。つまり、単一のEArrayDeclという表現を含む配列で終わったということです。これは単に[]を省略することで修正できます。 else - 鎖 - - ifの生成について

のはEIfを見てみましょう:

/** 
    An `if(econd) eif` or `if(econd) eif else eelse` expression. 
**/ 
EIf(econd : Expr, eif : Expr, eelse : Null<Expr>); 

チェーンを単独リンクリストのように考えることができます - 最初EIfは、次のEIfを参照する必要がある場合eelse最後にEIfの場合はeelse = nullで止まるまで続きます。だから私たちはあなたの例(擬似コード)のためにこれを生成したい:

EIf(doSomething(), isOk = false, /* else */ 
    EIf(doSomethingElse, isOk = false, /* else */ 
     EIf(doFinalThing(), isOk = false, null) 
    ) 
) 

再帰が、この適しています。

一般に、私がここでやっているような生の表現よりも、身元確認を扱う方が便利ですが、このような式を動的に生成するときは、前者が本当に可能かどうかはわかりません。

import haxe.macro.Context; 
import haxe.macro.Expr; 

class LazyUtils { 

    public macro static function tryUntilFalse(setBool:Expr, conditions:Array<Expr>):Expr { 
     return generateIfChain(setBool, conditions); 
    } 

    private static function generateIfChain(eif:Expr, conditions:Array<Expr>):Expr { 
     // get the next condition 
     var condition = conditions.shift(); 
     if (condition == null) { 
      return null; // no more conditions 
     } 

     // recurse deeper to generate the next if 
     var nextIf = generateIfChain(eif, conditions); 
     return { 
      expr: EIf(condition, eif, nextIf), 
      pos: Context.currentPos() 
     }; 
    } 
} 

Main.hx(ほとんど変わらず):

class Main { 

    static function main() { 
     var isOK = true; 
     LazyUtils.tryUntilFalse(isOK = false, 
      !doSomething(), 
      !doSomethingElse(), //Returns false, so should stop here. 
      !doFinalThing() 
     ); 
    } 

    static function doSomething():Bool { 
     trace("doSomething"); 
     return true; 
    } 

    static function doSomethingElse():Bool { 
     trace("doSomethingElse"); 
     return false; 
    } 

    static function doFinalThing():Bool { 
     trace("doFinalThing"); 
     return true; 
    } 
} 

物事をシンプルに保つために私の代わりにマクロでその取り扱いの呼び出しサイトで!と、関数呼び出しの引数を反転。

-D dump=prettyを使用すると、ASTダンプを生成し、生成されているコードを確認できます。結果は次のとおりです。

if ((! Main.doSomething()))isOK = false else if ((! Main.doSomethingElse()))isOK = false else if ((! Main.doFinalThing()))isOK = false; 
+0

+1ありがとうございます。'配列'についての知識は、基本的には 'rest'引数であることにうれしく思います。私はこの情報を見つけるためにどこを見始めるのか想像できません。ああ、あなたが共有する良い読み込み/リンクがあれば、それを見てみたい! :) – bigp

+0

(リンクでは、 'rest'引数に関することだけを意味するわけではありません.Haxeマクロスで一般的に役立つ参考資料やチュートリアルがあります)。 – bigp

+0

この記事では最後にリンク集があります:https://notes.underscorediscovery.com/haxe-compile-time-macros/ – Gama11

関連する問題