aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-09-12 13:05:08 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-09-12 13:05:08 -0400
commit56a4d13975f443fc9e81f0acbb1a896122aa5857 (patch)
tree09ea8a4d14ff7e3d849f9ed3cb3b76b81094fff9
parent43f4f3610e5258afbfb9e313c989e1e52f477c38 (diff)
Automatic promotion to single-argument enum tags with a unique type
-rw-r--r--compile.c33
-rw-r--r--parse.c4
-rw-r--r--types.c22
-rw-r--r--types.h1
4 files changed, 55 insertions, 5 deletions
diff --git a/compile.c b/compile.c
index 277c0a92..0763c372 100644
--- a/compile.c
+++ b/compile.c
@@ -75,6 +75,17 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
if (actual->tag == IntType || actual->tag == NumType)
return true;
+ if (needed->tag == EnumType) {
+ const char *tag = enum_single_value_tag(needed, actual);
+ binding_t *b = get_binding(Match(needed, EnumType)->env, tag);
+ assert(b && b->type->tag == FunctionType);
+ // Single-value enum constructor:
+ if (!promote(env, code, actual, Match(b->type, FunctionType)->args->type))
+ return false;
+ *code = CORD_all(b->code, "(", *code, ")");
+ return true;
+ }
+
// Text to C String
if (actual->tag == TextType && !Match(actual, TextType)->lang && needed->tag == CStringType) {
*code = CORD_all("Text$as_c_string(", *code, ")");
@@ -1320,7 +1331,8 @@ CORD compile_statement(env_t *env, ast_t *ast)
body = WrapAST(body, If, .condition=comp->filter, .body=body);
ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body);
return compile_statement(env, loop);
- } else { // Array comprehension
+ } else { // Array or Set comprehension
+ // TODO: support set comprehensions
ast_t *body = WrapAST(comp->expr, MethodCall, .name="insert", .self=FakeAST(StackReference, FakeAST(Var, env->comprehension_var)),
.args=new(arg_ast_t, .value=comp->expr));
if (comp->filter)
@@ -1450,6 +1462,15 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool
return val;
}
+static CORD compile_to_type(env_t *env, ast_t *ast, type_t *t)
+{
+ CORD code = compile(env, ast);
+ type_t *actual = get_type(env, ast);
+ if (!promote(env, &code, actual, t))
+ code_err(ast, "I expected a %T here, but this is a %T", t, actual);
+ return code;
+}
+
env_t *with_enum_scope(env_t *env, type_t *t)
{
if (t->tag != EnumType) return env;
@@ -2184,8 +2205,9 @@ CORD compile(env_t *env, ast_t *ast)
{
type_t *item_type = Match(array_type, ArrayType)->item_type;
CORD code = CORD_all("TypedArrayN(", compile_type(item_type), CORD_asprintf(", %ld", n));
- for (ast_list_t *item = array->items; item; item = item->next)
- code = CORD_all(code, ", ", compile(env, item->ast));
+ for (ast_list_t *item = array->items; item; item = item->next) {
+ code = CORD_all(code, ", ", compile_to_type(env, item->ast, item_type));
+ }
return CORD_cat(code, ")");
}
@@ -2260,7 +2282,8 @@ CORD compile(env_t *env, ast_t *ast)
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
auto e = Match(entry->ast, TableEntry);
- code = CORD_all(code, ",\n\t{", compile(env, e->key), ", ", compile(env, e->value), "}");
+ code = CORD_all(code, ",\n\t{", compile_to_type(env, e->key, key_t), ", ",
+ compile_to_type(env, e->value, value_t), "}");
}
return CORD_cat(code, ")");
}
@@ -2318,7 +2341,7 @@ CORD compile(env_t *env, ast_t *ast)
CORD_appendf(&code, ", %zu", n);
for (ast_list_t *item = set->items; item; item = item->next) {
- code = CORD_all(code, ",\n\t", compile(env, item->ast));
+ code = CORD_all(code, ", ", compile_to_type(env, item->ast, item_type));
}
return CORD_cat(code, ")");
}
diff --git a/parse.c b/parse.c
index 1aff4bc9..62eb7bdf 100644
--- a/parse.c
+++ b/parse.c
@@ -733,6 +733,8 @@ PARSER(parse_array) {
whitespace(&pos);
item_type = expect(ctx, pos-1, &pos, parse_type, "I couldn't parse a type for this array");
whitespace(&pos);
+ match(&pos, ",");
+ whitespace(&pos);
}
for (;;) {
@@ -843,6 +845,8 @@ PARSER(parse_set) {
if (match(&pos, ":"))
return NULL;
whitespace(&pos);
+ match(&pos, ",");
+ whitespace(&pos);
}
for (;;) {
diff --git a/types.c b/types.c
index d7b517d3..d70346a2 100644
--- a/types.c
+++ b/types.c
@@ -276,6 +276,25 @@ PUREFUNC bool has_stack_memory(type_t *t)
}
}
+PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t)
+{
+ const char *found = NULL;
+ for (tag_t *tag = Match(enum_type, EnumType)->tags; tag; tag = tag->next) {
+ if (tag->type->tag != StructType) continue;
+ auto s = Match(tag->type, StructType);
+ if (!s->fields || s->fields->next || !s->fields->type)
+ continue;
+
+ if (can_promote(t, s->fields->type)) {
+ if (found) // Ambiguous case, multiple matches
+ return NULL;
+ found = tag->name;
+ // Continue searching to check for ambiguous cases
+ }
+ }
+ return found;
+}
+
PUREFUNC bool can_promote(type_t *actual, type_t *needed)
{
// No promotion necessary:
@@ -296,6 +315,9 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed)
return cmp == NUM_PRECISION_EQUAL || cmp == NUM_PRECISION_LESS;
}
+ if (needed->tag == EnumType)
+ return (enum_single_value_tag(needed, actual) != NULL);
+
// Text to C String
if (actual->tag == TextType && !Match(actual, TextType)->lang && needed->tag == CStringType)
return true;
diff --git a/types.h b/types.h
index 82efab37..6dedeaf8 100644
--- a/types.h
+++ b/types.h
@@ -143,6 +143,7 @@ PUREFUNC bool has_heap_memory(type_t *t);
PUREFUNC bool has_stack_memory(type_t *t);
PUREFUNC bool can_send_over_channel(type_t *t);
PUREFUNC bool can_promote(type_t *actual, type_t *needed);
+PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t);
PUREFUNC bool is_int_type(type_t *t);
PUREFUNC bool is_numeric_type(type_t *t);
PUREFUNC bool supports_optionals(type_t *t);