1 // This file defines how to compile statements
7 #include "../environment.h"
9 #include "../packages.h"
10 #include "../stdlib/datatypes.h"
11 #include "../stdlib/paths.h"
12 #include "../stdlib/tables.h"
13 #include "../stdlib/text.h"
14 #include "../stdlib/util.h"
15 #include "../typecheck.h"
16 #include "compilation.h"
18 typedef ast_t *(*comprehension_body_t)(ast_t *, ast_t *);
21 Text_t with_source_info(env_t *env, ast_t *ast, Text_t code) {
22 if (code.length == 0 || !ast || !ast->file || !env->do_source_mapping) return code;
23 int64_t line = get_line_number(ast->file, ast->start);
24 return Texts("\n#line ", line, "\n", code);
27 static Text_t compile_simple_update_assignment(env_t *env, ast_t *ast, const char *op) {
28 binary_operands_t update = BINARY_OPERANDS(ast);
29 type_t *lhs_t = get_type(env, update.lhs);
30 if (is_idempotent(update.lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
31 return Texts(compile_lvalue(env, update.lhs), " ", op, "= ", compile_to_type(env, update.rhs, lhs_t), ";");
32 return compile_update_assignment(env, ast);
35 static Text_t _compile_statement(env_t *env, ast_t *ast) {
37 case When: return compile_when_statement(env, ast);
38 case DebugLog: return compile_debug_log(env, ast);
39 case Assert: return compile_assertion(env, ast);
41 DeclareMatch(decl, ast, Declare);
42 const char *name = Match(decl->var, Var)->name;
43 if (streq(name, "_")) { // Explicit discard
44 if (decl->value) return Texts("(void)", compile(env, decl->value), ";");
45 else return EMPTY_TEXT;
47 type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value);
48 if (t->tag == FunctionType) t = Type(ClosureType, t);
49 if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType)
50 code_err(ast, "You can't declare a variable with a ", type_to_text(t), " value");
52 Text_t val_code = compile_declared_value(env, ast);
53 return Texts(compile_declaration(t, Texts("_$", name)), " = ", val_code, ";");
56 case Assign: return compile_assignment_statement(env, ast);
57 case PlusUpdate: return compile_simple_update_assignment(env, ast, "+");
58 case MinusUpdate: return compile_simple_update_assignment(env, ast, "-");
59 case MultiplyUpdate: return compile_simple_update_assignment(env, ast, "*");
60 case DivideUpdate: return compile_simple_update_assignment(env, ast, "/");
61 case ModUpdate: return compile_simple_update_assignment(env, ast, "%");
67 case UnsignedLeftShiftUpdate:
68 case RightShiftUpdate:
69 case UnsignedRightShiftUpdate:
73 return compile_update_assignment(env, ast);
82 case Skip: return compile_skip(env, ast);
83 case Stop: return compile_stop(env, ast);
84 case Pass: return Text(";");
86 ast_t *body = Match(ast, Defer)->body;
87 Table_t closed_vars = get_closed_vars(env, NULL, body);
89 static int defer_id = 0;
90 env_t *defer_env = fresh_scope(env);
91 Text_t code = EMPTY_TEXT;
92 for (int64_t i = 0; i < (int64_t)closed_vars.entries.length; i++) {
96 } *entry = closed_vars.entries.data + closed_vars.entries.stride * i;
97 if (entry->b->type->tag == ModuleType) continue;
98 if (Text$starts_with(entry->b->code, Text("userdata->"), NULL)) {
99 Table$str_set(defer_env->locals, entry->name, entry->b);
101 Text_t defer_name = Texts("defer$", ++defer_id, "$", entry->name);
103 code = Texts(code, compile_declaration(entry->b->type, defer_name), " = ", entry->b->code, ";\n");
104 set_binding(defer_env, entry->name, entry->b->type, defer_name);
107 env->deferred = new (deferral_t, .defer_env = defer_env, .block = body, .next = env->deferred);
111 if (!env->fn) code_err(ast, "This return statement is not inside any function");
112 ast_t *ret = Match(ast, Return)->value;
114 Text_t code = EMPTY_TEXT;
115 for (deferral_t *deferred = env->deferred; deferred; deferred = deferred->next) {
116 code = Texts(code, compile_statement(deferred->defer_env, deferred->block));
120 if (env->fn->tag == Lambda) {
121 if (Match(env->fn, Lambda)->ret_type != NULL)
122 ret_type = parse_type_ast(env, Match(env->fn, Lambda)->ret_type);
123 else ret_type = ret ? get_type(env, ret) : Type(VoidType);
125 ret_type = get_function_return_type(env, env->fn);
129 if (ret_type->tag == VoidType || ret_type->tag == AbortType)
130 code_err(ast, "This function is not supposed to return any values, "
131 "according to its type signature");
133 env = with_enum_scope(env, ret_type);
134 if (env->fn->tag == ConvertDef) {
135 type_t *value_type = get_type(env, ret);
136 if (!type_eq(value_type, ret_type)) {
137 code_err(ret, "This value is a ", type_to_text(value_type),
138 " but this conversion needs an explicit ", type_to_text(ret_type));
141 Text_t value = compile_to_type(env, ret, ret_type);
143 code = Texts(compile_declaration(ret_type, Text("ret")), " = ", value, ";\n", code);
147 return Texts(code, "return ", value, ";");
149 if (ret_type->tag != VoidType)
150 code_err(ast, "This function expects you to return a ", type_to_text(ret_type), " value");
151 return Texts(code, "return;");
154 case While: return compile_while(env, ast);
155 case Repeat: return compile_repeat(env, ast);
156 case For: return compile_for_loop(env, ast);
157 case If: return compile_if_statement(env, ast);
158 case Block: return compile_block(env, ast);
159 case Comprehension: {
160 if (!env->comprehension_action) code_err(ast, "I don't know what to do with this comprehension!");
161 DeclareMatch(comp, ast, Comprehension);
162 if (comp->expr->tag == Comprehension) { // Nested comprehension
163 ast_t *body = comp->filter ? WrapAST(ast, If, .condition = comp->filter, .body = comp->expr) : comp->expr;
164 ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body);
165 return compile_statement(env, loop);
168 // List/Set/Table comprehension:
169 comprehension_body_t get_body = (void *)env->comprehension_action->fn;
170 ast_t *body = get_body(comp->expr, env->comprehension_action->userdata);
171 if (comp->filter) body = WrapAST(comp->expr, If, .condition = comp->filter, .body = body);
172 ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body);
173 return compile_statement(env, loop);
176 DeclareMatch(inline_code, ast, InlineCCode);
177 Text_t code = EMPTY_TEXT;
178 for (ast_list_t *chunk = inline_code->chunks; chunk; chunk = chunk->next) {
179 if (chunk->ast->tag == TextLiteral) {
180 code = Texts(code, Match(chunk->ast, TextLiteral)->text);
182 code = Texts(code, compile(env, chunk->ast));
188 DeclareMatch(use, ast, Use);
189 if (use->what == USE_LOCAL) {
190 Path_t path = Path$from_str(Match(ast, Use)->path);
191 Path_t in_file = Path$from_str(ast->file->filename);
192 path = Path$resolved(path, Path$parent(in_file));
193 Text_t suffix = get_id_suffix(Path$as_c_string(path));
194 return with_source_info(env, ast, Texts("$initialize", suffix, "();\n"));
195 } else if (use->what == USE_PACKAGE) {
196 OptionalPath_t installed = find_installed_package(ast);
198 List_t children = Path$glob(Path$child(installed, Text("/[!._0-9]*.tm")));
199 Text_t initialization = EMPTY_TEXT;
200 for (int64_t i = 0; i < (int64_t)children.length; i++) {
201 Path_t filename = *(Path_t *)(children.data + i * children.stride);
202 initialization = Texts(
203 initialization, with_source_info(env, ast, Texts("$initialize", get_id_suffix(filename), "();\n")));
205 return initialization;
210 case Metadata: return EMPTY_TEXT;
212 if (!is_discardable(env, ast))
214 ast, "The ", type_to_text(get_type(env, ast)),
215 " value of this statement is implicitly ignored. \n Use `_ := ` if you want to explicitly discard it.");
216 return Texts("(void)", compile(env, ast), ";");
220 Text_t compile_statement(env_t *env, ast_t *ast) {
221 Text_t stmt = _compile_statement(env, ast);
222 return with_source_info(env, ast, stmt);