简体   繁体   English

泛型构建枚举的一些错误

[英]Some errors with genericbuilding enums

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.但是我学到了很多东西,非常感谢 Gama11,他为我指明了正确的方向,以及这样一个美丽事物的核心团队: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要么.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要么宏.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.使用 -D dump=pretty 转储使用美化模式在转储子目录中键入 AST。 The output from dump=pretty is almost indistuingishable from regular Haxe code. dump=pretty 的输出与常规 Haxe 代码几乎没有区别。 When errors appear, you find iin the root of the dump directory a file called 'decoding_error.txt'.当出现错误时,您会在转储目录的根目录中找到一个名为“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但更好的方法是简单......要调试最简单的方法,请转到您的宏文件(在我的情况下为EitherMacro.hx)并执行

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.使用@:genericbuild() 的另一个好主意是首先手动构造一个枚举(或任何类型),然后使用@:genericbuild 跟踪它,或者如果使用@:build 出现太多错误。 Then you can copy those trace outputs and try use that code to craft the AST in the macro.然后您可以复制这些跟踪输出并尝试使用该代码在宏中制作 AST。 This will seriously speedup your development, especially in case of complicated macro's.这将大大加快您的开发速度,尤其是在复杂的宏的情况下。 Almost mindlessly ;-)几乎是无意识的;-)

Your macro has never run.您的宏从未运行过。

Replace your build() function with the following to verify将您的build()函数替换为以下内容以验证

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

I suppose @:genericBuild only works for class我想@:genericBuild只适用于class

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM