code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(198 lines)
1 // This file defines how to compile assignments
3 #include "../ast.h"
4 #include "../environment.h"
5 #include "../stdlib/datatypes.h"
6 #include "../stdlib/text.h"
7 #include "../stdlib/util.h"
8 #include "../typecheck.h"
9 #include "compilation.h"
11 public
12 Text_t compile_update_assignment(env_t *env, ast_t *ast) {
13 if (!is_update_assignment(ast)) code_err(ast, "This is not an update assignment");
15 binary_operands_t update = UPDATE_OPERANDS(ast);
17 type_t *lhs_t = get_type(env, update.lhs);
19 bool needs_idemotency_fix = !is_idempotent(update.lhs);
20 Text_t lhs = needs_idemotency_fix ? Text("(*lhs)") : compile_lvalue(env, update.lhs);
22 Text_t update_assignment = EMPTY_TEXT;
23 switch (ast->tag) {
24 case PlusUpdate: {
25 if (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType)
26 update_assignment = Texts(lhs, " += ", compile_to_type(env, update.rhs, lhs_t), ";");
27 break;
29 case MinusUpdate: {
30 if (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType)
31 update_assignment = Texts(lhs, " -= ", compile_to_type(env, update.rhs, lhs_t), ";");
32 break;
34 case MultiplyUpdate: {
35 if (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType)
36 update_assignment = Texts(lhs, " *= ", compile_to_type(env, update.rhs, lhs_t), ";");
37 break;
39 case DivideUpdate: {
40 if (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType)
41 update_assignment = Texts(lhs, " /= ", compile_to_type(env, update.rhs, lhs_t), ";");
42 break;
44 case LeftShiftUpdate: {
45 if (lhs_t->tag == IntType || lhs_t->tag == ByteType)
46 update_assignment = Texts(lhs, " <<= ", compile_to_type(env, update.rhs, lhs_t), ";");
47 break;
49 case RightShiftUpdate: {
50 if (lhs_t->tag == IntType || lhs_t->tag == ByteType)
51 update_assignment = Texts(lhs, " >>= ", compile_to_type(env, update.rhs, lhs_t), ";");
52 break;
54 case AndUpdate: {
55 if (lhs_t->tag == BoolType)
56 update_assignment =
57 Texts("if (", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";");
58 break;
60 case OrUpdate: {
61 if (lhs_t->tag == BoolType)
62 update_assignment =
63 Texts("if (!", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";");
64 break;
66 default: break;
69 if (update_assignment.length == 0) {
70 ast_t *binop = new (ast_t);
71 *binop = *ast;
72 binop->tag = binop_tag(binop->tag);
73 if (needs_idemotency_fix) binop->__data.Plus.lhs = LiteralCode(Text("*lhs"), .type = lhs_t);
74 update_assignment = Texts(lhs, " = ", compile_to_type(env, binop, lhs_t), ";");
77 if (needs_idemotency_fix)
78 return Texts("{ ", compile_declaration(Type(PointerType, .pointed = lhs_t), Text("lhs")), " = &",
79 compile_lvalue(env, update.lhs), "; ", update_assignment, "; }");
80 else return update_assignment;
83 public
84 Text_t compile_assignment(env_t *env, ast_t *target, Text_t value) {
85 return Texts(compile_lvalue(env, target), " = ", value);
88 public
89 Text_t compile_assignment_statement(env_t *env, ast_t *ast) {
90 DeclareMatch(assign, ast, Assign);
91 // Single assignment, no temp vars needed:
92 if (assign->targets && !assign->targets->next) {
93 type_t *lhs_t = get_type(env, assign->targets->ast);
94 if (assign->targets->ast->tag == Index && lhs_t->tag == OptionalType
95 && (value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType
96 || value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == ListType))
97 lhs_t = Match(lhs_t, OptionalType)->type;
98 if (has_stack_memory(lhs_t))
99 code_err(ast, "Stack references cannot be assigned to "
100 "variables because the "
101 "variable's scope may outlive the scope of the "
102 "stack memory.");
103 env_t *val_env = with_enum_scope(env, lhs_t);
104 Text_t val = compile_maybe_incref(val_env, assign->values->ast, lhs_t);
105 return Texts(compile_assignment(env, assign->targets->ast, val), ";\n");
108 Text_t code = Text("{ // Assignment\n");
109 int64_t i = 1;
110 for (ast_list_t *value = assign->values, *target = assign->targets; value && target;
111 value = value->next, target = target->next) {
112 type_t *lhs_t = get_type(env, target->ast);
113 if (target->ast->tag == Index && lhs_t->tag == OptionalType
114 && (value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType
115 || value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == ListType))
116 lhs_t = Match(lhs_t, OptionalType)->type;
117 if (has_stack_memory(lhs_t))
118 code_err(ast, "Stack references cannot be assigned to "
119 "variables because the "
120 "variable's scope may outlive the scope of the "
121 "stack memory.");
122 env_t *val_env = with_enum_scope(env, lhs_t);
123 Text_t val = compile_maybe_incref(val_env, value->ast, lhs_t);
124 code = Texts(code, compile_type(lhs_t), " $", i, " = ", val, ";\n");
125 i += 1;
127 i = 1;
128 for (ast_list_t *target = assign->targets; target; target = target->next) {
129 code = Texts(code, compile_assignment(env, target->ast, Texts("$", i)), ";\n");
130 i += 1;
132 return Texts(code, "\n}");
135 public
136 Text_t compile_lvalue(env_t *env, ast_t *ast) {
137 if (!can_be_mutated(env, ast)) {
138 if (ast->tag == Index) {
139 ast_t *subject = Match(ast, Index)->indexed;
140 code_err(subject, "This is an immutable value, you can't mutate "
141 "its contents");
142 } else if (ast->tag == FieldAccess) {
143 ast_t *subject = Match(ast, FieldAccess)->fielded;
144 type_t *t = get_type(env, subject);
145 code_err(subject, "This is an immutable ", type_to_text(t), " value, you can't assign to its fields");
146 } else {
147 code_err(ast, "This is a value of type ", type_to_text(get_type(env, ast)),
148 " and can't be used as an assignment target");
152 if (ast->tag == Index) {
153 DeclareMatch(index, ast, Index);
154 type_t *container_t = get_type(env, index->indexed);
155 if (container_t->tag == OptionalType)
156 code_err(index->indexed, "This value might be none, so it can't be "
157 "safely used as an assignment target");
159 if (!index->index && container_t->tag == PointerType) return compile(env, ast);
161 container_t = value_type(container_t);
162 type_t *index_t = get_type(env, index->index);
163 if (container_t->tag == ListType) {
164 if (!index->index) code_err(ast, "This list needs an index");
165 Text_t target_code = compile_to_pointer_depth(env, index->indexed, 1, false);
166 type_t *item_type = Match(container_t, ListType)->item_type;
167 Text_t index_code =
168 index->index->tag == Int
169 ? compile_int_to_type(env, index->index, Type(IntType, .bits = TYPE_IBITS64))
170 : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, index->index), ", no)")
171 : Texts("(Int64_t)(", compile(env, index->index), ")"));
172 return Texts("List_lvalue(", compile_type(item_type), ", ", target_code, ", ", index_code, ", ",
173 (int64_t)(ast->start - ast->file->text), ", ", (int64_t)(ast->end - ast->file->text), ")");
174 } else if (container_t->tag == TableType) {
175 if (!index->index) code_err(ast, "This table needs an index");
176 DeclareMatch(table_type, container_t, TableType);
177 if (table_type->default_value) {
178 type_t *value_type = get_type(env, table_type->default_value);
179 return Texts("*Table$get_or_setdefault(", compile_to_pointer_depth(env, index->indexed, 1, false), ", ",
180 compile_type(table_type->key_type), ", ", compile_type(value_type), ", ",
181 compile_maybe_incref(env, index->index, table_type->key_type), ", ",
182 compile_maybe_incref(env, table_type->default_value, table_type->value_type), ", ",
183 compile_type_info(container_t), ")");
185 return Texts("*(", compile_type(Type(PointerType, table_type->value_type)), ")Table$reserve(",
186 compile_to_pointer_depth(env, index->indexed, 1, false), ", ", "stack(",
187 compile_maybe_incref(env, index->index, table_type->key_type), ")", ", NULL,",
188 compile_type_info(container_t), ")");
189 } else {
190 code_err(ast, "I don't know how to assign to this target");
192 } else if (ast->tag == Var || ast->tag == FieldAccess || ast->tag == InlineCCode) {
193 return compile(env, ast);
194 } else {
195 code_err(ast, "I don't know how to assign to this");
197 return EMPTY_TEXT;