From 56a4d13975f443fc9e81f0acbb1a896122aa5857 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 12 Sep 2024 13:05:08 -0400 Subject: Automatic promotion to single-argument enum tags with a unique type --- compile.c | 33 ++++++++++++++++++++++++++++----- parse.c | 4 ++++ types.c | 22 ++++++++++++++++++++++ types.h | 1 + 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); -- cgit v1.2.3