|
| 1 | +# |
| 2 | +import std/[hashes, tables, strformat] |
| 3 | +import ../../pyerrors/simperr |
| 4 | + |
| 5 | +template GenPyEnumMeth*(Self, Value: typedesc; genObjMeth = true, nameError = NameError, Str = string){.dirty.} = |
| 6 | + ## XXX: NIM-BUG: as `std/tables` is not working with `bind` once required at compile time, |
| 7 | + ## `import std/tables` is a must before using this template. |
| 8 | + bind contains, `[]` |
| 9 | + bind hash, Table, Hash, withValue, `[]=`, `$`, items, len, fmt, formatValue |
| 10 | + |
| 11 | + var `Self.names`{.genSym, compileTime.}: Table[Value, string] ## self._name_ |
| 12 | + var `Self.member_map`{.genSym, compileTime.}: Table[Str, Self] ## cls._member_map_ |
| 13 | + |
| 14 | + using self: Self |
| 15 | + using cls: typedesc[Self] |
| 16 | + proc value*(self): Value{.inline.} = Value(self) |
| 17 | + |
| 18 | + proc name*(self): Str{.inline.} = `Self.names`[self.value] |
| 19 | + proc `name=`(self; name: Str){.inline.} = `Self.names`[self.value] = name |
| 20 | + proc repr*(self): Str = |
| 21 | + let vr = when compiles(typeof(self).value_repr(self.value)): |
| 22 | + typeof(self).value_repr(self.value) |
| 23 | + else: |
| 24 | + repr(self.value) |
| 25 | + fmt"<{$typeof(self)} {self.name}: {vr}>" |
| 26 | + |
| 27 | + proc `$`*(self): string = |
| 28 | + fmt"{$typeof(self)}.{self.name}" |
| 29 | + proc hash*(self): Hash = hash(`Self.names`[self.value]) |
| 30 | + when genObjMeth: |
| 31 | + proc `==`*(self; other: Self): bool = self.value == other.value |
| 32 | + proc add_member(cls; name: Str, self) = |
| 33 | + if name in `Self.member_map`: |
| 34 | + if `Self.member_map`[name] != self: |
| 35 | + let s = repr(`Self.member_map`[name]) |
| 36 | + raise newException(nameError, fmt"{repr(name)} is already bound: {s}") |
| 37 | + return |
| 38 | + # XXX: Python here also need to handle property and class attribute |
| 39 | + `Self.member_map`[name] = self |
| 40 | + proc add_alias*(self; name: Str) = Self.add_member(name, self) |
| 41 | + |
| 42 | + |
| 43 | + # _simple_enum's convert_class's Enum / IntEnum / StrEnum branch |
| 44 | + proc add_member*(enum_class: typedesc[Self]; name: Str, value: Value): Self = |
| 45 | + let member = Self(value) |
| 46 | + `Self.member_map`.withValue name, contained: |
| 47 | + # an alias to an existing member |
| 48 | + contained[].add_alias(name) |
| 49 | + result = contained[] |
| 50 | + do: |
| 51 | + # finish creating member |
| 52 | + member.name = name |
| 53 | + enum_class.add_member(name, member) |
| 54 | + result = member |
| 55 | + |
| 56 | + |
| 57 | + |
| 58 | + proc `[]`*(cls; name: string): Self = `Self.member_map`[name] |
| 59 | + proc len*(cls): int = len `Self.names` |
| 60 | + iterator items*(cls): Self = |
| 61 | + for k in `Self.names`.keys(): yield k |
| 62 | + |
| 63 | + template contains*(cls; value: Self): bool = true |
| 64 | + proc contains*(cls; value: Value): bool = |
| 65 | + `Self.names`.withValue value, name: |
| 66 | + return name in `Self.member_map` |
| 67 | + |
0 commit comments