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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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), ")");
}
|