Skip to content

Instantly share code, notes, and snippets.

@KeyMaster-
Created February 12, 2016 23:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KeyMaster-/ad55bd7a3e387a531d0f to your computer and use it in GitHub Desktop.
Save KeyMaster-/ad55bd7a3e387a531d0f to your computer and use it in GitHub Desktop.
A macro that extracts all static methods from a class that conform to a certain function signature, and returns an array with those functions.
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Type;
import haxe.macro.TypeTools;
import haxe.macro.MacroStringTools;
class FunctionFinder {
macro public static function get_functions(e:Expr, fun_signature:Expr) {
var function_type:Type = switch(fun_signature.expr) {
case EConst(c):
switch(c) {
case CIdent(s):
Context.getType(s);
default:
null;
}
default:
null;
}
function_type = TypeTools.follow(function_type);
var argument_types:Array<Type> = [];
var return_type:Type = null;
//Check if the type of the signature is actually a function. If so, exctract the argument and return types
switch(function_type) {
case TFun(args, ret):
for(arg in args) {
argument_types.push(arg.t);
}
return_type = ret;
default:
trace('You did not provide a function typedef!');
return null;
}
//Get the class type to get fields from later
var c:ClassType = null;
var def:TypedExprDef = Context.typeExpr(e).expr;
var c:ClassType = switch(def){
case TTypeExpr(m):
switch(m) {
case TClassDecl(c_ref):
c_ref.get();
default:
null;
}
default:
null;
}
if(c == null) {
trace('You did not provide a class!');
return null;
}
var functions:Array<Expr> = [];
var matching_functions:Array<Expr> = [];
for(field in c.statics.get()) {
if(field.expr() == null) continue; //If a static method hasn't been typed yet, expr() will return null - Don't know how to wait for all statics to be typed...
switch(field.expr().expr) {
case TFunction(func):
var matches = true;
for(i in 0...func.args.length) {
//I don't know how to compare types properly, so I hope unification works as well
if(!TypeTools.unify(func.args[i].v.t, argument_types[i])) {
matches = false;
break;
}
}
if(!matches) continue;
//Now we know that argument types are the same, test for the return type
if(TypeTools.unify(func.expr.t, return_type)) {
//Generate the path to the function we're looking at
var path = [].concat(c.pack);
path.push(c.name);
path.push(field.name);
functions.push(MacroStringTools.toFieldExpr(path));
}
default:
}
}
//Hand-made array declaration containing all our field accesses to functions
return {
pos:Context.currentPos(),
expr:EArrayDecl(functions)
};
}
}
class Main {
public static function test(a:Int):Void {
trace('test called with: ' + a);
}
//This function won't be included
public static function otherTest(a:String):Void {
trace('otherTest called with: ' + a);
}
public static function main() {
var functions:Array<Fun> = FunctionFinder.get_functions(Main, Fun);
for(fun in functions) {
fun(5);
}
}
}
typedef Fun = Int->Void;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment