diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-03-14 13:28:30 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-03-14 13:28:30 -0400 |
| commit | ecf425fb9aa12a0f3dd08d23bcd74919fb11b362 (patch) | |
| tree | 7ea113b9e33623c8be4b54196df4d438d360c285 | |
| parent | d5d3f564bbc716d2a9f3d534c97d3baff77dda59 (diff) | |
Rearrange expression/statement compilation code
| -rw-r--r-- | ast.c | 4 | ||||
| -rw-r--r-- | compile.c | 989 | ||||
| -rw-r--r-- | parse.c | 2 |
3 files changed, 517 insertions, 478 deletions
@@ -128,8 +128,8 @@ CORD ast_to_cord(ast_t *ast) T(If, "(condition=%r, body=%r, else=%r)", ast_to_cord(data.condition), ast_to_cord(data.body), ast_to_cord(data.else_body)) T(When, "(subject=%r, clauses=%r, else=%r)", ast_to_cord(data.subject), when_clauses_to_cord(data.clauses), ast_to_cord(data.else_body)) T(Reduction, "(iter=%r, combination=%r, fallback=%r)", ast_to_cord(data.iter), ast_to_cord(data.combination), ast_to_cord(data.fallback)) - T(Skip, "(%s)", data.target) - T(Stop, "(%s)", data.target) + T(Skip, "(%s)", data.target ? data.target : "NULL") + T(Stop, "(%s)", data.target ? data.target : "NULL") T(Pass, "") T(Return, "(%r)", ast_to_cord(data.value)) T(Extern, "(name=%s, type=%r)", data.name, type_ast_to_cord(data.type)) @@ -105,21 +105,499 @@ CORD compile_type(type_t *t) } } +static void check_assignable(env_t *env, ast_t *ast) +{ + if (!can_be_mutated(env, ast)) { + if (ast->tag == Index || ast->tag == FieldAccess) { + ast_t *subject = ast->tag == Index ? Match(ast, Index)->indexed : Match(ast, FieldAccess)->fielded; + code_err(subject, "This is a readonly pointer, which can't be assigned to"); + } else { + code_err(ast, "This is a value of type %T and can't be assigned to", get_type(env, ast)); + } + } +} + CORD compile_statement(env_t *env, ast_t *ast) { - CORD stmt; switch (ast->tag) { - case If: case When: case For: case While: case FunctionDef: case Return: case StructDef: case EnumDef: case LangDef: - case Declare: case Assign: case UpdateAssign: case DocTest: case Block: - stmt = compile(env, ast); - break; + case When: { + auto when = Match(ast, When); + type_t *subject_t = get_type(env, when->subject); + auto enum_t = Match(subject_t, EnumType); + CORD code = CORD_all("{ ", compile_type(subject_t), " $subject = ", compile(env, when->subject), ";\n" + "switch ($subject.$tag) {"); + type_t *result_t = get_type(env, ast); + (void)result_t; + for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { + const char *clause_tag_name = Match(clause->tag_name, Var)->name; + code = CORD_all(code, "case $tag$", enum_t->name, "$", clause_tag_name, ": {\n"); + type_t *tag_type = NULL; + for (tag_t *tag = enum_t->tags; tag; tag = tag->next) { + if (streq(tag->name, clause_tag_name)) { + tag_type = tag->type; + break; + } + } + assert(tag_type); + env_t *scope = env; + if (clause->var) { + code = CORD_all(code, compile_type(tag_type), " ", compile(env, clause->var), " = $subject.", clause_tag_name, ";\n"); + scope = fresh_scope(env); + set_binding(scope, Match(clause->var, Var)->name, new(binding_t, .type=tag_type)); + } + code = CORD_all(code, compile_statement(scope, clause->body), "\nbreak;\n}\n"); + } + if (when->else_body) { + code = CORD_all(code, "default: {\n", compile_statement(env, when->else_body), "\nbreak;\n}"); + } + code = CORD_all(code, "\n}\n}"); + return code; + } + case DocTest: { + auto test = Match(ast, DocTest); + type_t *expr_t = get_type(env, test->expr); + if (!expr_t) + code_err(test->expr, "I couldn't figure out the type of this expression"); + + CORD output = NULL; + if (test->output) { + const uint8_t *raw = (const uint8_t*)CORD_to_const_char_star(test->output); + uint8_t buf[128] = {0}; + size_t norm_len = sizeof(buf); + uint8_t *norm = u8_normalize(UNINORM_NFD, (uint8_t*)raw, strlen((char*)raw)+1, buf, &norm_len); + assert(norm[norm_len-1] == 0); + output = CORD_from_char_star((char*)norm); + if (norm && norm != buf) free(norm); + } + + if (test->expr->tag == Declare) { + auto decl = Match(test->expr, Declare); + return CORD_asprintf( + "%r\n" + "$test(&%r, %r, %r, %r, %ld, %ld);", + compile_statement(env, test->expr), + compile(env, decl->var), + compile_type_info(env, get_type(env, decl->value)), + compile(env, WrapAST(test->expr, TextLiteral, .cord=output)), + compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), + (int64_t)(test->expr->start - test->expr->file->text), + (int64_t)(test->expr->end - test->expr->file->text)); + } else if (test->expr->tag == Assign) { + auto assign = Match(test->expr, Assign); + if (!assign->targets->next && assign->targets->ast->tag == Var) { + // Common case: assigning to one variable: + check_assignable(env, assign->targets->ast); + CORD var = compile(env, assign->targets->ast); + CORD code = CORD_all(var, " = ", compile(env, assign->values->ast), ";"); + CORD_appendf(&code, "$test(&%r, %r, %r, %r, %ld, %ld);", + var, compile_type_info(env, get_type(env, assign->targets->ast)), + compile(env, WrapAST(test->expr, TextLiteral, .cord=test->output)), + compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), + (int64_t)(test->expr->start - test->expr->file->text), + (int64_t)(test->expr->end - test->expr->file->text)); + return code; + } else { + // Multi-assign or assignment to potentially non-idempotent targets + if (test->output && assign->targets->next) + code_err(ast, "Sorry, but doctesting with '=' is not supported for multi-assignments"); + + CORD code = "{ // Assignment\n"; + int64_t i = 1; + for (ast_list_t *value = assign->values; value; value = value->next) + CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(get_type(env, value->ast)), i++, compile(env, value->ast)); + i = 1; + for (ast_list_t *target = assign->targets; target; target = target->next) { + check_assignable(env, target->ast); + CORD_appendf(&code, "%r = $%ld;\n", compile(env, target->ast), i++); + } + + CORD_appendf(&code, "$test(&$1, %r, %r, %r, %ld, %ld);", + compile_type_info(env, get_type(env, assign->targets->ast)), + compile(env, WrapAST(test->expr, TextLiteral, .cord=test->output)), + compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), + (int64_t)(test->expr->start - test->expr->file->text), + (int64_t)(test->expr->end - test->expr->file->text)); + return CORD_cat(code, "\n}"); + } + } else if (expr_t->tag == VoidType || expr_t->tag == AbortType) { + return CORD_asprintf( + "%r;\n" + "$test(NULL, NULL, NULL, %r, %ld, %ld);", + compile_statement(env, test->expr), + compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), + (int64_t)(test->expr->start - test->expr->file->text), + (int64_t)(test->expr->end - test->expr->file->text)); + } else { + return CORD_asprintf( + "{ // Test:\n%r $expr = %r;\n" + "$test(&$expr, %r, %r, %r, %ld, %ld);\n" + "}", + compile_type(expr_t), + compile(env, test->expr), + compile_type_info(env, expr_t), + compile(env, WrapAST(test->expr, TextLiteral, .cord=output)), + compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), + (int64_t)(test->expr->start - test->expr->file->text), + (int64_t)(test->expr->end - test->expr->file->text)); + } + } + case Declare: { + auto decl = Match(ast, Declare); + type_t *t = get_type(env, decl->value); + if (t->tag == AbortType || t->tag == VoidType) + code_err(ast, "You can't declare a variable with a %T value", t); + return CORD_all(compile_declaration(t, Match(decl->var, Var)->name), " = ", compile(env, decl->value), ";"); + } + case Assign: { + auto assign = Match(ast, Assign); + // Single assignment: + if (assign->targets && !assign->targets->next) + return CORD_asprintf("%r = %r;", compile(env, assign->targets->ast), compile(env, assign->values->ast)); + + CORD code = "{ // Assignment\n"; + int64_t i = 1; + for (ast_list_t *value = assign->values; value; value = value->next) + CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(get_type(env, value->ast)), i++, compile(env, value->ast)); + i = 1; + for (ast_list_t *target = assign->targets; target; target = target->next) { + check_assignable(env, target->ast); + CORD_appendf(&code, "%r = $%ld;\n", compile(env, target->ast), i++); + } + return CORD_cat(code, "\n}"); + } + case UpdateAssign: { + auto update = Match(ast, UpdateAssign); + check_assignable(env, update->lhs); + CORD lhs = compile(env, update->lhs); + CORD rhs = compile(env, update->rhs); + + type_t *lhs_t = get_type(env, update->lhs); + type_t *rhs_t = get_type(env, update->rhs); + type_t *operand_t; + if (promote(env, &rhs, rhs_t, lhs_t)) + operand_t = lhs_t; + else if (promote(env, &lhs, lhs_t, rhs_t)) + operand_t = rhs_t; + else if (lhs_t->tag == ArrayType && promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) + operand_t = lhs_t; + else + code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t); + + switch (update->op) { + case BINOP_MULT: return CORD_asprintf("%r *= %r;", lhs, rhs); + case BINOP_DIVIDE: return CORD_asprintf("%r /= %r;", lhs, rhs); + case BINOP_MOD: return CORD_asprintf("%r = mod(%r, %r);", lhs, lhs, rhs); + case BINOP_MOD1: return CORD_asprintf("%r = mod1(%r, %r);", lhs, lhs, rhs); + case BINOP_PLUS: return CORD_asprintf("%r += %r;", lhs, rhs); + case BINOP_MINUS: return CORD_asprintf("%r -= %r;", lhs, rhs); + case BINOP_POWER: { + if (lhs_t->tag != NumType) + code_err(ast, "'^=' is only supported for Num types"); + if (lhs_t->tag == NumType && Match(lhs_t, NumType)->bits == 32) + return CORD_all(lhs, " = powf(", lhs, ", ", rhs, ")"); + else + return CORD_all(lhs, " = pow(", lhs, ", ", rhs, ")"); + } + case BINOP_LSHIFT: return CORD_asprintf("%r <<= %r;", lhs, rhs); + case BINOP_RSHIFT: return CORD_asprintf("%r >>= %r;", lhs, rhs); + case BINOP_AND: { + if (operand_t->tag == BoolType) + return CORD_asprintf("if (%r) %r = %r;", lhs, lhs, rhs); + else if (operand_t->tag == IntType) + return CORD_asprintf("%r &= %r;", lhs, rhs); + else + code_err(ast, "'or=' is not implemented for %T types", operand_t); + } + case BINOP_OR: { + if (operand_t->tag == BoolType) + return CORD_asprintf("if (!(%r)) %r = %r;", lhs, lhs, rhs); + else if (operand_t->tag == IntType) + return CORD_asprintf("%r |= %r;", lhs, rhs); + else + code_err(ast, "'or=' is not implemented for %T types", operand_t); + } + case BINOP_XOR: return CORD_asprintf("%r ^= %r;", lhs, rhs); + case BINOP_CONCAT: { + if (operand_t->tag == TextType) { + return CORD_asprintf("%r = CORD_cat(%r, %r);", lhs, lhs, rhs); + } else if (operand_t->tag == ArrayType) { + if (promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) { + // arr ++= item + if (update->lhs->tag == Var) + return CORD_all("Array__insert(&", lhs, ", $stack(", rhs, "), 0, ", compile_type_info(env, operand_t), ")"); + else + return CORD_all(lhs, "Array__concat(", lhs, ", $Array(", rhs, "), ", compile_type_info(env, operand_t), ")"); + } else { + // arr ++= [...] + if (update->lhs->tag == Var) + return CORD_all("Array__insert_all(&", lhs, ", ", rhs, ", 0, ", compile_type_info(env, operand_t), ")"); + else + return CORD_all(lhs, "Array__concat(", lhs, ", ", rhs, ", ", compile_type_info(env, operand_t), ")"); + } + } else { + code_err(ast, "'++=' is not implemented for %T types", operand_t); + } + } + default: code_err(ast, "Update assignments are not implemented for this operation"); + } + } + case StructDef: { + compile_struct_def(env, ast); + return CORD_EMPTY; + } + case EnumDef: { + compile_enum_def(env, ast); + return CORD_EMPTY; + } + case LangDef: { + // TODO: implement + auto def = Match(ast, LangDef); + CORD_appendf(&env->code->typedefs, "typedef CORD %s_t;\n", def->name); + CORD_appendf(&env->code->typedefs, "extern const TypeInfo %s;\n", def->name); + CORD_appendf(&env->code->typeinfos, "public const TypeInfo %s = {%zu, %zu, {.tag=TextInfo, .TextInfo={%s}}};\n", + def->name, sizeof(CORD), __alignof__(CORD), + Text__quoted(def->name, false)); + compile_namespace(env, def->name, def->namespace); + return CORD_EMPTY; + } + case FunctionDef: { + auto fndef = Match(ast, FunctionDef); + CORD name = compile(env, fndef->name); + type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType); + + CORD arg_signature = "("; + for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) { + type_t *arg_type = get_arg_ast_type(env, arg); + arg_signature = CORD_cat(arg_signature, compile_declaration(arg_type, arg->name)); + if (arg->next) arg_signature = CORD_cat(arg_signature, ", "); + } + arg_signature = CORD_cat(arg_signature, ")"); + + CORD ret_type_code = compile_type(ret_t); + + if (fndef->is_private) + env->code->staticdefs = CORD_all(env->code->staticdefs, "static ", ret_type_code, " ", name, arg_signature, ";\n"); + else + env->code->fndefs = CORD_all(env->code->fndefs, ret_type_code, " ", name, arg_signature, ";\n"); + + CORD code; + if (fndef->cache) { + code = CORD_all("static ", ret_type_code, " ", name, "$uncached", arg_signature); + } else { + code = CORD_all(ret_type_code, " ", name, arg_signature); + if (fndef->is_inline) + code = CORD_cat("inline ", code); + if (!fndef->is_private) + code = CORD_cat("public ", code); + } + + env_t *body_scope = global_scope(env); + for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) { + type_t *arg_type = get_arg_ast_type(env, arg); + set_binding(body_scope, arg->name, new(binding_t, .type=arg_type, .code=arg->name)); + } + + fn_context_t fn_ctx = (fn_context_t){ + .return_type=ret_t, + .closure_scope=NULL, + .closed_vars=NULL, + }; + body_scope->fn_ctx = &fn_ctx; + + CORD body = compile_statement(body_scope, fndef->body); + if (CORD_fetch(body, 0) != '{') + body = CORD_asprintf("{\n%r\n}", body); + env->code->funcs = CORD_all(env->code->funcs, code, " ", body, "\n"); + + if (fndef->cache && fndef->cache->tag == Int && Match(fndef->cache, Int)->i > 0) { + const char *arg_type_name = heap_strf("%s$args", CORD_to_const_char_star(name)); + ast_t *args_def = FakeAST(StructDef, .name=arg_type_name, .fields=fndef->args); + bind_statement(env, args_def); + (void)compile_statement(env, args_def); + type_t *args_t = Table_str_get(*env->types, arg_type_name); + assert(args_t); + + CORD all_args = CORD_EMPTY; + for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) + all_args = CORD_all(all_args, arg->name, arg->next ? ", " : CORD_EMPTY); + + CORD pop_code = CORD_EMPTY; + if (fndef->cache->tag == Int && Match(fndef->cache, Int)->i < INT64_MAX) { + pop_code = CORD_all("if (Table_length($cache) > ", compile(body_scope, fndef->cache), + ") Table_remove(&$cache, NULL, $table_info);\n"); + } + + CORD wrapper = CORD_all( + fndef->is_private ? CORD_EMPTY : "public ", ret_type_code, " ", name, arg_signature, "{\n" + "static table_t $cache = {};\n", + compile_type(args_t), " $args = {", all_args, "};\n" + "static const TypeInfo *$table_type = $TableInfo(", compile_type_info(env, args_t), ", ", compile_type_info(env, ret_t), ");\n", + ret_type_code, "*$cached = Table_get_raw($cache, &$args, $table_type);\n" + "if ($cached) return *$cached;\n", + ret_type_code, " $ret = ", name, "$uncached(", all_args, ");\n", + pop_code, + "Table_set(&$cache, &$args, &$ret, $table_type);\n" + "return $ret;\n" + "}\n"); + env->code->funcs = CORD_cat(env->code->funcs, wrapper); + } + + return CORD_EMPTY; + } + case Skip: { + if (Match(ast, Skip)->target) code_err(ast, "Named skips not yet implemented"); + return "continue"; + } + case Stop: { + if (Match(ast, Stop)->target) code_err(ast, "Named stops not yet implemented"); + return "break"; + } + case Pass: return ";"; + case Return: { + if (!env->fn_ctx) code_err(ast, "This return statement is not inside any function"); + auto ret = Match(ast, Return)->value; + assert(env->fn_ctx->return_type); + if (ret) { + type_t *ret_t = get_type(env, ret); + CORD value = compile(env, ret); + if (!promote(env, &value, ret_t, env->fn_ctx->return_type)) + code_err(ast, "This function expects a return value of type %T, but this return has type %T", + env->fn_ctx->return_type, ret_t); + return CORD_all("return ", value, ";"); + } else { + if (env->fn_ctx->return_type->tag != VoidType) + code_err(ast, "This function expects a return value of type %T", env->fn_ctx->return_type->tag); + return "return;"; + } + } + case While: { + auto while_ = Match(ast, While); + return CORD_asprintf("while (%r) %r", compile(env, while_->condition), compile_statement(env, while_->body)); + } + case For: { + auto for_ = Match(ast, For); + type_t *iter_t = get_type(env, for_->iter); + env_t *scope = for_scope(env, ast); + switch (iter_t->tag) { + case ArrayType: { + type_t *item_t = Match(iter_t, ArrayType)->item_type; + CORD index = for_->index ? compile(env, for_->index) : "$i"; + CORD value = compile(env, for_->value); + return CORD_all("$ARRAY_FOREACH(", compile(env, for_->iter), ", ", index, ", ", compile_type(item_t), ", ", value, ", ", + compile_statement(scope, for_->body), ", ", for_->empty ? compile_statement(env, for_->empty) : "{}", ")"); + } + case TableType: { + type_t *key_t = Match(iter_t, TableType)->key_type; + type_t *value_t = Match(iter_t, TableType)->value_type; + if (for_->index) { + CORD key = compile(env, for_->index); + CORD value = compile(env, for_->value); + + size_t value_offset = type_size(key_t); + if (type_align(value_t) > 1 && value_offset % type_align(value_t)) + value_offset += type_align(value_t) - (value_offset % type_align(value_t)); // padding + return CORD_all("$TABLE_FOREACH(", compile(env, for_->iter), ", ", compile_type(key_t), ", ", key, ", ", + compile_type(value_t), ", ", value, ", ", heap_strf("%zu", value_offset), + ", ", compile_statement(scope, for_->body), ", ", for_->empty ? compile_statement(env, for_->empty) : "{}", ")"); + } else { + CORD key = compile(env, for_->value); + return CORD_all("$ARRAY_FOREACH((", compile(env, for_->iter), ").entries, $i, ", compile_type(key_t), ", ", key, ", ", + compile_statement(scope, for_->body), ", ", for_->empty ? compile_statement(env, for_->empty) : "{}", ")"); + } + } + case IntType: { + CORD value = compile(env, for_->value); + CORD n = compile(env, for_->iter); + CORD index = for_->index ? compile(env, for_->index) : CORD_EMPTY; + if (for_->empty && index) { + return CORD_all( + "{\n" + "int64_t $n = ", n, ";\n" + "if ($n > 0) {\n" + "for (int64_t ", index, " = 1, ", value, "; (", value, "=", index,") <= $n; ++", index, ")\n" + "\t", compile_statement(scope, for_->body), "\n" + "}\n else ", compile_statement(env, for_->empty), + "\n}"); + } else if (for_->empty) { + return CORD_all( + "{\n" + "int64_t $n = ", n, ";\n" + "if ($n > 0) {\n" + "for (int64_t ", value, " = 1; ", value, " <= $n; ++", value, ")\n" + "\t", compile_statement(scope, for_->body), "\n" + "}\n else ", compile_statement(env, for_->empty), + "\n}"); + } else if (index) { + return CORD_all( + "for (int64_t ", value, ", ", index, " = 1, $n = ", n, "; (", value, "=", index,") <= $n; ++", value, ")\n" + "\t", compile_statement(scope, for_->body), "\n"); + } else { + return CORD_all( + "for (int64_t ", value, " = 1, $n = ", compile(env, for_->iter), "; ", value, " <= $n; ++", value, ")\n" + "\t", compile_statement(scope, for_->body), "\n"); + } + } + default: code_err(for_->iter, "Iteration is not implemented for type: %T", iter_t); + } + } + case If: { + auto if_ = Match(ast, If); + if (if_->condition->tag == Declare) { + auto decl = Match(if_->condition, Declare); + env_t *true_scope = fresh_scope(env); + const char *name = Match(decl->var, Var)->name; + CORD var_code = CORD_cat(env->scope_prefix, name); + type_t *var_t = get_type(env, decl->value); + if (var_t->tag == PointerType) { + auto ptr = Match(var_t, PointerType); + if (!ptr->is_optional) + code_err(if_->condition, "This pointer will always be non-null, so it should not be used in a conditional."); + var_t = Type(PointerType, .pointed=ptr->pointed, .is_optional=false, .is_stack=ptr->is_stack, .is_readonly=ptr->is_readonly); + } else { + code_err(if_->condition, "Only optional pointer types can be used in 'if var := ...' statements (this is a %T)", var_t); + } + set_binding(true_scope, name, new(binding_t, .type=var_t, .code=var_code)); + CORD code = CORD_all("{\n", + compile_type(var_t), " ", var_code, " = ", compile(env, decl->value), ";\n" + "if (", var_code, ") ", compile_statement(true_scope, if_->body)); + if (if_->else_body) + code = CORD_all(code, "\nelse ", compile_statement(env, if_->else_body)); + code = CORD_cat(code, "\n}"); + return code; + } else { + type_t *cond_t = get_type(env, if_->condition); + if (cond_t->tag == PointerType) { + if (!Match(cond_t, PointerType)->is_optional) + code_err(if_->condition, "This pointer will always be non-null, so it should not be used in a conditional."); + } else if (cond_t->tag != BoolType) { + code_err(if_->condition, "Only boolean values and optional pointers can be used in conditionals (this is a %T)", cond_t); + } + CORD code; + CORD_sprintf(&code, "if (%r) %r", compile(env, if_->condition), compile_statement(env, if_->body)); + if (if_->else_body) + code = CORD_all(code, "\nelse ", compile_statement(env, if_->else_body)); + return code; + } + } + case Block: { + ast_list_t *stmts = Match(ast, Block)->statements; + if (stmts && !stmts->next) + return compile_statement(env, stmts->ast); + + CORD code = "{\n"; + env = fresh_scope(env); + for (ast_list_t *stmt = stmts; stmt; stmt = stmt->next) { + bind_statement(env, stmt->ast); + code = CORD_all(code, compile_statement(env, stmt->ast), "\n"); + } + return CORD_cat(code, "}"); + } + case InlineCCode: return Match(ast, InlineCCode)->code; default: - stmt = CORD_asprintf("(void)%r;", compile(env, ast)); - break; + return CORD_asprintf("(void)%r;", compile(env, ast)); } // int64_t line = get_line_number(ast->file, ast->start); // return stmt ? CORD_asprintf("#line %ld\n%r", line, stmt) : stmt; - return stmt; } CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color) @@ -193,18 +671,6 @@ static CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_dept return val; } -static void check_assignable(env_t *env, ast_t *ast) -{ - if (!can_be_mutated(env, ast)) { - if (ast->tag == Index || ast->tag == FieldAccess) { - ast_t *subject = ast->tag == Index ? Match(ast, Index)->indexed : Match(ast, FieldAccess)->fielded; - code_err(subject, "This is a readonly pointer, which can't be assigned to"); - } else { - code_err(ast, "This is a value of type %T and can't be assigned to", get_type(env, ast)); - } - } -} - static CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args) { table_t used_args = {}; @@ -476,82 +942,6 @@ CORD compile(env_t *env, ast_t *ast) } code_err(ast, "unimplemented binop"); } - case UpdateAssign: { - auto update = Match(ast, UpdateAssign); - check_assignable(env, update->lhs); - CORD lhs = compile(env, update->lhs); - CORD rhs = compile(env, update->rhs); - - type_t *lhs_t = get_type(env, update->lhs); - type_t *rhs_t = get_type(env, update->rhs); - type_t *operand_t; - if (promote(env, &rhs, rhs_t, lhs_t)) - operand_t = lhs_t; - else if (promote(env, &lhs, lhs_t, rhs_t)) - operand_t = rhs_t; - else if (lhs_t->tag == ArrayType && promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) - operand_t = lhs_t; - else - code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t); - - switch (update->op) { - case BINOP_MULT: return CORD_asprintf("%r *= %r;", lhs, rhs); - case BINOP_DIVIDE: return CORD_asprintf("%r /= %r;", lhs, rhs); - case BINOP_MOD: return CORD_asprintf("%r = mod(%r, %r);", lhs, lhs, rhs); - case BINOP_MOD1: return CORD_asprintf("%r = mod1(%r, %r);", lhs, lhs, rhs); - case BINOP_PLUS: return CORD_asprintf("%r += %r;", lhs, rhs); - case BINOP_MINUS: return CORD_asprintf("%r -= %r;", lhs, rhs); - case BINOP_POWER: { - if (lhs_t->tag != NumType) - code_err(ast, "'^=' is only supported for Num types"); - if (lhs_t->tag == NumType && Match(lhs_t, NumType)->bits == 32) - return CORD_all(lhs, " = powf(", lhs, ", ", rhs, ")"); - else - return CORD_all(lhs, " = pow(", lhs, ", ", rhs, ")"); - } - case BINOP_LSHIFT: return CORD_asprintf("%r <<= %r;", lhs, rhs); - case BINOP_RSHIFT: return CORD_asprintf("%r >>= %r;", lhs, rhs); - case BINOP_AND: { - if (operand_t->tag == BoolType) - return CORD_asprintf("if (%r) %r = %r;", lhs, lhs, rhs); - else if (operand_t->tag == IntType) - return CORD_asprintf("%r &= %r;", lhs, rhs); - else - code_err(ast, "'or=' is not implemented for %T types", operand_t); - } - case BINOP_OR: { - if (operand_t->tag == BoolType) - return CORD_asprintf("if (!(%r)) %r = %r;", lhs, lhs, rhs); - else if (operand_t->tag == IntType) - return CORD_asprintf("%r |= %r;", lhs, rhs); - else - code_err(ast, "'or=' is not implemented for %T types", operand_t); - } - case BINOP_XOR: return CORD_asprintf("%r ^= %r;", lhs, rhs); - case BINOP_CONCAT: { - if (operand_t->tag == TextType) { - return CORD_asprintf("%r = CORD_cat(%r, %r);", lhs, lhs, rhs); - } else if (operand_t->tag == ArrayType) { - if (promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) { - // arr ++= item - if (update->lhs->tag == Var) - return CORD_all("Array__insert(&", lhs, ", $stack(", rhs, "), 0, ", compile_type_info(env, operand_t), ")"); - else - return CORD_all(lhs, "Array__concat(", lhs, ", $Array(", rhs, "), ", compile_type_info(env, operand_t), ")"); - } else { - // arr ++= [...] - if (update->lhs->tag == Var) - return CORD_all("Array__insert_all(&", lhs, ", ", rhs, ", 0, ", compile_type_info(env, operand_t), ")"); - else - return CORD_all(lhs, "Array__concat(", lhs, ", ", rhs, ", ", compile_type_info(env, operand_t), ")"); - } - } else { - code_err(ast, "'++=' is not implemented for %T types", operand_t); - } - } - default: code_err(ast, "Update assignments are not implemented for this operation"); - } - } case TextLiteral: { CORD literal = Match(ast, TextLiteral)->cord; if (literal == CORD_EMPTY) @@ -631,37 +1021,19 @@ CORD compile(env_t *env, ast_t *ast) case Block: { ast_list_t *stmts = Match(ast, Block)->statements; if (stmts && !stmts->next) - return compile_statement(env, stmts->ast); + return compile(env, stmts->ast); - CORD code = "{\n"; + CORD code = "({\n"; env = fresh_scope(env); for (ast_list_t *stmt = stmts; stmt; stmt = stmt->next) { bind_statement(env, stmt->ast); - code = CORD_all(code, compile_statement(env, stmt->ast), "\n"); - } - return CORD_cat(code, "}"); - } - case Declare: { - auto decl = Match(ast, Declare); - type_t *t = get_type(env, decl->value); - return CORD_all(compile_declaration(t, Match(decl->var, Var)->name), " = ", compile(env, decl->value), ";"); - } - case Assign: { - auto assign = Match(ast, Assign); - // Single assignment: - if (assign->targets && !assign->targets->next) - return CORD_asprintf("%r = %r;", compile(env, assign->targets->ast), compile(env, assign->values->ast)); - - CORD code = "{ // Assignment\n"; - int64_t i = 1; - for (ast_list_t *value = assign->values; value; value = value->next) - CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(get_type(env, value->ast)), i++, compile(env, value->ast)); - i = 1; - for (ast_list_t *target = assign->targets; target; target = target->next) { - check_assignable(env, target->ast); - CORD_appendf(&code, "%r = $%ld;\n", compile(env, target->ast), i++); + if (stmt->next) { + code = CORD_all(code, compile_statement(env, stmt->ast), "\n"); + } else { + code = CORD_all(code, compile(env, stmt->ast), ";\n"); + } } - return CORD_cat(code, "\n}"); + return CORD_cat(code, "})"); } case Min: case Max: { type_t *t = get_type(env, ast); @@ -781,90 +1153,6 @@ CORD compile(env_t *env, ast_t *ast) return CORD_cat(code, ")"); } - case FunctionDef: { - auto fndef = Match(ast, FunctionDef); - CORD name = compile(env, fndef->name); - type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType); - - CORD arg_signature = "("; - for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) { - type_t *arg_type = get_arg_ast_type(env, arg); - arg_signature = CORD_cat(arg_signature, compile_declaration(arg_type, arg->name)); - if (arg->next) arg_signature = CORD_cat(arg_signature, ", "); - } - arg_signature = CORD_cat(arg_signature, ")"); - - CORD ret_type_code = compile_type(ret_t); - - if (fndef->is_private) - env->code->staticdefs = CORD_all(env->code->staticdefs, "static ", ret_type_code, " ", name, arg_signature, ";\n"); - else - env->code->fndefs = CORD_all(env->code->fndefs, ret_type_code, " ", name, arg_signature, ";\n"); - - CORD code; - if (fndef->cache) { - code = CORD_all("static ", ret_type_code, " ", name, "$uncached", arg_signature); - } else { - code = CORD_all(ret_type_code, " ", name, arg_signature); - if (fndef->is_inline) - code = CORD_cat("inline ", code); - if (!fndef->is_private) - code = CORD_cat("public ", code); - } - - env_t *body_scope = global_scope(env); - for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) { - type_t *arg_type = get_arg_ast_type(env, arg); - set_binding(body_scope, arg->name, new(binding_t, .type=arg_type, .code=arg->name)); - } - - fn_context_t fn_ctx = (fn_context_t){ - .return_type=ret_t, - .closure_scope=NULL, - .closed_vars=NULL, - }; - body_scope->fn_ctx = &fn_ctx; - - CORD body = compile(body_scope, fndef->body); - if (CORD_fetch(body, 0) != '{') - body = CORD_asprintf("{\n%r\n}", body); - env->code->funcs = CORD_all(env->code->funcs, code, " ", body, "\n"); - - if (fndef->cache && fndef->cache->tag == Int && Match(fndef->cache, Int)->i > 0) { - const char *arg_type_name = heap_strf("%s$args", CORD_to_const_char_star(name)); - ast_t *args_def = FakeAST(StructDef, .name=arg_type_name, .fields=fndef->args); - bind_statement(env, args_def); - (void)compile(env, args_def); - type_t *args_t = Table_str_get(*env->types, arg_type_name); - assert(args_t); - - CORD all_args = CORD_EMPTY; - for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) - all_args = CORD_all(all_args, arg->name, arg->next ? ", " : CORD_EMPTY); - - CORD pop_code = CORD_EMPTY; - if (fndef->cache->tag == Int && Match(fndef->cache, Int)->i < INT64_MAX) { - pop_code = CORD_all("if (Table_length($cache) > ", compile(body_scope, fndef->cache), - ") Table_remove(&$cache, NULL, $table_info);\n"); - } - - CORD wrapper = CORD_all( - fndef->is_private ? CORD_EMPTY : "public ", ret_type_code, " ", name, arg_signature, "{\n" - "static table_t $cache = {};\n", - compile_type(args_t), " $args = {", all_args, "};\n" - "static const TypeInfo *$table_type = $TableInfo(", compile_type_info(env, args_t), ", ", compile_type_info(env, ret_t), ");\n", - ret_type_code, "*$cached = Table_get_raw($cache, &$args, $table_type);\n" - "if ($cached) return *$cached;\n", - ret_type_code, " $ret = ", name, "$uncached(", all_args, ");\n", - pop_code, - "Table_set(&$cache, &$args, &$ret, $table_type);\n" - "return $ret;\n" - "}\n"); - env->code->funcs = CORD_cat(env->code->funcs, wrapper); - } - - return CORD_EMPTY; - } case Lambda: { auto lambda = Match(ast, Lambda); static int64_t lambda_number = 1; @@ -1040,146 +1328,27 @@ CORD compile(env_t *env, ast_t *ast) code_err(call->fn, "This is not a function, it's a %T", fn_t); } } + case When: + code_err(ast, "'when' expressions are not yet implemented"); case If: { auto if_ = Match(ast, If); + if (!if_->else_body) + code_err(ast, "'if' expressions can only be used if you also have an 'else' block"); + + type_t *t = get_type(env, ast); + if (t->tag == VoidType || t->tag == AbortType) + code_err(ast, "This expression has a %T type, but it needs to have a real value", t); + if (if_->condition->tag == Declare) { - auto decl = Match(if_->condition, Declare); env_t *true_scope = fresh_scope(env); - const char *name = Match(decl->var, Var)->name; - CORD var_code = CORD_cat(env->scope_prefix, name); - type_t *var_t = get_type(env, decl->value); - if (var_t->tag == PointerType) { - auto ptr = Match(var_t, PointerType); - if (!ptr->is_optional) - code_err(if_->condition, "This pointer will always be non-null, so it should not be used in a conditional."); - var_t = Type(PointerType, .pointed=ptr->pointed, .is_optional=false, .is_stack=ptr->is_stack, .is_readonly=ptr->is_readonly); - } else { - code_err(if_->condition, "Only optional pointer types can be used in 'if var := ...' statements (this is a %T)", var_t); - } - set_binding(true_scope, name, new(binding_t, .type=var_t, .code=var_code)); - CORD code = CORD_all("{\n", - compile_type(var_t), " ", var_code, " = ", compile(env, decl->value), ";\n" - "if (", var_code, ") ", compile_statement(true_scope, if_->body)); - if (if_->else_body) - code = CORD_all(code, "\nelse ", compile_statement(env, if_->else_body)); - code = CORD_cat(code, "\n}"); - return code; + bind_statement(true_scope, if_->condition); + CORD condition = Match(Match(if_->condition, Declare)->var, Var)->name; + CORD decl = compile_statement(env, if_->condition); + return CORD_all("({ ", decl, "\n(", condition, ") ? ", + compile(true_scope, if_->body), " : ", + compile(env, if_->else_body), " })"); } else { - type_t *cond_t = get_type(env, if_->condition); - if (cond_t->tag == PointerType) { - if (!Match(cond_t, PointerType)->is_optional) - code_err(if_->condition, "This pointer will always be non-null, so it should not be used in a conditional."); - } else if (cond_t->tag != BoolType) { - code_err(if_->condition, "Only boolean values and optional pointers can be used in conditionals (this is a %T)", cond_t); - } - CORD code; - CORD_sprintf(&code, "if (%r) %r", compile(env, if_->condition), compile_statement(env, if_->body)); - if (if_->else_body) - code = CORD_all(code, "\nelse ", compile_statement(env, if_->else_body)); - return code; - } - } - case When: { - auto when = Match(ast, When); - type_t *subject_t = get_type(env, when->subject); - auto enum_t = Match(subject_t, EnumType); - CORD code = CORD_all("{ ", compile_type(subject_t), " $subject = ", compile(env, when->subject), ";\n" - "switch ($subject.$tag) {"); - type_t *result_t = get_type(env, ast); - (void)result_t; - for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { - const char *clause_tag_name = Match(clause->tag_name, Var)->name; - code = CORD_all(code, "case $tag$", enum_t->name, "$", clause_tag_name, ": {\n"); - type_t *tag_type = NULL; - for (tag_t *tag = enum_t->tags; tag; tag = tag->next) { - if (streq(tag->name, clause_tag_name)) { - tag_type = tag->type; - break; - } - } - assert(tag_type); - env_t *scope = env; - if (clause->var) { - code = CORD_all(code, compile_type(tag_type), " ", compile(env, clause->var), " = $subject.", clause_tag_name, ";\n"); - scope = fresh_scope(env); - set_binding(scope, Match(clause->var, Var)->name, new(binding_t, .type=tag_type)); - } - code = CORD_all(code, compile(scope, clause->body), "\nbreak;\n}\n"); - } - if (when->else_body) { - code = CORD_all(code, "default: {\n", compile(env, when->else_body), "\nbreak;\n}"); - } - code = CORD_all(code, "\n}\n}"); - return code; - } - case While: { - auto while_ = Match(ast, While); - return CORD_asprintf("while (%r) %r", compile(env, while_->condition), compile(env, while_->body)); - } - case For: { - auto for_ = Match(ast, For); - type_t *iter_t = get_type(env, for_->iter); - env_t *scope = for_scope(env, ast); - switch (iter_t->tag) { - case ArrayType: { - type_t *item_t = Match(iter_t, ArrayType)->item_type; - CORD index = for_->index ? compile(env, for_->index) : "$i"; - CORD value = compile(env, for_->value); - return CORD_all("$ARRAY_FOREACH(", compile(env, for_->iter), ", ", index, ", ", compile_type(item_t), ", ", value, ", ", - compile(scope, for_->body), ", ", for_->empty ? compile(env, for_->empty) : "{}", ")"); - } - case TableType: { - type_t *key_t = Match(iter_t, TableType)->key_type; - type_t *value_t = Match(iter_t, TableType)->value_type; - if (for_->index) { - CORD key = compile(env, for_->index); - CORD value = compile(env, for_->value); - - size_t value_offset = type_size(key_t); - if (type_align(value_t) > 1 && value_offset % type_align(value_t)) - value_offset += type_align(value_t) - (value_offset % type_align(value_t)); // padding - return CORD_all("$TABLE_FOREACH(", compile(env, for_->iter), ", ", compile_type(key_t), ", ", key, ", ", - compile_type(value_t), ", ", value, ", ", heap_strf("%zu", value_offset), - ", ", compile(scope, for_->body), ", ", for_->empty ? compile(env, for_->empty) : "{}", ")"); - } else { - CORD key = compile(env, for_->value); - return CORD_all("$ARRAY_FOREACH((", compile(env, for_->iter), ").entries, $i, ", compile_type(key_t), ", ", key, ", ", - compile(scope, for_->body), ", ", for_->empty ? compile(env, for_->empty) : "{}", ")"); - } - } - case IntType: { - CORD value = compile(env, for_->value); - CORD n = compile(env, for_->iter); - CORD index = for_->index ? compile(env, for_->index) : CORD_EMPTY; - if (for_->empty && index) { - return CORD_all( - "{\n" - "int64_t $n = ", n, ";\n" - "if ($n > 0) {\n" - "for (int64_t ", index, " = 1, ", value, "; (", value, "=", index,") <= $n; ++", index, ")\n" - "\t", compile(scope, for_->body), "\n" - "}\n else ", compile(env, for_->empty), - "\n}"); - } else if (for_->empty) { - return CORD_all( - "{\n" - "int64_t $n = ", n, ";\n" - "if ($n > 0) {\n" - "for (int64_t ", value, " = 1; ", value, " <= $n; ++", value, ")\n" - "\t", compile(scope, for_->body), "\n" - "}\n else ", compile(env, for_->empty), - "\n}"); - } else if (index) { - return CORD_all( - "for (int64_t ", value, ", ", index, " = 1, $n = ", n, "; (", value, "=", index,") <= $n; ++", value, ")\n" - "\t", compile_statement(scope, for_->body), "\n"); - } else { - return CORD_all( - "for (int64_t ", value, " = 1, $n = ", compile(env, for_->iter), "; ", value, " <= $n; ++", value, ")\n" - "\t", compile_statement(scope, for_->body), "\n"); - } - } - default: code_err(for_->iter, "Iteration is not implemented for type: %T", iter_t); + return CORD_all("((", compile(env, if_->condition), ") ? ", compile(env, if_->body), " : ", compile(env, if_->else_body), ")"); } } case Reduction: { @@ -1212,142 +1381,9 @@ CORD compile(env_t *env, ast_t *ast) set_binding(scope, "$iter_value", new(binding_t, .type=t)); ast_t *body = FakeAST(InlineCCode, CORD_all("$reduction = $i == 1 ? $iter_value : ", compile(scope, reduction->combination), ";")); ast_t *loop = FakeAST(For, .index=i, .value=item, .iter=reduction->iter, .body=body, .empty=empty); - code = CORD_all(code, compile(scope, loop), "\n$reduction;})"); + code = CORD_all(code, compile_statement(scope, loop), "\n$reduction;})"); return code; } - case Skip: { - if (Match(ast, Skip)->target) code_err(ast, "Named skips not yet implemented"); - return "continue"; - } - case Stop: { - if (Match(ast, Stop)->target) code_err(ast, "Named stops not yet implemented"); - return "break"; - } - case Pass: return ";"; - case Return: { - if (!env->fn_ctx) code_err(ast, "This return statement is not inside any function"); - auto ret = Match(ast, Return)->value; - assert(env->fn_ctx->return_type); - if (ret) { - type_t *ret_t = get_type(env, ret); - CORD value = compile(env, ret); - if (!promote(env, &value, ret_t, env->fn_ctx->return_type)) - code_err(ast, "This function expects a return value of type %T, but this return has type %T", - env->fn_ctx->return_type, ret_t); - return CORD_all("return ", value, ";"); - } else { - if (env->fn_ctx->return_type->tag != VoidType) - code_err(ast, "This function expects a return value of type %T", env->fn_ctx->return_type->tag); - return "return;"; - } - } - case StructDef: { - compile_struct_def(env, ast); - return CORD_EMPTY; - } - case EnumDef: { - compile_enum_def(env, ast); - return CORD_EMPTY; - } - case LangDef: { - // TODO: implement - auto def = Match(ast, LangDef); - CORD_appendf(&env->code->typedefs, "typedef CORD %s_t;\n", def->name); - CORD_appendf(&env->code->typedefs, "extern const TypeInfo %s;\n", def->name); - CORD_appendf(&env->code->typeinfos, "public const TypeInfo %s = {%zu, %zu, {.tag=TextInfo, .TextInfo={%s}}};\n", - def->name, sizeof(CORD), __alignof__(CORD), - Text__quoted(def->name, false)); - compile_namespace(env, def->name, def->namespace); - return CORD_EMPTY; - } - case DocTest: { - auto test = Match(ast, DocTest); - type_t *expr_t = get_type(env, test->expr); - if (!expr_t) - code_err(test->expr, "I couldn't figure out the type of this expression"); - - CORD output = NULL; - if (test->output) { - const uint8_t *raw = (const uint8_t*)CORD_to_const_char_star(test->output); - uint8_t buf[128] = {0}; - size_t norm_len = sizeof(buf); - uint8_t *norm = u8_normalize(UNINORM_NFD, (uint8_t*)raw, strlen((char*)raw)+1, buf, &norm_len); - assert(norm[norm_len-1] == 0); - output = CORD_from_char_star((char*)norm); - if (norm && norm != buf) free(norm); - } - - if (test->expr->tag == Declare) { - auto decl = Match(test->expr, Declare); - return CORD_asprintf( - "%r\n" - "$test(&%r, %r, %r, %r, %ld, %ld);", - compile(env, test->expr), - compile(env, decl->var), - compile_type_info(env, get_type(env, decl->value)), - compile(env, WrapAST(test->expr, TextLiteral, .cord=output)), - compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), - (int64_t)(test->expr->start - test->expr->file->text), - (int64_t)(test->expr->end - test->expr->file->text)); - } else if (test->expr->tag == Assign) { - auto assign = Match(test->expr, Assign); - if (!assign->targets->next && assign->targets->ast->tag == Var) { - // Common case: assigning to one variable: - check_assignable(env, assign->targets->ast); - CORD var = compile(env, assign->targets->ast); - CORD code = CORD_all(var, " = ", compile(env, assign->values->ast), ";"); - CORD_appendf(&code, "$test(&%r, %r, %r, %r, %ld, %ld);", - var, compile_type_info(env, get_type(env, assign->targets->ast)), - compile(env, WrapAST(test->expr, TextLiteral, .cord=test->output)), - compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), - (int64_t)(test->expr->start - test->expr->file->text), - (int64_t)(test->expr->end - test->expr->file->text)); - return code; - } else { - // Multi-assign or assignment to potentially non-idempotent targets - if (test->output && assign->targets->next) - code_err(ast, "Sorry, but doctesting with '=' is not supported for multi-assignments"); - - CORD code = "{ // Assignment\n"; - int64_t i = 1; - for (ast_list_t *value = assign->values; value; value = value->next) - CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(get_type(env, value->ast)), i++, compile(env, value->ast)); - i = 1; - for (ast_list_t *target = assign->targets; target; target = target->next) { - check_assignable(env, target->ast); - CORD_appendf(&code, "%r = $%ld;\n", compile(env, target->ast), i++); - } - - CORD_appendf(&code, "$test(&$1, %r, %r, %r, %ld, %ld);", - compile_type_info(env, get_type(env, assign->targets->ast)), - compile(env, WrapAST(test->expr, TextLiteral, .cord=test->output)), - compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), - (int64_t)(test->expr->start - test->expr->file->text), - (int64_t)(test->expr->end - test->expr->file->text)); - return CORD_cat(code, "\n}"); - } - } else if (expr_t->tag == VoidType || expr_t->tag == AbortType) { - return CORD_asprintf( - "%r;\n" - "$test(NULL, NULL, NULL, %r, %ld, %ld);", - compile(env, test->expr), - compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), - (int64_t)(test->expr->start - test->expr->file->text), - (int64_t)(test->expr->end - test->expr->file->text)); - } else { - return CORD_asprintf( - "{ // Test:\n%r $expr = %r;\n" - "$test(&$expr, %r, %r, %r, %ld, %ld);\n" - "}", - compile_type(expr_t), - compile(env, test->expr), - compile_type_info(env, expr_t), - compile(env, WrapAST(test->expr, TextLiteral, .cord=output)), - compile(env, WrapAST(test->expr, TextLiteral, .cord=test->expr->file->filename)), - (int64_t)(test->expr->start - test->expr->file->text), - (int64_t)(test->expr->end - test->expr->file->text)); - } - } case FieldAccess: { auto f = Match(ast, FieldAccess); type_t *fielded_t = get_type(env, f->fielded); @@ -1467,6 +1503,9 @@ CORD compile(env_t *env, ast_t *ast) case LinkerDirective: code_err(ast, "Linker directives are not supported yet"); case Extern: code_err(ast, "Externs are not supported yet"); case TableEntry: code_err(ast, "Table entries should not be compiled directly"); + case Declare: case Assign: case UpdateAssign: case For: case While: case StructDef: case LangDef: + case EnumDef: case FunctionDef: case Skip: case Stop: case Pass: case Return: case DocTest: + code_err(ast, "This is not a valid expression"); case Unknown: code_err(ast, "Unknown AST"); } code_err(ast, "Unknown AST: %W", ast); @@ -815,7 +815,7 @@ ast_t *parse_for_suffix(parse_ctx_t *ctx, ast_t *lhs) { if (match_word(&pos, "if")) { ast_t *condition = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'if'"); body = NewAST(ctx->file, body->start, condition->end, Block, - .statements=new(ast_list_t, .ast=WrapAST(condition, If, .condition=condition, .else_body=FakeAST(Skip)), + .statements=new(ast_list_t, .ast=WrapAST(condition, If, .condition=FakeAST(Not, condition), .body=FakeAST(Skip)), .next=new(ast_list_t, .ast=body))); } return NewAST(ctx->file, start, pos, For, .index=index, .value=value, .iter=iter, .body=body); |
