Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare instantiation in Haxe macro function

Tags:

macros

haxe

I want to create a macro that generates this code for me:

if (myEntity.get(Attack) == null) myEntity.add(new Attack());
if (myEntity.get(Confused) == null) myEntity.add(new Confused());
if (myEntity.get(Defend) == null) myEntity.add(new Defend());
if (myEntity.get(Offense) == null) myEntity.add(new Offense());

In code I'd like to declare/use it like this:

EntityMacroUtils.addComponents(myEntity, Attack, Confused, Defend, Offense);

The current macro function looks like this:

macro public static function addComponents(entity:ExprOf<Entity>, components:Array<ExprOf<Class<Component>>>):Expr
{
    var exprs:Array<Expr> = [];
    for (componentClass in components)
    {
        var instance = macro $e { new $componentClass() }; // problem is here
        var expr = macro if ($entity.get($componentClass) == null) $entity.add(instance);
        exprs.push(expr);
    }
    return macro $b{ exprs };
}

This macro function is incorrect, I get the error:

EntityMacroUtils.hx:17: characters 22-43 : Type not found : $componentClass

The problem is I don't know how to define new $componentClass(). How would I solve this?

I also want to avoid to have Type.createInstance in the output code.

like image 692
Mark Knol Avatar asked Aug 24 '15 09:08

Mark Knol


1 Answers

One way to programmatically generate instantiation code is by using "old school" enums AST building (compatible Haxe 3.0.1+):

// new pack.age.TheClass()
return {
    expr:ENew({name:"TheClass", pack:["pack", "age"], params:[]}, []),
    pos:Context.currentPos()
};

An improved syntax using reification is possible:

// new pack.age.TheClass()
var typePath = { name:"TheClass", pack:["pack", "age"], params:[] };
return macro new $typePath();

Now, for a convenient "instantiation helper" function syntax, we need to do some contorsions to extract a type path from the expression we receive in the macro function:

// new Foo(), new pack.Bar(), new pack.age.Baz()
instantiate(Foo, pack.Bar, pack.age.Baz);

macro static function instantiate(list:Array<Expr>)
{
    var news = [for (what in list) {
        var tp = makeTypePath(what);
        macro new $tp();
    }];
    return macro $b{news};
}

#if macro
static function makeTypePath(of:Expr, ?path:Array<String>):TypePath 
{
    switch (of.expr)
    {
        case EConst(CIdent(name)):
            if (path != null) {
                path.unshift(name);
                name = path.pop();
            }
            else path = [];
            return { name:name, pack:path, params:[] };

        case EField(e, field):
            if (path == null) path = [field];
            else path.unshift(field);
            return makeTypePath(e, path);

        default:
            throw "nope";
    }
}
#end
like image 63
Philippe Avatar answered Oct 14 '22 04:10

Philippe