aboutsummaryrefslogtreecommitdiff
path: root/src/compile/assertions.c
blob: 3ea6bf1e38b03c2746ec607bde7175ec6468e589 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// This file defines how to compile assertions

#include "../ast.h"
#include "../config.h"
#include "../environment.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/print.h"
#include "../stdlib/text.h"
#include "../stdlib/util.h"
#include "../typecheck.h"
#include "compilation.h"

public
Text_t compile_assertion(env_t *env, ast_t *ast) {
    ast_t *expr = Match(ast, Assert)->expr;
    ast_t *message = Match(ast, Assert)->message;
    const char *failure = NULL;
    switch (expr->tag) {
    case And: {
        DeclareMatch(and_, ast, And);
        return Texts(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_text(lhs_t), " and ", type_to_text(rhs_t));
        }

        ast_t *lhs_var = FakeAST(InlineCCode, .chunks = new (ast_list_t, .ast = FakeAST(TextLiteral, Text("_lhs"))),
                                 .type = operand_t);
        ast_t *rhs_var = FakeAST(InlineCCode, .chunks = new (ast_list_t, .ast = FakeAST(TextLiteral, Text("_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});
        int64_t line = get_line_number(ast->file, ast->start);
        return Texts(
            "{ // assertion\n", compile_declaration(operand_t, Text("_lhs")), " = ",
            compile_to_type(env, cmp.lhs, operand_t), ";\n", "\n#line ", line, "\n",
            compile_declaration(operand_t, Text("_rhs")), " = ", compile_to_type(env, cmp.rhs, operand_t), ";\n",
            "\n#line ", line, "\n", "if (!(", compile_condition(env, var_comparison), "))\n", "#line ", line, "\n",
            Texts("fail_source(", quoted_str(ast->file->filename), ", ", (int64_t)(expr->start - expr->file->text),
                  ", ", (int64_t)(expr->end - expr->file->text), ", ",
                  message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")")
                          : Text("\"This assertion failed!\""),
                  ", ", "\" (\", ", expr_as_text(Text("_lhs"), operand_t, Text("no")),
                  ", "
                  "\" ",
                  failure, " \", ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", \")\");\n"),
            "}\n");
    }
    default: {
        int64_t line = get_line_number(ast->file, ast->start);
        return Texts("if (!(", compile_condition(env, expr), "))\n", "#line ", line, "\n", "fail_source(",
                     quoted_str(ast->file->filename), ", ", (int64_t)(expr->start - expr->file->text), ", ",
                     (int64_t)(expr->end - expr->file->text), ", ",
                     message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")")
                             : Text("\"This assertion failed!\""),
                     ");\n");
    }
    }
}