Skip to content

Code Cookbook example custom generator needed and problems #160

@mingodad

Description

@mingodad

Trying to understand how to use haxe.macro.Context.onGenerate(types:Array<Type>) to create custom generators I noticed that the types parameter comes with a strange order instead of the declaration order (line number) see the output bellow, this make it's usage a bit more involved than if it came ordered by file/line number, also probably in doing a working example several corner cases will surface and internal changes can be made to make life easy for Haxe users.

Would be nice to have an entry in the Code Cookbook with a simple functional example and I suggest the one bellow as starting point (generate Haxe again for simplicity), any comments/suggestions/improvements are welcome !

  • TestOnGenerate.hx:
final _version = "1.0";

/** Get version string. */

function getVersion(): String {
	return _version;
}

enum Color {
	Red;
	Green;
	Blue;
	RGB(r: Int, g: Int, b: Int);
}

class Test {
  static var _count: Int = 0;
  public var name:String;
  public var x:Float;
  public var y:Float;

  public function new(name:String, x:Float, y:Float) {
    this.name = name;
    this.x = x;
    this.y = y;
  }
  public function toString() : String {
	return name + "::" + Std.string(x) + "::" + Std.string(y);
  }
}

class TestOnGenerate {
    static function main() {
        var t = new Test("dad", 1.4, 2.9);
	trace(t.toString() + "::" + getVersion());
    }
}
  • MyOnGenerate.hx:
#if macro
import haxe.macro.Type;
import haxe.macro.Context;

using haxe.macro.PositionTools;

/**
 * Callback on generating code from context
 */
private function mygenerate(types:Array<Type>, filterFileName: String):Void {
	var buf = new StringBuf();
	for (atype in types) {
		// trace(atype);
		var aref = atype.getParameters()[0].get();
		var apos = Context.getPosInfos(aref.pos);
		//trace(apos);
		if(filterFileName != null && apos.file != filterFileName) continue;
		switch (atype) {
			case TMono(t):
				trace(t + "::" + aref.pos);
			case TEnum(t, params):
				var t_ref = t.get();
				//trace(t.toString() + "::" + aref.pos + "::" + params.toString());
				buf.add('\nenum ${t_ref.name} {\n');
				var enumFields = [];
				for (efield in t_ref.constructs) {
					//trace(efield);
					var ebuf = new StringBuf();
					ebuf.add('\t${efield.name}');
					switch(efield.type) {
						case TEnum(_, _): ebuf.add(";\n");
						case TFun(args, _):
							ebuf.add("(");
							for (i in 0...args.length) {
								var arg = args[i];
								if (i > 0)
									ebuf.add(", ");
								var type_name = switch(arg.t) {
									case TAbstract(t, params): t.get().name;
									default:
										throw 'Unsupported paramter ${arg.t}';
										null;
								};
								ebuf.add('${arg.name}: ${type_name}');
							}
							ebuf.add(");\n");

						default:
							throw 'Unsupported paramter ${efield.type}';
					}
					enumFields.insert(efield.index, ebuf.toString());
				}
				for(elm in enumFields) buf.add(elm);
				buf.add("}\n");
				//trace(buf.toString());
			case TInst(t, params):
				var t_ref = t.get();
				trace(aref.name + "::" + aref.pos /*.getInfos().min*/ + "::" + params.toString());
				var classFields = t_ref.fields.get();
				buf.add('\nclass ${t_ref.name} {\n');
				//trace(classFields);
				if(t_ref.constructor != null) trace(t_ref.constructor.get());
				for (ifield in classFields) {
					trace(ifield);
					buf.add('\t${ifield.name};\n');
				}
				trace(t_ref.statics.get());
				buf.add("}\n");
				//trace(buf.toString());
			case TType(t, params):
				trace(aref.name + "::" + aref.pos /*.getInfos().min*/ + "::" + params.toString());
			case TFun(args, ret):
				trace(args + "::" + aref.pos + "::" + ret);
			case TAnonymous(a):
				trace(a + "::" + aref.pos);
			case TDynamic(t):
				trace(t + "::" + aref.pos);
			case TLazy(f):
				trace(f + "::" + aref.pos);
			case TAbstract(t, params):
				trace(aref.name + "::" + aref.pos /*.getInfos().min*/ + "::" + params.toString());
		}
	}
	trace(buf.toString());
}

function set_on_generate(?ffname: String) {
	haxe.macro.Context.onGenerate(function(types:Array<Type>){mygenerate(types, ffname);});
}
#end
  • TestOnGenerate.hxml:
-main TestOnGenerate
--macro MyOnGenerate.set_on_generate("TestOnGenerate.hx")
#--macro MyOnGenerate.set_on_generate()
#-dce full
#-D analyzer_optimize

Output:

haxe "TestOnGenerate.hxml"
MyOnGenerate.hx:58: Test::#pos(TestOnGenerate.hx:16: lines 16-30)::[]
MyOnGenerate.hx:62: {name: new, isFinal: false, namePos: #pos(TestOnGenerate.hx:22: characters 19-22), isPublic: true, isAbstract: false, doc: null, params: [], pos: #pos(TestOnGenerate.hx:22: lines 22-26), kind: FMethod(MethNormal), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TFun([{name: name, t: TInst(<...>,[]), opt: false},{name: x, t: TAbstract(<...>,[]), opt: false},{name: y, t: TAbstract(<...>,[]), opt: false}],TAbstract(Void,[])), expr: #fun}
MyOnGenerate.hx:64: {name: name, isFinal: false, namePos: #pos(TestOnGenerate.hx:18: characters 14-18), isPublic: true, isAbstract: false, doc: null, params: [], pos: #pos(TestOnGenerate.hx:18: characters 3-26), kind: FVar(AccNormal,AccNormal), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TInst(String,[]), expr: #fun}
MyOnGenerate.hx:64: {name: x, isFinal: false, namePos: #pos(TestOnGenerate.hx:19: characters 14-15), isPublic: true, isAbstract: false, doc: null, params: [], pos: #pos(TestOnGenerate.hx:19: characters 3-22), kind: FVar(AccNormal,AccNormal), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TAbstract(Float,[]), expr: #fun}
MyOnGenerate.hx:64: {name: y, isFinal: false, namePos: #pos(TestOnGenerate.hx:20: characters 14-15), isPublic: true, isAbstract: false, doc: null, params: [], pos: #pos(TestOnGenerate.hx:20: characters 3-22), kind: FVar(AccNormal,AccNormal), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TAbstract(Float,[]), expr: #fun}
MyOnGenerate.hx:64: {name: toString, isFinal: false, namePos: #pos(TestOnGenerate.hx:27: characters 19-27), isPublic: true, isAbstract: false, doc: null, params: [], pos: #pos(TestOnGenerate.hx:27: lines 27-29), kind: FMethod(MethNormal), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TFun([],TInst(String,[])), expr: #fun}
MyOnGenerate.hx:67: [{name: _count, isFinal: false, namePos: #pos(TestOnGenerate.hx:17: characters 14-20), isPublic: false, isAbstract: false, doc: null, params: [], pos: #pos(TestOnGenerate.hx:17: characters 3-30), kind: FVar(AccNormal,AccNormal), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TAbstract(Int,[]), expr: #fun}]
MyOnGenerate.hx:58: TestOnGenerate::#pos(TestOnGenerate.hx:32: lines 32-37)::[]
MyOnGenerate.hx:67: [{name: main, isFinal: false, namePos: #pos(TestOnGenerate.hx:33: characters 21-25), isPublic: false, isAbstract: false, doc: null, params: [], pos: #pos(TestOnGenerate.hx:33: lines 33-36), kind: FMethod(MethNormal), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TFun([],TAbstract(Void,[])), expr: #fun}]
MyOnGenerate.hx:58: TestOnGenerate_Fields_::#pos(TestOnGenerate.hx:1: character 1)::[]
MyOnGenerate.hx:67: [{name: _version, isFinal: true, namePos: #pos(TestOnGenerate.hx:1: characters 7-15), isPublic: true, isAbstract: false, doc: null, params: [], pos: #pos(TestOnGenerate.hx:1: characters 1-24), kind: FVar(AccNormal,AccNever), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TInst(String,[]), expr: #fun},{name: getVersion, isFinal: false, namePos: #pos(TestOnGenerate.hx:5: characters 10-20), isPublic: true, isAbstract: false, doc:  Get version string. , params: [], pos: #pos(TestOnGenerate.hx:5: lines 5-7), kind: FMethod(MethNormal), meta: {get: #fun, remove: #fun, has: #fun, extract: #fun, add: #fun}, overloads: overloads, isExtern: false, type: TFun([],TInst(String,[])), expr: #fun}]
MyOnGenerate.hx:84: 
enum Color {
	Red;
	Green;
	Blue;
	RGB(r: Int, g: Int, b: Int);
}

class Test {
	name;
	x;
	y;
	toString;
}

class TestOnGenerate {
}

class TestOnGenerate_Fields_ {
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions