From ab55eee556ecbe6a8bd0a4f4cd92e38b021f6841 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 21 Apr 2025 16:50:40 -0400 Subject: Add `assert` --- src/compile.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) (limited to 'src/compile.c') 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)); } -- cgit v1.2.3