Использование локальных переменных в функции, созданной с помощью макроса haxe - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть 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{} reification:

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 голосов
/ 26 ноября 2018

Значение параметра lang неизвестно во время компиляции / макроса, поэтому я не вижу, как можно сгенерировать выражение доступа к полю, например langObjects["mytext"].en.Во время выполнения, когда фактически вызывается getLangText(), lang может быть "en" или чем-то еще.Таким образом, для обработки всех возможных значений по-прежнему требуется регистр переключения, цепочка if-else или отражение.

Если вместо использования созданного макросом сборки getLangText() был макросом выражения / amacro 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);
};

Возможно, это то, что вы ищете?Трудно сказать, не зная, какую проблему вы пытаетесь решить.

...