aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compile.c334
-rw-r--r--src/compile/lists.c215
-rw-r--r--src/compile/lists.h1
-rw-r--r--src/compile/sets.c90
-rw-r--r--src/compile/sets.h1
-rw-r--r--src/compile/tables.c70
-rw-r--r--src/compile/tables.h1
7 files changed, 382 insertions, 330 deletions
diff --git a/src/compile.c b/src/compile.c
index fa47e63c..3f7585fd 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -1964,11 +1964,7 @@ Text_t compile(env_t *env, ast_t *ast) {
compile(env, call->self), "}, ", compile_type_info(self_t), ")");
}
- 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;
-
+ type_t *self_value_t = value_type(self_t);
if (self_value_t->tag == TypeInfoType || self_value_t->tag == ModuleType) {
return compile(env,
WrapAST(ast, FunctionCall,
@@ -1984,331 +1980,10 @@ Text_t compile(env_t *env, ast_t *ast) {
.fn = WrapAST(call->self, FieldAccess, .fielded = call->self, .field = call->name),
.args = call->args));
- Text_t self = compile(env, call->self);
-
-#define EXPECT_POINTER(article, name) \
- do { \
- if (pointer_depth < 1) \
- code_err(call->self, "I expected " article " " name " pointer here, not " article " " name " value"); \
- else if (pointer_depth > 1) \
- code_err(call->self, "I expected " article " " name " pointer here, not a nested " name " pointer"); \
- } while (0)
switch (self_value_t->tag) {
- case ListType: {
- type_t *item_t = Match(self_value_t, ListType)->item_type;
- Text_t padded_item_size = Texts("sizeof(", compile_type(item_t), ")");
-
- if (streq(call->name, "insert")) {
- EXPECT_POINTER("a", "list");
- arg_t *arg_spec =
- new (arg_t, .name = "item", .type = item_t,
- .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "0")));
- return Texts("List$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- padded_item_size, ")");
- } else if (streq(call->name, "insert_all")) {
- EXPECT_POINTER("a", "list");
- arg_t *arg_spec =
- new (arg_t, .name = "items", .type = self_value_t,
- .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "0")));
- return Texts("List$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- padded_item_size, ")");
- } else if (streq(call->name, "remove_at")) {
- EXPECT_POINTER("a", "list");
- arg_t *arg_spec = new (
- arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "-1"),
- .next = new (arg_t, .name = "count", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "1")));
- return Texts("List$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- padded_item_size, ")");
- } else if (streq(call->name, "remove_item")) {
- EXPECT_POINTER("a", "list");
- arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t,
- .next = new (arg_t, .name = "max_count", .type = INT_TYPE,
- .default_val = FakeAST(Int, .str = "-1")));
- return Texts("List$remove_item_value(", self, ", ", 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 = "item", .type = item_t);
- return Texts("List$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- compile_type_info(self_value_t), ")");
- } else if (streq(call->name, "sample")) {
- type_t *random_num_type = parse_type_string(env, "func(->Num)?");
- self = compile_to_pointer_depth(env, call->self, 0, false);
- arg_t *arg_spec =
- new (arg_t, .name = "count", .type = INT_TYPE,
- .next = new (arg_t, .name = "weights",
- .type = Type(ListType, .item_type = Type(NumType, .bits = TYPE_NBITS64)),
- .default_val = FakeAST(None),
- .next = new (arg_t, .name = "random", .type = random_num_type,
- .default_val = FakeAST(None))));
- return Texts("List$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- padded_item_size, ")");
- } else if (streq(call->name, "shuffle")) {
- type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?");
- EXPECT_POINTER("a", "list");
- arg_t *arg_spec =
- new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None));
- return Texts("List$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- padded_item_size, ")");
- } else if (streq(call->name, "shuffled")) {
- type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?");
- self = compile_to_pointer_depth(env, call->self, 0, false);
- arg_t *arg_spec =
- new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None));
- return Texts("List$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- padded_item_size, ")");
- } else if (streq(call->name, "random")) {
- type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?");
- self = compile_to_pointer_depth(env, call->self, 0, false);
- arg_t *arg_spec =
- new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None));
- return Texts("List$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- compile_type(item_t), ")");
- } else if (streq(call->name, "sort") || streq(call->name, "sorted")) {
- if (streq(call->name, "sort")) EXPECT_POINTER("a", "list");
- else self = compile_to_pointer_depth(env, call->self, 0, false);
- Text_t comparison;
- if (call->args) {
- type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
- type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
- {.name = "y", .type = item_ptr});
- arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t));
- comparison = compile_arguments(env, ast, arg_spec, call->args);
- } else {
- comparison = Texts("((Closure_t){.fn=generic_compare, "
- ".userdata=(void*)",
- compile_type_info(item_t), "})");
- }
- return Texts("List$", call->name, "(", self, ", ", comparison, ", ", padded_item_size, ")");
- } else if (streq(call->name, "heapify")) {
- EXPECT_POINTER("a", "list");
- Text_t comparison;
- if (call->args) {
- type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
- type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
- {.name = "y", .type = item_ptr});
- arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t));
- comparison = compile_arguments(env, ast, arg_spec, call->args);
- } else {
- comparison = Texts("((Closure_t){.fn=generic_compare, "
- ".userdata=(void*)",
- compile_type_info(item_t), "})");
- }
- return Texts("List$heapify(", self, ", ", comparison, ", ", padded_item_size, ")");
- } else if (streq(call->name, "heap_push")) {
- EXPECT_POINTER("a", "list");
- type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
- type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
- {.name = "y", .type = item_ptr});
- ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, "
- ".userdata=(void*)",
- compile_type_info(item_t), "})"),
- .type = Type(ClosureType, .fn = fn_t));
- arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t,
- .next = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t),
- .default_val = default_cmp));
- Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args);
- return Texts("List$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")");
- } else if (streq(call->name, "heap_pop")) {
- EXPECT_POINTER("a", "list");
- type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
- type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
- {.name = "y", .type = item_ptr});
- ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, "
- ".userdata=(void*)",
- compile_type_info(item_t), "})"),
- .type = Type(ClosureType, .fn = fn_t));
- arg_t *arg_spec =
- new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), .default_val = default_cmp);
- Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args);
- return Texts("List$heap_pop_value(", self, ", ", arg_code, ", ", compile_type(item_t), ", _, ",
- promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")");
- } else if (streq(call->name, "binary_search")) {
- self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL);
- type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
- type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
- {.name = "y", .type = item_ptr});
- ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, "
- ".userdata=(void*)",
- compile_type_info(item_t), "})"),
- .type = Type(ClosureType, .fn = fn_t));
- arg_t *arg_spec = new (arg_t, .name = "target", .type = item_t,
- .next = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t),
- .default_val = default_cmp));
- Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args);
- return Texts("List$binary_search_value(", self, ", ", arg_code, ")");
- } else if (streq(call->name, "clear")) {
- EXPECT_POINTER("a", "list");
- (void)compile_arguments(env, ast, NULL, call->args);
- return Texts("List$clear(", self, ")");
- } else if (streq(call->name, "find")) {
- self = compile_to_pointer_depth(env, call->self, 0, false);
- arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t);
- return Texts("List$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- compile_type_info(self_value_t), ")");
- } else if (streq(call->name, "where")) {
- self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL);
- type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
- type_t *predicate_type =
- Type(ClosureType, .fn = NewFunctionType(Type(BoolType), {.name = "item", .type = item_ptr}));
- arg_t *arg_spec = new (arg_t, .name = "predicate", .type = predicate_type);
- return Texts("List$first(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
- } else if (streq(call->name, "from")) {
- self = compile_to_pointer_depth(env, call->self, 0, true);
- arg_t *arg_spec = new (arg_t, .name = "first", .type = INT_TYPE);
- return Texts("List$from(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
- } else if (streq(call->name, "to")) {
- self = compile_to_pointer_depth(env, call->self, 0, true);
- arg_t *arg_spec = new (arg_t, .name = "last", .type = INT_TYPE);
- return Texts("List$to(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
- } else if (streq(call->name, "slice")) {
- self = compile_to_pointer_depth(env, call->self, 0, true);
- arg_t *arg_spec = new (arg_t, .name = "first", .type = INT_TYPE,
- .next = new (arg_t, .name = "last", .type = INT_TYPE));
- return Texts("List$slice(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
- } else if (streq(call->name, "by")) {
- self = compile_to_pointer_depth(env, call->self, 0, true);
- arg_t *arg_spec = new (arg_t, .name = "stride", .type = INT_TYPE);
- return Texts("List$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- padded_item_size, ")");
- } else if (streq(call->name, "reversed")) {
- self = compile_to_pointer_depth(env, call->self, 0, true);
- (void)compile_arguments(env, ast, NULL, call->args);
- return Texts("List$reversed(", self, ", ", padded_item_size, ")");
- } else if (streq(call->name, "unique")) {
- self = compile_to_pointer_depth(env, call->self, 0, false);
- (void)compile_arguments(env, ast, NULL, call->args);
- return Texts("Table$from_entries(", self, ", Set$info(", compile_type_info(item_t), "))");
- } else if (streq(call->name, "pop")) {
- EXPECT_POINTER("a", "list");
- arg_t *arg_spec = new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, "-1"));
- Text_t index = compile_arguments(env, ast, arg_spec, call->args);
- return Texts("List$pop(", self, ", ", index, ", ", compile_type(item_t), ", _, ",
- promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")");
- } else if (streq(call->name, "counts")) {
- self = compile_to_pointer_depth(env, call->self, 0, false);
- (void)compile_arguments(env, ast, NULL, call->args);
- return Texts("List$counts(", self, ", ", compile_type_info(self_value_t), ")");
- } else code_err(ast, "There is no '", call->name, "' method for lists");
- }
- case SetType: {
- DeclareMatch(set, self_value_t, SetType);
- 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 = set->item_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, "add")) {
- EXPECT_POINTER("a", "set");
- arg_t *arg_spec = new (arg_t, .name = "item", .type = set->item_type);
- return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
- ", NULL, ", compile_type_info(self_value_t), ")");
- } else if (streq(call->name, "add_all")) {
- EXPECT_POINTER("a", "set");
- arg_t *arg_spec = new (arg_t, .name = "items",
- .type = Type(ListType, .item_type = Match(self_value_t, SetType)->item_type));
- return Texts("({ Table_t *set = ", self, "; ",
- "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ",
- "for (int64_t i = 0; i < to_add.length; i++)\n"
- "Table$set(set, to_add.data + i*to_add.stride, NULL, ",
- compile_type_info(self_value_t), ");\n", "(void)0; })");
- } else if (streq(call->name, "remove")) {
- EXPECT_POINTER("a", "set");
- arg_t *arg_spec = new (arg_t, .name = "item", .type = set->item_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, "remove_all")) {
- EXPECT_POINTER("a", "set");
- arg_t *arg_spec = new (arg_t, .name = "items",
- .type = Type(ListType, .item_type = Match(self_value_t, SetType)->item_type));
- return Texts("({ Table_t *set = ", self, "; ",
- "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ",
- "for (int64_t i = 0; i < to_add.length; i++)\n"
- "Table$remove(set, to_add.data + i*to_add.stride, ",
- compile_type_info(self_value_t), ");\n", "(void)0; })");
- } else if (streq(call->name, "clear")) {
- EXPECT_POINTER("a", "set");
- (void)compile_arguments(env, ast, NULL, call->args);
- return Texts("Table$clear(", self, ")");
- } 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, "overlap")) {
- 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$overlap(", 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, "is_subset_of")) {
- self = compile_to_pointer_depth(env, call->self, 0, false);
- arg_t *arg_spec = new (
- arg_t, .name = "other", .type = self_value_t,
- .next = new (arg_t, .name = "strict", .type = Type(BoolType), .default_val = FakeAST(Bool, false)));
- return Texts("Table$is_subset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
- compile_type_info(self_value_t), ")");
- } else if (streq(call->name, "is_superset_of")) {
- self = compile_to_pointer_depth(env, call->self, 0, false);
- arg_t *arg_spec = new (
- arg_t, .name = "other", .type = self_value_t,
- .next = new (arg_t, .name = "strict", .type = Type(BoolType), .default_val = FakeAST(Bool, false)));
- return Texts("Table$is_superset_of(", 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");
- }
- case TableType: {
- 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("a", "table");
- 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("a", "table");
- 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("a", "table");
- (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 code_err(ast, "There is no '", call->name, "' method for tables");
- }
+ case ListType: return compile_list_method_call(env, ast);
+ case SetType: return compile_set_method_call(env, ast);
+ case TableType: return compile_table_method_call(env, ast);
default: {
DeclareMatch(methodcall, ast, MethodCall);
type_t *fn_t = get_method_type(env, methodcall->self, methodcall->name);
@@ -2318,7 +1993,6 @@ Text_t compile(env_t *env, ast_t *ast) {
return Texts(b->code, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, args), ")");
}
}
-#undef EXPECT_POINTER
}
case FunctionCall: return compile_function_call(env, ast);
case Deserialize: {
diff --git a/src/compile/lists.c b/src/compile/lists.c
index 01036cda..e5b024ee 100644
--- a/src/compile/lists.c
+++ b/src/compile/lists.c
@@ -11,6 +11,11 @@
#include "../environment.h"
#include "../stdlib/text.h"
#include "../stdlib/util.h"
+#include "../typecheck.h"
+#include "functions.h"
+#include "optionals.h"
+#include "pointers.h"
+#include "promotion.h"
static ast_t *add_to_list_comprehension(ast_t *item, ast_t *subject) {
return WrapAST(item, MethodCall, .name = "insert", .self = subject, .args = new (arg_ast_t, .value = item));
@@ -58,4 +63,214 @@ list_comprehension: {
}
}
+public
+Text_t compile_list_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 list pointer here, not a list value"); \
+ else if (pointer_depth > 1) code_err(call->self, "I expected a list pointer here, not a nested list pointer"); \
+ } while (0)
+ type_t *item_t = Match(self_value_t, ListType)->item_type;
+ Text_t padded_item_size = Texts("sizeof(", compile_type(item_t), ")");
+
+ if (streq(call->name, "insert")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec =
+ new (arg_t, .name = "item", .type = item_t,
+ .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "0")));
+ return Texts("List$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ padded_item_size, ")");
+ } else if (streq(call->name, "insert_all")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec =
+ new (arg_t, .name = "items", .type = self_value_t,
+ .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "0")));
+ return Texts("List$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ padded_item_size, ")");
+ } else if (streq(call->name, "remove_at")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec =
+ new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "-1"),
+ .next = new (arg_t, .name = "count", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "1")));
+ return Texts("List$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ padded_item_size, ")");
+ } else if (streq(call->name, "remove_item")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec =
+ new (arg_t, .name = "item", .type = item_t,
+ .next = new (arg_t, .name = "max_count", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "-1")));
+ return Texts("List$remove_item_value(", self, ", ", 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 = "item", .type = item_t);
+ return Texts("List$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ compile_type_info(self_value_t), ")");
+ } else if (streq(call->name, "sample")) {
+ type_t *random_num_type = parse_type_string(env, "func(->Num)?");
+ self = compile_to_pointer_depth(env, call->self, 0, false);
+ arg_t *arg_spec =
+ new (arg_t, .name = "count", .type = INT_TYPE,
+ .next = new (
+ arg_t, .name = "weights", .type = Type(ListType, .item_type = Type(NumType, .bits = TYPE_NBITS64)),
+ .default_val = FakeAST(None),
+ .next = new (arg_t, .name = "random", .type = random_num_type, .default_val = FakeAST(None))));
+ return Texts("List$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ padded_item_size, ")");
+ } else if (streq(call->name, "shuffle")) {
+ type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?");
+ EXPECT_POINTER();
+ arg_t *arg_spec = new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None));
+ return Texts("List$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ padded_item_size, ")");
+ } else if (streq(call->name, "shuffled")) {
+ type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?");
+ self = compile_to_pointer_depth(env, call->self, 0, false);
+ arg_t *arg_spec = new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None));
+ return Texts("List$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ padded_item_size, ")");
+ } else if (streq(call->name, "random")) {
+ type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?");
+ self = compile_to_pointer_depth(env, call->self, 0, false);
+ arg_t *arg_spec = new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None));
+ return Texts("List$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ compile_type(item_t), ")");
+ } else if (streq(call->name, "sort") || streq(call->name, "sorted")) {
+ if (streq(call->name, "sort")) EXPECT_POINTER();
+ else self = compile_to_pointer_depth(env, call->self, 0, false);
+ Text_t comparison;
+ if (call->args) {
+ type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
+ type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
+ {.name = "y", .type = item_ptr});
+ arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t));
+ comparison = compile_arguments(env, ast, arg_spec, call->args);
+ } else {
+ comparison = Texts("((Closure_t){.fn=generic_compare, "
+ ".userdata=(void*)",
+ compile_type_info(item_t), "})");
+ }
+ return Texts("List$", call->name, "(", self, ", ", comparison, ", ", padded_item_size, ")");
+ } else if (streq(call->name, "heapify")) {
+ EXPECT_POINTER();
+ Text_t comparison;
+ if (call->args) {
+ type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
+ type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
+ {.name = "y", .type = item_ptr});
+ arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t));
+ comparison = compile_arguments(env, ast, arg_spec, call->args);
+ } else {
+ comparison = Texts("((Closure_t){.fn=generic_compare, "
+ ".userdata=(void*)",
+ compile_type_info(item_t), "})");
+ }
+ return Texts("List$heapify(", self, ", ", comparison, ", ", padded_item_size, ")");
+ } else if (streq(call->name, "heap_push")) {
+ EXPECT_POINTER();
+ type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
+ type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
+ {.name = "y", .type = item_ptr});
+ ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, "
+ ".userdata=(void*)",
+ compile_type_info(item_t), "})"),
+ .type = Type(ClosureType, .fn = fn_t));
+ arg_t *arg_spec =
+ new (arg_t, .name = "item", .type = item_t,
+ .next = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), .default_val = default_cmp));
+ Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args);
+ return Texts("List$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")");
+ } else if (streq(call->name, "heap_pop")) {
+ EXPECT_POINTER();
+ type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
+ type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
+ {.name = "y", .type = item_ptr});
+ ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, "
+ ".userdata=(void*)",
+ compile_type_info(item_t), "})"),
+ .type = Type(ClosureType, .fn = fn_t));
+ arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), .default_val = default_cmp);
+ Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args);
+ return Texts("List$heap_pop_value(", self, ", ", arg_code, ", ", compile_type(item_t), ", _, ",
+ promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")");
+ } else if (streq(call->name, "binary_search")) {
+ self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL);
+ type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
+ type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr},
+ {.name = "y", .type = item_ptr});
+ ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, "
+ ".userdata=(void*)",
+ compile_type_info(item_t), "})"),
+ .type = Type(ClosureType, .fn = fn_t));
+ arg_t *arg_spec =
+ new (arg_t, .name = "target", .type = item_t,
+ .next = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), .default_val = default_cmp));
+ Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args);
+ return Texts("List$binary_search_value(", self, ", ", arg_code, ")");
+ } else if (streq(call->name, "clear")) {
+ EXPECT_POINTER();
+ (void)compile_arguments(env, ast, NULL, call->args);
+ return Texts("List$clear(", self, ")");
+ } else if (streq(call->name, "find")) {
+ self = compile_to_pointer_depth(env, call->self, 0, false);
+ arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t);
+ return Texts("List$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ compile_type_info(self_value_t), ")");
+ } else if (streq(call->name, "where")) {
+ self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL);
+ type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true);
+ type_t *predicate_type =
+ Type(ClosureType, .fn = NewFunctionType(Type(BoolType), {.name = "item", .type = item_ptr}));
+ arg_t *arg_spec = new (arg_t, .name = "predicate", .type = predicate_type);
+ return Texts("List$first(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
+ } else if (streq(call->name, "from")) {
+ self = compile_to_pointer_depth(env, call->self, 0, true);
+ arg_t *arg_spec = new (arg_t, .name = "first", .type = INT_TYPE);
+ return Texts("List$from(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
+ } else if (streq(call->name, "to")) {
+ self = compile_to_pointer_depth(env, call->self, 0, true);
+ arg_t *arg_spec = new (arg_t, .name = "last", .type = INT_TYPE);
+ return Texts("List$to(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
+ } else if (streq(call->name, "slice")) {
+ self = compile_to_pointer_depth(env, call->self, 0, true);
+ arg_t *arg_spec =
+ new (arg_t, .name = "first", .type = INT_TYPE, .next = new (arg_t, .name = "last", .type = INT_TYPE));
+ return Texts("List$slice(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
+ } else if (streq(call->name, "by")) {
+ self = compile_to_pointer_depth(env, call->self, 0, true);
+ arg_t *arg_spec = new (arg_t, .name = "stride", .type = INT_TYPE);
+ return Texts("List$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size,
+ ")");
+ } else if (streq(call->name, "reversed")) {
+ self = compile_to_pointer_depth(env, call->self, 0, true);
+ (void)compile_arguments(env, ast, NULL, call->args);
+ return Texts("List$reversed(", self, ", ", padded_item_size, ")");
+ } else if (streq(call->name, "unique")) {
+ self = compile_to_pointer_depth(env, call->self, 0, false);
+ (void)compile_arguments(env, ast, NULL, call->args);
+ return Texts("Table$from_entries(", self, ", Set$info(", compile_type_info(item_t), "))");
+ } else if (streq(call->name, "pop")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec = new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, "-1"));
+ Text_t index = compile_arguments(env, ast, arg_spec, call->args);
+ return Texts("List$pop(", self, ", ", index, ", ", compile_type(item_t), ", _, ",
+ promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")");
+ } else if (streq(call->name, "counts")) {
+ self = compile_to_pointer_depth(env, call->self, 0, false);
+ (void)compile_arguments(env, ast, NULL, call->args);
+ return Texts("List$counts(", self, ", ", compile_type_info(self_value_t), ")");
+ } else {
+ code_err(ast, "There is no '", call->name, "' method for lists");
+ }
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/compile/lists.h b/src/compile/lists.h
index b9bf74d6..4f39b392 100644
--- a/src/compile/lists.h
+++ b/src/compile/lists.h
@@ -3,3 +3,4 @@
#include "../stdlib/datatypes.h"
Text_t compile_typed_list(env_t *env, ast_t *ast, type_t *list_type);
+Text_t compile_list_method_call(env_t *env, ast_t *ast);
diff --git a/src/compile/sets.c b/src/compile/sets.c
index a1471a5a..7cb56fff 100644
--- a/src/compile/sets.c
+++ b/src/compile/sets.c
@@ -4,7 +4,11 @@
#include "../environment.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/text.h"
+#include "../typecheck.h"
#include "../types.h"
+#include "functions.h"
+#include "optionals.h"
+#include "pointers.h"
#include "promotion.h"
static ast_t *add_to_set_comprehension(ast_t *item, ast_t *subject) {
@@ -49,3 +53,89 @@ set_comprehension: {
return code;
}
}
+
+public
+Text_t compile_set_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 set pointer here, not a set value"); \
+ else if (pointer_depth > 1) code_err(call->self, "I expected a set pointer here, not a nested set pointer"); \
+ } while (0)
+ DeclareMatch(set, self_value_t, SetType);
+ 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 = set->item_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, "add")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec = new (arg_t, .name = "item", .type = set->item_type);
+ return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", NULL, ",
+ compile_type_info(self_value_t), ")");
+ } else if (streq(call->name, "add_all")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec =
+ new (arg_t, .name = "items", .type = Type(ListType, .item_type = Match(self_value_t, SetType)->item_type));
+ return Texts("({ Table_t *set = ", self, "; ",
+ "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ",
+ "for (int64_t i = 0; i < to_add.length; i++)\n"
+ "Table$set(set, to_add.data + i*to_add.stride, NULL, ",
+ compile_type_info(self_value_t), ");\n", "(void)0; })");
+ } else if (streq(call->name, "remove")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec = new (arg_t, .name = "item", .type = set->item_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, "remove_all")) {
+ EXPECT_POINTER();
+ arg_t *arg_spec =
+ new (arg_t, .name = "items", .type = Type(ListType, .item_type = Match(self_value_t, SetType)->item_type));
+ return Texts("({ Table_t *set = ", self, "; ",
+ "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ",
+ "for (int64_t i = 0; i < to_add.length; i++)\n"
+ "Table$remove(set, to_add.data + i*to_add.stride, ",
+ compile_type_info(self_value_t), ");\n", "(void)0; })");
+ } 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, "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, "overlap")) {
+ 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$overlap(", 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, "is_subset_of")) {
+ self = compile_to_pointer_depth(env, call->self, 0, false);
+ arg_t *arg_spec =
+ new (arg_t, .name = "other", .type = self_value_t,
+ .next = new (arg_t, .name = "strict", .type = Type(BoolType), .default_val = FakeAST(Bool, false)));
+ return Texts("Table$is_subset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
+ compile_type_info(self_value_t), ")");
+ } else if (streq(call->name, "is_superset_of")) {
+ self = compile_to_pointer_depth(env, call->self, 0, false);
+ arg_t *arg_spec =
+ new (arg_t, .name = "other", .type = self_value_t,
+ .next = new (arg_t, .name = "strict", .type = Type(BoolType), .default_val = FakeAST(Bool, false)));
+ return Texts("Table$is_superset_of(", 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");
+}
diff --git a/src/compile/sets.h b/src/compile/sets.h
index bfbcf3b4..21c96b07 100644
--- a/src/compile/sets.h
+++ b/src/compile/sets.h
@@ -5,3 +5,4 @@
#include "../types.h"
Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type);
+Text_t compile_set_method_call(env_t *env, ast_t *ast);
diff --git a/src/compile/tables.c b/src/compile/tables.c
index 0ddd310c..b53aacb0 100644
--- a/src/compile/tables.c
+++ b/src/compile/tables.c
@@ -4,7 +4,11 @@
#include "../environment.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/text.h"
+#include "../typecheck.h"
#include "../types.h"
+#include "functions.h"
+#include "optionals.h"
+#include "pointers.h"
#include "promotion.h"
static ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) {
@@ -74,3 +78,69 @@ table_comprehension: {
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 code_err(ast, "There is no '", call->name, "' method for tables");
+}
diff --git a/src/compile/tables.h b/src/compile/tables.h
index c8c19cb1..579c313f 100644
--- a/src/compile/tables.h
+++ b/src/compile/tables.h
@@ -4,3 +4,4 @@
#include "../types.h"
Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type);
+Text_t compile_table_method_call(env_t *env, ast_t *ast);