Skip to content

Instantly share code, notes, and snippets.

@nadako
Last active January 3, 2016 07:19
Show Gist options
  • Save nadako/8428429 to your computer and use it in GitHub Desktop.
Save nadako/8428429 to your computer and use it in GitHub Desktop.
Incomplete backport of @:enum abstracts from Haxe 3.1 to Haxe 3.0
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.TypeTools;
/**
* Частичный бекпорт функциональности @:enum abstract из Haxe 3.1
*
* @:enum abstract'ы - элегантная замена набору констант с проверкой значений на этапе компиляции.
*
* См. http://new.haxe.org/manual/types-abstract-enum.html для подробностей.
*
* Отличия от полноценного @:enum abstract из Haxe 3.1:
* - вместо @:enum нужно писать @:build(EnumAbstract.build())
* - нужно писать static var вместо просто var (иначе до build-макроса дело даже не дойдет)
* - не поддерживает типы параметров (но нам и не надо - нас вообще интересует тупо Int/String)
* - не поддерживает дополнительные поля (нам и не надо, если что - сделаем)
* - нельзя обращаться к константам без префикса класса (нет поддержки в компиляторе версии 3.0)
* - нет проверки на полность при switch (нет поддержки в компиляторе версии 3.0)
*
* Когда перейдем на новый Haxe - это нужно будет выпилить и юзать встроенные @:enum абстракты.
*
* Пример использования:
*
* @:build(EnumAbstract.build())
* abstract BattleState(Int)
* {
* static var Start = 0;
* static var Progress = 1;
* static var End = 2;
* }
*
*/
class EnumAbstract
{
macro static public function build():Array<Field>
{
// получаем тип абстракта
var a = switch(Context.getLocalClass().get().kind)
{
case KAbstractImpl(a): a;
default: throw "EnumAbstract.build cannot be used on non-abstract";
};
var tA = a.get();
if (tA.params.length > 0)
Context.error("EnumAbstract does not support type parameters", tA.pos);
var ctA = TAbstract(a, []);
// получаем рантайм-тип абстракта
var tThis = tA.type;
// итерируем по полям
var fields = Context.getBuildFields();
for (field in fields)
{
switch(field.kind)
{
// для всех указанных переменных проверяем тип и делаем преобразование в нечто что можно юзать
case FVar(t, e):
if (e == null)
Context.error("Value required", field.pos);
if (t != null)
Context.error("Type must be inferred", field.pos);
var tE = Context.typeof(e);
if (!Context.unify(tE, tThis))
Context.error('${tE.toString()} should be ${tThis.toString()}', e.pos);
field.access = [APublic, AStatic, AInline];
field.kind = FVar(ctA.toComplexType(), macro untyped $e);
default:
}
}
return fields;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment