better maps and json; fixed anonymous struct bug
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 4 Oct 2021 16:26:57 +0000 (11:26 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 4 Oct 2021 16:26:57 +0000 (11:26 -0500)
21 files changed:
bin/onyx
core/container/map.onyx
core/conv.onyx
core/intrinsics/onyx.onyx
core/memory.onyx
examples/11_map.onyx
modules/json/types.onyx
modules/ui/ui.onyx
src/astnodes.c
src/checker.c
src/parser.c
tests/aoc-2020/day14.onyx
tests/aoc-2020/day15.onyx
tests/aoc-2020/day17.onyx
tests/aoc-2020/day20.onyx
tests/aoc-2020/day21.onyx
tests/aoc-2020/day22.onyx
tests/aoc-2020/day24.onyx
tests/aoc-2020/day25.onyx
tests/persist_locals.onyx
tests/struct_robustness.onyx

index 3db06439e05ab835055870dad6dd28f550381b29..f789cd6c28477ea756bc71126690185e61e47aa5 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 30941b57b99b57d0b926c9c6cf7009cd4be321fe..a504b11452a5e39ee60b25d1fc1a659e2c16fa64 100644 (file)
@@ -1,11 +1,18 @@
 package core.map
 
-#private_file array  :: package core.array
-#private_file hash   :: package core.hash
-use package core.intrinsics.onyx { __zero_value }
+#private_file {
+    array  :: package core.array
+    hash   :: package core.hash
+    memory :: package core.memory
+    math   :: package core.math
+
+    use package core.intrinsics.onyx { __zero_value, __initialize }
+}
 
 Map :: struct (K: type_expr, V: type_expr) {
-    hashes  : [..] i32;
+    allocator : Allocator;
+
+    hashes  : [] i32;
     entries : [..] Entry(K, V);
 
     // The value provided by `map.get`, if nothing was found.
@@ -18,23 +25,26 @@ Map :: struct (K: type_expr, V: type_expr) {
     }
 }
 
-make :: ($Key: type_expr, $Value: type_expr, default := __zero_value(Value), hash_count: i32 = 16) -> Map(Key, Value) {
+make :: ($Key: type_expr, $Value: type_expr, default := __zero_value(Value)) -> Map(Key, Value) {
     map : Map(Key, Value);
-    init(^map, default = default, hash_count = hash_count);
+    init(^map, default = default);
     return map;
 }
 
-init :: (use map: ^Map($K, $V), default := __zero_value(V), hash_count: i32 = 16) {
-    array.init(^hashes, hash_count);
-    array.init(^entries, 4);
+init :: (use map: ^Map($K, $V), default := __zero_value(V)) {
+    __initialize(map);
 
+    allocator = context.allocator;
     default_value = default;
 
-    for i: 0 .. hash_count do array.push(^hashes, -1);
+    memory.alloc_slice(^hashes, 8, allocator=allocator);
+    memory.fill_slice(hashes, -1);
+
+    array.init(^entries, allocator=allocator);
 }
 
 free :: (use map: ^Map($K, $V)) {
-    array.free(^hashes);
+    memory.free_slice(^hashes, allocator=allocator);
     array.free(^entries);
 }
 
@@ -46,13 +56,10 @@ put :: (use map: ^Map($K, $V), key: K, value: V) {
         return;
     }
 
-    array.push(^entries, .{
-        key = key,
-        value = value,
-        hashes[lr.hash_index],
-    });
-
+    entries << .{ hashes[lr.hash_index], key, value };
     hashes[lr.hash_index] = entries.count - 1;
+
+    if full(map) do grow(map);
 }
 
 has :: (use map: ^Map($K, $V), key: K) -> bool {
@@ -83,7 +90,7 @@ delete :: (use map: ^Map($K, $V), key: K) {
     if lr.entry_prev < 0   do hashes[lr.hash_index]       = entries[lr.entry_index].next;
     else                   do entries[lr.entry_prev].next = entries[lr.entry_index].next;
 
-    if lr.entry_index == entries.count - 1 {
+    if lr.entry_index == entries.count - 1{
         array.pop(^entries);
         return;
     }
@@ -118,28 +125,62 @@ empty :: (use map: ^Map($K, $V)) -> bool {
 // Private symbols
 //
 
-#private_file
-MapLookupResult :: struct {
-    hash_index  : i32 = -1;
-    entry_index : i32 = -1;
-    entry_prev  : i32 = -1;
-}
+#private_file {
+    MapLookupResult :: struct {
+        hash_index  : i32 = -1;
+        entry_index : i32 = -1;
+        entry_prev  : i32 = -1;
+    }
+
+    lookup :: (use map: ^Map($K, $V), key: K) -> MapLookupResult {
+        lr := MapLookupResult.{};
+
+        hash_value: u32 = hash.to_u32(key); // You cannot use this type for the key, unless you add an overload.
 
-#private_file
-lookup :: (use map: ^Map($K, $V), key: K) -> MapLookupResult {
-    lr := MapLookupResult.{};
+        lr.hash_index = hash_value % hashes.count;
+        lr.entry_index = hashes[lr.hash_index];
 
-    hash_value: u32 = hash.to_u32(key); // You cannot use this type for the key, unless you add an overload.
+        while lr.entry_index >= 0 {
+            if entries[lr.entry_index].key == key do return lr;
 
-    lr.hash_index = hash_value % hashes.count;
-    lr.entry_index = hashes[lr.hash_index];
+            lr.entry_prev = lr.entry_index;
+            lr.entry_index = entries[lr.entry_index].next;
+        }
 
-    while lr.entry_index >= 0 {
-        if entries[lr.entry_index].key == key do return lr;
+        return lr;
+    }
+
+    add_entry :: (use map: ^Map($K, $V), key: K) -> i32 {
+        entry: Map.Entry(K, V);
+        entry.key = key;
+        entry.next = -1;
+
+        entries << entry;
+
+        return entries.count - 1;
+    }
 
-        lr.entry_prev = lr.entry_index;
-        lr.entry_index = entries[lr.entry_index].next;
+    full :: (use map: ^Map($K, $V)) => entries.count >= ~~(0.75f * ~~hashes.count);
+
+    grow :: (use map: ^Map($K, $V)) {
+        new_size := math.max(hashes.count << 1, 8);
+        rehash(map, new_size);
     }
 
-    return lr;
+    rehash :: (use map: ^Map($K, $V), new_size: i32) {
+        memory.free_slice(^hashes, allocator);
+        memory.alloc_slice(^hashes, new_size, allocator);
+        memory.fill_slice(hashes, -1);
+
+        for ^entry: entries do entry.next = -1;
+
+        index := 0;
+        for ^entry: entries {
+            defer index += 1;
+
+            lr := lookup(map, entry.key); 
+            entries[index].next = hashes[lr.hash_index];
+            hashes[lr.hash_index] = index;
+        }
+    }
 }
index d648f2a883e898c9ad61d19e2c3d53346a648ed7..76eaf087ac52be2c8bcb9f37286f39705b28e29a 100644 (file)
@@ -477,8 +477,12 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str {
                 if info.kind == .Struct {
                     s := cast(^Type_Info_Struct) info;
 
-                    if s.name.count > 0 do output->write(s.name);
-                    output->write(" { ");
+                    if s.name.count > 0 {
+                        output->write(s.name);
+                        output->write(" { ");
+                    } else {
+                        output->write("{ ");
+                    }
 
                     {
                         format := formatting;
@@ -534,7 +538,11 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str {
 
                 // This assumes that the following type_info kinds are basically the same.
                 if info.kind == .Dynamic_Array || info.kind == .Slice || info.kind == .Variadic_Argument {
-                    output->write("[ ");
+                    if formatting.pretty_printing {
+                        output->write("[");
+                    } else {
+                        output->write("[ ");
+                    }
 
                     a := cast(^Type_Info_Dynamic_Array) info;
                     arr := cast(^array.Untyped_Array) v.data;
@@ -543,7 +551,7 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str {
 
                     format := formatting;
                     format.quote_strings = true;
-                    if format.pretty_printing do format.indentation += 2;
+                    if format.pretty_printing do format.indentation += 4;
 
                     for i: count {
                         if i != 0 do output->write(", ");
@@ -558,7 +566,7 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str {
 
 
                     if formatting.pretty_printing {
-                        format.indentation -= 2;
+                        format.indentation -= 4;
                         output->write("\n");
                         for _: format.indentation do output->write(#char " ");
                         output->write(#char "]");
index 6df73adf18554a6410c7a197e3f3bef31f8d6112..cf752a906914565b55fbce0b513bcd90d6e6964e 100644 (file)
@@ -2,3 +2,9 @@ package core.intrinsics.onyx
 
 __initialize :: (val: ^$T)      -> void #intrinsic ---
 __zero_value :: ($T: type_expr) -> T    #intrinsic ---
+
+init :: macro ($T: type_expr) -> T {
+    val: T;
+    __initialize(^val);
+    return val;
+}
index 17a99ea98bf1d09840caf73c1554e89ad99d2b08..7fcbc969cb6154f94d14ca473c7800c7c778fe6d 100644 (file)
@@ -59,6 +59,20 @@ copy_slice :: (sl: [] $T, allocator := context.allocator) -> [] T {
     return .{ data = data, count = sl.count };
 }
 
+resize_slice :: (sl: [] $T, new_size: i32, allocator := context.allocator) -> [] T {
+    new_slice: [] T;
+    new_slice.data = raw_alloc(allocator, sizeof T * new_size);
+    new_slice.count = new_size;
+
+    copy(new_slice.data, sl.data, sl.count * sizeof T);
+
+    return new_slice;
+}
+
+fill_slice :: macro (sl: [] $T, v: T) {
+    for ^it: sl do *it = v;
+}
+
 align :: #match {
     (size: ^u64, align: u64) {
         if *size % align != 0 {
index 5cab4dac9fc217f03080aa442d833ca9959172e9..80b25e694535d2dc17568e96452401183bceb2ec 100644 (file)
@@ -9,7 +9,7 @@ main :: (args: [] cstr) {
 
     // To use it, simply create one like so:
     ages : Map(str, u32);
-    map.init(^ages, default=0, hash_count=16);
+    map.init(^ages, default=0);
     defer map.free(^ages);
 
     // Alternatively you can use the map.make function to achieve the
@@ -19,9 +19,7 @@ main :: (args: [] cstr) {
     // The defer statement ensures that the map memory will be freed
     // when this procedure exits, no matter through which way. The
     // 'default' argument is used to specify what value should be
-    // returned when the map does not contain the requested key. The
-    // hash_count is an implementation detail that will go away in the
-    // near future. It is the maximum number of chains in the hash table.
+    // returned when the map does not contain the requested key.
 
     // To put an entry into the map, use the map.put procedure.
     map.put(^ages, "Dwight", 32);
index 74546d826d4ad93ccf0be9b5bfc4ae09922203c1..2f6567ef40e91f05a66f3371a49c8107c2d113a0 100644 (file)
@@ -93,6 +93,9 @@ Value_Float :: struct {
 Value_String :: struct {
     use base := Value.{ type = .String };
     str_: str;
+
+    // Set if the string should not be freed from the allocator.
+    static := false;
 }
 
 Value_Array :: struct {
@@ -104,6 +107,8 @@ Value_Object :: struct {
     use base := Value.{ type = .Object };
     object_: [..] struct {
         key   : str;
+        key_is_static := false;
+
         value : ^Value;
     };
 }
@@ -133,11 +138,35 @@ get_idx :: (v: ^Value, idx: i32) -> ^Value {
     return v_arr.array_[idx];
 }
 
+set :: #match {
+    macro (v: ^Value, key: str, value: ^Value, key_is_static := false) {
+        if v.type == .Object {
+            Value_Object_ :: Value_Object ;
+
+            (cast(^Value_Object_) v).object_ << .{ key, key_is_static, value };
+        }
+    },
+
+    // Quick thing for allocating json values on the stack.
+    macro (v: ^Value, key: str, value: str, key_is_static := false, value_is_static := false) {
+        Value_String_ :: Value_String ;
+        Value_Object_ :: Value_Object ;
+
+        if v.type == .Object {
+            json_value := init(Value_String_);
+            json_value.str_ = value;
+            json_value.static = value_is_static;
+
+            (cast(^Value_Object_) v).object_ << .{ key, key_is_static, ^json_value };
+        }
+    }
+}
+
 free :: #match {
     (v: ^Value, allocator: Allocator) do switch v.type {
         case .String {
             v_str := cast(^Value_String) v;
-            raw_free(allocator, v_str.str_.data);
+            if !v_str.static do raw_free(allocator, v_str.str_.data);
         }
 
         case .Array {
@@ -151,7 +180,7 @@ free :: #match {
         case .Object {
             v_obj := cast(^Value_Object) v;
             for ^entry: v_obj.object_ {
-                raw_free(allocator, entry.key.data);
+                if !entry.key_is_static do raw_free(allocator, entry.key.data);
                 free(entry.value, allocator);
             }
             array.free(^v_obj.object_);
@@ -162,3 +191,7 @@ free :: #match {
         free(root, allocator);
     },
 }
+
+static_string :: (s: str) -> Value_String {
+    return .{ str_ = s, static = true };
+}
index 5cfe8f7f2ddf72e5dafae7b6f41c210ba0f0017f..d9153969444d33048898b580da345371f2c58a44 100644 (file)
@@ -16,11 +16,11 @@ UI_Id :: #type u32
 }
 
 init_ui :: () {
-    map.init(^font_registry, hash_count=4);
+    map.init(^font_registry);
 
-    map.init(^animation_states, default=.{}, hash_count=4);
-    map.init(^workspace_states, default=.{}, hash_count=4);
-    map.init(^scrollable_region_states, default=.{}, hash_count=4);
+    map.init(^animation_states, default=.{});
+    map.init(^workspace_states, default=.{});
+    map.init(^scrollable_region_states, default=.{});
 
     // Register a default font so things don't break everywhere
     builtin_font := create_font(#file_contents "./fonts/builtin.fnt", #file_contents "./fonts/builtin.data");
index 972b9a8c060e05e1279dfe1e51d8c2d2b39026aa..3ab5bac2ee7713f7496c3007c51eac51719c80f8 100644 (file)
@@ -567,7 +567,7 @@ b32 unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
         if (func == NULL) return 0;
 
         // FIXME: This is incorrect. It should actually yield and not return a failure.
-        if (func == &node_that_signals_a_yield) return 0;
+        if (func == (AstFunction *) &node_that_signals_a_yield) return 0;
 
         *pnode = (AstTyped *) func;
         node = *pnode;
index 2681a4342494ab72906eb07a36ffc8cc0ae01df8..fd282119ac225eb71d132a87f19a0e64bb896b82 100644 (file)
@@ -1138,6 +1138,7 @@ CheckStatus check_array_literal(AstArrayLiteral* al) {
     }
 
     al->flags |= Ast_Flag_Comptime;
+    assert(al->type->kind == Type_Kind_Array);
 
     Type* elem_type = al->type->Array.elem;
     bh_arr_each(AstTyped *, expr, al->values) {
index 71cac81eb65978d7309eb0fad0ea410c70174126..cc4891ed53423963258bedef8ef1d9b1f4c0e68d 100644 (file)
@@ -1964,6 +1964,7 @@ static AstStructType* parse_struct(OnyxParser* parser) {
         return (AstStructType *) poly_struct;
 
     } else {
+        ENTITY_SUBMIT(s_node);
         return s_node;
     }
 }
@@ -2597,7 +2598,7 @@ static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol
         }
 
         // HACK: This should maybe be entered elsewhere?
-        ENTITY_SUBMIT(node);
+        if (node->kind != Ast_Kind_Struct_Type) ENTITY_SUBMIT(node);
     }
 
     AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
index 78a16dc727377ff45f00178a5d6624b7554addaf..97c8397c37ef214f41d66495444b95868a56e344 100644 (file)
@@ -78,7 +78,7 @@ main :: (args: [] cstr) {
 
        file := reader.make(contents);
 
-       mem := map.make(u64, u64, default=0, hash_count=257);
+       mem := map.make(u64, u64, default=0);
        defer map.free(^mem);
 
        mask : Bitmask;
index c5417d9bc9160270f3c78f58136c6bb214d61950..50d2aa6179b8533f8c4a9e94afe1e3f540ad13eb 100644 (file)
@@ -14,7 +14,7 @@ main :: (args: [] cstr) {
     // The current implementation of Map is rather slow at a large scale.
     // Any changes to the implementation of Map should be tested on this
     // file to validate if they 1) work and 2) are faster.
-    nums := map.make(u32, spoken_times, .{}, 32767);
+    nums := map.make(u32, spoken_times, .{});
     defer map.free(^nums);
 
     turn := 1;
index 19c29ab9a0926ce136cd927e2cf11b3d7bc88d80..fa793c63f044f232dedaccfed84b1db50353c406 100644 (file)
@@ -41,7 +41,7 @@ main :: (args: [] cstr) {
 
     file := reader.make(contents);
 
-    cubes := map.make(CubePos, CubeState, .{}, 2047);
+    cubes := map.make(CubePos, CubeState, .{});
     defer map.free(^cubes);
 
     z := 0;
index c616f27566c7f461acbbb736a96ce5ffb0b64f54..917a28adfd6c1796dac17bac184062a8cbe1f42b 100644 (file)
@@ -246,7 +246,7 @@ main :: (args: [] cstr) {
        tiles := array.make(Tile);
        defer array.free(^tiles);
 
-    tile_map := map.make(u32, #type ^Tile, null, 67);
+    tile_map := map.make(u32, #type ^Tile, null);
     defer map.free(^tile_map);
 
        tile_data := calloc(200 * sizeof TileData);
index a2326d1a9cf8fd44876eca082c3d7553a5bd6a3b..c1dc72a5db35e1a489db6193a278b8c9834b5f72 100644 (file)
@@ -36,7 +36,7 @@ main :: (args: [] cstr) {
 
        file := reader.make(contents);
 
-    map.init(^ingredient_map, .{}, 127);
+    map.init(^ingredient_map, .{});
     map.init(^allergen_map, .{});
     defer {
         map.free(^ingredient_map);
index 616d62f4173ed1b3236dba98dc7cdf85d5bd9aa8..7e4d9d6331888f986125be2bdc7287db3d07bc70 100644 (file)
@@ -60,7 +60,7 @@ encode_hands :: (alloc: Allocator, p1: ^[..] u32, p2: ^[..] u32) -> str {
 }
 
 recursive_combat :: (player1: ^[..] u32, player2: ^[..] u32) -> u32 {
-    hand_seen := map.make(str, bool, false, 31);
+    hand_seen := map.make(str, bool, false);
     defer map.free(^hand_seen);
 
     while player1.count > 0 && player2.count > 0 {
index 79a535141265869547debb639e40f5bd17fe1cbb..efef79d614fa6a0388a9c805f720e78278eabebd 100644 (file)
@@ -31,7 +31,7 @@ main :: (args: [] cstr) {
     file_stream := io.string_stream_make(contents);
        file := io.reader_make(^file_stream);
 
-       grid := map.make(Vec2, Cell, .{}, 1021); // `true` is black
+       grid := map.make(Vec2, Cell, .{}); // `true` is black
        defer map.free(^grid);
 
        while !io.stream_end_of_file(^file_stream) {
index 443a1cb8fef145ddf09403c777c1a2cfb20f6f76..5d47bfaead120331f5e73673f002e483c0083616 100644 (file)
@@ -22,7 +22,7 @@ power_mod :: (base: u32, exp: u32, mod: u32) -> u32 {
 dlp_bsgs :: (n: u32, a: u32, b: u32) -> u32 {
     m := cast(u32) math.ceil(math.sqrt(cast(f64) n));
 
-    t := map.make(u32, u32, default=0, hash_count=m/2);
+    t := map.make(u32, u32, default=0);
     defer map.free(^t);
 
     tmp: u64 = 1;
index 81f319f09196e22e2a46126dbbb5d63272b3dc5a..d3e9c40776b20c26d1e2eaccd0f039ef696e8e2d 100644 (file)
@@ -29,7 +29,7 @@ foo :: (x: i32) -> i32 {
 cached_fib :: (n: u64) -> u64 {
     #persist cache : map.Map(u64, u64);
     if cache.hashes.data == null {
-        map.init(^cache, hash_count=128);
+        map.init(^cache);
     }
 
     if n <= 1 do return n;
@@ -54,4 +54,4 @@ cached_fib :: (n: u64) -> u64 {
 
         printf("{} ", cache.entries.count);
     }
-}
\ No newline at end of file
+}
index 55d5f974ec99ec087692fd3af684610266640104..09648d30cd6b16f8a6ac594c1886ec72894fb52e 100644 (file)
@@ -140,11 +140,9 @@ main :: (args: [] cstr) {
     test_polymorphic_union_with_use :: () {
         println("\n\nTesting a polymorphic union with use.");
 
-        PolyStruct :: struct (T: type_expr, R: type_expr) {
-            use container : struct #union {
-                t_data : T;
-                r_data : R;
-            };
+        PolyStruct :: struct (T: type_expr, R: type_expr) #union {
+            t_data : T;
+            r_data : R;
         }
 
         printf("{} == 16\n", sizeof PolyStruct(i32, [] u32));