Add enum utilities and add enum with holes support (#115)

This commit is contained in:
lchenut 2022-06-10 01:29:07 +02:00 committed by GitHub
parent 412a691f5d
commit c2f0cbf0d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 109 additions and 57 deletions

View File

@ -1,5 +1,6 @@
import
macros
macros,
sequtils
template init*(lvalue: var auto) =
mixin init
@ -67,26 +68,28 @@ proc baseType*(obj: RootObj): cstring =
proc baseType*(obj: ref RootObj): cstring =
obj[].baseType
when false:
# TODO: Implementing this doesn't seem possible at the moment.
#
# When given enum like:
#
# type WithoutHoles2 = enum
# A2 = 2, B2 = 3, C2 = 4
#
# ...the code below will print:
#
# EnumTy
# Empty
# Sym "A2"
# Sym "B2"
# Sym "C2"
#
macro hasHoles*(T: type[enum]): bool =
let t = getType(T)[1]
echo t.treeRepr
return newLit(true)
macro enumRangeInt64*(a: type[enum]): untyped =
## This macro returns an array with all the ordinal values of an enum
let
values = a.getType[1][1..^1]
valuesOrded = values.mapIt(newCall("int64", it))
newNimNode(nnkBracket).add(valuesOrded)
macro hasHoles*(T: type[enum]): bool =
# As an enum is always sorted, just substract the first and the last ordinal value
# and compare the result to the number of element in it will do the trick.
let len = T.getType[1].len - 2
quote: `T`.high.ord - `T`.low.ord != `len`
proc contains*[I: SomeInteger](e: type[enum], v: I): bool =
when I is uint64:
if v > int.high.uint64:
return false
when e.hasHoles():
v.int64 in enumRangeInt64(e)
else:
v.int64 in e.low.int64 .. e.high.int64
func checkedEnumAssign*[E: enum, I: SomeInteger](res: var E, value: I): bool =
## This function can be used to safely assign a tainted integer value (coming
@ -94,14 +97,7 @@ func checkedEnumAssign*[E: enum, I: SomeInteger](res: var E, value: I): bool =
## if the integer value is within the acceped values of the enum and `false`
## otherwise.
# TODO: Enums with holes are not supported yet
# static: doAssert(not hasHoles(E))
when I is SomeSignedInt or low(E).int > 0:
if value < I(low(E)):
return false
if value > I(high(E)):
if value notin E:
return false
res = E value
@ -113,4 +109,3 @@ func isZeroMemory*[T](x: T): bool =
if b != 0:
return false
return true

View File

@ -70,23 +70,81 @@ suite "Objects":
T6 is DistinctBar
T6 isnot Bar
when false:
# TODO: Not possible yet (see objects.nim)
test "hasHoles":
type
WithoutHoles = enum
A1, B1, C1
test "enumRangeInt64":
type
WithoutHoles = enum
A1, A2, A3
WithoutHoles2 = enum
B1 = 4, B2 = 5, B3 = 6
WithHoles = enum
C1 = 1, C2 = 3, C3 = 5
WithoutHoles2 = enum
A2 = 2, B2 = 3, C2 = 4
check:
enumRangeInt64(WithoutHoles) == [ 0'i64, 1, 2 ]
enumRangeInt64(WithoutHoles2) == [ 4'i64, 5, 6 ]
enumRangeInt64(WithHoles) == [ 1'i64, 3, 5 ]
WithHoles = enum
A3, B3 = 2, C3
check:
hasHoles(WithoutHoles2) == false
hasHoles(WithoutHoles) == false
hasHoles(WithHoles) == true
test "contains":
type
WithoutHoles = enum
A1, A2, A3
WithoutHoles2 = enum
B1 = 4, B2 = 5, B3 = 6
WithHoles = enum
C1 = 1, C2 = 3, C3 = 5
WithoutHoles3 = enum
D1 = -1, D2 = 0, D3 = 1
WithHoles2 = enum
E1 = -5, E2 = 0, E3 = 5
check:
1 in WithoutHoles
5 notin WithoutHoles
1 notin WithoutHoles2
5 in WithoutHoles2
1 in WithHoles
2 notin WithHoles
6 notin WithHoles
5 in WithHoles
1.byte in WithoutHoles
4294967295'u32 notin WithoutHoles3
-1.int8 in WithoutHoles3
-4.int16 notin WithoutHoles3
-5.int16 in WithHoles2
5.uint64 in WithHoles2
-12.int8 notin WithHoles2
int64.high notin WithoutHoles
int64.high notin WithHoles
int64.low notin WithoutHoles
int64.low notin WithHoles
int64.high.uint64 * 2 notin WithoutHoles
int64.high.uint64 * 2 notin WithHoles
test "hasHoles":
type
EnumWithOneValue = enum
A0
WithoutHoles = enum
A1, B1, C1
WithoutHoles2 = enum
A2 = 2, B2 = 3, C2 = 4
WithHoles = enum
A3, B3 = 2, C3
WithBigHoles = enum
A4 = 0, B4 = 2000, C4 = 4000
check:
hasHoles(EnumWithOneValue) == false
hasHoles(WithoutHoles) == false
hasHoles(WithoutHoles2) == false
hasHoles(WithHoles) == true
hasHoles(WithBigHoles) == true
test "checkedEnumAssign":
type
@ -96,42 +154,41 @@ suite "Objects":
AnotherEnum = enum
A2 = 2, B2, C2
EnumWithHoles = enum
A3, B3 = 3, C3
var
e1 = A1
e2 = A2
e3 = A3
check:
checkedEnumAssign(e1, 2)
e1 == C1
check:
not checkedEnumAssign(e1, 5)
e1 == C1
check:
checkedEnumAssign(e1, 0)
e1 == A1
check:
not checkedEnumAssign(e1, -1)
e1 == A1
check:
checkedEnumAssign(e2, 2)
e2 == A2
check:
not checkedEnumAssign(e2, 5)
e2 == A2
check:
checkedEnumAssign(e2, 4)
e2 == C2
check:
not checkedEnumAssign(e2, 1)
e2 == C2
checkedEnumAssign(e3, 4)
e3 == C3
not checkedEnumAssign(e3, 1)
e3 == C3
checkedEnumAssign(e3, 0)
e3 == A3
not checkedEnumAssign(e3, -1)
e3 == A3
test "isZeroMemory":
type
Foo = object