aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtins/array.c43
-rw-r--r--builtins/array.h1
-rw-r--r--compile.c26
-rw-r--r--test/arrays.tm13
-rw-r--r--typecheck.c6
5 files changed, 81 insertions, 8 deletions
diff --git a/builtins/array.c b/builtins/array.c
index 2d45591c..7086ff7f 100644
--- a/builtins/array.c
+++ b/builtins/array.c
@@ -320,6 +320,49 @@ public array_t Array$reversed(array_t array)
return reversed;
}
+typedef struct {
+ array_t arr;
+ int64_t i, j, item_size;
+ bool self_pairs:1, ordered:1;
+} pair_info_t;
+
+static bool next_pair(void *x, void *y, pair_info_t *info)
+{
+ if (info->i > info->arr.length || info->j > info->arr.length)
+ return false;
+
+ memcpy(x, info->arr.data + info->arr.stride * (info->i-1), info->item_size);
+ memcpy(y, info->arr.data + info->arr.stride * (info->j-1), info->item_size);
+ info->j += 1;
+ if (!info->self_pairs && info->j == info->i)
+ info->j += 1;
+
+ if (info->j > info->arr.length) {
+ info->i += 1;
+ if (info->ordered)
+ info->j = 1;
+ else if (info->self_pairs)
+ info->j = info->i;
+ else
+ info->j = info->i + 1;
+ }
+ return true;
+}
+
+public closure_t Array$pairs(array_t arr, bool self_pairs, bool ordered, const TypeInfo *type)
+{
+ return (closure_t){
+ .fn=next_pair,
+ .userdata=new(pair_info_t,
+ .arr=arr,
+ .i=1,
+ .j=self_pairs ? 1 : 2,
+ .item_size=get_item_size(type),
+ .self_pairs=self_pairs,
+ .ordered=ordered),
+ };
+}
+
public array_t Array$concat(array_t x, array_t y, const TypeInfo *type)
{
int64_t item_size = get_item_size(type);
diff --git a/builtins/array.h b/builtins/array.h
index 56794a22..1b7fa4cb 100644
--- a/builtins/array.h
+++ b/builtins/array.h
@@ -69,6 +69,7 @@ array_t Array$to(array_t *array, int64_t last);
array_t Array$by(array_t *array, int64_t stride);
array_t Array$reversed(array_t array);
array_t Array$concat(array_t x, array_t y, const TypeInfo *type);
+closure_t Array$pairs(array_t x, bool self_pairs, bool ordered, const TypeInfo *type);
uint32_t Array$hash(const array_t *arr, const TypeInfo *type);
int32_t Array$compare(const array_t *x, const array_t *y, const TypeInfo *type);
bool Array$equal(const array_t *x, const array_t *y, const TypeInfo *type);
diff --git a/compile.c b/compile.c
index 9db1642e..7c69ace3 100644
--- a/compile.c
+++ b/compile.c
@@ -909,21 +909,28 @@ CORD compile_statement(env_t *env, ast_t *ast)
closure_fn_args = new(arg_t, .name="userdata", .type=Type(PointerType, .pointed=Type(MemoryType)), .next=closure_fn_args);
REVERSE_LIST(closure_fn_args);
CORD fn_type_code = compile_type(Type(FunctionType, .args=closure_fn_args, .ret=Match(fn_t, FunctionType)->ret));
- next_fn = CORD_all("((", fn_type_code, ")next.fn)");
+ next_fn = CORD_all("((", fn_type_code, ")next.fn)(");
} else {
- next_fn = "next";
+ next_fn = "next(";
}
- code = CORD_all(code, "for(; ", next_fn, "(");
for (ast_list_t *var = for_->vars; var; var = var->next) {
const char *name = Match(var->ast, Var)->name;
- code = CORD_all(code, "&$", name);
+ next_fn = CORD_all(next_fn, "&$", name);
if (var->next || iter_t->tag == ClosureType)
- code = CORD_all(code, ", ");
+ next_fn = CORD_all(next_fn, ", ");
}
if (iter_t->tag == ClosureType)
- code = CORD_all(code, "next.userdata");
- code = CORD_all(code, "); ) {\n\t", body, "}\n", stop, "\n}\n");
+ next_fn = CORD_all(next_fn, "next.userdata");
+ next_fn = CORD_all(next_fn, ")");
+
+ if (for_->empty) {
+ code = CORD_all(code, "if (", next_fn, ") {\n"
+ "\tdo{\n\t\t", body, "\t} while(", next_fn, ");\n"
+ "} else {\n\t", compile_statement(env, for_->empty), "}", stop, "\n}\n");
+ } else {
+ code = CORD_all(code, "for(; ", next_fn, "; ) {\n\t", body, "}\n", stop, "\n}\n");
+ }
return code;
}
default: code_err(for_->iter, "Iteration is not implemented for type: %T", iter_t);
@@ -1880,6 +1887,11 @@ CORD compile(env_t *env, ast_t *ast)
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Array$reversed(", self, ")");
+ } else if (streq(call->name, "pairs")) {
+ CORD self = compile_to_pointer_depth(env, call->self, 0, false);
+ arg_t *arg_spec = new(arg_t, .name="self_pairs", .default_val=FakeAST(Bool, false), .type=Type(BoolType),
+ .next=new(arg_t, .name="ordered", .default_val=FakeAST(Bool, false), .type=Type(BoolType)));
+ return CORD_all("Array$pairs(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(env, self_value_t), ")");
} else code_err(ast, "There is no '%s' method for arrays", call->name);
}
case TableType: {
diff --git a/test/arrays.tm b/test/arrays.tm
index fb6d16ca..ed43acb0 100644
--- a/test/arrays.tm
+++ b/test/arrays.tm
@@ -143,3 +143,16 @@ func main():
>> [i*10 for i in 10]:by(2):by(-1)
= [90, 70, 50, 30, 10]
+
+ do:
+ strs := ["A", "B", "C"]
+ >> ["{x}{y}" for x, y in strs:pairs()]
+ = ["AB", "AC", "BC"]
+ >> ["{x}{y}" for x, y in strs:pairs(self_pairs=yes)]
+ = ["AA", "AB", "AC", "BB", "BC", "CC"]
+ >> ["{x}{y}" for x, y in strs:pairs(ordered=yes)]
+ = ["AB", "AC", "BA", "BC", "CA", "CB"]
+ >> ["{x}{y}" for x, y in strs:pairs(self_pairs=yes, ordered=yes)]
+ = ["AA", "AB", "AC", "BA", "BB", "BC", "CA", "CB", "CC"]
+ >> ["!" for x,y in [:Text]:pairs()]
+ = []
diff --git a/typecheck.c b/typecheck.c
index ba04ad53..0fda02e2 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -678,7 +678,11 @@ type_t *get_type(env_t *env, ast_t *ast)
else if (streq(call->name, "heapify")) return Type(VoidType);
else if (streq(call->name, "heap_push")) return Type(VoidType);
else if (streq(call->name, "heap_pop")) return Match(self_value_t, ArrayType)->item_type;
- else code_err(ast, "There is no '%s' method for arrays", call->name);
+ else if (streq(call->name, "pairs")) {
+ type_t *ref_t = Type(PointerType, .pointed=Match(self_value_t, ArrayType)->item_type, .is_stack=true);
+ arg_t *args = new(arg_t, .name="x", .type=ref_t, .next=new(arg_t, .name="y", .type=ref_t));
+ return Type(ClosureType, .fn=Type(FunctionType, .args=args, .ret=Type(BoolType)));
+ } else code_err(ast, "There is no '%s' method for arrays", call->name);
}
case TableType: {
auto table = Match(self_value_t, TableType);