code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(147 lines)
1 // This file defines how to compile conditionals
3 #include "../ast.h"
4 #include "../config.h"
5 #include "../environment.h"
6 #include "../stdlib/datatypes.h"
7 #include "../stdlib/text.h"
8 #include "../stdlib/util.h"
9 #include "../typecheck.h"
10 #include "compilation.h"
12 public
13 Text_t compile_condition(env_t *env, ast_t *ast) {
14 type_t *t = get_type(env, ast);
15 if (t->tag == BoolType) {
16 return compile(env, ast);
17 } else if (t->tag == TextType) {
18 return Texts("(", compile(env, ast), ").length");
19 } else if (t->tag == ListType) {
20 return Texts("(", compile(env, ast), ").length");
21 } else if (t->tag == TableType) {
22 return Texts("(", compile(env, ast), ").entries.length");
23 } else if (t->tag == OptionalType) {
24 return Texts("!", check_none(t, compile(env, ast)));
25 } else if (t->tag == PointerType) {
26 code_err(ast, "This pointer will always be non-none, so it should not be "
27 "used in a conditional.");
28 } else {
29 code_err(ast, type_to_text(t), " values cannot be used for conditionals");
31 return EMPTY_TEXT;
34 public
35 Text_t compile_if_statement(env_t *env, ast_t *ast) {
36 DeclareMatch(if_, ast, If);
37 ast_t *condition = if_->condition;
38 if (condition->tag == Declare) {
39 DeclareMatch(decl, condition, Declare);
40 if (decl->value == NULL) code_err(condition, "This declaration must have a value");
42 env_t *truthy_scope = fresh_scope(env);
43 ast_t *var = decl->var;
44 type_t *var_type = get_type(truthy_scope, decl->value);
46 const char *name = Match(var, Var)->name;
47 bind_statement(truthy_scope, condition);
49 Text_t code = Texts("if (true) {\n", compile_statement(env, condition), //
50 "if (", compile_condition(truthy_scope, var), ")");
52 env_t *nonnull_scope = truthy_scope;
53 if (var_type->tag == OptionalType) {
54 nonnull_scope = fresh_scope(truthy_scope);
55 set_binding(nonnull_scope, name, Match(var_type, OptionalType)->type,
56 optional_into_nonnone(var_type, compile(truthy_scope, var)));
59 code = Texts(code, compile_block(nonnull_scope, if_->body));
61 if (if_->else_body) {
62 Text_t label = Texts("_falsey_", (int64_t)(ast->start - ast->file->text));
63 code = Texts(code, "else goto ", label,
64 ";\n"
65 "} else {\n",
66 label, ":;\n", compile_inline_block(env, if_->else_body), "}\n");
67 } else {
68 code = Texts(code, "}\n");
71 return code;
72 } else {
73 Text_t code = Texts("if (", compile_condition(env, condition), ")");
74 env_t *truthy_scope = env;
75 type_t *cond_t = get_type(env, condition);
76 if (condition->tag == Var && cond_t->tag == OptionalType) {
77 truthy_scope = fresh_scope(env);
78 set_binding(truthy_scope, Match(condition, Var)->name, Match(cond_t, OptionalType)->type,
79 optional_into_nonnone(cond_t, compile(truthy_scope, condition)));
81 code = Texts(code, compile_statement(truthy_scope, if_->body));
82 if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body));
83 return code;
87 public
88 Text_t compile_if_expression(env_t *env, ast_t *ast) {
89 DeclareMatch(if_, ast, If);
90 ast_t *condition = if_->condition;
91 Text_t decl_code = EMPTY_TEXT;
92 env_t *truthy_scope = env, *falsey_scope = env;
94 Text_t condition_code;
95 if (condition->tag == Declare) {
96 DeclareMatch(decl, condition, Declare);
97 if (decl->value == NULL) code_err(condition, "This declaration must have a value");
98 type_t *condition_type =
99 decl->type ? parse_type_ast(env, decl->type) : get_type(env, Match(condition, Declare)->value);
100 if (condition_type->tag != OptionalType)
101 code_err(condition,
102 "This `if var := ...:` declaration should be an "
103 "optional "
104 "type, not ",
105 type_to_text(condition_type));
107 if (is_incomplete_type(condition_type)) code_err(condition, "This type is incomplete!");
109 decl_code = compile_statement(env, condition);
110 ast_t *var = Match(condition, Declare)->var;
111 truthy_scope = fresh_scope(env);
112 bind_statement(truthy_scope, condition);
113 condition_code = compile_condition(truthy_scope, var);
114 set_binding(truthy_scope, Match(var, Var)->name, Match(condition_type, OptionalType)->type,
115 optional_into_nonnone(condition_type, compile(truthy_scope, var)));
116 } else if (condition->tag == Var) {
117 type_t *condition_type = get_type(env, condition);
118 condition_code = compile_condition(env, condition);
119 if (condition_type->tag == OptionalType) {
120 truthy_scope = fresh_scope(env);
121 set_binding(truthy_scope, Match(condition, Var)->name, Match(condition_type, OptionalType)->type,
122 optional_into_nonnone(condition_type, compile(truthy_scope, condition)));
124 } else {
125 condition_code = compile_condition(env, condition);
128 type_t *true_type = get_type(truthy_scope, if_->body);
129 ast_t *else_body = if_->else_body;
130 if (else_body && else_body->tag == Block && Match(else_body, Block)->statements
131 && !Match(else_body, Block)->statements->next)
132 else_body = Match(else_body, Block)->statements->ast;
133 if (else_body == NULL || else_body->tag == None) else_body = WrapAST(ast, None, .type = true_type);
134 type_t *false_type = get_type(falsey_scope, else_body);
135 if (true_type->tag == AbortType || true_type->tag == ReturnType)
136 return Texts("({ ", decl_code, "if (", condition_code, ") ", compile_statement(truthy_scope, if_->body), "\n",
137 compile(falsey_scope, else_body), "; })");
138 else if (false_type->tag == AbortType || false_type->tag == ReturnType)
139 return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", compile_statement(falsey_scope, else_body),
140 "\n", compile(truthy_scope, if_->body), "; })");
141 else if (decl_code.length > 0)
142 return Texts("({ ", decl_code, "(", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ",
143 compile(falsey_scope, else_body), ";})");
144 else
145 return Texts("((", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ",
146 compile(falsey_scope, else_body), ")");