diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/compile.c | 334 | ||||
| -rw-r--r-- | src/compile/lists.c | 215 | ||||
| -rw-r--r-- | src/compile/lists.h | 1 | ||||
| -rw-r--r-- | src/compile/sets.c | 90 | ||||
| -rw-r--r-- | src/compile/sets.h | 1 | ||||
| -rw-r--r-- | src/compile/tables.c | 70 | ||||
| -rw-r--r-- | src/compile/tables.h | 1 |
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); |
