aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-08-10 16:03:41 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-08-10 16:03:41 -0400
commitb37bd70b602b7ac6427dcf29f7cd9241b0a7ae09 (patch)
treeafa3ba4caebb728c43b96e176b8a569b3ad5f469
parent671f81137ee2e5de632526109e02c4b79197e432 (diff)
For tables, deprecate support for square bracket indexing and .default
values, replacing them with a `:bump()` function for tables with numeric values. This means that counters can be implemented easily without the need to mask complexity.
-rw-r--r--ast.c5
-rw-r--r--ast.h2
-rw-r--r--builtins/datatypes.h1
-rw-r--r--builtins/table.c33
-rw-r--r--builtins/table.h9
-rw-r--r--compile.c70
-rw-r--r--parse.c10
-rw-r--r--repl.c4
-rw-r--r--test/enums.tm10
-rw-r--r--test/structs.tm22
-rw-r--r--test/tables.tm32
-rw-r--r--typecheck.c20
12 files changed, 72 insertions, 146 deletions
diff --git a/ast.c b/ast.c
index 8f804c5f..0f325320 100644
--- a/ast.c
+++ b/ast.c
@@ -117,10 +117,9 @@ CORD ast_to_xml(ast_t *ast)
T(Set, "<Set>%r%r</Set>",
optional_tagged_type("item-type", data.item_type),
ast_list_to_xml(data.items))
- T(Table, "<Table>%r%r%r%r%r</Table>",
+ T(Table, "<Table>%r%r%r%r</Table>",
optional_tagged_type("key-type", data.key_type), optional_tagged_type("value-type", data.value_type),
- ast_list_to_xml(data.entries), optional_tagged("fallback", data.fallback),
- optional_tagged("default", data.default_value))
+ ast_list_to_xml(data.entries), optional_tagged("fallback", data.fallback))
T(TableEntry, "<TableEntry>%r%r</TableEntry>", ast_to_xml(data.key), ast_to_xml(data.value))
T(Comprehension, "<Comprehension>%r%r%r%r%r</Comprehension>", optional_tagged("expr", data.expr),
ast_list_to_xml(data.vars), optional_tagged("iter", data.iter),
diff --git a/ast.h b/ast.h
index c049e17a..41760e38 100644
--- a/ast.h
+++ b/ast.h
@@ -187,7 +187,7 @@ struct ast_s {
} Set;
struct {
type_ast_t *key_type, *value_type;
- ast_t *fallback, *default_value;
+ ast_t *fallback;
ast_list_t *entries;
} Table;
struct {
diff --git a/builtins/datatypes.h b/builtins/datatypes.h
index 12c7cbc4..b0f62d03 100644
--- a/builtins/datatypes.h
+++ b/builtins/datatypes.h
@@ -47,7 +47,6 @@ typedef struct table_s {
array_t entries;
bucket_info_t *bucket_info;
struct table_s *fallback;
- void *default_value;
} table_t;
typedef struct {
diff --git a/builtins/table.c b/builtins/table.c
index 5329ec24..e2044a2d 100644
--- a/builtins/table.c
+++ b/builtins/table.c
@@ -137,9 +137,6 @@ public void *Table$get(table_t t, const void *key, const TypeInfo *type)
void *ret = Table$get_raw(*iter, key, type);
if (ret) return ret;
}
- for (const table_t *iter = &t; iter; iter = iter->fallback) {
- if (iter->default_value) return iter->default_value;
- }
return NULL;
}
@@ -259,9 +256,6 @@ public void *Table$reserve(table_t *t, const void *key, const void *value, const
value = Table$get_raw(*iter, key, type);
if (value) break;
}
- for (table_t *iter = t; !value && iter; iter = iter->fallback) {
- if (iter->default_value) value = iter->default_value;
- }
}
maybe_copy_on_write(t, type);
@@ -404,9 +398,6 @@ public bool Table$equal(const table_t *x, const table_t *y, const TypeInfo *type
if (Table$length(*x) != Table$length(*y))
return false;
- if ((x->default_value != NULL) != (y->default_value != NULL))
- return false;
-
if ((x->fallback != NULL) != (y->fallback != NULL))
return false;
@@ -433,13 +424,6 @@ public int32_t Table$compare(const table_t *x, const table_t *y, const TypeInfo
if (diff != 0) return diff;
}
- if (!x->default_value != !y->default_value) {
- return (!x->default_value) - (!y->default_value);
- } else if (x->default_value && y->default_value) {
- int32_t diff = generic_compare(x->default_value, y->default_value, table.value);
- if (diff != 0) return diff;
- }
-
if (!x->fallback != !y->fallback) {
return (!x->fallback) - (!y->fallback);
} else if (x->fallback && y->fallback) {
@@ -460,7 +444,6 @@ public uint32_t Table$hash(const table_t *t, const TypeInfo *type)
Array$hash(&t->entries, $ArrayInfo(table.key)),
Array$hash(&t->entries + value_offset(type), $ArrayInfo(table.value)),
t->fallback ? Table$hash(t->fallback, type) : 0,
- t->default_value ? generic_hash(t->default_value, table.value) : 0,
};
uint32_t hash;
halfsiphash(&components, sizeof(components), TOMO_HASH_KEY, (uint8_t*)&hash, sizeof(hash));
@@ -495,11 +478,6 @@ public CORD Table$as_text(const table_t *t, bool colorize, const TypeInfo *type)
c = CORD_cat(c, Table$as_text(t->fallback, colorize, type));
}
- if (t->default_value) {
- c = CORD_cat(c, "; default=");
- c = CORD_cat(c, generic_as_text(t->default_value, colorize, table.value));
- }
-
c = CORD_cat(c, "}");
return c;
}
@@ -545,9 +523,6 @@ public table_t Table$overlap(table_t a, table_t b, const TypeInfo *type)
*result.fallback = Table$overlap(*a.fallback, b, type);
}
- if (a.default_value && b.default_value && generic_equal(a.default_value, b.default_value, type->TableInfo.value))
- result.default_value = a.default_value;
-
return result;
}
@@ -573,9 +548,6 @@ public table_t Table$with(table_t a, table_t b, const TypeInfo *type)
result.fallback = a.fallback ? a.fallback : b.fallback;
}
- // B's default value takes precedence over A's
- result.default_value = b.default_value ? b.default_value : a.default_value;
-
return result;
}
@@ -598,11 +570,6 @@ public table_t Table$without(table_t a, table_t b, const TypeInfo *type)
*result.fallback = Table$without(*a.fallback, b, type);
}
- if (a.default_value) {
- if (!b.default_value || !generic_equal(a.default_value, b.default_value, type->TableInfo.value))
- result.default_value = a.default_value;
- }
-
return result;
}
diff --git a/builtins/table.h b/builtins/table.h
index 3b1c7f98..842131e8 100644
--- a/builtins/table.h
+++ b/builtins/table.h
@@ -11,7 +11,7 @@
#include "types.h"
#include "util.h"
-#define Table(key_t, val_t, key_info, value_info, fb, def, N, ...) ({ \
+#define Table(key_t, val_t, key_info, value_info, fb, N, ...) ({ \
struct { key_t k; val_t v; } ents[N] = {__VA_ARGS__}; \
table_t table = Table$from_entries((array_t){ \
.data=memcpy(GC_MALLOC(sizeof(ents)), ents, sizeof(ents)), \
@@ -19,7 +19,6 @@
.stride=(void*)&ents[1] - (void*)&ents[0], \
}, $TableInfo(key_info, value_info)); \
table.fallback = fb; \
- table.default_value = def; \
table; })
#define Set(item_t, item_info, N, ...) ({ \
item_t ents[N] = {__VA_ARGS__}; \
@@ -52,6 +51,12 @@ void Table$set(table_t *t, const void *key, const void *value, const TypeInfo *t
#define Table$set_value(t, key_expr, value_expr, type) ({ __typeof(key_expr) k = key_expr; __typeof(value_expr) v = value_expr; \
Table$set(t, &k, &v, type); })
#define Table$reserve_value(t, key_expr, type) ({ __typeof(key_expr) k = key_expr; Table$reserve(t, &k, NULL, type); })
+#define Table$bump(t_expr, key_expr, amount_expr, type) ({ __typeof(key_expr) key = key_expr; \
+ table_t *t = t_expr; \
+ __typeof(amount_expr) *val = Table$get_raw(*t, &key, type); \
+ if (val) *val += amount_expr; \
+ else { __typeof(amount_expr) init = amount_expr; Table$set(t, &key, &init, type); } (void)0; })
+
void Table$remove(table_t *t, const void *key, const TypeInfo *type);
#define Table$remove_value(t, key_expr, type) ({ __typeof(key_expr) k = key_expr; Table$remove(t, &k, type); })
diff --git a/compile.c b/compile.c
index 8ac4d37a..7739fad6 100644
--- a/compile.c
+++ b/compile.c
@@ -186,15 +186,6 @@ static CORD compile_lvalue(env_t *env, ast_t *ast)
compile(env, index->index), ", ", CORD_asprintf("%ld", padded_type_size(item_type)),
", ", Text$quoted(ast->file->filename, false), ", ", heap_strf("%ld", ast->start - ast->file->text),
", ", heap_strf("%ld", ast->end - ast->file->text), ")");
- } else if (container_t->tag == TableType) {
- CORD target_code = compile_to_pointer_depth(env, index->indexed, 1, false);
- type_t *value_t = Match(container_t, TableType)->value_type;
- CORD key = compile(env, index->index);
- if (!promote(env, &key, get_type(env, index->index), Match(container_t, TableType)->key_type))
- code_err(index->index, "I couldn't promote this type from %T to %T",
- get_type(env, index->index), Match(container_t, TableType)->key_type);
- return CORD_all("*(", compile_type(value_t), "*)Table$reserve_value(", target_code, ", (", compile_type(Match(container_t, TableType)->key_type), ")",
- compile(env, index->index),", ", compile_type_info(env, container_t), ")");
} else {
code_err(ast, "I don't know how to assign to this target");
}
@@ -1731,8 +1722,6 @@ CORD compile(env_t *env, ast_t *ast)
CORD code = "((table_t){";
if (table->fallback)
code = CORD_all(code, ".fallback=", compile(env, table->fallback),",");
- if (table->default_value)
- code = CORD_all(code, ".default_value=heap(", compile(env, table->default_value),"),");
return CORD_cat(code, "})");
}
@@ -1756,11 +1745,6 @@ CORD compile(env_t *env, ast_t *ast)
else
code = CORD_all(code, ", /*fallback:*/ NULL");
- if (table->default_value)
- code = CORD_all(code, ", /*default:*/ heap(", compile(env, table->default_value), ")");
- else
- code = CORD_all(code, ", /*default:*/ NULL");
-
size_t n = 0;
for (ast_list_t *entry = table->entries; entry; entry = entry->next)
++n;
@@ -1783,8 +1767,6 @@ CORD compile(env_t *env, ast_t *ast)
if (table->fallback)
code = CORD_all(code, ".fallback=heap(", compile(env, table->fallback), "), ");
- if (table->default_value)
- code = CORD_all(code, ".default_value=heap(", compile(env, table->default_value), "), ");
code = CORD_cat(code, "};");
set_binding(scope, scope->comprehension_var, new(binding_t, .type=table_type, .code=scope->comprehension_var));
@@ -2112,9 +2094,19 @@ CORD compile(env_t *env, ast_t *ast)
auto table = Match(self_value_t, TableType);
if (streq(call->name, "get")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
- arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type, .next=new(arg_t, .name="default", .type=table->value_type));
- return CORD_all("Table$get_value_or_default(", self, ", ", compile_type(table->key_type), ", ", compile_type(table->value_type), ", ",
- compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(env, self_value_t), ")");
+ if (call->args->next) {
+ arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type, .next=new(arg_t, .name="default", .type=table->value_type));
+ return CORD_all("Table$get_value_or_default(", self, ", ", compile_type(table->key_type), ", ", compile_type(table->value_type), ", ",
+ compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(env, self_value_t), ")");
+ } else {
+ arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type);
+ file_t *f = ast->file;
+ return CORD_all("Table$get_value_or_fail(", self, ", ", compile_type(table->key_type), ", ", compile_type(table->value_type), ", ",
+ compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(env, self_value_t), ", ",
+ Text$quoted(f->filename, false), ", ", CORD_asprintf("%ld", (int64_t)(ast->start - f->text)), ", ",
+ CORD_asprintf("%ld", (int64_t)(ast->end - f->text)),
+ ")");
+ }
} else if (streq(call->name, "has")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type);
@@ -2126,6 +2118,17 @@ CORD compile(env_t *env, ast_t *ast)
.next=new(arg_t, .name="value", .type=table->value_type));
return CORD_all("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")");
+ } else if (streq(call->name, "bump")) {
+ CORD self = compile_to_pointer_depth(env, call->self, 1, false);
+ if (!(table->value_type->tag == IntType || table->value_type->tag == NumType))
+ code_err(ast, "bump() is only supported for tables with numeric value types, not %T", self_value_t);
+ ast_t *one = table->value_type->tag == IntType
+ ? FakeAST(Int, .i=1, .bits=Match(table->value_type, IntType)->bits)
+ : FakeAST(Num, .n=1, .bits=Match(table->value_type, NumType)->bits);
+ arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type,
+ .next=new(arg_t, .name="amount", .type=table->value_type, .default_val=one));
+ return CORD_all("Table$bump(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "remove")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type);
@@ -2326,8 +2329,6 @@ CORD compile(env_t *env, ast_t *ast)
",\n .length=t->entries.length,\n .stride=t->entries.stride,\n .data_refcount=3};})");
} else if (streq(f->field, "fallback")) {
return CORD_all("(", compile_to_pointer_depth(env, f->fielded, 0, false), ").fallback");
- } else if (streq(f->field, "default")) {
- return CORD_all("(", compile_to_pointer_depth(env, f->fielded, 0, false), ").default_value");
}
code_err(ast, "There is no '%s' field on tables", f->field);
}
@@ -2349,16 +2350,13 @@ CORD compile(env_t *env, ast_t *ast)
code_err(ast, "This pointer is potentially null, so it can't be safely dereferenced");
if (ptr->pointed->tag == ArrayType) {
return CORD_all("({ array_t *arr = ", compile(env, indexing->indexed), "; ARRAY_INCREF(*arr); *arr; })");
- } else if (ptr->pointed->tag == TableType) {
- return CORD_all("({ table_t *t = ", compile(env, indexing->indexed), "; TABLE_INCREF(*t); *t; })");
} else {
return CORD_all("*(", compile(env, indexing->indexed), ")");
}
}
type_t *container_t = value_type(indexed_type);
type_t *index_t = get_type(env, indexing->index);
- switch (container_t->tag) {
- case ArrayType: {
+ if (container_t->tag == ArrayType) {
if (index_t->tag != IntType)
code_err(indexing->index, "Arrays can only be indexed by integers, not %T", index_t);
type_t *item_type = Match(container_t, ArrayType)->item_type;
@@ -2372,22 +2370,8 @@ CORD compile(env_t *env, ast_t *ast)
Text$quoted(f->filename, false), ", ", CORD_asprintf("%ld", (int64_t)(indexing->index->start - f->text)), ", ",
CORD_asprintf("%ld", (int64_t)(indexing->index->end - f->text)),
")");
- }
- case TableType: {
- type_t *key_t = Match(container_t, TableType)->key_type;
- type_t *value_t = Match(container_t, TableType)->value_type;
- CORD table = compile_to_pointer_depth(env, indexing->indexed, 0, false);
- CORD key = compile(env, indexing->index);
- if (!promote(env, &key, index_t, key_t))
- code_err(indexing->index, "This value has type %T, but this table can only be index with keys of type %T", index_t, key_t);
- file_t *f = indexing->index->file;
- return CORD_all("Table$get_value_or_fail(", table, ", ", compile_type(key_t), ", ", compile_type(value_t), ", ",
- key, ", ", compile_type_info(env, container_t), ", ",
- Text$quoted(f->filename, false), ", ", CORD_asprintf("%ld", (int64_t)(indexing->index->start - f->text)), ", ",
- CORD_asprintf("%ld", (int64_t)(indexing->index->end - f->text)),
- ")");
- }
- default: code_err(ast, "Indexing is not supported for type: %T", container_t);
+ } else {
+ code_err(ast, "Indexing is not supported for type: %T", container_t);
}
}
case InlineCCode: {
diff --git a/parse.c b/parse.c
index 2b7b5816..4c9e95aa 100644
--- a/parse.c
+++ b/parse.c
@@ -743,7 +743,7 @@ PARSER(parse_table) {
whitespace(&pos);
- ast_t *fallback = NULL, *default_val = NULL;
+ ast_t *fallback = NULL;
if (match(&pos, ";")) {
for (;;) {
whitespace(&pos);
@@ -754,12 +754,6 @@ PARSER(parse_table) {
if (fallback)
parser_err(ctx, attr_start, pos, "This table already has a fallback");
fallback = expect(ctx, attr_start, &pos, parse_expr, "I expected a fallback table");
- } else if (match(&pos, "default")) {
- whitespace(&pos);
- if (!match(&pos, "=")) parser_err(ctx, attr_start, pos, "I expected an '=' after 'default'");
- if (default_val)
- parser_err(ctx, attr_start, pos, "This table already has a default value");
- default_val = expect(ctx, attr_start, &pos, parse_expr, "I expected a default value for this table");
} else {
break;
}
@@ -771,7 +765,7 @@ PARSER(parse_table) {
whitespace(&pos);
expect_closing(ctx, &pos, "}", "I wasn't able to parse the rest of this table");
- return NewAST(ctx->file, start, pos, Table, .key_type=key_type, .value_type=value_type, .entries=entries, .fallback=fallback, .default_value=default_val);
+ return NewAST(ctx->file, start, pos, Table, .key_type=key_type, .value_type=value_type, .entries=entries, .fallback=fallback);
}
PARSER(parse_set) {
diff --git a/repl.c b/repl.c
index fc531b86..7d4eae33 100644
--- a/repl.c
+++ b/repl.c
@@ -507,10 +507,6 @@ void eval(env_t *env, ast_t *ast, void *dest)
}
if (table_ast->fallback)
eval(env, table_ast->fallback, &table.fallback);
- if (table_ast->default_value) {
- table.default_value = GC_MALLOC(value_size);
- eval(env, table_ast->default_value, table.default_value);
- }
memcpy(dest, &table, sizeof(table_t));
break;
}
diff --git a/test/enums.tm b/test/enums.tm
index 553fcf84..65ef6398 100644
--- a/test/enums.tm
+++ b/test/enums.tm
@@ -36,11 +36,11 @@ func main():
= yes
>> x := Foo.One(123)
- >> t := {x:"found"; default="missing"}
- >> t[x]
- = "found"
- >> t[Foo.Zero]
- = "missing"
+ >> t := {x}
+ >> t:has(x)
+ = yes
+ >> t:has(Foo.Zero)
+ = no
>> choose_text(Foo.Zero)
= "Zero"
diff --git a/test/structs.tm b/test/structs.tm
index 4fdec6ae..c9022ede 100644
--- a/test/structs.tm
+++ b/test/structs.tm
@@ -29,11 +29,11 @@ func test_metamethods():
>> x < Pair(11, 20)
= yes
- >> t2 := {x:"found"; default="missing"}
- >> t2[x]
- = "found"
- >> t2[y]
- = "missing"
+ >> set := {x}
+ >> set:has(x)
+ = yes
+ >> set:has(y)
+ = no
func test_mixed():
>> x := Mixed(10, "Hello")
@@ -46,11 +46,11 @@ func test_mixed():
= no
>> x < Mixed(11, "Hello")
= yes
- >> t := {x:"found"; default="missing"}
- >> t[x]
- = "found"
- >> t[y]
- = "missing"
+ >> set := {x}
+ >> set:has(x)
+ = yes
+ >> set:has(y)
+ = no
func main():
test_literals()
@@ -63,7 +63,7 @@ func main():
= Password(...)
>> users_by_password := {my_pass:"User1", Password("xxx"):"User2"}
= {Password(...):"User1", Password(...):"User2"}
- >> users_by_password[my_pass]
+ >> users_by_password:get(my_pass)
= "User1"
>> CorecursiveA(@CorecursiveB())
diff --git a/test/tables.tm b/test/tables.tm
index 6dbc1b78..d02a5272 100644
--- a/test/tables.tm
+++ b/test/tables.tm
@@ -1,12 +1,12 @@
func main():
- >> t := {"one":1, "two":2; default=999}
- = {"one":1, "two":2; default=999}
+ >> t := {"one":1, "two":2}
+ = {"one":1, "two":2}
- >> t["one"]
+ >> t:get("one", 999)
= 1
- >> t["two"]
+ >> t:get("two", 999)
= 2
- >> t["???"]
+ >> t:get("???", 999)
= 999
t_str := ""
@@ -17,8 +17,6 @@ func main():
>> #t
= 2
- >> t.default
- = @%999?
>> t.fallback
= !{Text:Int}
@@ -28,21 +26,19 @@ func main():
= [1, 2]
>> t2 := {"three":3; fallback=t}
- = {"three":3; fallback={"one":1, "two":2; default=999}}
+ = {"three":3; fallback={"one":1, "two":2}}
- >> t2["one"]
+ >> t2:get("one", 999)
= 1
- >> t2["three"]
+ >> t2:get("three", 999)
= 3
- >> t2["???"]
+ >> t2:get("???", 999)
= 999
>> #t2
= 1
- >> t2.default
- = !Int
>> t2.fallback
- = @%{"one":1, "two":2; default=999}?
+ = @%{"one":1, "two":2}?
t2_str := ""
for k,v in t2:
@@ -64,6 +60,8 @@ func main():
do:
>> plain := {1:10, 2:20, 3:30}
+ >> plain:get(2)
+ = 20
>> plain:get(2, -999)
= 20
>> plain:get(456, -999)
@@ -79,9 +77,3 @@ func main():
>> fallback:get(1, -999)
= 10
- >> default := {5:50; default=0}
- >> default:has(28273)
- = yes
- >> default:get(28273, -999)
- = 0
-
diff --git a/typecheck.c b/typecheck.c
index ce8b07ed..4938bc9d 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -578,8 +578,6 @@ type_t *get_type(env_t *env, ast_t *ast)
key_type = parse_type_ast(env, table->key_type);
value_type = parse_type_ast(env, table->value_type);
} else {
- if (table->default_value)
- value_type = get_type(env, table->default_value);
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
ast_t *entry_ast = entry->ast;
env_t *scope = env;
@@ -660,25 +658,16 @@ type_t *get_type(env_t *env, ast_t *ast)
}
type_t *value_t = value_type(indexed_t);
- switch (value_t->tag) {
- case ArrayType: {
+ if (value_t->tag == ArrayType) {
if (!indexing->index) return indexed_t;
type_t *index_t = get_type(env, indexing->index);
- switch (index_t->tag) {
- case IntType:
+ if (index_t->tag == IntType) {
return Match(value_t, ArrayType)->item_type;
- default: code_err(indexing->index, "I only know how to index lists using integers, not %T", index_t);
}
- }
- case TableType: {
- return Match(value_t, TableType)->value_type;
- }
- // TODO: support ranges like (99..123)[5]
- // TODO: support slicing arrays like ([1,2,3,4])[2..10]
- default: {
+ code_err(indexing->index, "I only know how to index lists using integers, not %T", index_t);
+ } else {
code_err(ast, "I don't know how to index %T values", indexed_t);
}
- }
}
case FunctionCall: {
auto call = Match(ast, FunctionCall);
@@ -741,6 +730,7 @@ type_t *get_type(env_t *env, ast_t *ast)
if (streq(call->name, "get")) return table->value_type;
else if (streq(call->name, "has")) return Type(BoolType);
else if (streq(call->name, "set")) return Type(VoidType);
+ else if (streq(call->name, "bump")) return Type(VoidType);
else if (streq(call->name, "remove")) return Type(VoidType);
else if (streq(call->name, "clear")) return Type(VoidType);
else if (streq(call->name, "sorted")) return self_value_t;