From b37bd70b602b7ac6427dcf29f7cd9241b0a7ae09 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 10 Aug 2024 16:03:41 -0400 Subject: 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. --- ast.c | 5 ++-- ast.h | 2 +- builtins/datatypes.h | 1 - builtins/table.c | 33 ------------------------- builtins/table.h | 9 +++++-- compile.c | 70 ++++++++++++++++++++-------------------------------- parse.c | 10 ++------ repl.c | 4 --- test/enums.tm | 10 ++++---- test/structs.tm | 22 ++++++++--------- test/tables.tm | 32 +++++++++--------------- typecheck.c | 20 ++++----------- 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, "%r%r", optional_tagged_type("item-type", data.item_type), ast_list_to_xml(data.items)) - T(Table, "%r%r%r%r%r
", + T(Table, "%r%r%r%r
", 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, "%r%r", ast_to_xml(data.key), ast_to_xml(data.value)) T(Comprehension, "%r%r%r%r%r", 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; -- cgit v1.2.3