From ac85147f593006a925cda7287a89282cc7d925cd Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Thu, 1 Dec 2016 13:26:15 +0000 Subject: [PATCH 1/7] First draft of EnumSet --- base/Enums.jl | 153 +++++++++++++++++++++++++++++++++++++++++++++++- base/exports.jl | 3 + base/set.jl | 2 +- test/enums.jl | 35 +++++++++++ 4 files changed, 190 insertions(+), 3 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 0435d6cb8bcc0..44d80379bdc71 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -3,9 +3,10 @@ module Enums import Core.Intrinsics.box -export Enum, @enum +export AbstractEnum, Enum, @enum, EnumSet, @enumset -abstract Enum +abstract AbstractEnum +abstract Enum <: AbstractEnum Base.convert{T<:Integer}(::Type{T}, x::Enum) = convert(T, box(Int32, x)) @@ -140,4 +141,152 @@ macro enum(T,syms...) return blk end +abstract EnumSet <: AbstractEnum + +Base.convert{T<:Integer}(::Type{T}, x::EnumSet) = convert(T, convert(Unsigned, x)) + +for op in (:|, :&, :xor) + @eval function Base.$op{T<:EnumSet}(x::T,y::T) + reinterpret(T, ($op)(convert(Unsigned, x), convert(Unsigned, y))) + end +end + +Base.union{T<:EnumSet}(x::T, y::T) = x | y +Base.intersect{T<:EnumSet}(x::T, y::T) = x & y +Base.issubset{T<:EnumSet}(x::T, y::T) = x & y == x +Base.setdiff{T<:EnumSet}(x::T, y::T) = x & xor(x, y) + +""" + @enumset EnumName[::U] EnumValue1[=x] EnumValue2[=y] + +Create an [`EnumSet`](:obj:`EnumSet`) type with name `EnumName` and base member values of +`EnumValue1` and `EnumValue2`, based on the unsigned integer type `U` (`UInt32` by +default). The optional assigned values of `x` and `y` must have exactly 1 bit set, and +not overlap. `EnumName` can be used just like other types and enum member values as +regular values, such as + +```jldoctest +julia> @enumset FRUITSET apple=1<<0 orange=1<<1 kiwi=1<<2 + +julia> f(x::FRUITSET) = "I'm a FRUITSET with value: \$(Int(x))" +f (generic function with 1 method) + +julia> f(apple|kiwi) +"I'm a FRUITSET with value: 5" +``` +""" +macro enumset(T,syms...) + if isempty(syms) + throw(ArgumentError("no arguments given for EnumSet $T")) + end + if isa(T,Symbol) + typename = T + basetype = UInt32 + elseif isa(T,Expr) && T.head == :(::) && length(T.args) == 2 && isa(T.args[1], Symbol) + typename = T.args[1] + basetype = eval(current_module(),T.args[2]) + if !isa(basetype, DataType) || !(basetype <: Unsigned) || !isbits(basetype) + throw(ArgumentError("invalid base type for Enum $typename, $T=::$basetype; base type must be an unsigned integer bitstype")) + end + else + throw(ArgumentError("invalid type expression for EnumSet $T")) + end + vals = Array{Tuple{Symbol,basetype}}(0) + mask = zero(basetype) + for s in syms + if isa(s,Symbol) + if mask & prevpow2(typemax(basetype)) != 0 + throw(ArgumentError("overflow in value \"$s\" of EnumSet $typename")) + end + i = max(prevpow2(mask) << 1, 1) + elseif isa(s,Expr) && + (s.head == :(=) || s.head == :kw) && + length(s.args) == 2 && isa(s.args[1],Symbol) + i = eval(current_module(),s.args[2]) # allow exprs, e.g. uint128"1" + if !isa(i, Integer) + throw(ArgumentError("invalid value for EnumSet $typename, $s=$i; values must be integers")) + end + i = convert(basetype, i) + if count_ones(i) != 1 + throw(ArgumentError("invalid value for EnumSet $typename, $s=$i; value must have eactly 1 bit set")) + elseif mask & i != 0 + throw(ArgumentError("invalid value for EnumSet $typename, $s=$i; overlap with earlier value")) + end + s = s.args[1] + hasexpr = true + else + throw(ArgumentError(string("invalid argument for Enum ", typename, ": ", s))) + end + if !Base.isidentifier(s) + throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier.")) + end + push!(vals, (s,i)) + mask |= i + end + values = basetype[i[2] for i in vals] + blk = quote + # enum definition + Base.@__doc__(bitstype $(sizeof(basetype) * 8) $(esc(typename)) <: EnumSet) + $(esc(typename))() = reinterpret($(esc(typename)), zero($basetype)) + function Base.convert(::Type{$(esc(typename))}, x::Integer) + if 0 <= x <= $mask && + (xx = convert($basetype, x); xx & $mask == xx) + return reinterpret($(esc(typename)), xx) + else + throw(ArgumentError(string($"invalid value for Enum $(typename): ", x))) + end + end + Base.convert(::Type{$basetype}, x::$(esc(typename))) = reinterpret($basetype, x) + Base.convert(::Type{Unsigned}, x::$(esc(typename))) = reinterpret($basetype, x) + Base.typemin(x::Type{$(esc(typename))}) = $(esc(typename))(0) + Base.typemax(x::Type{$(esc(typename))}) = $(esc(typename))($mask) + Base.isless(x::$(esc(typename)), y::$(esc(typename))) = isless($basetype(x), $basetype(y)) + let insts = ntuple(i->$(esc(typename))($values[i]), $(length(vals))) + Base.instances(::Type{$(esc(typename))}) = insts + end + function Base.print(io::IO, x::$(esc(typename))) + showcompact(io, typeof(x)) + print(io, '(') + first = true + for (sym, i) in $vals + if i & $basetype(x) != 0 + if first + first = false + else + print(io, '|') + end + print(io, sym) + end + end + print(io, ')') + end + function Base.show(io::IO, x::$(esc(typename))) + print(io, x) + if !get(io, :compact, false) + print(io, " = ") + show(io, $basetype(x)) + end + end + function Base.show(io::IO, t::Type{$(esc(typename))}) + Base.show_datatype(io, t) + end + function Base.show(io::IO, ::MIME"text/plain", t::Type{$(esc(typename))}) + print(io, "EnumSet ") + Base.show_datatype(io, t) + print(io, ":") + for (sym, i) in $vals + print(io, "\n", sym, " = ") + show(io, i) + end + end + end + for (sym,i) in vals + push!(blk.args, :(const $(esc(sym)) = $(esc(typename))($i))) + end + push!(blk.args, :nothing) + blk.head = :toplevel + return blk +end + + end # module diff --git a/base/exports.jl b/base/exports.jl index 81e68387f2c2b..0cd3588939abc 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -27,6 +27,7 @@ export # Types AbstractChannel, + AbstractEnum, AbstractMatrix, AbstractSet, AbstractUnitRange, @@ -59,6 +60,7 @@ export Dims, EachLine, Enum, + EnumSet, Enumerate, Factorization, FileMonitor, @@ -1409,6 +1411,7 @@ export @assert, @enum, + @enumset, @label, @goto, @view, diff --git a/base/set.jl b/base/set.jl index 3fc0c9045e4b0..3d169a14dacb7 100644 --- a/base/set.jl +++ b/base/set.jl @@ -107,7 +107,7 @@ function issubset(l, r) end const ⊆ = issubset ⊊(l::Set, r::Set) = <(l, r) -⊈(l::Set, r::Set) = !⊆(l, r) +⊈(l, r) = !⊆(l, r) """ unique(itr) diff --git a/test/enums.jl b/test/enums.jl index bf52678f4a32b..f718d497735ad 100644 --- a/test/enums.jl +++ b/test/enums.jl @@ -169,3 +169,38 @@ let b = IOBuffer() seekstart(b) @test deserialize(b) === apple end + + +@enumset VegetableSet carrot potato broccoli +@test typeof(VegetableSet) == DataType +@test isbits(VegetableSet) +@test typeof(carrot) <: VegetableSet <: EnumSet +@test Int(carrot) == 1 +@test Int(potato) == 2 +@test Int(broccoli) == 4 +@test VegetableSet(0) == VegetableSet() +@test VegetableSet(1) == carrot +@test VegetableSet(2) == potato +@test VegetableSet(3) == carrot|potato +@test VegetableSet(4) == broccoli +@test VegetableSet(5) == carrot|broccoli +@test VegetableSet(6) == potato|broccoli +@test VegetableSet(7) == carrot|potato|broccoli +@test_throws ArgumentError VegetableSet(8) +@test_throws ArgumentError VegetableSet(-1) +@test VegetableSet(0x01) == carrot +@test VegetableSet(big(1)) == carrot +@test_throws MethodError VegetableSet(0.0) +@test typemin(VegetableSet) == VegetableSet() +@test typemax(VegetableSet) == carrot|potato|broccoli +@test convert(UInt8,carrot) === 0x01 +@test convert(UInt16,potato) === 0x0002 +@test convert(UInt128,broccoli) === 0x00000000000000000000000000000004 +@test typeof(convert(BigInt,carrot)) <: BigInt +@test convert(BigInt,carrot) == 1 +@test carrot ⊆ carrot +@test carrot ⊆ carrot ∪ potato +@test carrot ⊈ potato +@test VegetableSet() ⊆ carrot +@test carrot ⊈ VegetableSet() +@test setdiff(carrot ∪ potato, carrot) == potato From d6ccaefb3fc574bb6046522bc297ea5c7cb42745 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Thu, 1 Dec 2016 14:34:40 +0000 Subject: [PATCH 2/7] use EnumSet for libgit2 --- base/libgit2/consts.jl | 87 ++++++++++++++++++++++-------------------- base/libgit2/types.jl | 12 +++--- 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index 9249e27283632..270ab0e08426d 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -27,36 +27,38 @@ module Consts const REF_LISTALL = REF_OID | REF_SYMBOLIC # checkout - const CHECKOUT_NONE = Cuint(0) - const CHECKOUT_SAFE = Cuint(1 << 0) - const CHECKOUT_FORCE = Cuint(1 << 1) - const CHECKOUT_RECREATE_MISSING = Cuint(1 << 2) - const CHECKOUT_ALLOW_CONFLICTS = Cuint(1 << 4) - const CHECKOUT_REMOVE_UNTRACKED = Cuint(1 << 5) - const CHECKOUT_REMOVE_IGNORED = Cuint(1 << 6) - const CHECKOUT_UPDATE_ONLY = Cuint(1 << 7) - const CHECKOUT_DONT_UPDATE_INDEX = Cuint(1 << 8) - const CHECKOUT_NO_REFRESH = Cuint(1 << 9) - const CHECKOUT_SKIP_UNMERGED = Cuint(1 << 10) - const CHECKOUT_USE_OURS = Cuint(1 << 11) - const CHECKOUT_USE_THEIRS = Cuint(1 << 12) - const CHECKOUT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 13) - const CHECKOUT_SKIP_LOCKED_DIRECTORIES = Cuint(1 << 18) - const CHECKOUT_DONT_OVERWRITE_IGNORED = Cuint(1 << 19) - const CHECKOUT_CONFLICT_STYLE_MERGE = Cuint(1 << 20) - const CHECKOUT_CONFLICT_STYLE_DIFF3 = Cuint(1 << 21) - const CHECKOUT_DONT_REMOVE_EXISTING = Cuint(1 << 22) + @enumset(CHECKOUT, + CHECKOUT_SAFE = 1 << 0, + CHECKOUT_FORCE = 1 << 1, + CHECKOUT_RECREATE_MISSING = 1 << 2, + CHECKOUT_ALLOW_CONFLICTS = 1 << 4, + CHECKOUT_REMOVE_UNTRACKED = 1 << 5, + CHECKOUT_REMOVE_IGNORED = 1 << 6, + CHECKOUT_UPDATE_ONLY = 1 << 7, + CHECKOUT_DONT_UPDATE_INDEX = 1 << 8, + CHECKOUT_NO_REFRESH = 1 << 9, + CHECKOUT_SKIP_UNMERGED = 1 << 10, + CHECKOUT_USE_OURS = 1 << 11, + CHECKOUT_USE_THEIRS = 1 << 12, + CHECKOUT_DISABLE_PATHSPEC_MATCH = 1 << 13, + CHECKOUT_SKIP_LOCKED_DIRECTORIES = 1 << 18, + CHECKOUT_DONT_OVERWRITE_IGNORED = 1 << 19, + CHECKOUT_CONFLICT_STYLE_MERGE = 1 << 20, + CHECKOUT_CONFLICT_STYLE_DIFF3 = 1 << 21, + CHECKOUT_DONT_REMOVE_EXISTING = 1 << 22) + const CHECKOUT_NONE = CHECKOUT() const CHECKOUT_UPDATE_SUBMODULES = Cuint(1 << 16) const CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = Cuint(1 << 17) - const CHECKOUT_NOTIFY_NONE = Cuint(0) - const CHECKOUT_NOTIFY_CONFLICT = Cuint(1 << 0) - const CHECKOUT_NOTIFY_DIRTY = Cuint(1 << 1) - const CHECKOUT_NOTIFY_UPDATED = Cuint(1 << 2) - const CHECKOUT_NOTIFY_UNTRACKED = Cuint(1 << 3) - const CHECKOUT_NOTIFY_IGNORED = Cuint(1 << 4) - const CHECKOUT_NOTIFY_ALL = 0x0FFFF + @enumset(CHECKOUT_NOTIFY, + CHECKOUT_NOTIFY_CONFLICT = 1 << 0, + CHECKOUT_NOTIFY_DIRTY = 1 << 1, + CHECKOUT_NOTIFY_UPDATED = 1 << 2, + CHECKOUT_NOTIFY_UNTRACKED = 1 << 3, + CHECKOUT_NOTIFY_IGNORED = 1 << 4) + const CHECKOUT_NOTIFY_NONE = CHECKOUT_NOTIFY() + const CHECKOUT_NOTIFY_ALL = typemax(CHECKOUT_NOTIFY) # diff const DIFF_OPTIONS_VERSION = Cuint(1) @@ -222,22 +224,23 @@ module Consts const STATUS_SHOW_WORKDIR_ONLY = Cint(2) # status options - const STATUS_OPT_INCLUDE_UNTRACKED = Cuint(1 << 0) - const STATUS_OPT_INCLUDE_IGNORED = Cuint(1 << 1) - const STATUS_OPT_INCLUDE_UNMODIFIED = Cuint(1 << 2) - const STATUS_OPT_EXCLUDE_SUBMODULES = Cuint(1 << 3) - const STATUS_OPT_RECURSE_UNTRACKED_DIRS = Cuint(1 << 4) - const STATUS_OPT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 5) - const STATUS_OPT_RECURSE_IGNORED_DIRS = Cuint(1 << 6) - const STATUS_OPT_RENAMES_HEAD_TO_INDEX = Cuint(1 << 7) - const STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = Cuint(1 << 8) - const STATUS_OPT_SORT_CASE_SENSITIVELY = Cuint(1 << 9) - const STATUS_OPT_SORT_CASE_INSENSITIVELY = Cuint(1 << 10) - const STATUS_OPT_RENAMES_FROM_REWRITES = Cuint(1 << 11) - const STATUS_OPT_NO_REFRESH = Cuint(1 << 12) - const STATUS_OPT_UPDATE_INDEX = Cuint(1 << 13) - const STATUS_OPT_INCLUDE_UNREADABLE = Cuint(1 << 14) - const STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = Cuint(1 << 15) + @enumset(STATUS_OPT, + STATUS_OPT_INCLUDE_UNTRACKED = 1 << 0, + STATUS_OPT_INCLUDE_IGNORED = 1 << 1, + STATUS_OPT_INCLUDE_UNMODIFIED = 1 << 2, + STATUS_OPT_EXCLUDE_SUBMODULES = 1 << 3, + STATUS_OPT_RECURSE_UNTRACKED_DIRS = 1 << 4, + STATUS_OPT_DISABLE_PATHSPEC_MATCH = 1 << 5, + STATUS_OPT_RECURSE_IGNORED_DIRS = 1 << 6, + STATUS_OPT_RENAMES_HEAD_TO_INDEX = 1 << 7, + STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = 1 << 8, + STATUS_OPT_SORT_CASE_SENSITIVELY = 1 << 9, + STATUS_OPT_SORT_CASE_INSENSITIVELY = 1 << 10, + STATUS_OPT_RENAMES_FROM_REWRITES = 1 << 11, + STATUS_OPT_NO_REFRESH = 1 << 12, + STATUS_OPT_UPDATE_INDEX = 1 << 13, + STATUS_OPT_INCLUDE_UNREADABLE = 1 << 14, + STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = 1 << 15) @enum(GIT_SUBMODULE_IGNORE, SUBMODULE_IGNORE_UNSPECIFIED = -1, # use the submodule's configuration SUBMODULE_IGNORE_NONE = 1, # any change or untracked == dirty diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 36b01fa0f46ac..48a4ae2dd7c47 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -62,14 +62,14 @@ reset!(p::AbstractCredentials, cnt::Int=3) = nothing immutable CheckoutOptions version::Cuint - checkout_strategy::Cuint + checkout_strategy::Consts.CHECKOUT disable_filters::Cint dir_mode::Cuint file_mode::Cuint file_open_flags::Cint - notify_flags::Cuint + notify_flags::Consts.CHECKOUT_NOTIFY notify_cb::Ptr{Void} notify_payload::Ptr{Void} @@ -89,12 +89,12 @@ immutable CheckoutOptions perfdata_cb::Ptr{Void} perfdata_payload::Ptr{Void} end -CheckoutOptions(; checkout_strategy::Cuint = Consts.CHECKOUT_SAFE, +CheckoutOptions(; checkout_strategy::Consts.CHECKOUT = Consts.CHECKOUT_SAFE, disable_filters::Cint = zero(Cint), dir_mode::Cuint = Cuint(0), # Cuint(0o755), file_mode::Cuint = Cuint(0), #Cuint(0o644), file_open_flags::Cint = zero(Cint), - notify_flags::Cuint = Consts.CHECKOUT_NOTIFY_NONE, + notify_flags::Consts.CHECKOUT_NOTIFY = Consts.CHECKOUT_NOTIFY_NONE, notify_cb::Ptr{Void} = Ptr{Void}(0), notify_payload::Ptr{Void} = Ptr{Void}(0), progress_cb::Ptr{Void} = Ptr{Void}(0), @@ -541,11 +541,11 @@ Base.show(io::IO, rbo::RebaseOperation) = print(io, "RebaseOperation($(string(rb immutable StatusOptions version::Cuint show::Cint - flags::Cuint + flags::Consts.STATUS_OPT pathspec::StrArrayStruct end StatusOptions(; show::Cint = Consts.STATUS_SHOW_INDEX_AND_WORKDIR, - flags::Cuint = Consts.STATUS_OPT_INCLUDE_UNTRACKED | + flags::Consts.STATUS_OPT = Consts.STATUS_OPT_INCLUDE_UNTRACKED | Consts.STATUS_OPT_RECURSE_UNTRACKED_DIRS | Consts.STATUS_OPT_RENAMES_HEAD_TO_INDEX | Consts.STATUS_OPT_SORT_CASE_SENSITIVELY, From e3f79b159477292507a3715bd3b2c5e45e75b5dc Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Fri, 2 Dec 2016 10:05:16 +0000 Subject: [PATCH 3/7] alignment --- base/libgit2/types.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 48a4ae2dd7c47..993a8810eec4a 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -546,9 +546,9 @@ immutable StatusOptions end StatusOptions(; show::Cint = Consts.STATUS_SHOW_INDEX_AND_WORKDIR, flags::Consts.STATUS_OPT = Consts.STATUS_OPT_INCLUDE_UNTRACKED | - Consts.STATUS_OPT_RECURSE_UNTRACKED_DIRS | - Consts.STATUS_OPT_RENAMES_HEAD_TO_INDEX | - Consts.STATUS_OPT_SORT_CASE_SENSITIVELY, + Consts.STATUS_OPT_RECURSE_UNTRACKED_DIRS | + Consts.STATUS_OPT_RENAMES_HEAD_TO_INDEX | + Consts.STATUS_OPT_SORT_CASE_SENSITIVELY, pathspec::StrArrayStruct = StrArrayStruct()) = StatusOptions(one(Cuint), show, From 19c88b707547d3a7aad97d64a17e9a3b6ad207bc Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Fri, 2 Dec 2016 22:28:18 +0000 Subject: [PATCH 4/7] rename to FlagEnum --- base/Enums.jl | 38 ++++++++++++++++++-------------------- base/exports.jl | 4 ++-- base/libgit2/consts.jl | 6 +++--- test/enums.jl | 17 +++++++++-------- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 44d80379bdc71..1ec0cfea54d7e 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -3,7 +3,7 @@ module Enums import Core.Intrinsics.box -export AbstractEnum, Enum, @enum, EnumSet, @enumset +export AbstractEnum, Enum, @enum, FlagEnum, @flagenum abstract AbstractEnum abstract Enum <: AbstractEnum @@ -141,25 +141,24 @@ macro enum(T,syms...) return blk end -abstract EnumSet <: AbstractEnum -Base.convert{T<:Integer}(::Type{T}, x::EnumSet) = convert(T, convert(Unsigned, x)) +abstract FlagEnum <: AbstractEnum + +Base.convert{T<:Integer}(::Type{T}, x::FlagEnum) = convert(T, convert(Unsigned, x)) for op in (:|, :&, :xor) - @eval function Base.$op{T<:EnumSet}(x::T,y::T) + @eval function Base.$op{T<:FlagEnum}(x::T,y::T) reinterpret(T, ($op)(convert(Unsigned, x), convert(Unsigned, y))) end end +Base.in{T<:FlagEnum}(x::T, y::T) = x & y == x +Base.~{T<:FlagEnum}(x::T) = reinterpret(T, convert(Unsigned, typemax(T)) & ~convert(Unsigned, x)) -Base.union{T<:EnumSet}(x::T, y::T) = x | y -Base.intersect{T<:EnumSet}(x::T, y::T) = x & y -Base.issubset{T<:EnumSet}(x::T, y::T) = x & y == x -Base.setdiff{T<:EnumSet}(x::T, y::T) = x & xor(x, y) """ @enumset EnumName[::U] EnumValue1[=x] EnumValue2[=y] -Create an [`EnumSet`](:obj:`EnumSet`) type with name `EnumName` and base member values of +Create an [`FlagEnum`](:obj:`FlagEnum`) type with name `EnumName` and base member values of `EnumValue1` and `EnumValue2`, based on the unsigned integer type `U` (`UInt32` by default). The optional assigned values of `x` and `y` must have exactly 1 bit set, and not overlap. `EnumName` can be used just like other types and enum member values as @@ -175,9 +174,9 @@ julia> f(apple|kiwi) "I'm a FRUITSET with value: 5" ``` """ -macro enumset(T,syms...) +macro flagenum(T,syms...) if isempty(syms) - throw(ArgumentError("no arguments given for EnumSet $T")) + throw(ArgumentError("no arguments given for FlagEnum $T")) end if isa(T,Symbol) typename = T @@ -189,14 +188,14 @@ macro enumset(T,syms...) throw(ArgumentError("invalid base type for Enum $typename, $T=::$basetype; base type must be an unsigned integer bitstype")) end else - throw(ArgumentError("invalid type expression for EnumSet $T")) + throw(ArgumentError("invalid type expression for FlagEnum $T")) end vals = Array{Tuple{Symbol,basetype}}(0) mask = zero(basetype) for s in syms if isa(s,Symbol) if mask & prevpow2(typemax(basetype)) != 0 - throw(ArgumentError("overflow in value \"$s\" of EnumSet $typename")) + throw(ArgumentError("overflow in value \"$s\" of FlagEnum $typename")) end i = max(prevpow2(mask) << 1, 1) elseif isa(s,Expr) && @@ -204,13 +203,13 @@ macro enumset(T,syms...) length(s.args) == 2 && isa(s.args[1],Symbol) i = eval(current_module(),s.args[2]) # allow exprs, e.g. uint128"1" if !isa(i, Integer) - throw(ArgumentError("invalid value for EnumSet $typename, $s=$i; values must be integers")) + throw(ArgumentError("invalid value for FlagEnum $typename, $s=$i; values must be integers")) end i = convert(basetype, i) if count_ones(i) != 1 - throw(ArgumentError("invalid value for EnumSet $typename, $s=$i; value must have eactly 1 bit set")) + throw(ArgumentError("invalid value for FlagEnum $typename, $s=$i; value must have eactly 1 bit set")) elseif mask & i != 0 - throw(ArgumentError("invalid value for EnumSet $typename, $s=$i; overlap with earlier value")) + throw(ArgumentError("invalid value for FlagEnum $typename, $s=$i; overlap with earlier value")) end s = s.args[1] hasexpr = true @@ -226,7 +225,7 @@ macro enumset(T,syms...) values = basetype[i[2] for i in vals] blk = quote # enum definition - Base.@__doc__(bitstype $(sizeof(basetype) * 8) $(esc(typename)) <: EnumSet) + Base.@__doc__(bitstype $(sizeof(basetype) * 8) $(esc(typename)) <: FlagEnum) $(esc(typename))() = reinterpret($(esc(typename)), zero($basetype)) function Base.convert(::Type{$(esc(typename))}, x::Integer) if 0 <= x <= $mask && @@ -237,10 +236,9 @@ macro enumset(T,syms...) end end Base.convert(::Type{$basetype}, x::$(esc(typename))) = reinterpret($basetype, x) - Base.convert(::Type{Unsigned}, x::$(esc(typename))) = reinterpret($basetype, x) + Base.convert(::Type{Unsigned}, x::$(esc(typename))) = reinterpret($basetype, x) Base.typemin(x::Type{$(esc(typename))}) = $(esc(typename))(0) Base.typemax(x::Type{$(esc(typename))}) = $(esc(typename))($mask) - Base.isless(x::$(esc(typename)), y::$(esc(typename))) = isless($basetype(x), $basetype(y)) let insts = ntuple(i->$(esc(typename))($values[i]), $(length(vals))) Base.instances(::Type{$(esc(typename))}) = insts end @@ -271,7 +269,7 @@ macro enumset(T,syms...) Base.show_datatype(io, t) end function Base.show(io::IO, ::MIME"text/plain", t::Type{$(esc(typename))}) - print(io, "EnumSet ") + print(io, "FlagEnum ") Base.show_datatype(io, t) print(io, ":") for (sym, i) in $vals diff --git a/base/exports.jl b/base/exports.jl index 0cd3588939abc..3539c5255cd0e 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -60,10 +60,10 @@ export Dims, EachLine, Enum, - EnumSet, Enumerate, Factorization, FileMonitor, + FlagEnum, FloatRange, Future, Hermitian, @@ -1411,7 +1411,7 @@ export @assert, @enum, - @enumset, + @flagenum, @label, @goto, @view, diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index 270ab0e08426d..7d0d72e29a187 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -27,7 +27,7 @@ module Consts const REF_LISTALL = REF_OID | REF_SYMBOLIC # checkout - @enumset(CHECKOUT, + @flagenum(CHECKOUT, CHECKOUT_SAFE = 1 << 0, CHECKOUT_FORCE = 1 << 1, CHECKOUT_RECREATE_MISSING = 1 << 2, @@ -51,7 +51,7 @@ module Consts const CHECKOUT_UPDATE_SUBMODULES = Cuint(1 << 16) const CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = Cuint(1 << 17) - @enumset(CHECKOUT_NOTIFY, + @flagenum(CHECKOUT_NOTIFY, CHECKOUT_NOTIFY_CONFLICT = 1 << 0, CHECKOUT_NOTIFY_DIRTY = 1 << 1, CHECKOUT_NOTIFY_UPDATED = 1 << 2, @@ -224,7 +224,7 @@ module Consts const STATUS_SHOW_WORKDIR_ONLY = Cint(2) # status options - @enumset(STATUS_OPT, + @flagenum(STATUS_OPT, STATUS_OPT_INCLUDE_UNTRACKED = 1 << 0, STATUS_OPT_INCLUDE_IGNORED = 1 << 1, STATUS_OPT_INCLUDE_UNMODIFIED = 1 << 2, diff --git a/test/enums.jl b/test/enums.jl index f718d497735ad..6d19a7a411af2 100644 --- a/test/enums.jl +++ b/test/enums.jl @@ -171,10 +171,10 @@ let b = IOBuffer() end -@enumset VegetableSet carrot potato broccoli +@flagenum VegetableSet carrot potato broccoli @test typeof(VegetableSet) == DataType @test isbits(VegetableSet) -@test typeof(carrot) <: VegetableSet <: EnumSet +@test typeof(carrot) <: VegetableSet <: FlagEnum @test Int(carrot) == 1 @test Int(potato) == 2 @test Int(broccoli) == 4 @@ -198,9 +198,10 @@ end @test convert(UInt128,broccoli) === 0x00000000000000000000000000000004 @test typeof(convert(BigInt,carrot)) <: BigInt @test convert(BigInt,carrot) == 1 -@test carrot ⊆ carrot -@test carrot ⊆ carrot ∪ potato -@test carrot ⊈ potato -@test VegetableSet() ⊆ carrot -@test carrot ⊈ VegetableSet() -@test setdiff(carrot ∪ potato, carrot) == potato +@test carrot ∈ carrot +@test carrot ∈ carrot | potato +@test carrot ∉ potato +@test VegetableSet() ∈ carrot +@test carrot ∉ VegetableSet() +@test carrot ∈ ~VegetableSet() +@test ~carrot == potato|broccoli From 284afdc2e60951f9484cf35374b9e3997a25514c Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Mon, 5 Dec 2016 14:51:01 +0000 Subject: [PATCH 5/7] update names --- base/Enums.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 1ec0cfea54d7e..0284f68898fae 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -156,22 +156,22 @@ Base.~{T<:FlagEnum}(x::T) = reinterpret(T, convert(Unsigned, typemax(T)) & ~conv """ - @enumset EnumName[::U] EnumValue1[=x] EnumValue2[=y] + @flagenum FlagEnum[::U] EnumValue1[=x] EnumValue2[=y] -Create an [`FlagEnum`](:obj:`FlagEnum`) type with name `EnumName` and base member values of -`EnumValue1` and `EnumValue2`, based on the unsigned integer type `U` (`UInt32` by -default). The optional assigned values of `x` and `y` must have exactly 1 bit set, and -not overlap. `EnumName` can be used just like other types and enum member values as -regular values, such as +Create an [`FlagEnum`](:obj:`FlagEnum`) type with name `EnumName` and base member values +of `EnumValue1` and `EnumValue2`, based on the unsigned integer type `U` (`UInt32` by +default). If the values `x` and `y` are provided, they must each have a single bit on, and +naturally, not coincide. The `EnumName` type can be used just like other types, and enum +member values as regular values, such as ```jldoctest -julia> @enumset FRUITSET apple=1<<0 orange=1<<1 kiwi=1<<2 +julia> @flagenum FRUITFLAGS apple=1<<0 orange=1<<1 kiwi=1<<2 -julia> f(x::FRUITSET) = "I'm a FRUITSET with value: \$(Int(x))" +julia> f(x::FRUITFLAGS) = "I'm a FRUITFLAGS with value: \$(Int(x))" f (generic function with 1 method) julia> f(apple|kiwi) -"I'm a FRUITSET with value: 5" +"I'm a FRUITFLAGS with value: 5" ``` """ macro flagenum(T,syms...) From c39a6a2a204f8cffc5548498270972c9081c9ce3 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Thu, 8 Dec 2016 22:06:46 +0100 Subject: [PATCH 6/7] change case --- base/Enums.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 0284f68898fae..660b22832e285 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -156,22 +156,22 @@ Base.~{T<:FlagEnum}(x::T) = reinterpret(T, convert(Unsigned, typemax(T)) & ~conv """ - @flagenum FlagEnum[::U] EnumValue1[=x] EnumValue2[=y] + @flagenum EnumName[::U] enumvalue1[=x] enumvalue1[=y] Create an [`FlagEnum`](:obj:`FlagEnum`) type with name `EnumName` and base member values -of `EnumValue1` and `EnumValue2`, based on the unsigned integer type `U` (`UInt32` by -default). If the values `x` and `y` are provided, they must each have a single bit on, and -naturally, not coincide. The `EnumName` type can be used just like other types, and enum -member values as regular values, such as +of `enumvalue1` and `enumvalue1`, based on the unsigned integer type `U` (`UInt32` by +default). If the values `x` and `y` are provided, they must each have exactly a single bit +on, and naturally, not coincide. The `EnumName` type can be used just like other types, +and enum member values as regular values, such as ```jldoctest -julia> @flagenum FRUITFLAGS apple=1<<0 orange=1<<1 kiwi=1<<2 +julia> @flagenum FruitFlags apple=1<<0 orange=1<<1 kiwi=1<<2 -julia> f(x::FRUITFLAGS) = "I'm a FRUITFLAGS with value: \$(Int(x))" +julia> f(x::FruitFlags) = "I'm a FruitFlags with value: \$(Int(x))" f (generic function with 1 method) julia> f(apple|kiwi) -"I'm a FRUITFLAGS with value: 5" +"I'm a FruitFlags with value: 5" ``` """ macro flagenum(T,syms...) From 7f10b766fabe5b6a7fbf04ddbd2dc1d07858a5fc Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Thu, 8 Dec 2016 22:25:25 +0100 Subject: [PATCH 7/7] fix up for new docs --- base/Enums.jl | 10 +++++----- doc/src/stdlib/base.md | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 660b22832e285..68e36e5c36747 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -158,11 +158,11 @@ Base.~{T<:FlagEnum}(x::T) = reinterpret(T, convert(Unsigned, typemax(T)) & ~conv """ @flagenum EnumName[::U] enumvalue1[=x] enumvalue1[=y] -Create an [`FlagEnum`](:obj:`FlagEnum`) type with name `EnumName` and base member values -of `enumvalue1` and `enumvalue1`, based on the unsigned integer type `U` (`UInt32` by -default). If the values `x` and `y` are provided, they must each have exactly a single bit -on, and naturally, not coincide. The `EnumName` type can be used just like other types, -and enum member values as regular values, such as +Create a `FlagEnum` type with name `EnumName` and base member values of `enumvalue1` and +`enumvalue1`, based on the unsigned integer type `U` (`UInt32` by default). If the values +`x` and `y` are provided, they must each have exactly a single bit on, and naturally, not +coincide. The `EnumName` type can be used just like other types, and enum member values as +regular values, such as ```jldoctest julia> @flagenum FruitFlags apple=1<<0 orange=1<<1 kiwi=1<<2 diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index 071b45b7cf03a..a11d94c480a66 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -112,6 +112,7 @@ Base.typejoin Base.typeintersect Base.Val Base.Enums.@enum +Base.Enums.@flagenum Base.instances ```