aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-09-12 00:55:43 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-09-12 00:55:43 -0400
commit327d466b9543aee3983277ca4158e5b7aa06fdf8 (patch)
tree64028cc0ca38b76461a85be02e3103db600f0bcc
parentfa7e52787f81f3673f9d57e9e4ba7fc015ca8432 (diff)
Table:get() now uses optional values instead of default or failure modes
-rw-r--r--builtins/table.h12
-rw-r--r--compile.c37
-rw-r--r--docs/tables.md63
-rw-r--r--test/structs.tm2
-rw-r--r--test/tables.tm34
-rw-r--r--typecheck.c10
6 files changed, 65 insertions, 93 deletions
diff --git a/builtins/table.h b/builtins/table.h
index 8e6e5de1..e589e6ec 100644
--- a/builtins/table.h
+++ b/builtins/table.h
@@ -31,16 +31,10 @@
Table_t Table$from_entries(Array_t entries, const TypeInfo *type);
void *Table$get(Table_t t, const void *key, const TypeInfo *type);
-#define Table$get_value_or_fail(table_expr, key_t, val_t, key_expr, info_expr, start, end) ({ \
- const Table_t t = table_expr; key_t k = key_expr; const TypeInfo* info = info_expr; \
- val_t *v = Table$get(t, &k, info); \
- if (__builtin_expect(v == NULL, 0)) \
- fail_source(__SOURCE_FILE__, start, end, "The key %k is not in this table\n", (Text_t[1]){generic_as_text(&k, no, info->TableInfo.key)}); \
- *v; })
-#define Table$get_value_or_default(table_expr, key_t, val_t, key_expr, default_val, info_expr) ({ \
+#define Table$get_optional(table_expr, key_t, val_t, key_expr, nonnull_var, nonnull_expr, null_expr, info_expr) ({ \
const Table_t t = table_expr; const key_t k = key_expr; \
- val_t *v = Table$get(t, &k, info_expr); \
- v ? *v : default_val; })
+ val_t *nonnull_var = Table$get(t, &k, info_expr); \
+ nonnull_var ? nonnull_expr : null_expr; })
#define Table$has_value(table_expr, key_expr, info_expr) ({ \
const Table_t t = table_expr; __typeof(key_expr) k = key_expr; \
(Table$get(t, &k, info_expr) != NULL); })
diff --git a/compile.c b/compile.c
index cc334e76..9e5b8339 100644
--- a/compile.c
+++ b/compile.c
@@ -32,14 +32,14 @@ CORD promote_to_optional(type_t *t, CORD code)
return code;
} else if (t->tag == IntType) {
switch (Match(t, IntType)->bits) {
- case TYPE_IBITS8: return CORD_all("((OptionalInt8_t){.i=", code, "})");
- case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){.i=", code, "})");
- case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){.i=", code, "})");
- case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){.i=", code, "})");
+ case TYPE_IBITS8: return CORD_all("((OptionalInt8_t){", code, "})");
+ case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){", code, "})");
+ case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){", code, "})");
+ case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){", code, "})");
default: errx(1, "Unsupported in type: %T", t);
}
} else if (t->tag == StructType) {
- return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.value=", code, "})");
+ return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){", code, "})");
} else {
return code;
}
@@ -1708,6 +1708,9 @@ static bool string_literal_is_all_ascii(CORD literal)
CORD compile_null(type_t *t)
{
+ if (t->tag == OptionalType)
+ t = Match(t, OptionalType)->type;
+
if (t == THREAD_TYPE) return "NULL";
switch (t->tag) {
@@ -2695,26 +2698,12 @@ 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);
- 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), ", ",
- CORD_asprintf("%ld", (int64_t)(ast->start - f->text)), ", ",
- CORD_asprintf("%ld", (int64_t)(ast->end - f->text)),
- ")");
- }
- } else if (streq(call->name, "get_or_null")) {
- if (table->value_type->tag != PointerType)
- code_err(ast, "The table method :get_or_null() is only supported for tables whose value type is a pointer, not %T", table->value_type);
- CORD self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_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), ", NULL, ", compile_type_info(env, self_value_t), ")");
+ return CORD_all(
+ "Table$get_optional(", self, ", ", compile_type(table->key_type), ", ",
+ compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ "_, ", optional_into_nonnull(table->value_type, "(*_)"), ", ", compile_null(table->value_type), ", ",
+ compile_type_info(env, self_value_t), ")");
} 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);
diff --git a/docs/tables.md b/docs/tables.md
index b18b6209..9de07b67 100644
--- a/docs/tables.md
+++ b/docs/tables.md
@@ -38,6 +38,23 @@ t := {i: 10*i for i in 10 if i mod 2 == 0}
t := {-1:-10, i: 10*i for i in 10}
```
+### Getting Values
+
+To get a value from a table, use `:get(key)`, which returns an _optional_
+value, depending on whether it was present in the table or not. For convenience,
+you can use the `!` postifx operator to perform a check to ensure that the value
+was found or error if it wasn't:
+
+```tomo
+>> t := {"x":1, "y":2}
+>> t:get("x")
+= 1?
+>> t:get("????")
+= !Int
+>> t:get("x")!
+= 1
+```
+
### Fallback Tables
Tables can specify a fallback table that is used when looking up a value if it
@@ -151,61 +168,35 @@ Nothing.
### `get`
**Description:**
-Retrieves the value associated with a key, or returns a default value if the key is not present.
+Retrieves the value associated with a key, or returns null if the key is not present.
**Usage:**
```markdown
-t:get(key: K, default: V) -> V
+t:get(key: K) -> V?
```
**Parameters:**
- `t`: The table.
- `key`: The key whose associated value is to be retrieved.
-- `default`: The value to return if the key is not present. If this argument is
- not provided, a runtime error will be created if the key is not present.
**Returns:**
-The value associated with the key or the default value if the key is not found.
+The value associated with the key or null if the key is not found.
**Example:**
```markdown
>> t := {"A":1, "B":2}
>> t:get("A")
-= 1
+= 1? : Int?
->> t:get("xxx", 0)
-= 0
-```
-
----
+>> t:get("????")
+= !Int : Int?
-### `get_or_null`
+>> t:get("A")!
+= 1 : Int
-**Description:**
-Retrieves the value associated with a key, or returns `null` if the key is not present.
-This method is only available on tables whose values are pointers.
-
-**Usage:**
-```markdown
-t:get_or_null(key: K) -> @V?
-```
-
-**Parameters:**
-
-- `t`: The table.
-- `key`: The key whose associated value is to be retrieved.
-
-**Returns:**
-A mutable reference to the value associated with the key or `null` if the key is not found.
-
-**Example:**
-```markdown
->> t := {"A": @[10]}
->> t:get_or_null("A")
-= @[10]?
->> t:get_or_null("xxx")
-= !@[Int]
+>> t:get("????"):or_else(0)
+= 0 : Int
```
---
diff --git a/test/structs.tm b/test/structs.tm
index c9022ede..53d356f5 100644
--- a/test/structs.tm
+++ b/test/structs.tm
@@ -63,7 +63,7 @@ func main():
= Password(...)
>> users_by_password := {my_pass:"User1", Password("xxx"):"User2"}
= {Password(...):"User1", Password(...):"User2"}
- >> users_by_password:get(my_pass)
+ >> users_by_password:get(my_pass)!
= "User1"
>> CorecursiveA(@CorecursiveB())
diff --git a/test/tables.tm b/test/tables.tm
index e59283ed..50eb5ef5 100644
--- a/test/tables.tm
+++ b/test/tables.tm
@@ -2,12 +2,16 @@ func main():
>> t := {"one":1, "two":2}
= {"one":1, "two":2}
- >> t:get("one", 999)
+ >> t:get("one")
+ = 1?
+ >> t:get("two")
+ = 2?
+ >> t:get("???")
+ = !Int
+ >> t:get("one")!
= 1
- >> t:get("two", 999)
- = 2
- >> t:get("???", 999)
- = 999
+ >> t:get("???"):or_else(-1)
+ = -1
t_str := ""
for k,v in t:
@@ -28,12 +32,12 @@ func main():
>> t2 := {"three":3; fallback=t}
= {"three":3; fallback={"one":1, "two":2}}
- >> t2:get("one", 999)
- = 1
- >> t2:get("three", 999)
- = 3
- >> t2:get("???", 999)
- = 999
+ >> t2:get("one")
+ = 1?
+ >> t2:get("three")
+ = 3?
+ >> t2:get("???")
+ = !Int
>> t2.length
= 1
@@ -60,11 +64,11 @@ func main():
do:
>> plain := {1:10, 2:20, 3:30}
- >> plain:get(2)
+ >> plain:get(2)!
= 20
- >> plain:get(2, -999)
+ >> plain:get(2)!
= 20
- >> plain:get(456, -999)
+ >> plain:get(456):or_else(-999)
= -999
>> plain:has(2)
= yes
@@ -74,6 +78,6 @@ func main():
>> fallback := {4:40; fallback=plain}
>> fallback:has(1)
= yes
- >> fallback:get(1, -999)
+ >> fallback:get(1):or_else(-999)
= 10
diff --git a/typecheck.c b/typecheck.c
index b70db784..192a335b 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -832,14 +832,8 @@ type_t *get_type(env_t *env, ast_t *ast)
auto table = Match(self_value_t, TableType);
if (streq(call->name, "bump")) return Type(VoidType);
else if (streq(call->name, "clear")) return Type(VoidType);
- else if (streq(call->name, "get")) return table->value_type;
- else if (streq(call->name, "get_or_null")) {
- if (table->value_type->tag != PointerType)
- code_err(ast, "The table method :get_or_null() is only supported for tables whose value type is a pointer, not %T",
- table->value_type);
- auto ptr = Match(table->value_type, PointerType);
- return Type(OptionalType, .type=Type(PointerType, .pointed=ptr->pointed, .is_stack=ptr->is_stack, .is_readonly=ptr->is_readonly));
- } else if (streq(call->name, "has")) return Type(BoolType);
+ else if (streq(call->name, "get")) return Type(OptionalType, .type=table->value_type);
+ else if (streq(call->name, "has")) return Type(BoolType);
else if (streq(call->name, "remove")) return Type(VoidType);
else if (streq(call->name, "set")) return Type(VoidType);
else if (streq(call->name, "sorted")) return self_value_t;