[英]How to pass an Array of Expr (expressions) to Haxe Macro?
我基本上試圖通過為所有條件提供一個公共結果語句來創建一個自動生成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 :("); };
}
}
然后,在理論上它可以這樣使用:
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>
傳遞多個表達式?
您可能無法使xConds
參數的行為與您預期的一樣,因為具有Array<Expr>
類型的表達式宏的最后一個參數隱式地是一個rest參數 。 這意味着您最終得到一個包含單個EArrayDecl
表達式的數組。 這可以通過省略[]
。
關於生成if
- else
-chain - 讓我們來看看EIf
:
/**
An `if(econd) eif` or `if(econd) eif else eelse` expression.
**/
EIf( econd : Expr, eif : Expr, eelse : Null<Expr> );
鏈可以被看作是一個單向鏈表-的eelse
如果第一個EIf
要引用下一個EIf
等等,直到我們有停止eelse = null
最后EIf
。 所以我們想為你的例子生成這個(偽代碼):
EIf(doSomething(), isOk = false, /* else */
EIf(doSomethingElse, isOk = false, /* else */
EIf(doFinalThing(), isOk = false, null)
)
)
遞歸適用於此。
通常,使用reification比使用像我這樣的原始表達式更方便,但是我不確定在動態生成這樣的表達式時前者是否真的可行。
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;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.