123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 | // This file defines how to compile tables
#include "../ast.h"
#include "../environment.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/text.h"
#include "../typecheck.h"
#include "../types.h"
#include "compilation.h"
static ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) {
DeclareMatch(e, entry, TableEntry);
return WrapAST(
entry, MethodCall, .name = "set", .self = subject,
.args = new (arg_ast_t, .value = e->key,
.next = new (arg_ast_t, .value = e->value ? e->value : WrapAST(entry, Var, .name = "PRESENT"))));
}
Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) {
DeclareMatch(table, ast, Table);
if (!table->entries) {
Text_t code = Text("((Table_t){");
if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), ")");
return Texts(code, "})");
}
type_t *key_t = Match(table_type, TableType)->key_type;
if (key_t == NULL) code_err(ast, "I couldn't figure out the key type for this table");
type_t *value_t = Match(table_type, TableType)->value_type;
if (value_t == NULL) code_err(ast, "I couldn't figure out the value type for this table");
if (value_t->tag == OptionalType)
code_err(ast, "Tables whose values are optional (", type_to_text(value_t), ") are not currently supported.");
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
if (entry->ast->tag == Comprehension) goto table_comprehension;
}
{ // No comprehension:
env_t *key_scope = key_t->tag == EnumType ? with_enum_scope(env, key_t) : env;
env_t *value_scope = value_t->tag == EnumType ? with_enum_scope(env, value_t) : env;
Text_t code = Texts("Table(", compile_type(key_t), ", ", compile_type(value_t), ", ", compile_type_info(key_t),
", ", compile_type_info(value_t));
if (table->fallback) code = Texts(code, ", /*fallback:*/ heap(", compile(env, table->fallback), ")");
else code = Texts(code, ", /*fallback:*/ NULL");
size_t n = 0;
for (ast_list_t *entry = table->entries; entry; entry = entry->next)
++n;
code = Texts(code, ", ", (int64_t)n);
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
DeclareMatch(e, entry->ast, TableEntry);
code = Texts(code, ",\n\t{", compile_to_type(key_scope, e->key, key_t), ", ",
compile_to_type(value_scope, e->value ? e->value : WrapAST(entry->ast, Var, .name = "PRESENT"),
value_t),
"}");
}
return Texts(code, ")");
}
table_comprehension: {
static int64_t comp_num = 1;
env_t *scope = fresh_scope(env);
const char *comprehension_name = String("table$", comp_num++);
ast_t *comprehension_var =
LiteralCode(Texts("&", comprehension_name), .type = Type(PointerType, .pointed = table_type, .is_stack = true));
Text_t code = Texts("({ Table_t ", comprehension_name, " = {.entries=EMPTY_LIST");
if (table->fallback) code = Texts(code, ", .fallback=heap(", compile(env, table->fallback), "), ");
code = Texts(code, "};");
Closure_t comp_action = {.fn = add_to_table_comprehension, .userdata = comprehension_var};
scope->comprehension_action = &comp_action;
for (ast_list_t *entry = table->entries; entry; entry = entry->next) {
if (entry->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, entry->ast));
else code = Texts(code, compile_statement(env, add_to_table_comprehension(entry->ast, comprehension_var)));
}
code = Texts(code, " ", comprehension_name, "; })");
return code;
}
}
public
Text_t compile_table_method_call(env_t *env, ast_t *ast) {
DeclareMatch(call, ast, MethodCall);
type_t *self_t = get_type(env, call->self);
int64_t pointer_depth = 0;
type_t *self_value_t = self_t;
for (; self_value_t->tag == PointerType; self_value_t = Match(self_value_t, PointerType)->pointed)
pointer_depth += 1;
Text_t self = compile(env, call->self);
#define EXPECT_POINTER() \
do { \
if (pointer_depth < 1) code_err(call->self, "I expected a table pointer here, not a table value"); \
else if (pointer_depth > 1) \
code_err(call->self, "I expected a table pointer here, not a nested table pointer"); \
} while (0)
DeclareMatch(table, self_value_t, TableType);
if (streq(call->name, "get")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type);
return Texts("Table$get_optional(", self, ", ", compile_type(table->key_type), ", ",
compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
"_, ", optional_into_nonnone(table->value_type, Text("(*_)")), ", ",
compile_none(table->value_type), ", ", compile_type_info(self_value_t), ")");
} else if (streq(call->name, "get_or_set")) {
self = compile_to_pointer_depth(env, call->self, 1, false);
arg_t *arg_spec = new (
arg_t, .name = "key", .type = table->key_type,
.next = new (arg_t, .name = "default", .type = table->value_type, .default_val = table->default_value));
return Texts("*Table$get_or_setdefault(", self, ", ", compile_type(table->key_type), ", ",
compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(self_value_t), ")");
} else if (streq(call->name, "has")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type);
return Texts("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(self_value_t), ")");
} else if (streq(call->name, "set")) {
EXPECT_POINTER();
arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type,
.next = new (arg_t, .name = "value", .type = table->value_type));
return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(self_value_t), ")");
} else if (streq(call->name, "remove")) {
EXPECT_POINTER();
arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type);
return Texts("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(self_value_t), ")");
} else if (streq(call->name, "clear")) {
EXPECT_POINTER();
(void)compile_arguments(env, ast, NULL, call->args);
return Texts("Table$clear(", self, ")");
} else if (streq(call->name, "sorted")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args);
return Texts("Table$sorted(", self, ", ", compile_type_info(self_value_t), ")");
} else if (streq(call->name, "with_fallback")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new (arg_t, .name = "fallback", .type = Type(OptionalType, self_value_t));
return Texts("Table$with_fallback(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
} else if (streq(call->name, "intersection")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t);
return Texts("Table$intersection(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(self_value_t), ")");
} else if (streq(call->name, "with")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t);
return Texts("Table$with(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(self_value_t), ")");
} else if (streq(call->name, "without")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t);
return Texts("Table$without(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(self_value_t), ")");
} else if (streq(call->name, "difference")) {
self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t);
return Texts("Table$difference(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(self_value_t), ")");
} else code_err(ast, "There is no '", call->name, "' method for tables");
}
|