import "@std/meta" #! keys may be enums or native enum compositions that normalize to an enum, including aliases. #! shared variant names follow native composition identity; payload-bearing sums are not enum keys. EnumMap proc($E, $V type) type { match typeinfo!(E) { .enum |info|: return struct { present [info.fields.len]mut bool # fixme: replace with bitset values [info.fields.len]mut V } else: compile_error!("EnumMap key must be an enum") } } init proc( $E, $V type, values meta.EnumFieldStruct(E, ?V, some!(null)), ) EnumMap(E, V) { return initialize(E, V, null, values) } #! initializes absent value slots without making their keys present. #! use fully initialized storage when a sparse map must be materialized at compile time. init_filled proc( $E, $V type, storage_default V, values meta.EnumFieldStruct(E, ?V, some!(null)), ) EnumMap(E, V) { return initialize(E, V, some!(storage_default), values) } @hide:file initialize proc( $E, $V type, storage_default ?V, values meta.EnumFieldStruct(E, ?V, some!(null)), ) EnumMap(E, V) { map EnumMap(E, V) := undefined match typeinfo!(E) { .enum |info|: inline for info.fields |field, i| { map.present[i] = false if field!(values, field.name) |value| { map.present[i] = true map.values[i] = value } else if (storage_default) |value| map.values[i] = value } else: compile_error!("EnumMap key must be an enum") } return map } get proc($E, $V type, map @EnumMap(E, V), key E) ?V { # fixme: linear lookup; implement an enum index/discriminant map for O(1) lookup match typeinfo!(E) { .enum |info|: inline for info.fields |field, i| { if key == field!(E, field.name) { if (map.present[i]) return some!(map.values[i]) return null } } else: compile_error!("EnumMap key must be an enum") } return null }