How do I create a macro that generates an array of constructors dynamically known by name in Haxe?
I am targeting javascript .
I have a macro that runs on Context.onGenerate () that saves a subset of the fully qualified type names to a file. Then another build macro (which will run on the next buid) reads the list of type names from the file to create a static field addition to the class that should contain those types (constructors) in an array.
The field I want to create from the second macro would be something like this:
public static _entities:Array<Class<entities.Entity>> = [
entities.Foo,
entities.Bar,
...
];
To create the following javascript
MyClass._entities = [ entities_Foo, entities_Bar, ... ];
Now I tried to write the field by hand to make sure everything is generated correctly - it does. However, I cannot figure out how to write the macro correctly, I am stuck with adding an identifier constant as the value of an array expression, which always ends up with an "Unknown identifier" error :
var id = { expr: EConst( CIdent( "entities.Foo" ) ),
pos: Context.currentPos() };
var ex = EArrayDecl([ id ]);
fields.push( {
name : "_entities",
access : [Access.APublic, Access.AStatic ],
pos : Context.currentPos(),
kind : FVar(
macro:Array<Class<entities.Entity>>,
// I've tried writing it without reification: (see above vars)
{ expr: ex, pos:Context.currentPos() }
// Or w/ reification:
macro $a{[ $i{ "entities.Foo" } ]}
)
});
Am I trying to do this using macros? If this could guide me in steps to accomplish this?
Thank.
The problem is that you are trying to output it as a single identifier, and in fact it is a dotted path that should be presented as a string EField
for the first one EIdent
. Fortunately, Haxe has a convenient reification "way" for this: try $p{path.split(".")}
(where path
is your "entities.Foo"
string).
After a bit more in-depth searching for API references, I figured out how to do this. It turns out I need TypedExpr instead of an id constant.
A TTypeExpr with a ModuleType TClassDecl will give the correct result. So my example code above is:
static function getTypeRef( name:String ):Ref<ClassType>
{
var type = Context.getType( name );
switch( type )
{
default: return Context.error( "Expected a ClassType", Context.currentPos() );
case TInst( cr, _ ):
return cr;
}
}
static function getTypes()
{
// Obtain ClassType by identifier
var fooCls = getTypeRef( "entities.Foo" );
// Get a TypedExpr for the ClassType
var typedExpr:TypedExpr = {
expr : TTypeExpr( TClassDecl( fooCls ) ),
t : TInst( fooCls, [] ),
pos : Context.currentPos()
};
// Convert to Expr
var expr:Expr = Context.getTypedExpr( typedExpr );
var fields = Context.getBuildFields();
fields.push( {
name : "_entities",
access : [Access.APublic, Access.AStatic ],
pos : Context.currentPos(),
kind : FVar(
macro:Array<Class<entities.Entity>>,
macro $a{[ ${expr} ]} // <- Now it works here
)
});
return fields;
}