Skip to content

Commit 533ad7c

Browse files
committed
some Enum improvements
- define fewer methods per type - normalize print and show methods - check for duplicate names
1 parent c71b225 commit 533ad7c

File tree

4 files changed

+60
-40
lines changed

4 files changed

+60
-40
lines changed

base/Enums.jl

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Core.Intrinsics.bitcast
66
export Enum, @enum
77

88
function basetype end
9+
function namemap end
910

1011
"""
1112
Enum{T<:Integer}
@@ -19,6 +20,42 @@ Base.cconvert(::Type{T}, x::Enum{T2}) where {T<:Integer,T2<:Integer} = T(x)
1920
Base.write(io::IO, x::Enum{T}) where {T<:Integer} = write(io, T(x))
2021
Base.read(io::IO, ::Type{T}) where {T<:Enum} = T(read(io, Enums.basetype(T)))
2122

23+
Base.isless(x::T, y::T) where {T<:Enum} = isless(basetype(T)(x), basetype(T)(y))
24+
25+
Base.Symbol(x::Enum) = namemap(typeof(x))[Integer(x)]::Symbol
26+
27+
Base.print(io::IO, x::Enum) = print(io, Symbol(x))
28+
29+
function Base.show(io::IO, x::Enum)
30+
sym = Symbol(x)
31+
if !get(io, :compact, false)
32+
from = get(io, :module, Main)
33+
def = typeof(x).name.module
34+
if from === nothing || !Base.isvisible(sym, def, from)
35+
show(io, def)
36+
print(io, ".")
37+
end
38+
end
39+
print(io, sym)
40+
end
41+
42+
function Base.show(io::IO, ::MIME"text/plain", x::Enum)
43+
print(io, x, "::")
44+
show(IOContext(io, :compact => true), typeof(x))
45+
print(io, " = ")
46+
show(io, Integer(x))
47+
end
48+
49+
function Base.show(io::IO, ::MIME"text/plain", t::Type{<:Enum})
50+
print(io, "Enum ")
51+
Base.show_datatype(io, t)
52+
print(io, ":")
53+
for x in instances(t)
54+
print(io, "\n", Symbol(x), " = ")
55+
show(io, Integer(x))
56+
end
57+
end
58+
2259
# generate code to test whether expr is in the given set of values
2360
function membershiptest(expr, values)
2461
lo, hi = extrema(values)
@@ -74,7 +111,7 @@ To list all the instances of an enum use `instances`, e.g.
74111
75112
```jldoctest fruitenum
76113
julia> instances(Fruit)
77-
(apple::Fruit = 1, orange::Fruit = 2, kiwi::Fruit = 3)
114+
(apple, orange, kiwi)
78115
```
79116
"""
80117
macro enum(T, syms...)
@@ -92,7 +129,9 @@ macro enum(T, syms...)
92129
elseif !isa(T, Symbol)
93130
throw(ArgumentError("invalid type expression for enum $T"))
94131
end
95-
vals = Vector{Tuple{Symbol,Integer}}()
132+
values = basetype[]
133+
seen = Set{Symbol}()
134+
namemap = Dict{basetype,Symbol}()
96135
lo = hi = 0
97136
i = zero(basetype)
98137
hasexpr = false
@@ -103,7 +142,7 @@ macro enum(T, syms...)
103142
for s in syms
104143
s isa LineNumberNode && continue
105144
if isa(s, Symbol)
106-
if i == typemin(basetype) && !isempty(vals)
145+
if i == typemin(basetype) && !isempty(values)
107146
throw(ArgumentError("overflow in value \"$s\" of Enum $typename"))
108147
end
109148
elseif isa(s, Expr) &&
@@ -122,19 +161,23 @@ macro enum(T, syms...)
122161
if !Base.isidentifier(s)
123162
throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier."))
124163
end
125-
push!(vals, (s,i))
126-
if length(vals) == 1
164+
if hasexpr && haskey(namemap, i)
165+
throw(ArgumentError("values for Enum $typename are not unique"))
166+
end
167+
namemap[i] = s
168+
push!(values, i)
169+
if s in seen
170+
throw(ArgumentError("names for Enum $typename are not unique"))
171+
end
172+
push!(seen, s)
173+
if length(values) == 1
127174
lo = hi = i
128175
else
129176
lo = min(lo, i)
130177
hi = max(hi, i)
131178
end
132179
i += oneunit(i)
133180
end
134-
values = basetype[i[2] for i in vals]
135-
if hasexpr && values != unique(values)
136-
throw(ArgumentError("values for Enum $typename are not unique"))
137-
end
138181
blk = quote
139182
# enum definition
140183
Base.@__doc__(primitive type $(esc(typename)) <: Enum{$(basetype)} $(sizeof(basetype) * 8) end)
@@ -143,41 +186,15 @@ macro enum(T, syms...)
143186
return bitcast($(esc(typename)), convert($(basetype), x))
144187
end
145188
Enums.basetype(::Type{$(esc(typename))}) = $(esc(basetype))
189+
Enums.namemap(::Type{$(esc(typename))}) = $(esc(namemap))
146190
Base.typemin(x::Type{$(esc(typename))}) = $(esc(typename))($lo)
147191
Base.typemax(x::Type{$(esc(typename))}) = $(esc(typename))($hi)
148-
Base.isless(x::$(esc(typename)), y::$(esc(typename))) = isless($basetype(x), $basetype(y))
149-
let insts = ntuple(i->$(esc(typename))($values[i]), $(length(vals)))
192+
let insts = ntuple(i->$(esc(typename))($values[i]), $(length(values)))
150193
Base.instances(::Type{$(esc(typename))}) = insts
151194
end
152-
function Base.print(io::IO, x::$(esc(typename)))
153-
for (sym, i) in $vals
154-
if i == $(basetype)(x)
155-
print(io, sym); break
156-
end
157-
end
158-
end
159-
function Base.show(io::IO, x::$(esc(typename)))
160-
if get(io, :compact, false)
161-
print(io, x)
162-
else
163-
print(io, x, "::")
164-
show(IOContext(io, :compact => true), typeof(x))
165-
print(io, " = ")
166-
show(io, $basetype(x))
167-
end
168-
end
169-
function Base.show(io::IO, ::MIME"text/plain", t::Type{$(esc(typename))})
170-
print(io, "Enum ")
171-
Base.show_datatype(io, t)
172-
print(io, ":")
173-
for (sym, i) in $vals
174-
print(io, "\n", sym, " = ")
175-
show(io, i)
176-
end
177-
end
178195
end
179196
if isa(typename, Symbol)
180-
for (sym,i) in vals
197+
for (i, sym) in namemap
181198
push!(blk.args, :(const $(esc(sym)) = $(esc(typename))($i)))
182199
end
183200
end

base/reflection.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ enumerated types (see `@enum`).
696696
julia> @enum Color red blue green
697697
698698
julia> instances(Color)
699-
(red::Color = 0, blue::Color = 1, green::Color = 2)
699+
(red, blue, green)
700700
```
701701
"""
702702
function instances end

base/show.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ function show(io::IO, x::Core.IntrinsicFunction)
384384
end
385385

386386
show(io::IO, ::Core.TypeofBottom) = print(io, "Union{}")
387+
show(io::IO, ::MIME"text/plain", ::Core.TypeofBottom) = print(io, "Union{}")
387388

388389
function show(io::IO, x::Union)
389390
print(io, "Union")

test/enums.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,10 @@ end
127127

128128
# test for unique Enum values
129129
@test_throws ArgumentError("values for Enum Test14 are not unique") @macrocall(@enum(Test14, _zero_Test14, _one_Test14, _two_Test14=0))
130+
# and names
131+
@test_throws ArgumentError("names for Enum Test15 are not unique") @macrocall(@enum(Test15, _zero_Test15, _one_Test15, _zero_Test15))
130132

131-
@test repr(apple) == "apple::Fruit = 0"
133+
@test repr(apple) == "$(@__MODULE__).apple"
132134
@test string(apple) == "apple"
133135

134136
@test repr("text/plain", Fruit) == "Enum $(string(Fruit)):\napple = 0\norange = 1\nkiwi = 2"

0 commit comments

Comments
 (0)