如何将Expr(表达式)数组传递给Haxe Macro?

big*_*igp 5 arrays syntax macros expression haxe

我基本上试图if/else if通过为所有条件提供一个共同的结果语句来创建一个自动生成链的宏.

这是我到目前为止所尝试的(修改代码仅作为示例):

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 :("); };
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,在理论上它可以这样使用:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

哪个应该生成这个条件树:

if (!doSomething()) isOK = false;
else if (!doSomethingElse()) isOK = false;
else if (!doFinalThing()) isOK = false;
Run Code Online (Sandbox Code Playgroud)

或者,我想它可以输出这个:

if(!doSomething() || !doSomethingElse() || !doFinalThing()) isOK = false;
Run Code Online (Sandbox Code Playgroud)

现在回过头来看,这是真的 - 编写一个完整的宏来生成更容易以原始格式输入的代码可能没什么意义.

但是为了学习宏,有谁知道是否可以Array<Expr>像我在上面的代码示例中尝试的那样传递多个表达式?

Gam*_*a11 4

您可能无法使xConds参数的行为符合您的预期,因为具有该类型的表达式宏的最终参数隐式Array<Expr>是一个剩余参数。这意味着您最终得到了一个包含单个EArrayDecl表达式的数组。这可以通过简单地省略[].

关于生成if- -else链 - 让我们看一下EIf

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

该链可以被认为是一个单链表 -eelse如果第一个EIf应该引用下一个EIf,依此类推,直到我们停止eelse = null最后一个EIf。所以我们想为您的示例生成这个(伪代码):

EIf(doSomething(), isOk = false, /* else */
    EIf(doSomethingElse, isOk = false, /* else */
        EIf(doFinalThing(), isOk = false, null)
    )
)
Run Code Online (Sandbox Code Playgroud)

递归对此很有效。

通常,使用具体化比像我在这里那样使用原始表达式更方便,但我不确定在动态生成这样的表达式时,前者是否真的可能。

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()
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

并且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;
    }
}
Run Code Online (Sandbox Code Playgroud)

为了简单起见,我!在调用站点反转了函数调用参数,而不是在宏中处理它。

您可以使用它-D dump=pretty来生成 AST 转储并检查正在生成哪些代码。结果如下:

if ((! Main.doSomething()))isOK = false else if ((! Main.doSomethingElse()))isOK = false else if ((! Main.doFinalThing()))isOK = false;
Run Code Online (Sandbox Code Playgroud)