diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-04-21 16:50:40 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-04-21 16:50:40 -0400 |
| commit | ab55eee556ecbe6a8bd0a4f4cd92e38b021f6841 (patch) | |
| tree | 47c339c48c9aaeffb931588c1b6241d35690c5a2 /src/compile.c | |
| parent | f2eab0d205d1a60e9ce7a8e2420196e12d7eed10 (diff) | |
Add `assert`
Diffstat (limited to 'src/compile.c')
| -rw-r--r-- | src/compile.c | 72 |
1 files changed, 71 insertions, 1 deletions
diff --git a/src/compile.c b/src/compile.c index 02b23919..6b320b41 100644 --- a/src/compile.c +++ b/src/compile.c @@ -447,6 +447,11 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, DocTest)->expr); break; } + case Assert: { + add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Assert)->expr); + add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Assert)->message); + break; + } case Deserialize: { add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Deserialize)->value); break; @@ -1236,6 +1241,71 @@ static CORD _compile_statement(env_t *env, ast_t *ast) (int64_t)(test->expr->end - test->expr->file->text)); } } + case Assert: { + ast_t *expr = Match(ast, Assert)->expr; + ast_t *message = Match(ast, Assert)->message; + const char *failure = NULL; + switch (expr->tag) { + case And: { + auto and_ = Match(expr, And); + return CORD_all( + compile_statement(env, WrapAST(ast, Assert, .expr=and_->lhs, .message=message)), + compile_statement(env, WrapAST(ast, Assert, .expr=and_->rhs, .message=message))); + } + case Equals: failure = "!="; goto assert_comparison; + case NotEquals: failure = "=="; goto assert_comparison; + case LessThan: failure = ">="; goto assert_comparison; + case LessThanOrEquals: failure = ">"; goto assert_comparison; + case GreaterThan: failure = "<="; goto assert_comparison; + case GreaterThanOrEquals: failure = "<"; goto assert_comparison; { + assert_comparison: + binary_operands_t cmp = BINARY_OPERANDS(expr); + type_t *lhs_t = get_type(env, cmp.lhs); + type_t *rhs_t = get_type(env, cmp.rhs); + type_t *operand_t; + if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) { + operand_t = rhs_t; + } else if (cmp.rhs->tag == Int && is_numeric_type(lhs_t)) { + operand_t = lhs_t; + } else if (can_compile_to_type(env, cmp.rhs, lhs_t)) { + operand_t = lhs_t; + } else if (can_compile_to_type(env, cmp.lhs, rhs_t)) { + operand_t = rhs_t; + } else { + code_err(ast, "I can't do comparisons between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + } + + ast_t *lhs_var = FakeAST(InlineCCode, .chunks=new(ast_list_t, .ast=FakeAST(TextLiteral, "_lhs")), .type=operand_t); + ast_t *rhs_var = FakeAST(InlineCCode, .chunks=new(ast_list_t, .ast=FakeAST(TextLiteral, "_rhs")), .type=operand_t); + ast_t *var_comparison = new(ast_t, .file=expr->file, .start=expr->start, .end=expr->end, .tag=expr->tag, + .__data.Equals={.lhs=lhs_var, .rhs=rhs_var}); + return CORD_all("{ // assertion\n", + compile_declaration(operand_t, "_lhs"), " = ", compile_to_type(env, cmp.lhs, operand_t), ";\n", + compile_declaration(operand_t, "_rhs"), " = ", compile_to_type(env, cmp.rhs, operand_t), ";\n", + "if (!(", compile_condition(env, var_comparison), "))\n", + CORD_asprintf("fail_source(%r, %ld, %ld, %r, \" (\", %r, \" %s \", %r, \")\");\n", + CORD_quoted(ast->file->filename), + (long)(expr->start - expr->file->text), + (long)(expr->end - expr->file->text), + message ? CORD_all("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") + : "\"This assertion failed!\"", + expr_as_text("_lhs", operand_t, "no"), + failure, + expr_as_text("_rhs", operand_t, "no")), + "}\n"); + + } + default: { + return CORD_all("if (!(", compile_condition(env, expr), "))\n", + CORD_asprintf("fail_source(%r, %ld, %ld, %r);\n", + CORD_quoted(ast->file->filename), + (long)(expr->start - expr->file->text), + (long)(expr->end - expr->file->text), + message ? CORD_all("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") + : "\"This assertion failed!\"")); + } + } + } case Declare: { DeclareMatch(decl, ast, Declare); const char *name = Match(decl->var, Var)->name; @@ -3867,7 +3937,7 @@ CORD compile(env_t *env, ast_t *ast) case Extern: code_err(ast, "Externs are not supported as expressions"); case TableEntry: code_err(ast, "Table entries should not be compiled directly"); case Declare: case Assign: case UPDATE_CASES: case For: case While: case Repeat: case StructDef: case LangDef: case Extend: - case EnumDef: case FunctionDef: case ConvertDef: case Skip: case Stop: case Pass: case Return: case DocTest: + case EnumDef: case FunctionDef: case ConvertDef: case Skip: case Stop: case Pass: case Return: case DocTest: case Assert: code_err(ast, "This is not a valid expression"); default: case Unknown: code_err(ast, "Unknown AST: ", ast_to_xml_str(ast)); } |
