Browse Source

Support base types other than Int32.

pull/8/head
Fredrik Ekre 4 years ago
parent
commit
1a50603d73
  1. 24
      src/EnumX.jl
  2. 28
      test/runtests.jl

24
src/EnumX.jl

@ -7,11 +7,21 @@ export @enumx
abstract type Enum{T} <: Base.Enum{T} end abstract type Enum{T} <: Base.Enum{T} end
macro enumx(args...) macro enumx(args...)
return enumx(args...) return enumx(__module__, args...)
end end
function enumx(name, args...) function enumx(_module_, name, args...)
namemap = Dict{Int32,Symbol}() if name isa Symbol
modname = name
baseT = Int32
elseif name isa Expr && name.head == :(::) && name.args[1] isa Symbol && length(name.args) == 2
modname = name.args[1]
baseT = Core.eval(_module_, name.args[2])
else
throw(ArgumentError("invalid EnumX.@enumx type specification: $(name)"))
end
name = modname
namemap = Dict{baseT,Symbol}()
next = 0 next = 0
for arg in args for arg in args
@assert arg isa Symbol # TODO @assert arg isa Symbol # TODO
@ -19,13 +29,13 @@ function enumx(name, args...)
next += 1 next += 1
end end
module_block = quote module_block = quote
primitive type Type <: Enum{Int32} 32 end primitive type Type <: Enum{$(baseT)} $(sizeof(baseT) * 8) end
let namemap = $(namemap) let namemap = $(namemap)
check_valid(x) = x in keys(namemap) || check_valid(x) = x in keys(namemap) ||
throw(ArgumentError("invalid value $(x) for Enum $($(QuoteNode(name)))")) throw(ArgumentError("invalid value $(x) for Enum $($(QuoteNode(modname)))"))
global function $(esc(:Type))(x::Integer) global function $(esc(:Type))(x::Integer)
check_valid(x) check_valid(x)
return Base.bitcast($(esc(:Type)), convert(Int32, x)) return Base.bitcast($(esc(:Type)), convert($(baseT), x))
end end
Base.Enums.namemap(::Base.Type{$(esc(:Type))}) = namemap Base.Enums.namemap(::Base.Type{$(esc(:Type))}) = namemap
Base.Enums.instances(::Base.Type{$(esc(:Type))}) = Base.Enums.instances(::Base.Type{$(esc(:Type))}) =
@ -37,7 +47,7 @@ function enumx(name, args...)
Expr(:const, Expr(:(=), esc(v), Expr(:call, esc(:Type), k))) Expr(:const, Expr(:(=), esc(v), Expr(:call, esc(:Type), k)))
) )
end end
return Expr(:toplevel, Expr(:module, false, esc(name), module_block), nothing) return Expr(:toplevel, Expr(:module, false, esc(modname), module_block), nothing)
end end
function Base.show(io::IO, ::MIME"text/plain", x::E) where E <: Enum function Base.show(io::IO, ::MIME"text/plain", x::E) where E <: Enum

28
test/runtests.jl

@ -2,6 +2,9 @@
using EnumX, Test using EnumX, Test
const T16 = Int16
getInt64() = Int64
@testset "EnumX" begin @testset "EnumX" begin
# Basic # Basic
@ -68,4 +71,29 @@ let io = IOBuffer()
@test str == "Fruit.Banana = 1" @test str == "Fruit.Banana = 1"
end end
# Base type specification
@enumx Fruit8::Int8 Apple
@test Fruit8.Type <: EnumX.Enum{Int8} <: Base.Enum{Int8}
@test Base.Enums.basetype(Fruit8.Type) === Int8
@test Integer(Fruit8.Apple) === Int8(0)
@enumx Fruit16::T16 Apple
@test Fruit16.Type <: EnumX.Enum{Int16} <: Base.Enum{Int16}
@test Base.Enums.basetype(Fruit16.Type) === Int16
@test Integer(Fruit16.Apple) === Int16(0)
@enumx Fruit64::getInt64() Apple
@test Fruit64.Type <: EnumX.Enum{Int64} <: Base.Enum{Int64}
@test Base.Enums.basetype(Fruit64.Type) === Int64
@test Integer(Fruit64.Apple) == Int64(0)
try
@macroexpand @enumx (Fr + uit) Apple
catch err
err isa LoadError && (err = err.error)
@test err isa ArgumentError
@test err.msg == "invalid EnumX.@enumx type specification: Fr + uit"
end
end # testset end # testset

Loading…
Cancel
Save