diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-08-24 18:04:03 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-08-24 18:04:03 -0400 |
| commit | 9a663450129398537a80ed454920c3134fe1aa6f (patch) | |
| tree | 0097f73db863f0339b8053b9ce10aef9cd8abb8d | |
| parent | 7cb55ec0061246685ffe8c6d1d92307495c28943 (diff) | |
Move conditionals into a file
| -rw-r--r-- | src/compile/conditionals.c | 125 | ||||
| -rw-r--r-- | src/compile/conditionals.h | 10 | ||||
| -rw-r--r-- | src/compile/expressions.c | 54 | ||||
| -rw-r--r-- | src/compile/statements.c | 58 | ||||
| -rw-r--r-- | src/compile/statements.h | 1 |
5 files changed, 138 insertions, 110 deletions
diff --git a/src/compile/conditionals.c b/src/compile/conditionals.c new file mode 100644 index 00000000..98919152 --- /dev/null +++ b/src/compile/conditionals.c @@ -0,0 +1,125 @@ +// This file defines how to compile conditionals + +#include "../ast.h" +#include "../config.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/text.h" +#include "../stdlib/util.h" +#include "../typecheck.h" +#include "expressions.h" +#include "optionals.h" +#include "statements.h" + +public +Text_t compile_condition(env_t *env, ast_t *ast) { + type_t *t = get_type(env, ast); + if (t->tag == BoolType) { + return compile(env, ast); + } else if (t->tag == TextType) { + return Texts("(", compile(env, ast), ").length"); + } else if (t->tag == ListType) { + return Texts("(", compile(env, ast), ").length"); + } else if (t->tag == TableType || t->tag == SetType) { + return Texts("(", compile(env, ast), ").entries.length"); + } else if (t->tag == OptionalType) { + return Texts("!", check_none(t, compile(env, ast))); + } else if (t->tag == PointerType) { + code_err(ast, "This pointer will always be non-none, so it should not be " + "used in a conditional."); + } else { + code_err(ast, type_to_str(t), " values cannot be used for conditionals"); + } + return EMPTY_TEXT; +} + +public +Text_t compile_if_statement(env_t *env, ast_t *ast) { + DeclareMatch(if_, ast, If); + ast_t *condition = if_->condition; + if (condition->tag == Declare) { + if (Match(condition, Declare)->value == NULL) code_err(condition, "This declaration must have a value"); + env_t *truthy_scope = fresh_scope(env); + Text_t code = Texts("IF_DECLARE(", compile_statement(truthy_scope, condition), ", "); + bind_statement(truthy_scope, condition); + ast_t *var = Match(condition, Declare)->var; + code = Texts(code, compile_condition(truthy_scope, var), ", "); + type_t *cond_t = get_type(truthy_scope, var); + if (cond_t->tag == OptionalType) { + set_binding(truthy_scope, Match(var, Var)->name, Match(cond_t, OptionalType)->type, + optional_into_nonnone(cond_t, compile(truthy_scope, var))); + } + code = Texts(code, compile_statement(truthy_scope, if_->body), ")"); + if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); + return code; + } else { + Text_t code = Texts("if (", compile_condition(env, condition), ")"); + env_t *truthy_scope = env; + type_t *cond_t = get_type(env, condition); + if (condition->tag == Var && cond_t->tag == OptionalType) { + truthy_scope = fresh_scope(env); + set_binding(truthy_scope, Match(condition, Var)->name, Match(cond_t, OptionalType)->type, + optional_into_nonnone(cond_t, compile(truthy_scope, condition))); + } + code = Texts(code, compile_statement(truthy_scope, if_->body)); + if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); + return code; + } +} + +public +Text_t compile_if_expression(env_t *env, ast_t *ast) { + DeclareMatch(if_, ast, If); + ast_t *condition = if_->condition; + Text_t decl_code = EMPTY_TEXT; + env_t *truthy_scope = env, *falsey_scope = env; + + Text_t condition_code; + if (condition->tag == Declare) { + DeclareMatch(decl, condition, Declare); + if (decl->value == NULL) code_err(condition, "This declaration must have a value"); + type_t *condition_type = + decl->type ? parse_type_ast(env, decl->type) : get_type(env, Match(condition, Declare)->value); + if (condition_type->tag != OptionalType) + code_err(condition, + "This `if var := ...:` declaration should be an " + "optional " + "type, not ", + type_to_str(condition_type)); + + if (is_incomplete_type(condition_type)) code_err(condition, "This type is incomplete!"); + + decl_code = compile_statement(env, condition); + ast_t *var = Match(condition, Declare)->var; + truthy_scope = fresh_scope(env); + bind_statement(truthy_scope, condition); + condition_code = compile_condition(truthy_scope, var); + set_binding(truthy_scope, Match(var, Var)->name, Match(condition_type, OptionalType)->type, + optional_into_nonnone(condition_type, compile(truthy_scope, var))); + } else if (condition->tag == Var) { + type_t *condition_type = get_type(env, condition); + condition_code = compile_condition(env, condition); + if (condition_type->tag == OptionalType) { + truthy_scope = fresh_scope(env); + set_binding(truthy_scope, Match(condition, Var)->name, Match(condition_type, OptionalType)->type, + optional_into_nonnone(condition_type, compile(truthy_scope, condition))); + } + } else { + condition_code = compile_condition(env, condition); + } + + type_t *true_type = get_type(truthy_scope, if_->body); + type_t *false_type = get_type(falsey_scope, if_->else_body); + if (true_type->tag == AbortType || true_type->tag == ReturnType) + return Texts("({ ", decl_code, "if (", condition_code, ") ", compile_statement(truthy_scope, if_->body), "\n", + compile(falsey_scope, if_->else_body), "; })"); + else if (false_type->tag == AbortType || false_type->tag == ReturnType) + return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", compile_statement(falsey_scope, if_->else_body), + "\n", compile(truthy_scope, if_->body), "; })"); + else if (decl_code.length > 0) + return Texts("({ ", decl_code, "(", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ", + compile(falsey_scope, if_->else_body), ";})"); + else + return Texts("((", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ", + compile(falsey_scope, if_->else_body), ")"); +} diff --git a/src/compile/conditionals.h b/src/compile/conditionals.h new file mode 100644 index 00000000..26546e10 --- /dev/null +++ b/src/compile/conditionals.h @@ -0,0 +1,10 @@ +// This file defines how to compile conditionals +#pragma once + +#include "../ast.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" + +Text_t compile_condition(env_t *env, ast_t *ast); +Text_t compile_if_statement(env_t *env, ast_t *ast); +Text_t compile_if_expression(env_t *env, ast_t *ast); diff --git a/src/compile/expressions.c b/src/compile/expressions.c index 28db606b..e485e450 100644 --- a/src/compile/expressions.c +++ b/src/compile/expressions.c @@ -355,60 +355,6 @@ Text_t compile(env_t *env, ast_t *ast) { "when; })"); } case If: { - DeclareMatch(if_, ast, If); - ast_t *condition = if_->condition; - Text_t decl_code = EMPTY_TEXT; - env_t *truthy_scope = env, *falsey_scope = env; - - Text_t condition_code; - if (condition->tag == Declare) { - DeclareMatch(decl, condition, Declare); - if (decl->value == NULL) code_err(condition, "This declaration must have a value"); - type_t *condition_type = - decl->type ? parse_type_ast(env, decl->type) : get_type(env, Match(condition, Declare)->value); - if (condition_type->tag != OptionalType) - code_err(condition, - "This `if var := ...:` declaration should be an " - "optional " - "type, not ", - type_to_str(condition_type)); - - if (is_incomplete_type(condition_type)) code_err(condition, "This type is incomplete!"); - - decl_code = compile_statement(env, condition); - ast_t *var = Match(condition, Declare)->var; - truthy_scope = fresh_scope(env); - bind_statement(truthy_scope, condition); - condition_code = compile_condition(truthy_scope, var); - set_binding(truthy_scope, Match(var, Var)->name, Match(condition_type, OptionalType)->type, - optional_into_nonnone(condition_type, compile(truthy_scope, var))); - } else if (condition->tag == Var) { - type_t *condition_type = get_type(env, condition); - condition_code = compile_condition(env, condition); - if (condition_type->tag == OptionalType) { - truthy_scope = fresh_scope(env); - set_binding(truthy_scope, Match(condition, Var)->name, Match(condition_type, OptionalType)->type, - optional_into_nonnone(condition_type, compile(truthy_scope, condition))); - } - } else { - condition_code = compile_condition(env, condition); - } - - type_t *true_type = get_type(truthy_scope, if_->body); - type_t *false_type = get_type(falsey_scope, if_->else_body); - if (true_type->tag == AbortType || true_type->tag == ReturnType) - return Texts("({ ", decl_code, "if (", condition_code, ") ", compile_statement(truthy_scope, if_->body), - "\n", compile(falsey_scope, if_->else_body), "; })"); - else if (false_type->tag == AbortType || false_type->tag == ReturnType) - return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", - compile_statement(falsey_scope, if_->else_body), "\n", compile(truthy_scope, if_->body), - "; })"); - else if (decl_code.length > 0) - return Texts("({ ", decl_code, "(", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ", - compile(falsey_scope, if_->else_body), ";})"); - else - return Texts("((", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ", - compile(falsey_scope, if_->else_body), ")"); } case Reduction: { DeclareMatch(reduction, ast, Reduction); diff --git a/src/compile/statements.c b/src/compile/statements.c index 7683b552..114e73cd 100644 --- a/src/compile/statements.c +++ b/src/compile/statements.c @@ -3,7 +3,6 @@ #include <glob.h> #include "../ast.h" -#include "expressions.h" #include "../config.h" #include "../environment.h" #include "../modules.h" @@ -17,7 +16,9 @@ #include "../typecheck.h" #include "assignments.h" #include "blocks.h" +#include "conditionals.h" #include "declarations.h" +#include "expressions.h" #include "functions.h" #include "optionals.h" #include "pointers.h" @@ -35,28 +36,6 @@ Text_t with_source_info(env_t *env, ast_t *ast, Text_t code) { return Texts("\n#line ", String(line), "\n", code); } -public -Text_t compile_condition(env_t *env, ast_t *ast) { - type_t *t = get_type(env, ast); - if (t->tag == BoolType) { - return compile(env, ast); - } else if (t->tag == TextType) { - return Texts("(", compile(env, ast), ").length"); - } else if (t->tag == ListType) { - return Texts("(", compile(env, ast), ").length"); - } else if (t->tag == TableType || t->tag == SetType) { - return Texts("(", compile(env, ast), ").entries.length"); - } else if (t->tag == OptionalType) { - return Texts("!", check_none(t, compile(env, ast))); - } else if (t->tag == PointerType) { - code_err(ast, "This pointer will always be non-none, so it should not be " - "used in a conditional."); - } else { - code_err(ast, type_to_str(t), " values cannot be used for conditionals"); - } - return EMPTY_TEXT; -} - static Text_t _compile_statement(env_t *env, ast_t *ast) { switch (ast->tag) { case When: { @@ -955,38 +934,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) { default: code_err(for_->iter, "Iteration is not implemented for type: ", type_to_str(iter_t)); } } - case If: { - DeclareMatch(if_, ast, If); - ast_t *condition = if_->condition; - if (condition->tag == Declare) { - if (Match(condition, Declare)->value == NULL) code_err(condition, "This declaration must have a value"); - env_t *truthy_scope = fresh_scope(env); - Text_t code = Texts("IF_DECLARE(", compile_statement(truthy_scope, condition), ", "); - bind_statement(truthy_scope, condition); - ast_t *var = Match(condition, Declare)->var; - code = Texts(code, compile_condition(truthy_scope, var), ", "); - type_t *cond_t = get_type(truthy_scope, var); - if (cond_t->tag == OptionalType) { - set_binding(truthy_scope, Match(var, Var)->name, Match(cond_t, OptionalType)->type, - optional_into_nonnone(cond_t, compile(truthy_scope, var))); - } - code = Texts(code, compile_statement(truthy_scope, if_->body), ")"); - if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); - return code; - } else { - Text_t code = Texts("if (", compile_condition(env, condition), ")"); - env_t *truthy_scope = env; - type_t *cond_t = get_type(env, condition); - if (condition->tag == Var && cond_t->tag == OptionalType) { - truthy_scope = fresh_scope(env); - set_binding(truthy_scope, Match(condition, Var)->name, Match(cond_t, OptionalType)->type, - optional_into_nonnone(cond_t, compile(truthy_scope, condition))); - } - code = Texts(code, compile_statement(truthy_scope, if_->body)); - if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); - return code; - } - } + case If: return compile_if_statement(env, ast); case Block: { return compile_block(env, ast); } diff --git a/src/compile/statements.h b/src/compile/statements.h index 41b1d8f4..4284a61c 100644 --- a/src/compile/statements.h +++ b/src/compile/statements.h @@ -5,6 +5,5 @@ #include "../environment.h" #include "../stdlib/datatypes.h" -Text_t compile_condition(env_t *env, ast_t *ast); Text_t compile_statement(env_t *env, ast_t *ast); Text_t with_source_info(env_t *env, ast_t *ast, Text_t code); |
