Some errors with genericbuilding enums

十年热恋 提交于 2019-12-01 23:05:26

问题


Solved For a first ever macro to write this wasnt the easiest. But I learned a lot, much kudo's to Gama11 who pointed me in the right direction, and the coreteam for such a thing of beauty: Haxe.

And I even added some slick doc field strings, so you get nice info during autocompletion.

Main.hx

var e1:Either<String, Int, Bool> = Either3._1('test');
var e2:Either<String, Int, Bool> = Either3._2(1);
var e3:Either<String, Int, Bool> = Either3._3(true);
var error:Either<String, Int, Bool> = Either3._3('Bool expected, but got a String this will give an error');

Either.hx

package;

@:genericBuild(EitherMacro.build())
class Either<Rest> {} 

/*@:genericbuild only works on classes, but 
can still override the class with an enum. Funky. */

EitherMacro.hx

package;


#if macro
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.Tools;


class EitherMacro {
    static var eitherTypes = new Map<Int,Bool>();

    static function build():ComplexType {
        return switch (Context.getLocalType()) {
            case TInst(_.get() => {name: "Either"}, params):
                buildEitherEnum(params);
            default:
                throw false;
        }
        return macro:Dynamic;
    }

    static function buildEitherEnum(params:Array<Type>):ComplexType {
        var numParams = params.length;
        var name='Either$numParams';
        if (!eitherTypes.exists(numParams)){
            Context.defineType(defineType(name, params));
            eitherTypes[numParams] = true;
        }
        return TPath({pack: [], name: name, params: [for (t in params) TPType(t.toComplexType())]});
    }

    private static inline function defineType(name:String, params:Array<Type>){
        var typeParams:Array<TypeParamDecl> = [];
        var typeStrings:Array<String>=[];
        var numParams = params.length;
        var fields:Array<Field>=[];
        for (i in 0...numParams) {
            var t=i+1;
            typeStrings.push(params[i].toString());
        }
        var constDocStr=typeStrings.join(',');
        for (i in 0...numParams) {
            var t=i+1;
            var typeString:String=typeStrings[i];
            typeParams.push({name:'T$t'});

            fields.push(
                {
                    name:   '_$t',
                    pos:    Context.currentPos(),

                    doc: 'from $name<$constDocStr> _$t(v: $typeString)',
                    kind:FFun({
                            ret: null, 
                            params: [{name:'T$t'}], 
                            expr: null, 
                            args: [
                                {
                                    name: 'v', 
                                    type: TPath(
                                        {
                                        name:'T$t',
                                        params:[],
                                        pack:[]
                                        }
                                    )
                                }
                                ]
                        }
                    )
                }
            );
        }
        var docStr:String="Either represents values which are either of type ";
        for(k in 0...typeStrings.length){
            if(k!=typeStrings.length-1){
                docStr+=typeStrings[k]+" or ";
            } else {
                docStr+=typeStrings[k]+".";
            }
        }

        return {
            pack:[],
            name:name,
            pos:Context.currentPos(),
            doc:docStr,
            isExtern: false,
            meta:null,
            kind:TDEnum,
            fields:fields,
            params:typeParams
        }
    }
}
#end

Debugging your macro's the easy way

usage of -D dump=pretty dumps typed AST in dump subdirectory using prettified mode. The output from dump=pretty is almost indistuingishable from regular Haxe code. When errors appear, you find iin the root of the dump directory a file called 'decoding_error.txt'. Its contents might look like this:

{
    doc: null
    fields: null <- expected value
    isExtern: null
    kind: null <- expected value
    meta: null
    name: null <- expected value
    pack: null <- expected value
    params: null
    pos: null <- expected value
}
line 3: expected value
line 5: expected value
line 7: expected value
line 8: expected value
line 10: expected value

This made it much easier for me to debug. But the even better way, is way simple... To debug the easiest way, go to your macrofile (in my case EitherMacro.hx) and do

class EitherMacro{
   public static function build(){
      var fields=Context.getBuildFields();
      var type=Context.getLocalType();
      trace(type);
      for(f in fields){
         trace(f);
      }

      // your other code
/*
If you use @:build)() instead of @:genericbuild 
to debug. Make sure the buildfunction returns 
Array<Field> and put at the last line 

return Context.getBuildFields();

if you use @:genericbuild you must return 
ComplexType, and you can add as the line 
return macro:Dynamic; if you have no working return yet.
*/


   }
}

the output might look like this:

source/EnumBuilder2.hx:18: TEnum(SomeEnum,[TInst(SomeEnum.T1,[]),TInst(SomeEnum.T2,[]),TInst(SomeEnum.T3,[])])


source/EnumBuilder2.hx:20: {name: _1, doc: null, pos: #pos(source/SomeEnum.hx:4: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:4: characters 5-7)}
source/EnumBuilder2.hx:20: {name: _2, doc: null, pos: #pos(source/SomeEnum.hx:5: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:5: characters 5-7)}
source/EnumBuilder2.hx:20: {name: _3, doc: null, pos: #pos(source/SomeEnum.hx:6: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:6: characters 5-7)}

Another good idea with @:genericbuild(), is to first constructed an enum by hand(or whatever type) and after that trace it using a @:genericbuild, or if you got too much errors using @:build. Then you can copy those trace outputs and try use that code to craft the AST in the macro. This will seriously speedup your development, especially in case of complicated macro's. Almost mindlessly ;-)


回答1:


Your macro has never run.

Replace your build() function with the following to verify

    static function build():ComplexType {
        trace('build');
        return macro:Dynamic;
    }

I suppose @:genericBuild only works for class



来源:https://stackoverflow.com/questions/57481810/some-errors-with-genericbuilding-enums

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!