code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(169 lines)
1 // This file defines how to compile tables
3 #include "../ast.h"
4 #include "../environment.h"
5 #include "../stdlib/datatypes.h"
6 #include "../stdlib/text.h"
7 #include "../typecheck.h"
8 #include "../types.h"
9 #include "compilation.h"
11 static ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) {
12 DeclareMatch(e, entry, TableEntry);
13 return WrapAST(
14 entry, MethodCall, .name = "set", .self = subject,
15 .args = new (arg_ast_t, .value = e->key,
16 .next = new (arg_ast_t, .value = e->value ? e->value : WrapAST(entry, Var, .name = "PRESENT"))));
19 Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) {
20 DeclareMatch(table, ast, Table);
21 if (!table->entries) {
22 Text_t code = Text("((Table_t){");
23 if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), ")");
24 return Texts(code, "})");
27 type_t *key_t = Match(table_type, TableType)->key_type;
28 if (key_t == NULL) code_err(ast, "I couldn't figure out the key type for this table");
29 type_t *value_t = Match(table_type, TableType)->value_type;
30 if (value_t == NULL) code_err(ast, "I couldn't figure out the value type for this table");
32 if (value_t->tag == OptionalType)
33 code_err(ast, "Tables whose values are optional (", type_to_text(value_t), ") are not currently supported.");
35 for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
36 if (entry->ast->tag == Comprehension) goto table_comprehension;
39 { // No comprehension:
40 env_t *key_scope = key_t->tag == EnumType ? with_enum_scope(env, key_t) : env;
41 env_t *value_scope = value_t->tag == EnumType ? with_enum_scope(env, value_t) : env;
42 Text_t code = Texts("Table(", compile_type(key_t), ", ", compile_type(value_t), ", ", compile_type_info(key_t),
43 ", ", compile_type_info(value_t));
44 if (table->fallback) code = Texts(code, ", /*fallback:*/ heap(", compile(env, table->fallback), ")");
45 else code = Texts(code, ", /*fallback:*/ NULL");
47 size_t n = 0;
48 for (ast_list_t *entry = table->entries; entry; entry = entry->next)
49 ++n;
50 code = Texts(code, ", ", (int64_t)n);
52 for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
53 DeclareMatch(e, entry->ast, TableEntry);
54 code = Texts(code, ",\n\t{", compile_to_type(key_scope, e->key, key_t), ", ",
55 compile_to_type(value_scope, e->value ? e->value : WrapAST(entry->ast, Var, .name = "PRESENT"),
56 value_t),
57 "}");
59 return Texts(code, ")");
62 table_comprehension: {
63 static int64_t comp_num = 1;
64 env_t *scope = fresh_scope(env);
65 const char *comprehension_name = String("table$", comp_num++);
66 ast_t *comprehension_var =
67 LiteralCode(Texts("&", comprehension_name), .type = Type(PointerType, .pointed = table_type, .is_stack = true));
69 Text_t code = Texts("({ Table_t ", comprehension_name, " = {.entries=EMPTY_LIST");
70 if (table->fallback) code = Texts(code, ", .fallback=heap(", compile(env, table->fallback), "), ");
72 code = Texts(code, "};");
74 Closure_t comp_action = {.fn = add_to_table_comprehension, .userdata = comprehension_var};
75 scope->comprehension_action = &comp_action;
76 for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
77 if (entry->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, entry->ast));
78 else code = Texts(code, compile_statement(env, add_to_table_comprehension(entry->ast, comprehension_var)));
80 code = Texts(code, " ", comprehension_name, "; })");
81 return code;
85 public
86 Text_t compile_table_method_call(env_t *env, ast_t *ast) {
87 DeclareMatch(call, ast, MethodCall);
88 type_t *self_t = get_type(env, call->self);
90 int64_t pointer_depth = 0;
91 type_t *self_value_t = self_t;
92 for (; self_value_t->tag == PointerType; self_value_t = Match(self_value_t, PointerType)->pointed)
93 pointer_depth += 1;
95 Text_t self = compile(env, call->self);
97 #define EXPECT_POINTER() \
98 do { \
99 if (pointer_depth < 1) code_err(call->self, "I expected a table pointer here, not a table value"); \
100 else if (pointer_depth > 1) \
101 code_err(call->self, "I expected a table pointer here, not a nested table pointer"); \
102 } while (0)
104 DeclareMatch(table, self_value_t, TableType);
105 if (streq(call->name, "get")) {
106 self = compile_to_pointer_depth(env, call->self, 0, false);
107 arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type);
108 return Texts("Table$get_optional(", self, ", ", compile_type(table->key_type), ", ",
109 compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
110 "_, ", optional_into_nonnone(table->value_type, Text("(*_)")), ", ",
111 compile_none(table->value_type), ", ", compile_type_info(self_value_t), ")");
112 } else if (streq(call->name, "get_or_set")) {
113 self = compile_to_pointer_depth(env, call->self, 1, false);
114 arg_t *arg_spec = new (
115 arg_t, .name = "key", .type = table->key_type,
116 .next = new (arg_t, .name = "default", .type = table->value_type, .default_val = table->default_value));
117 return Texts("*Table$get_or_setdefault(", self, ", ", compile_type(table->key_type), ", ",
118 compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
119 compile_type_info(self_value_t), ")");
120 } else if (streq(call->name, "has")) {
121 self = compile_to_pointer_depth(env, call->self, 0, false);
122 arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type);
123 return Texts("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
124 compile_type_info(self_value_t), ")");
125 } else if (streq(call->name, "set")) {
126 EXPECT_POINTER();
127 arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type,
128 .next = new (arg_t, .name = "value", .type = table->value_type));
129 return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
130 compile_type_info(self_value_t), ")");
131 } else if (streq(call->name, "remove")) {
132 EXPECT_POINTER();
133 arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type);
134 return Texts("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
135 compile_type_info(self_value_t), ")");
136 } else if (streq(call->name, "clear")) {
137 EXPECT_POINTER();
138 (void)compile_arguments(env, ast, NULL, call->args);
139 return Texts("Table$clear(", self, ")");
140 } else if (streq(call->name, "sorted")) {
141 self = compile_to_pointer_depth(env, call->self, 0, false);
142 (void)compile_arguments(env, ast, NULL, call->args);
143 return Texts("Table$sorted(", self, ", ", compile_type_info(self_value_t), ")");
144 } else if (streq(call->name, "with_fallback")) {
145 self = compile_to_pointer_depth(env, call->self, 0, false);
146 arg_t *arg_spec = new (arg_t, .name = "fallback", .type = Type(OptionalType, self_value_t));
147 return Texts("Table$with_fallback(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
148 } else if (streq(call->name, "intersection")) {
149 self = compile_to_pointer_depth(env, call->self, 0, false);
150 arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t);
151 return Texts("Table$intersection(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
152 compile_type_info(self_value_t), ")");
153 } else if (streq(call->name, "with")) {
154 self = compile_to_pointer_depth(env, call->self, 0, false);
155 arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t);
156 return Texts("Table$with(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
157 compile_type_info(self_value_t), ")");
158 } else if (streq(call->name, "without")) {
159 self = compile_to_pointer_depth(env, call->self, 0, false);
160 arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t);
161 return Texts("Table$without(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
162 compile_type_info(self_value_t), ")");
163 } else if (streq(call->name, "difference")) {
164 self = compile_to_pointer_depth(env, call->self, 0, false);
165 arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t);
166 return Texts("Table$difference(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
167 compile_type_info(self_value_t), ")");
168 } else code_err(ast, "There is no '", call->name, "' method for tables");