From a4e6f96951612fad622aa0998f0b37422a5572b2 Mon Sep 17 00:00:00 2001 From: hl-valdemar Date: Wed, 22 Jul 2026 03:12:19 +0200 Subject: [PATCH] static string map --- source/main.hon | 1 + std/meta/meta.hon | 16 ++-- std/meta/meta.test.hon | 16 ++++ std/static_string_map/static_string_map.hon | 93 +++++++++++++++++++ .../static_string_map.test.hon | 0 std/std.hon | 2 + std/testing/testing.hon | 4 +- 7 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 std/static_string_map/static_string_map.hon create mode 100644 std/static_string_map/static_string_map.test.hon diff --git a/source/main.hon b/source/main.hon index a521b2a..5ce0b1c 100644 --- a/source/main.hon +++ b/source/main.hon @@ -6,6 +6,7 @@ import "@std/arraylist" test import "@std/enums" test import "@std/arraylist" test import "@std/hashmap" +test import "@std/static_string_map" program :: `# these are immutable diff --git a/std/meta/meta.hon b/std/meta/meta.hon index 5780e89..5c6bcd0 100644 --- a/std/meta/meta.hon +++ b/std/meta/meta.hon @@ -1,6 +1,8 @@ -Layout :: enum { - auto - c +Layout :: enum { auto, c } + +ArrayInfo :: struct { + child type + len usize } FieldInfo :: struct { @@ -28,7 +30,7 @@ TypeInfo :: union(enum) { bool void integer void float void - array void + array ArrayInfo pointer void slice void range void @@ -44,9 +46,9 @@ TypeInfo :: union(enum) { EnumFieldStruct func($E, $Field type, $default ?Field) type { match typeinfo!(E) { .enum |info|: { - names [info.fields.len]mut []u8 = undefined - field_types [info.fields.len]mut type = undefined - defaults [info.fields.len]mut ?Field = undefined + names [info.fields.len]mut []u8 = undefined + field_types [info.fields.len]mut type = undefined + defaults [info.fields.len]mut ?Field = undefined expand for info.fields |field, index| { names[index] = field.name field_types[index] = Field diff --git a/std/meta/meta.test.hon b/std/meta/meta.test.hon index a0d64b2..69a2e56 100644 --- a/std/meta/meta.test.hon +++ b/std/meta/meta.test.hon @@ -7,6 +7,22 @@ TestTokenKind :: enum(u8) { } TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(null)) +TestArrayAlias :: alias [3]u16 + +hide array_info_matches func($Array, $Child type, $len usize) bool { + match typeinfo!(Array) { + .array |info|: return info.child == Child and info.len == len + else: return false + } +} + +array_reflection_exposes_child_and_logical_length test { + try testing.expect($(array_info_matches([4]i32, i32, 4))) + try testing.expect($(array_info_matches([0]bool, bool, 0))) + try testing.expect($(array_info_matches(TestArrayAlias, u16, 3))) + try testing.expect($(array_info_matches([2]mut i64, i64, 2))) + try testing.expect($(array_info_matches([2;0]u8, u8, 2))) +} enum_field_struct_defaults test { names TestNames = { diff --git a/std/static_string_map/static_string_map.hon b/std/static_string_map/static_string_map.hon new file mode 100644 index 0000000..7ae48a5 --- /dev/null +++ b/std/static_string_map/static_string_map.hon @@ -0,0 +1,93 @@ +import "@std/mem" + +StaticStringMap func($V type) type { + return struct { + keys [][]u8 + values []V + len_indexes []u32 + min_len u32 + max_len u32 + } +} + +hide Pair func($V type) type { + return struct { []u8, V } +} + +init func($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) { + if N > usize(maxval!(u32)) { + compile_error!("static string map has too many entries") + } + + keys [N]mut []u8 = undefined + values [N]mut V = undefined + + # assert no duplicate keys + for entries |entry, i| { + if entry.0.len > usize(maxval!(u32)) { + compile_error!("static string map key is too long") + } + + for (0..i) |prior| if mem.eql(u8, entry.0, entries[prior].0) { + compile_error!("duplicate static string map key") + } + + keys[i] = entry.0 + values[i] = entry.1 + } + + if N == 0 { + len_indexes [0]u32 = undefined + return StaticStringMap(V){ + keys = keys[..], + values = values[..], + len_indexes = len_indexes[..], + min_len = 0, + max_len = 0, + } + } + + # fixme: insertion sort is compile-time O(N^2); replace if large maps affect builds + for 1..N |i| { + key :: keys[i] + value :: values[i] + j usize = i + while j > 0 and keys[j - 1].len > key.len : j -= 1 { + keys[j] = keys[j - 1] + values[j] = values[j - 1] + } + keys[j] = key + values[j] = value + } + + min_len u32 :: u32(keys[0].len) + max_len u32 :: u32(keys[N - 1].len) + len_indexes [usize(max_len) + 1]mut u32 = undefined + entry_index usize = 0 + for 0..=(usize(max_len)) |length| { + while entry_index < N and keys[entry_index].len < length : entry_index += 1 {} + len_indexes[length] = u32(entry_index) + } + + return StaticStringMap(V) { + keys = keys[..], + values = values[..], + len_indexes = len_indexes[..], + min_len = min_len, + max_len = max_len, + } +} + +get func($V type, map @StaticStringMap(V), key []u8) ?V { + if (map.keys.len == 0 or key.len > maxval!(u32)) return null + + length u32 = u32(key.len) + if (length < map.min_len or length > map.max_len) return null + + idx usize = usize(map.len_indexes[usize(length)]) + while idx < map.keys.len : idx += 1 { + candidate :: map.keys[idx] + if (candidate.len != key.len) return null + if mem.eql(u8, candidate, key) return map.values[idx] + } +} diff --git a/std/static_string_map/static_string_map.test.hon b/std/static_string_map/static_string_map.test.hon new file mode 100644 index 0000000..e69de29 diff --git a/std/std.hon b/std/std.hon index 0da939b..5ea6cf9 100644 --- a/std/std.hon +++ b/std/std.hon @@ -1,7 +1,9 @@ import "io" import "enums" import "arraylist" +import "static_string_map" Io :: alias io.Io ArrayList :: alias arraylist.ArrayList EnumMap :: alias enums.EnumMap +StaticStringMap :: alias static_string_map.StaticStringMap diff --git a/std/testing/testing.hon b/std/testing/testing.hon index b5932ae..5e5e688 100644 --- a/std/testing/testing.hon +++ b/std/testing/testing.hon @@ -6,8 +6,8 @@ Error :: enum { } SourceLocation :: struct { - file []u8 - line usize + file []u8 + line usize column usize }