Skip to content

Instantly share code, notes, and snippets.

@Antriel
Created January 14, 2020 20:49
Show Gist options
  • Save Antriel/83276dec9fa51affebb3855ab1232dad to your computer and use it in GitHub Desktop.
Save Antriel/83276dec9fa51affebb3855ab1232dad to your computer and use it in GitHub Desktop.
Build macro to take all public functions of a class and add (lazy) singleton static function equivalents.
package antriel.macros;
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Expr.Position;
import haxe.macro.Type.ClassType;
using Lambda;
class Singletonize {
public static macro function build(selfInit:Bool = false):Array<Field> {
var fields = Context.getBuildFields();
// var type:ClassType = switch Context.getLocalType() { case TInst(r, _): r.get(); case _: null; }
// var supFields = type.superClass == null ? [] : type.superClass.t.get().fields.get();
if(fields.find(function(f) return f.name == "new") == null) {//add empty new if needed
fields.push({
name: "new",
access: [],
kind: FFun({ args: [], ret: null, expr: macro { } }),
pos: Context.currentPos()
});
}
for(i in 0...fields.length) switch(fields[i]) {
case { name: name, pos: pos, kind: FFun({ args: args, ret: ret }), access: access }
if(name != "new" && access.has(APublic)):
fields[i].name = '_${name}';//TODO possible optimization: mangle names to be shorter
var argsExpr:Array<Expr> = args.map(function(a) return macro $i{a.name} ).array();
fields.push({
name: name,
access: [APublic, AStatic, AInline],
kind: FFun({
args: args,
ret: ret,
expr: macro return ${{
expr: ECall({
expr: EField(macro inst, '_'+name),//inst.<name>
pos: Context.currentPos()
}, argsExpr),//inst.<name>(...)
pos: Context.currentPos()
}}
}),
pos: pos
});
case { name: "new", kind:FFun({expr:{expr:EBlock(exprs)}}) }:
if(!selfInit) exprs.push(macro inst = this);
case _:
}
//add super fields
// for(f in supFields) switch(f) {
// case { name: name, isPublic: true, kind: FMethod }
// }
// this would be a bit complex (extern overloaded methods), gonna do it manually for current need
fields.push({
name: "inst",
access: [APrivate, AStatic],
kind: FVar(haxe.macro.TypeTools.toComplexType(TInst(Context.getLocalClass(), [])),
!selfInit ? null : {
pos: Context.currentPos(),
expr: ENew({
pack: Context.getLocalClass().get().pack,
name: Context.getLocalClass().get().name
}, [])
}),
pos: Context.currentPos()
});
return fields;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment