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.
source to share
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).
source to share
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;
}
source to share