JuliaData / StructTypes.jl

Abstract definitions and convenience methods for describing, processing, and constructing Julia objects
https://juliadata.github.io/StructTypes.jl/stable/
MIT License
79 stars 22 forks source link

Macro for subtypes lowering with custom type field #93

Closed sairus7 closed 1 year ago

sairus7 commented 1 year ago

According to this solution for lowering subtypes that does not have additional type field: The need for a type::String field and subtypekey for abstract types

I've tried to write such a macro, but I think I get this wrong, can you look at it? @quinnj

macro lowersubtype(type, subtypekey)
    T = Core.eval(__module__, type)
    vars = [QuoteNode(e) for e in fieldnames(T)]
    xvars = [:(x.$(e)) for e in fieldnames(T)]
    types = [esc(e) for e in fieldtypes(T)]
    nt = :(NamedTuple{(QuoteNode($subtypekey), $(vars...),), Tuple{String, $(types...)}})
    return quote
        StructTypes.StructType(::Type{$(esc(type))}) = StructTypes.CustomStruct()
        StructTypes.lower(x::$(esc(type))) = (cmd = $subtypekey, $(xvars...))
        StructTypes.lowertype(::Type{$(esc(type))}) = $nt
        $(esc(type))(x::$nt) = $(esc(type))($(xvars...))
    end
end

struct Foo
    x::Int
    y::Float64
end

@lowersubtype Foo "type"
sairus7 commented 1 year ago

Finally got this working:

macro register_subtype(struct_type, field_name, field_value)
    T = Core.eval(__module__, struct_type)
    x_names = [:(x.$(e)) for e in fieldnames(T)] # x.a, x.b, ...
    name_types = [:($n::$(esc(t))) for (n, t) in zip(fieldnames(T), fieldtypes(T))] # a::Int, b::String, ...
    quote
        StructTypes.StructType(::Type{$(esc(struct_type))}) = StructTypes.CustomStruct()
        StructTypes.lower(x::$(esc(struct_type))) = ($(esc(field_name)) = $field_value, $(x_names...))
        StructTypes.lowertype(::Type{$(esc(struct_type))}) = @NamedTuple{$field_name::$(esc(String)), $(name_types...)}
        $(esc(struct_type))(x::@NamedTuple{$field_name::$(esc(String)), $(name_types...)}) = $(esc(struct_type))($(x_names...))
    end
end

struct Foo
    x::Int
    y::String
end

@register_subtype Foo foo "bar"

e = Foo(1, "r")

StructTypes.StructType(Foo)
le = StructTypes.lower(e)
StructTypes.lowertype(M.NewEventName)

Foo(le)