diff options
Diffstat (limited to 'src/compile')
| -rw-r--r-- | src/compile/assertions.c | 86 | ||||
| -rw-r--r-- | src/compile/assertions.h | 8 | ||||
| -rw-r--r-- | src/compile/doctests.c | 1 | ||||
| -rw-r--r-- | src/compile/statements.c | 76 |
4 files changed, 97 insertions, 74 deletions
diff --git a/src/compile/assertions.c b/src/compile/assertions.c new file mode 100644 index 00000000..6034b828 --- /dev/null +++ b/src/compile/assertions.c @@ -0,0 +1,86 @@ +// 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 "conditionals.h" +#include "declarations.h" +#include "promotions.h" +#include "statements.h" +#include "text.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_str(lhs_t), " and ", type_to_str(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 ", String(line), "\n", + compile_declaration(operand_t, Text("_rhs")), " = ", compile_to_type(env, cmp.rhs, operand_t), + ";\n", "\n#line ", String(line), "\n", "if (!(", compile_condition(env, var_comparison), "))\n", + "#line ", String(line), "\n", + Texts("fail_source(", quoted_str(ast->file->filename), ", ", + String((int64_t)(expr->start - expr->file->text)), ", ", + String((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 ", String(line), "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", String((int64_t)(expr->start - expr->file->text)), ", ", + String((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"); + } + } +} diff --git a/src/compile/assertions.h b/src/compile/assertions.h new file mode 100644 index 00000000..b5f604d2 --- /dev/null +++ b/src/compile/assertions.h @@ -0,0 +1,8 @@ +// This file defines how to compile assertions +#pragma once + +#include "../ast.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" + +Text_t compile_assertion(env_t *env, ast_t *ast); diff --git a/src/compile/doctests.c b/src/compile/doctests.c index 4d544656..720385d3 100644 --- a/src/compile/doctests.c +++ b/src/compile/doctests.c @@ -15,6 +15,7 @@ #include "statements.h" #include "types.h" +public Text_t compile_doctest(env_t *env, ast_t *ast) { DeclareMatch(test, ast, DocTest); type_t *expr_t = get_type(env, test->expr); diff --git a/src/compile/statements.c b/src/compile/statements.c index e94e14a9..3ba560d2 100644 --- a/src/compile/statements.c +++ b/src/compile/statements.c @@ -14,6 +14,7 @@ #include "../stdlib/text.h" #include "../stdlib/util.h" #include "../typecheck.h" +#include "assertions.h" #include "assignments.h" #include "blocks.h" #include "conditionals.h" @@ -24,7 +25,6 @@ #include "functions.h" #include "promotions.h" #include "statements.h" -#include "text.h" #include "whens.h" typedef ast_t *(*comprehension_body_t)(ast_t *, ast_t *); @@ -40,79 +40,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) { switch (ast->tag) { case When: return compile_when_statement(env, ast); case DocTest: return compile_doctest(env, ast); - 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: { - 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_str(lhs_t), " and ", type_to_str(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 ", String(line), "\n", - compile_declaration(operand_t, Text("_rhs")), " = ", - compile_to_type(env, cmp.rhs, operand_t), ";\n", "\n#line ", String(line), "\n", "if (!(", - compile_condition(env, var_comparison), "))\n", "#line ", String(line), "\n", - Texts("fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(expr->start - expr->file->text)), ", ", - String((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 ", String(line), "\n", "fail_source(", - quoted_str(ast->file->filename), ", ", String((int64_t)(expr->start - expr->file->text)), ", ", - String((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"); - } - } - } + case Assert: return compile_assertion(env, ast); case Declare: { DeclareMatch(decl, ast, Declare); const char *name = Match(decl->var, Var)->name; |
