Abstract enum -> macro build static func

102 views
Skip to first unread message

Théo Sabattié

unread,
Aug 18, 2016, 3:54:51 AM8/18/16
to Haxe
Hi,

I would like know if a string can be an @:enum abstract

I didn't find specific method for that, so I decided to use an iteration on enum abstract to know if value exist or not.
Task is needed in a lot of @:enum abstract so I wanted use macro, but I have some difficulty...

From:

@:enum abstract Primary(String) to String 
{
    var FLOAT  = "Float";
    var INT    = "Int";
    var STRING = "String";
    var BOOL   = "Bool";
}

I would like obtain:

@:enum abstract Primary(String) to String 
{
    var FLOAT  = "Float";
    var INT    = "Int";
    var STRING = "String";
    var BOOL   = "Bool";

    private static var values:Array<String> = ["Float", "Int", "String", "Bool"];

    public static function getAllValues():Array<String> {
        return values.slice(0);
    }

    public static function hasValue(value:String){
        return values.indexOf(value) != -1;
    }
}

in an additive way, an other macro to add:

@:enum abstract Primary(String) to String 
{
    var FLOAT  = "Float";
    var INT    = "Int";
    var STRING = "String";
    var BOOL   = "Bool";

    private static var values:Array<String> = ["Float", "Int", "String", "Bool"];

    public static function getAllValues():Array<String> {
        return values.slice(0);
    }

    public static function hasValue(value:String){
        return values.indexOf(value) != -1;
    }

    private inline function new(s:String) {
        this = s;
    }

    @:from
    static public function fromString(s:String) {
        if (!Primary.hasValue(s)) {
            throw s + " does not exist on Primary";
        }
        return new Primary(s);
    }
}

Below, my current code:

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

class AbstractEnumBuildingMacro
{
    public static macro function build(typePath:Expr):Array<Field> {
        var type:Type           = Context.getType(typePath.toString());
        var fields:Array<Field> = Context.getBuildFields();
        var pos:Position        = Context.currentPos();
        var valueExprs          = [];
        
        switch (type.follow()) {
            case TAbstract(_.get() => ab, _) if (ab.meta.has(":enum")):
                valueExprs.push(macro "start");
                
                for (field in ab.impl.get().statics.get()) {
                    if (field.meta.has(":enum") && field.meta.has(":impl")) {
                        var fieldName = field.name;
                        valueExprs.push(macro $typePath.$fieldName);
                    } else {
                        valueExprs.push(macro false);
                    }
                }
                
                valueExprs.push(macro "end");
                
            default:
                throw new Error(type.toString() + " should be @:enum abstract", typePath.pos);
        }
        
        var getAllValuesFunc:Function = { 
            expr: macro {
                return values.slice(0);
            },
            ret: (macro:Array<Dynamic>),
            args:[]
        }
        
        var hasValueFunc:Function = { 
            expr: macro {
                return values.indexOf(value) != -1;
            },
            ret: (macro:Bool),
            args:[ {
               name:"value", 
               type: (macro:Dynamic)
            }]
        }
        
        fields.push({
            name:  "values",
            access:  [Access.APrivate, Access.AStatic],
            kind: FieldType.FVar(macro:Array<String>, macro $a{valueExprs}), 
            pos: pos,
        });
        
        fields.push({
            name:  "getAllValues",
            access:  [Access.APublic, Access.AStatic],
            kind: FieldType.FFun(getAllValuesFunc), 
            pos: pos,
        });
        
        fields.push({
            name:  "hasValue",
            access:  [Access.APublic, Access.AStatic],
            kind: FieldType.FFun(hasValueFunc), 
            pos: pos,
        });
        
        return fields;
    }
}

1/ Functions seem valid but "values" is an empty array.
I take a part of the code from cookbook : http://code.haxe.org/category/macros/enum-abstract-values.html
Have you an idea to fix that?

2/ How can I get type of abstract and use it for function?

Thanks,

Théo Sabattié

Théo Sabattié

unread,
Aug 18, 2016, 4:20:48 AM8/18/16
to Haxe
valueExprs.push(macro "start");
// ...
valueExprs.push(macro "end");

(It's just for test; there is not other expected values in array)

Skial Bainn

unread,
Aug 18, 2016, 5:14:09 AM8/18/16
to haxe...@googlegroups.com
I'm not entirely familiar with build macros working on abstracts, but I've compiled your code and done some testing using Haxe 3.3.0+ and it looks like the implementation class has not been fully typed when your build macro has been called.

Loading the abstract type through a static macro call with `Context.getType` and then accessing the implementation class seems to return the built, see the small gist for an example.

--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.

Cambiata

unread,
Aug 18, 2016, 10:27:03 AM8/18/16
to Haxe
Just for the record, there's this article on the Haxe Code Cookbook:
http://code.haxe.org/category/macros/enum-abstract-values.html

Jonas

Skial Bainn

unread,
Aug 18, 2016, 10:35:53 AM8/18/16
to haxe...@googlegroups.com
Good point, I missed that in the original message :)

--

Théo Sabattié

unread,
Aug 18, 2016, 10:56:04 AM8/18/16
to Haxe
Just for the record, there's this article on the Haxe Code Cookbook: 

Thanks @Cambiatia, but I talk about that in my first message...
I wanted get that on a build macro and stock it in static var.


1/ Functions seem valid but "values" is an empty array.
I take a part of the code from cookbook : http://code.haxe.org/category/macros/enum-abstract-values.html
Have you an idea to fix that?

Ok' @skial, thanks for analysis and testing.
Temporary I did that:

@:enum abstract EnumPrimary(String) to String 
{
    var FLOAT  = "Float";
    var INT    = "Int";
    var STRING = "String";
    var BOOL   = "Bool";

    private static var values:Array<String> = AbstractEnumTools.getValues(EnumPrimary);
    public static function getAllValues():Array<String> {
        return values.slice(0);
    }
    
    public static function hasValue(value:String){
        return values.indexOf(value) != -1;
    }

    private inline function new(s:String) {
        this = s;
    }

    @:from
    static public function fromString(s:String) {
        if (!hasValue(s)) {
            throw s + " does not exist on Primary";
        }
        return new EnumPrimary(s);
    }
}

So I have to repeat code on lot of my enum... 
It's pity for build macro.

Does someone know a better way to use build macro? :3

Le jeudi 18 août 2016 09:54:51 UTC+2, Théo Sabattié a écrit :
Reply all
Reply to author
Forward
0 new messages