aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ast.h4
-rw-r--r--src/compile/expressions.c2
-rw-r--r--src/compile/optionals.c2
-rw-r--r--src/compile/tables.c12
-rw-r--r--src/environment.c6
-rw-r--r--src/environment.h1
-rw-r--r--src/formatter/formatter.c6
-rw-r--r--src/formatter/types.c3
-rw-r--r--src/parse/containers.c6
-rw-r--r--src/parse/types.c2
-rw-r--r--src/stdlib/tables.c7
-rw-r--r--src/typecheck.c14
-rw-r--r--src/types.c4
-rw-r--r--test/tables.tm10
14 files changed, 51 insertions, 28 deletions
diff --git a/src/ast.h b/src/ast.h
index 93723dbb..2fec9c57 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -116,7 +116,7 @@ struct type_ast_s {
type_ast_t *item;
} ListTypeAST;
struct {
- type_ast_t *key, *value;
+ type_ast_t *key, *value /* value may be NULL */;
ast_t *default_value;
} TableTypeAST;
struct {
@@ -339,7 +339,7 @@ struct ast_s {
ast_list_t *entries;
} Table;
struct {
- ast_t *key, *value;
+ ast_t *key, *value /* value may be NULL */;
} TableEntry;
struct {
ast_list_t *vars;
diff --git a/src/compile/expressions.c b/src/compile/expressions.c
index fcd1d136..108bda80 100644
--- a/src/compile/expressions.c
+++ b/src/compile/expressions.c
@@ -27,7 +27,7 @@ Text_t compile_empty(type_t *t) {
if (t->tag == OptionalType) return compile_none(t);
if (t == PATH_TYPE) return Text("NONE_PATH");
- else if (t == PATH_TYPE_TYPE) return Text("((OptionalPathType_t){})");
+ else if (t == PATH_TYPE_TYPE) return Text("PATHTYPE_ABSOLUTE");
switch (t->tag) {
case BigIntType: return Text("I(0)");
diff --git a/src/compile/optionals.c b/src/compile/optionals.c
index c8a7276c..b4930d02 100644
--- a/src/compile/optionals.c
+++ b/src/compile/optionals.c
@@ -53,7 +53,7 @@ Text_t compile_none(type_t *t) {
if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type");
if (t == PATH_TYPE) return Text("NONE_PATH");
- else if (t == PATH_TYPE_TYPE) return Text("((OptionalPathType_t){.type = PATHTYPE_NONE})");
+ else if (t == PATH_TYPE_TYPE) return Text("PATHTYPE_NONE");
switch (t->tag) {
case BigIntType: return Text("NONE_INT");
diff --git a/src/compile/tables.c b/src/compile/tables.c
index 2b6d3538..958e5af5 100644
--- a/src/compile/tables.c
+++ b/src/compile/tables.c
@@ -10,8 +10,10 @@
static ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) {
DeclareMatch(e, entry, TableEntry);
- return WrapAST(entry, MethodCall, .name = "set", .self = subject,
- .args = new (arg_ast_t, .value = e->key, .next = new (arg_ast_t, .value = e->value)));
+ return WrapAST(
+ entry, MethodCall, .name = "set", .self = subject,
+ .args = new (arg_ast_t, .value = e->key,
+ .next = new (arg_ast_t, .value = e->value ? e->value : WrapAST(entry, Var, .name = "EMPTY"))));
}
Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) {
@@ -47,8 +49,10 @@ Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) {
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
DeclareMatch(e, entry->ast, TableEntry);
- code = Texts(code, ",\n\t{", compile_to_type(key_scope, e->key, key_t), ", ",
- compile_to_type(value_scope, e->value, value_t), "}");
+ code = Texts(
+ code, ",\n\t{", compile_to_type(key_scope, e->key, key_t), ", ",
+ compile_to_type(value_scope, e->value ? e->value : WrapAST(entry->ast, Var, .name = "EMPTY"), value_t),
+ "}");
}
return Texts(code, ")");
}
diff --git a/src/environment.c b/src/environment.c
index 7d0046de..a3cb5ba8 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -18,6 +18,8 @@ public
type_t *PATH_TYPE = NULL;
public
type_t *PATH_TYPE_TYPE = NULL;
+public
+type_t *EMPTY_TYPE = NULL;
static type_t *declare_type(env_t *env, const char *def_str) {
ast_t *ast = parse_file_str(def_str);
@@ -68,7 +70,7 @@ env_t *global_env(bool source_mapping) {
PATH_TYPE_TYPE = declare_type(env, "enum PathType(Relative, Absolute, Home)");
PATH_TYPE = declare_type(env, "struct Path(type:PathType, components:[Text])");
- type_t *empty_type = declare_type(env, "struct Empty()");
+ EMPTY_TYPE = declare_type(env, "struct Empty()");
typedef struct {
const char *name, *code, *type_str;
@@ -88,7 +90,7 @@ env_t *global_env(bool source_mapping) {
MAKE_TYPE("Void", Type(VoidType), Text("void"), Text("Void$info")),
MAKE_TYPE("Abort", Type(AbortType), Text("void"), Text("Abort$info")),
MAKE_TYPE("Memory", Type(MemoryType), Text("void"), Text("Memory$info")),
- MAKE_TYPE("Empty", empty_type, Text("Empty$$type"), Text("Empty$$info")),
+ MAKE_TYPE("Empty", EMPTY_TYPE, Text("Empty$$type"), Text("Empty$$info")),
MAKE_TYPE( //
"Bool", Type(BoolType), Text("Bool_t"), Text("Bool$info"),
{"parse", "Bool$parse", "func(text:Text, remainder:&Text? = none -> Bool?)"}),
diff --git a/src/environment.h b/src/environment.h
index c726508d..6389cc7a 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -87,3 +87,4 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name);
extern type_t *TEXT_TYPE;
extern type_t *PATH_TYPE;
extern type_t *PATH_TYPE_TYPE;
+extern type_t *EMPTY_TYPE;
diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c
index 55fff920..5cb5d3cc 100644
--- a/src/formatter/formatter.c
+++ b/src/formatter/formatter.c
@@ -223,7 +223,8 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) {
}
/*inline*/ case TableEntry: {
DeclareMatch(entry, ast, TableEntry);
- return Texts(fmt_inline(entry->key, comments), "=", fmt_inline(entry->value, comments));
+ if (entry->value) return Texts(fmt_inline(entry->key, comments), ": ", fmt_inline(entry->value, comments));
+ else return Texts(fmt_inline(entry->key, comments));
}
/*inline*/ case Declare: {
DeclareMatch(decl, ast, Declare);
@@ -629,7 +630,8 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) {
/*multiline*/ case TableEntry: {
if (inlined_fits) return inlined;
DeclareMatch(entry, ast, TableEntry);
- return Texts(fmt(entry->key, comments, indent), ": ", fmt(entry->value, comments, indent));
+ if (entry->value) return Texts(fmt(entry->key, comments, indent), ": ", fmt(entry->value, comments, indent));
+ else return Texts(fmt(entry->key, comments, indent));
}
/*multiline*/ case Declare: {
if (inlined_fits) return inlined;
diff --git a/src/formatter/types.c b/src/formatter/types.c
index 670405f6..929c8ff9 100644
--- a/src/formatter/types.c
+++ b/src/formatter/types.c
@@ -19,7 +19,8 @@ Text_t format_type(type_ast_t *type) {
}
case TableTypeAST: {
DeclareMatch(table, type, TableTypeAST);
- Text_t code = Texts("{", format_type(table->key), ":", format_type(table->value));
+ Text_t code = Texts("{", format_type(table->key));
+ if (table->value != NULL) code = Texts(code, ":", format_type(table->value));
if (table->default_value) {
OptionalText_t val = format_inline_code(table->default_value, (Table_t){});
assert(val.tag != TEXT_NONE);
diff --git a/src/parse/containers.c b/src/parse/containers.c
index 55ff336e..8f9922f3 100644
--- a/src/parse/containers.c
+++ b/src/parse/containers.c
@@ -2,7 +2,6 @@
#include <stdarg.h>
#include <stdbool.h>
-#include <string.h>
#include "../ast.h"
#include "../stdlib/util.h"
@@ -50,8 +49,9 @@ ast_t *parse_table(parse_ctx_t *ctx, const char *pos) {
ast_t *key = optional(ctx, &pos, parse_extended_expr);
if (!key) break;
whitespace(ctx, &pos);
- if (!match(&pos, ":")) return NULL;
- ast_t *value = expect(ctx, pos - 1, &pos, parse_expr, "I couldn't parse the value for this table entry");
+ ast_t *value = NULL;
+ if (match(&pos, ":"))
+ value = expect(ctx, pos - 1, &pos, parse_expr, "I couldn't parse the value for this table entry");
ast_t *entry = NewAST(ctx->file, entry_start, pos, TableEntry, .key = key, .value = value);
ast_t *suffixed = parse_comprehension_suffix(ctx, entry);
while (suffixed) {
diff --git a/src/parse/types.c b/src/parse/types.c
index 192103dc..8ecc602e 100644
--- a/src/parse/types.c
+++ b/src/parse/types.c
@@ -27,8 +27,6 @@ type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) {
type_ast_t *value_type = NULL;
if (match(&pos, ":")) {
value_type = expect(ctx, start, &pos, parse_type, "I couldn't parse the rest of this table type");
- } else {
- return NULL;
}
spaces(&pos);
ast_t *default_value = NULL;
diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c
index c093e4c2..fdd85b56 100644
--- a/src/stdlib/tables.c
+++ b/src/stdlib/tables.c
@@ -534,8 +534,9 @@ Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *type) {
__typeof(type->TableInfo) table = type->TableInfo;
if (!t) {
- return Text$concat(Text("{"), generic_as_text(NULL, false, table.key), Text(":"),
- generic_as_text(NULL, false, table.value), Text("}"));
+ return table.value->size > 0 ? Texts("{", generic_as_text(NULL, false, table.key), ":",
+ generic_as_text(NULL, false, table.value), "}")
+ : Texts("{", generic_as_text(NULL, false, table.key), "}");
}
int64_t val_off = (int64_t)value_offset(type);
@@ -544,7 +545,7 @@ Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *type) {
if (i > 0) text = Text$concat(text, Text(", "));
void *entry = GET_ENTRY(*t, i);
text = Text$concat(text, generic_as_text(entry, colorize, table.key));
- if (table.value != &Void$info)
+ if (table.value->size > 0)
text = Text$concat(text, Text(": "), generic_as_text(entry + val_off, colorize, table.value));
}
diff --git a/src/typecheck.c b/src/typecheck.c
index 429a162f..9342f069 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -73,8 +73,9 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) {
if (has_stack_memory(key_type))
code_err(key_type_ast, "Tables can't have stack references because the list may outlive the stack frame.");
- type_t *val_type = parse_type_ast(env, table_type->value);
- if (!val_type) code_err(table_type->value, "I can't figure out what type this is.");
+ type_t *val_type = table_type->value ? parse_type_ast(env, table_type->value) : EMPTY_TYPE;
+ if (!val_type) code_err(ast, "I can't figure out what the value type for this entry is.");
+
if (has_stack_memory(val_type))
code_err(table_type->value,
"Tables can't have stack references because the list may outlive the stack frame.");
@@ -814,7 +815,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
DeclareMatch(e, entry_ast, TableEntry);
type_t *key_t = get_type(scope, e->key);
- type_t *value_t = get_type(scope, e->value);
+ type_t *value_t = e->value ? get_type(scope, e->value) : EMPTY_TYPE;
type_t *key_merged = key_type ? type_or_type(key_type, key_t) : key_t;
if (!key_merged) ambiguous_key_type = true;
@@ -846,8 +847,8 @@ type_t *get_type(env_t *env, ast_t *ast) {
return get_type(scope, comp->expr);
} else if (comp->expr->tag == TableEntry) {
DeclareMatch(e, comp->expr, TableEntry);
- return Type(TableType, .key_type = get_type(scope, e->key), .value_type = get_type(scope, e->value),
- .env = env);
+ return Type(TableType, .key_type = get_type(scope, e->key),
+ .value_type = e->value ? get_type(scope, e->value) : EMPTY_TYPE, .env = env);
} else {
return Type(ListType, .item_type = get_type(scope, comp->expr));
}
@@ -1711,7 +1712,8 @@ PUREFUNC bool can_compile_to_type(env_t *env, ast_t *ast, type_t *needed) {
for (ast_list_t *entry = Match(ast, Table)->entries; entry; entry = entry->next) {
if (entry->ast->tag != TableEntry) continue; // TODO: fix this
DeclareMatch(e, entry->ast, TableEntry);
- if (!can_compile_to_type(env, e->key, key_type) || !can_compile_to_type(env, e->value, value_type))
+ if (!can_compile_to_type(env, e->key, key_type)
+ || !(e->value ? can_compile_to_type(env, e->value, value_type) : type_eq(value_type, EMPTY_TYPE)))
return false;
}
return true;
diff --git a/src/types.c b/src/types.c
index bf986c80..d08755a2 100644
--- a/src/types.c
+++ b/src/types.c
@@ -37,7 +37,9 @@ Text_t type_to_text(type_t *t) {
}
case TableType: {
DeclareMatch(table, t, TableType);
- return Texts("{", type_to_text(table->key_type), "=", type_to_text(table->value_type), "}");
+ return (table->value_type && table->value_type != EMPTY_TYPE)
+ ? Texts("{", type_to_text(table->key_type), ":", type_to_text(table->value_type), "}")
+ : Texts("{", type_to_text(table->key_type), "}");
}
case ClosureType: {
return type_to_text(Match(t, ClosureType)->fn);
diff --git a/test/tables.tm b/test/tables.tm
index 2eb1409e..68b77baf 100644
--- a/test/tables.tm
+++ b/test/tables.tm
@@ -87,3 +87,13 @@ func main()
assert a.intersection(b) == {"B":2}
assert a.difference(b) == {"A":1, "D":40}
assert a.without(b) == {"A":1, "C":3}
+
+ do
+ # Set operations with sets
+ a := {"A", "B", "C"}
+ b := {"B", "C", "D"}
+ assert a.with(b) == {"A", "B", "C", "D"}
+ assert a.with(b) == a ++ b
+ assert a.intersection(b) == {"B", "C"}
+ assert a.difference(b) == {"A", "D"}
+ assert a.without(b) == {"A"}