COMMUNITY

Use macros to enrich output code

Hello everyone.
I’ve pass by the Discord today to get some help and some nice people already gave me some strong advices. I’ve made some good progress but I’m still stuck with the usage of macros.

Here’s my issue:
I sometimes use macros to “add” untyped code for a specific target (lets say Javascript in this example)
It alows me to use specific keywords (let say async/await). I would like to use macro on build to do the same thing.

Example:

In a Macros.hx I have my macros

import haxe.macro.*;

class Macros {
	public static macro function async(expr:Expr):Expr {
		return macro untyped __js__("async {0}", $expr)();
	}

	public static macro function await(expr:Expr) {
		return macro untyped __js__("await {0}", $expr);
	}
}

And I use them in my Main.hx:

import Macros;

class Main {
	static function main() {
		Macros.async(function():Void {
			var result:Dynamic = Macros.await(null);
			trace(result);
		});
	}
}

And that gave me the expected output code:

class Main {
	static main() {
		async function() {
			var result = await null;
			console.log("src/Main.hx:7:",result);
		}();
	}
}
Main.main();

Now my plan is to use such Main.hx:

@:build(Macro.build())
class Main {
	public static function main() {
		@async function test():Void {
			var result:Dynamic = @await null;
			trace(result);
		}
	}
}

I’m trying to figure out how to parse for the meta tags, retrieve the meta name and according to this value transform the expression that follows.

Now, here is my Macro.hx containing the build() function:

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.ExprTools;

class Macro {
	public static function build() {
		var fields = Context.getBuildFields();
		for (field in fields) {
			switch (field.kind) {
				case FFun(f):
					f.expr = transform(f.expr);
				// trace(ExprTools.toString(f.expr));
				default:
			}
		}
		return fields;
	}

	public static function transform(e:Expr):Expr {
		return switch (e.expr) {
			case EMeta({name: name}, f):
				if (name == "async") {
					e = macro untyped __js__("async {0}", ${f});
				}
				if (name == "await") {
					e = macro untyped __js__("await {0}", ${f});
				}
				ExprTools.map(e, transform);
			default:
				ExprTools.map(e, transform);
		}
	}
}

The trace returns:

untyped __js__("async {0}", function test():Void {
	var result:Dynamic = untyped __js__("await {0}", null);
	trace(result);
});

Seams to me as the same as what my macros are doing in the first Macros.hx file, yet the output is very different:

class Main {
	static main() {
		var tmp = async(function ($this) {
			var $r;
			var test = function () {
				var result = await null;
				console.log("src/Main.hx:6:", result);
			};
			$r = test;
			return $r;
		}(this));
		tmp();
	}
}
Main.main();

Any idea on how to proceed to get the same output using build macros?

Best regards;