在使用haxe宏构建的函数中使用局部变量

时间:2018-11-25 21:55:44

标签: macros haxe

我有一个LangBuilder宏类;它用于在编译时构建各种语言的langObjects:Map<String, Dynamic>文本,并通过@:build宏将这种结构注入类中。地图的每个项目都有一个支持每种语言的字段。结果是:

@:build(LangBuilder.build())
class Lang{}

trace(Lang.langObjects["hello-world"].en); //outputs "Hello World!"
trace(Lang.langObjects["hello-world"].it); //outputs "Ciao Mondo!"

这很完美,但是我认为我可以使用函数langObjects来隐藏getLangText结构,该函数带有文本ID(例如"hello-world")和语言代码(例如"it")。

所以我正在尝试将此函数添加到类中

public static function getLangText(id:String, lang:String)

其非宏版本可以表示为:

public static function getLangText(id:String, lang:String){
    var _langObj_id = langObjects[id];
    switch(lang){
        case "it":
            return _langObj_id.it;
        case "en":
            return _langObj_id.en;
    }
    return "Missing Translation";

如果我使用以下代码将此函数转换为宏:

var code = macro {
  var _langObj_id = langObjects[$i{"id"}];
  switch($i{"lang"}){
    case "it":
      return _langObj_id.it;
    case "en":
      return _langObj_id.en;
  }
  return "Missing translation";
};

var myFunc:Function = {
  args: [{
    name: "id",
    type: TPath({name: "String", pack: []}),
    meta: null
  },
  {
    name: "lang",
    type: TPath({name: "String", pack: []}),
    meta: null
  }],
  ret: (macro:String),
  expr: macro $code
};

fields.push({
  pos: Context.currentPos(),
  name: "getLangText",
  meta: null,
  kind: FieldType.FFun(myFunc),
  doc: null,
  access: [Access.APublic, Access.AStatic]
});

...它可以正常工作。但是,我想知道如何在没有switch的情况下编写代码,使其更加灵活并了解有关haxe宏的知识。我看到了一些示例,其中可以使用$p{}object.$fieldName在宏中访问字段。但是haxe手册警告说,第二种形式只能用于简单标识符。例如object.${fieldName}不起作用。

所以我尝试以下代码:

var code = macro {
  var l:String = $i{"lang"};
  var _langObj_id = langObjects[$i{"id"}];
  return _langObj_id.$l;
};

编译器给出错误

  

未知标识符:l

包含return _langObj_id.$l;的行上。

然后我尝试使用$p{}的形式:

var code = macro {
  var _langObj_id = langObjects[$i{"id"}];
  return macro $p{["_langObj_id", $i{"lang"}]};
};

但错误类似:

  

未知标识符:lang

我当然可以将langObjects结构更改为Map<String, Map<String, String>>,然后将代码更改为:

var code = macro {
  return macro langObjects[$i{"id"}][$i{"lang"}];
};

我认为这可以解决问题,但是现在我试图理解为什么_langObj_id.$lang$p{["_langObj_id", $i{"lang"}]}都不起作用,以及在类似情况下访问字段的正确方法是什么那个。

1 个答案:

答案 0 :(得分:2)

lang参数的值在编译时/宏时未知,因此我看不到如何生成langObjects["mytext"].en之类的字段访问表达式。在运行时实际调用getLangText()时,lang可以是"en"或其他任何值。因此,仍然需要切换大小写,if-else-chain或反射来处理所有可能的值。

如果getLangText()是表达式宏/ macro function而不是由构建宏创建,则函数调用将在编译时进行评估,并替换为其返回的表达式。这将使您能够根据参数生成适当的字段访问表达式。看起来可能像这样:

class Macro {
    public static var langObjects = ["mytext" => {en: "hello", de: "hallo"}];

    public static macro function getLangText(id:String, lang:String) {
        return macro {
            var langObject = Macro.langObjects[$v{id}];
            langObject.$lang;
        }
    }
}
class Main {
    static function main() {
        trace(Macro.getLangText("mytext", "en"));
        trace(Macro.getLangText("mytext", "de"));
    }
}

在JS目标上编译为以下内容:

Main.main = function() {
    var langObject = Macro.langObjects.get("mytext");
    console.log("source/Main.hx:3:",langObject.en);
    var langObject1 = Macro.langObjects.get("mytext");
    console.log("source/Main.hx:4:",langObject1.de);
};

也许这就是您想要的?在不知道您要解决什么问题的情况下很难说。