Make the compiler stricter about not promoting local value variables to

pointers
This commit is contained in:
Bruce Hill 2024-11-09 17:26:01 -05:00
parent 8dd51a113e
commit 145a078387
9 changed files with 99 additions and 87 deletions

116
compile.c
View File

@ -2703,49 +2703,58 @@ CORD compile(env_t *env, ast_t *ast)
case MethodCall: { case MethodCall: {
auto call = Match(ast, MethodCall); auto call = Match(ast, MethodCall);
type_t *self_t = get_type(env, call->self); type_t *self_t = get_type(env, call->self);
type_t *self_value_t = value_type(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;
CORD 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) { switch (self_value_t->tag) {
case ArrayType: { case ArrayType: {
type_t *item_t = Match(self_value_t, ArrayType)->item_type; type_t *item_t = Match(self_value_t, ArrayType)->item_type;
CORD padded_item_size = CORD_asprintf("%ld", type_size(item_t)); CORD padded_item_size = CORD_asprintf("%ld", type_size(item_t));
ast_t *default_rng = FakeAST(InlineCCode, .code="default_rng", .type=RNG_TYPE); ast_t *default_rng = FakeAST(InlineCCode, .code="default_rng", .type=RNG_TYPE);
if (streq(call->name, "insert")) { if (streq(call->name, "insert")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, 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"))); .next=new(arg_t, .name="at", .type=INT_TYPE, .default_val=FakeAST(Int, .str="0")));
return CORD_all("Array$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Array$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
padded_item_size, ")"); padded_item_size, ")");
} else if (streq(call->name, "insert_all")) { } else if (streq(call->name, "insert_all")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
arg_t *arg_spec = new(arg_t, .name="items", .type=self_value_t, 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"))); .next=new(arg_t, .name="at", .type=INT_TYPE, .default_val=FakeAST(Int, .str="0")));
return CORD_all("Array$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Array$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
padded_item_size, ")"); padded_item_size, ")");
} else if (streq(call->name, "remove_at")) { } else if (streq(call->name, "remove_at")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
arg_t *arg_spec = new(arg_t, .name="index", .type=INT_TYPE, .default_val=FakeAST(Int, .str="-1"), 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"))); .next=new(arg_t, .name="count", .type=INT_TYPE, .default_val=FakeAST(Int, .str="1")));
return CORD_all("Array$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Array$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
padded_item_size, ")"); padded_item_size, ")");
} else if (streq(call->name, "remove_item")) { } else if (streq(call->name, "remove_item")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, 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"))); .next=new(arg_t, .name="max_count", .type=INT_TYPE, .default_val=FakeAST(Int, .str="-1")));
return CORD_all("Array$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Array$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "random")) { } else if (streq(call->name, "random")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="rng", .type=RNG_TYPE, .default_val=default_rng); arg_t *arg_spec = new(arg_t, .name="rng", .type=RNG_TYPE, .default_val=default_rng);
return CORD_all("Array$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type(item_t), ")"); return CORD_all("Array$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type(item_t), ")");
} else if (streq(call->name, "has")) { } else if (streq(call->name, "has")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); arg_t *arg_spec = new(arg_t, .name="item", .type=item_t);
return CORD_all("Array$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Array$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "sample")) { } else if (streq(call->name, "sample")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="count", .type=INT_TYPE, arg_t *arg_spec = new(arg_t, .name="count", .type=INT_TYPE,
.next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType)), .next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType)),
.default_val=FakeAST(Null, .type=new(type_ast_t, .tag=ArrayTypeAST, .default_val=FakeAST(Null, .type=new(type_ast_t, .tag=ArrayTypeAST,
@ -2754,18 +2763,18 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("Array$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Array$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
padded_item_size, ")"); padded_item_size, ")");
} else if (streq(call->name, "shuffle")) { } else if (streq(call->name, "shuffle")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
arg_t *arg_spec = new(arg_t, .name="rng", .type=RNG_TYPE, .default_val=default_rng); arg_t *arg_spec = new(arg_t, .name="rng", .type=RNG_TYPE, .default_val=default_rng);
return CORD_all("Array$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); return CORD_all("Array$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")");
} else if (streq(call->name, "shuffled")) { } else if (streq(call->name, "shuffled")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="rng", .type=RNG_TYPE, .default_val=default_rng); arg_t *arg_spec = new(arg_t, .name="rng", .type=RNG_TYPE, .default_val=default_rng);
return CORD_all("Array$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); return CORD_all("Array$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")");
} else if (streq(call->name, "sort") || streq(call->name, "sorted")) { } else if (streq(call->name, "sort") || streq(call->name, "sorted")) {
CORD self = streq(call->name, "sort") if (streq(call->name, "sort"))
? compile_to_pointer_depth(env, call->self, 1, false) EXPECT_POINTER("an", "array");
// No need to do an ARRAY_COPY() here because it happens inside Array$sorted(): else
: compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
CORD comparison; CORD comparison;
if (call->args) { if (call->args) {
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
@ -2778,7 +2787,7 @@ CORD compile(env_t *env, ast_t *ast)
} }
return CORD_all("Array$", call->name, "(", self, ", ", comparison, ", ", padded_item_size, ")"); return CORD_all("Array$", call->name, "(", self, ", ", comparison, ", ", padded_item_size, ")");
} else if (streq(call->name, "heapify")) { } else if (streq(call->name, "heapify")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
CORD comparison; CORD comparison;
if (call->args) { if (call->args) {
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
@ -2791,7 +2800,7 @@ CORD compile(env_t *env, ast_t *ast)
} }
return CORD_all("Array$heapify(", self, ", ", comparison, ", ", padded_item_size, ")"); return CORD_all("Array$heapify(", self, ", ", comparison, ", ", padded_item_size, ")");
} else if (streq(call->name, "heap_push")) { } else if (streq(call->name, "heap_push")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
.ret=Type(IntType, .bits=TYPE_IBITS32)); .ret=Type(IntType, .bits=TYPE_IBITS32));
@ -2804,7 +2813,7 @@ CORD compile(env_t *env, ast_t *ast)
CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args);
return CORD_all("Array$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")"); return CORD_all("Array$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")");
} else if (streq(call->name, "heap_pop")) { } else if (streq(call->name, "heap_pop")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
.ret=Type(IntType, .bits=TYPE_IBITS32)); .ret=Type(IntType, .bits=TYPE_IBITS32));
@ -2816,7 +2825,7 @@ CORD compile(env_t *env, ast_t *ast)
CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args);
return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", padded_item_size, ", ", compile_type(item_t), ")"); return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", padded_item_size, ", ", compile_type(item_t), ")");
} else if (streq(call->name, "binary_search")) { } else if (streq(call->name, "binary_search")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL);
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)),
.ret=Type(IntType, .bits=TYPE_IBITS32)); .ret=Type(IntType, .bits=TYPE_IBITS32));
@ -2829,43 +2838,43 @@ CORD compile(env_t *env, ast_t *ast)
CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args);
return CORD_all("Array$binary_search_value(", self, ", ", arg_code, ")"); return CORD_all("Array$binary_search_value(", self, ", ", arg_code, ")");
} else if (streq(call->name, "clear")) { } else if (streq(call->name, "clear")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("an", "array");
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Array$clear(", self, ")"); return CORD_all("Array$clear(", self, ")");
} else if (streq(call->name, "find")) { } else if (streq(call->name, "find")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); arg_t *arg_spec = new(arg_t, .name="item", .type=item_t);
return CORD_all("Array$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Array$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type_info(env, self_value_t), ")"); ", ", compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "first")) { } else if (streq(call->name, "first")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL);
type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true);
type_t *predicate_type = Type( type_t *predicate_type = Type(
ClosureType, .fn=Type(FunctionType, .args=new(arg_t, .name="item", .type=item_ptr), .ret=Type(BoolType))); ClosureType, .fn=Type(FunctionType, .args=new(arg_t, .name="item", .type=item_ptr), .ret=Type(BoolType)));
arg_t *arg_spec = new(arg_t, .name="predicate", .type=predicate_type); arg_t *arg_spec = new(arg_t, .name="predicate", .type=predicate_type);
return CORD_all("Array$first(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); return CORD_all("Array$first(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
} else if (streq(call->name, "from")) { } else if (streq(call->name, "from")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, true); self = compile_to_pointer_depth(env, call->self, 0, true);
arg_t *arg_spec = new(arg_t, .name="first", .type=INT_TYPE); arg_t *arg_spec = new(arg_t, .name="first", .type=INT_TYPE);
return CORD_all("Array$from(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); return CORD_all("Array$from(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
} else if (streq(call->name, "to")) { } else if (streq(call->name, "to")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, true); self = compile_to_pointer_depth(env, call->self, 0, true);
arg_t *arg_spec = new(arg_t, .name="last", .type=INT_TYPE); arg_t *arg_spec = new(arg_t, .name="last", .type=INT_TYPE);
return CORD_all("Array$to(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); return CORD_all("Array$to(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")");
} else if (streq(call->name, "by")) { } else if (streq(call->name, "by")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, true); self = compile_to_pointer_depth(env, call->self, 0, true);
arg_t *arg_spec = new(arg_t, .name="stride", .type=INT_TYPE); arg_t *arg_spec = new(arg_t, .name="stride", .type=INT_TYPE);
return CORD_all("Array$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); return CORD_all("Array$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")");
} else if (streq(call->name, "reversed")) { } else if (streq(call->name, "reversed")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, true); self = compile_to_pointer_depth(env, call->self, 0, true);
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Array$reversed(", self, ", ", padded_item_size, ")"); return CORD_all("Array$reversed(", self, ", ", padded_item_size, ")");
} else if (streq(call->name, "unique")) { } else if (streq(call->name, "unique")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Table$from_entries(", self, ", Set$info(", compile_type_info(env, item_t), "))"); return CORD_all("Table$from_entries(", self, ", Set$info(", compile_type_info(env, item_t), "))");
} else if (streq(call->name, "counts")) { } else if (streq(call->name, "counts")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Array$counts(", self, ", ", compile_type_info(env, self_value_t), ")"); return CORD_all("Array$counts(", self, ", ", compile_type_info(env, self_value_t), ")");
} else code_err(ast, "There is no '%s' method for arrays", call->name); } else code_err(ast, "There is no '%s' method for arrays", call->name);
@ -2873,61 +2882,63 @@ CORD compile(env_t *env, ast_t *ast)
case SetType: { case SetType: {
auto set = Match(self_value_t, SetType); auto set = Match(self_value_t, SetType);
if (streq(call->name, "has")) { if (streq(call->name, "has")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="key", .type=set->item_type); arg_t *arg_spec = new(arg_t, .name="key", .type=set->item_type);
return CORD_all("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "add")) { } else if (streq(call->name, "add")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("a", "set");
arg_t *arg_spec = new(arg_t, .name="item", .type=set->item_type); arg_t *arg_spec = new(arg_t, .name="item", .type=set->item_type);
return CORD_all("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", NULL, ", return CORD_all("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", NULL, ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "add_all")) { } else if (streq(call->name, "add_all")) {
EXPECT_POINTER("a", "set");
arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ArrayType, .item_type=Match(self_value_t, SetType)->item_type)); arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ArrayType, .item_type=Match(self_value_t, SetType)->item_type));
return CORD_all("({ Table_t *set = ", compile_to_pointer_depth(env, call->self, 1, false), "; ", return CORD_all("({ Table_t *set = ", self, "; ",
"Array_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", "Array_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ",
"for (int64_t i = 0; i < to_add.length; i++)\n" "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(env, self_value_t), ");\n", "Table$set(set, to_add.data + i*to_add.stride, NULL, ", compile_type_info(env, self_value_t), ");\n",
"(void)0; })"); "(void)0; })");
} else if (streq(call->name, "remove")) { } else if (streq(call->name, "remove")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("a", "set");
arg_t *arg_spec = new(arg_t, .name="item", .type=set->item_type); arg_t *arg_spec = new(arg_t, .name="item", .type=set->item_type);
return CORD_all("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "remove_all")) { } else if (streq(call->name, "remove_all")) {
EXPECT_POINTER("a", "set");
arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ArrayType, .item_type=Match(self_value_t, SetType)->item_type)); arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ArrayType, .item_type=Match(self_value_t, SetType)->item_type));
return CORD_all("({ Table_t *set = ", compile_to_pointer_depth(env, call->self, 1, false), "; ", return CORD_all("({ Table_t *set = ", self, "; ",
"Array_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", "Array_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ",
"for (int64_t i = 0; i < to_add.length; i++)\n" "for (int64_t i = 0; i < to_add.length; i++)\n"
"Table$remove(set, to_add.data + i*to_add.stride, ", compile_type_info(env, self_value_t), ");\n", "Table$remove(set, to_add.data + i*to_add.stride, ", compile_type_info(env, self_value_t), ");\n",
"(void)0; })"); "(void)0; })");
} else if (streq(call->name, "clear")) { } else if (streq(call->name, "clear")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("a", "set");
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Table$clear(", self, ")"); return CORD_all("Table$clear(", self, ")");
} else if (streq(call->name, "with")) { } else if (streq(call->name, "with")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t);
return CORD_all("Table$with(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Table$with(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type_info(env, self_value_t), ")"); ", ", compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "overlap")) { } else if (streq(call->name, "overlap")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t);
return CORD_all("Table$overlap(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Table$overlap(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type_info(env, self_value_t), ")"); ", ", compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "without")) { } else if (streq(call->name, "without")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t);
return CORD_all("Table$without(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Table$without(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type_info(env, self_value_t), ")"); ", ", compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "is_subset_of")) { } else if (streq(call->name, "is_subset_of")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t, 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))); .next=new(arg_t, .name="strict", .type=Type(BoolType), .default_val=FakeAST(Bool, false)));
return CORD_all("Table$is_subset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Table$is_subset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type_info(env, self_value_t), ")"); ", ", compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "is_superset_of")) { } else if (streq(call->name, "is_superset_of")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t, 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))); .next=new(arg_t, .name="strict", .type=Type(BoolType), .default_val=FakeAST(Bool, false)));
return CORD_all("Table$is_superset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Table$is_superset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
@ -2940,31 +2951,31 @@ CORD compile(env_t *env, ast_t *ast)
arg_t *front_default_end = new(arg_t, .name="front", .type=Type(BoolType), .default_val=FakeAST(Bool, false)); arg_t *front_default_end = new(arg_t, .name="front", .type=Type(BoolType), .default_val=FakeAST(Bool, false));
arg_t *front_default_start = new(arg_t, .name="front", .type=Type(BoolType), .default_val=FakeAST(Bool, true)); arg_t *front_default_start = new(arg_t, .name="front", .type=Type(BoolType), .default_val=FakeAST(Bool, true));
if (streq(call->name, "give")) { if (streq(call->name, "give")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, .next=front_default_end); arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, .next=front_default_end);
return CORD_all("Channel$give_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Channel$give_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
padded_item_size, ")"); padded_item_size, ")");
} else if (streq(call->name, "give_all")) { } else if (streq(call->name, "give_all")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="to_give", .type=Type(ArrayType, .item_type=item_t), .next=front_default_end); arg_t *arg_spec = new(arg_t, .name="to_give", .type=Type(ArrayType, .item_type=item_t), .next=front_default_end);
return CORD_all("Channel$give_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Channel$give_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
padded_item_size, ")"); padded_item_size, ")");
} else if (streq(call->name, "get")) { } else if (streq(call->name, "get")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = front_default_start; arg_t *arg_spec = front_default_start;
return CORD_all("Channel$get_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Channel$get_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type(item_t), ", ", padded_item_size, ")"); ", ", compile_type(item_t), ", ", padded_item_size, ")");
} else if (streq(call->name, "peek")) { } else if (streq(call->name, "peek")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = front_default_start; arg_t *arg_spec = front_default_start;
return CORD_all("Channel$peek_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), return CORD_all("Channel$peek_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args),
", ", compile_type(item_t), ")"); ", ", compile_type(item_t), ")");
} else if (streq(call->name, "clear")) { } else if (streq(call->name, "clear")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Channel$clear(", self, ")"); return CORD_all("Channel$clear(", self, ")");
} else if (streq(call->name, "view")) { } else if (streq(call->name, "view")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Channel$view(", self, ")"); return CORD_all("Channel$view(", self, ")");
} else code_err(ast, "There is no '%s' method for channels", call->name); } else code_err(ast, "There is no '%s' method for channels", call->name);
@ -2972,7 +2983,7 @@ CORD compile(env_t *env, ast_t *ast)
case TableType: { case TableType: {
auto table = Match(self_value_t, TableType); auto table = Match(self_value_t, TableType);
if (streq(call->name, "get")) { if (streq(call->name, "get")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type);
return CORD_all( return CORD_all(
"Table$get_optional(", self, ", ", compile_type(table->key_type), ", ", "Table$get_optional(", self, ", ", compile_type(table->key_type), ", ",
@ -2980,18 +2991,18 @@ CORD compile(env_t *env, ast_t *ast)
"_, ", optional_into_nonnull(table->value_type, "(*_)"), ", ", compile_null(table->value_type), ", ", "_, ", optional_into_nonnull(table->value_type, "(*_)"), ", ", compile_null(table->value_type), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "has")) { } else if (streq(call->name, "has")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type);
return CORD_all("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "set")) { } else if (streq(call->name, "set")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("a", "table");
arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type, arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type,
.next=new(arg_t, .name="value", .type=table->value_type)); .next=new(arg_t, .name="value", .type=table->value_type));
return CORD_all("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "bump")) { } else if (streq(call->name, "bump")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("a", "table");
if (!(table->value_type->tag == IntType || table->value_type->tag == NumType)) if (!(table->value_type->tag == IntType || table->value_type->tag == NumType))
code_err(ast, "bump() is only supported for tables with numeric value types, not %T", self_value_t); code_err(ast, "bump() is only supported for tables with numeric value types, not %T", self_value_t);
ast_t *one = table->value_type->tag == IntType ast_t *one = table->value_type->tag == IntType
@ -3002,16 +3013,16 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("Table$bump(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Table$bump(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "remove")) { } else if (streq(call->name, "remove")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("a", "table");
arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type);
return CORD_all("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
compile_type_info(env, self_value_t), ")"); compile_type_info(env, self_value_t), ")");
} else if (streq(call->name, "clear")) { } else if (streq(call->name, "clear")) {
CORD self = compile_to_pointer_depth(env, call->self, 1, false); EXPECT_POINTER("a", "table");
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Table$clear(", self, ")"); return CORD_all("Table$clear(", self, ")");
} else if (streq(call->name, "sorted")) { } else if (streq(call->name, "sorted")) {
CORD self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
(void)compile_arguments(env, ast, NULL, call->args); (void)compile_arguments(env, ast, NULL, call->args);
return CORD_all("Table$sorted(", self, ", ", compile_type_info(env, self_value_t), ")"); return CORD_all("Table$sorted(", self, ", ", compile_type_info(env, self_value_t), ")");
} else code_err(ast, "There is no '%s' method for tables", call->name); } else code_err(ast, "There is no '%s' method for tables", call->name);
@ -3025,6 +3036,7 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all(b->code, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, args), ")"); return CORD_all(b->code, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, args), ")");
} }
} }
#undef EXPECT_POINTER
} }
case FunctionCall: { case FunctionCall: {
auto call = Match(ast, FunctionCall); auto call = Match(ast, FunctionCall);

View File

@ -8,7 +8,7 @@ _HELP := "
func parse_ini(path:Path -> {Text:{Text:Text}}): func parse_ini(path:Path -> {Text:{Text:Text}}):
text := path:read() or exit("Could not read INI file: $\[31;1]$(path.text_content)$\[]") text := path:read() or exit("Could not read INI file: $\[31;1]$(path.text_content)$\[]")
sections := {:Text:@{Text:Text}} sections := @{:Text:@{Text:Text}}
current_section := @{:Text:Text} current_section := @{:Text:Text}
# Line wraps: # Line wraps:
@ -26,7 +26,7 @@ func parse_ini(path:Path -> {Text:{Text:Text}}):
value := line:replace($/{..}={..}/, "\2"):trim() value := line:replace($/{..}={..}/, "\2"):trim()
current_section:set(key, value) current_section:set(key, value)
return {k:v[] for k,v in sections} return {k:v[] for k,v in sections[]}
func main(path:Path, key:Text?): func main(path:Path, key:Text?):
keys := (key or ""):split($|/|) keys := (key or ""):split($|/|)

View File

@ -3,7 +3,7 @@ use <stdio.h>
timestamp_format := CString("%F %T") timestamp_format := CString("%F %T")
logfiles := {:Path} logfiles := @{:Path}
func _timestamp(->Text): func _timestamp(->Text):
c_str := inline C:CString { c_str := inline C:CString {
@ -17,22 +17,22 @@ func _timestamp(->Text):
func info(text:Text, newline=yes): func info(text:Text, newline=yes):
say("$\[2]⚫ $text$\[]", newline) say("$\[2]⚫ $text$\[]", newline)
for file in logfiles: for file in logfiles[]:
file:append("$(_timestamp()) [info] $text$\n") file:append("$(_timestamp()) [info] $text$\n")
func debug(text:Text, newline=yes): func debug(text:Text, newline=yes):
say("$\[32]🟢 $text$\[]", newline) say("$\[32]🟢 $text$\[]", newline)
for file in logfiles: for file in logfiles[]:
file:append("$(_timestamp()) [debug] $text$\n") file:append("$(_timestamp()) [debug] $text$\n")
func warn(text:Text, newline=yes): func warn(text:Text, newline=yes):
say("$\[33;1]🟡 $text$\[]", newline) say("$\[33;1]🟡 $text$\[]", newline)
for file in logfiles: for file in logfiles[]:
file:append("$(_timestamp()) [warn] $text$\n") file:append("$(_timestamp()) [warn] $text$\n")
func error(text:Text, newline=yes): func error(text:Text, newline=yes):
say("$\[31;1]🔴 $text$\[]", newline) say("$\[31;1]🔴 $text$\[]", newline)
for file in logfiles: for file in logfiles[]:
file:append("$(_timestamp()) [error] $text$\n") file:append("$(_timestamp()) [error] $text$\n")
func add_logfile(file:Path): func add_logfile(file:Path):

View File

@ -14,7 +14,7 @@ func _get_file_dependencies(file:Path -> {Dependency}):
!! Could not read file: $file !! Could not read file: $file
return {:Dependency} return {:Dependency}
deps := {:Dependency} deps := @{:Dependency}
if lines := file:by_line(): if lines := file:by_line():
for line in lines: for line in lines:
if line:matches($/use {..}.tm/): if line:matches($/use {..}.tm/):
@ -23,7 +23,7 @@ func _get_file_dependencies(file:Path -> {Dependency}):
else if line:matches($/use {id}/): else if line:matches($/use {id}/):
module_name := line:replace($/use {..}/, "\1") module_name := line:replace($/use {..}/, "\1")
deps:add(Dependency.Module(module_name)) deps:add(Dependency.Module(module_name))
return deps return deps[]
func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency:{Dependency}}): func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency:{Dependency}}):
return if dependencies:has(dep) return if dependencies:has(dep)
@ -34,9 +34,9 @@ func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency:{Dependen
_get_file_dependencies(path) _get_file_dependencies(path)
is Module(module): is Module(module):
dir := (~/.local/share/tomo/installed/$module) dir := (~/.local/share/tomo/installed/$module)
module_deps := {:Dependency} module_deps := @{:Dependency}
visited := {:Path} visited := @{:Path}
unvisited := {f:resolved() for f in dir:files() if f:ends_with(".tm")} unvisited := @{f:resolved() for f in dir:files() if f:ends_with(".tm")}
while unvisited.length > 0: while unvisited.length > 0:
file := unvisited.items[-1] file := unvisited.items[-1]
unvisited:remove(file) unvisited:remove(file)
@ -48,7 +48,7 @@ func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency:{Dependen
unvisited:add(f) unvisited:add(f)
is Module(m): is Module(m):
module_deps:add(file_dep) module_deps:add(file_dep)
module_deps module_deps[]
dependencies:set(dep, dep_deps) dependencies:set(dep, dep_deps)

View File

@ -33,7 +33,7 @@ func wrap(text:Text, width:Int, min_split=3, hyphen="-" -> Text):
... and I can't split it without splitting into chunks smaller than $min_split. ... and I can't split it without splitting into chunks smaller than $min_split.
") ")
lines := [:Text] lines := @[:Text]
line := "" line := ""
for word in text:split($/{whitespace}/): for word in text:split($/{whitespace}/):
letters := word:split() letters := word:split()
@ -93,10 +93,10 @@ func main(files:[Path], width=80, inplace=no, min_split=3, rewrap=yes, hyphen=UN
(/dev/stdout) (/dev/stdout)
first := yes first := yes
wrapped_paragraphs := [:Text] wrapped_paragraphs := @[:Text]
for paragraph in text:split($/{2+ nl}/): for paragraph in text:split($/{2+ nl}/):
wrapped_paragraphs:insert( wrapped_paragraphs:insert(
wrap(paragraph, width=width, min_split=min_split, hyphen=hyphen) wrap(paragraph, width=width, min_split=min_split, hyphen=hyphen)
) )
out:write(\n\n:join(wrapped_paragraphs) ++ \n) out:write(\n\n:join(wrapped_paragraphs[]) ++ \n)

View File

@ -84,31 +84,31 @@ func main():
= [30, 20, 10] = [30, 20, 10]
do: do:
>> nums := [10, -20, 30] >> nums := @[10, -20, 30]
# Sorted function doesn't mutate original: # Sorted function doesn't mutate original:
>> nums:sorted() >> nums:sorted()
= [-20, 10, 30] = [-20, 10, 30]
>> nums >> nums
= [10, -20, 30] = @[10, -20, 30]
# Sort function does mutate in place: # Sort function does mutate in place:
>> nums:sort() >> nums:sort()
>> nums >> nums
= [-20, 10, 30] = @[-20, 10, 30]
# Custom sort functions: # Custom sort functions:
>> nums:sort(func(x,y:&Int): x:abs() <> y:abs()) >> nums:sort(func(x,y:&Int): x:abs() <> y:abs())
>> nums >> nums
= [10, -20, 30] = @[10, -20, 30]
>> nums:sort(func(x,y:&Int): y[] <> x[]) >> nums:sort(func(x,y:&Int): y[] <> x[])
>> nums >> nums
= [30, 10, -20] = @[30, 10, -20]
>> ["A", "B", "C"]:sample(10, [1.0, 0.5, 0.0]) >> ["A", "B", "C"]:sample(10, [1.0, 0.5, 0.0])
do: do:
>> heap := [random:int(1, 50) for _ in 10] >> heap := @[random:int(1, 50) for _ in 10]
>> heap:heapify() >> heap:heapify()
>> heap >> heap
sorted := [:Int] sorted := @[:Int]
while heap.length > 0: while heap.length > 0:
sorted:insert(heap:heap_pop()) sorted:insert(heap:heap_pop())
>> sorted == sorted:sorted() >> sorted == sorted:sorted()
@ -116,7 +116,7 @@ func main():
for _ in 10: for _ in 10:
heap:heap_push(random:int(1, 50)) heap:heap_push(random:int(1, 50))
>> heap >> heap
sorted = [:Int] sorted = @[:Int]
while heap.length > 0: while heap.length > 0:
sorted:insert(heap:heap_pop()) sorted:insert(heap:heap_pop())
>> sorted == sorted:sorted() >> sorted == sorted:sorted()
@ -155,12 +155,12 @@ func main():
say("$(x)$(y)") say("$(x)$(y)")
do: do:
>> nums := [-7, -4, -1, 2, 5] >> nums := @[-7, -4, -1, 2, 5]
>> nums:sort() >> nums:sort()
>> [nums:binary_search(i) for i in nums] >> [nums:binary_search(i) for i in nums[]]
= [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]
>> nums:sort(func(a,b:&Int): a:abs() <> b:abs()) >> nums:sort(func(a,b:&Int): a:abs() <> b:abs())
>> [nums:binary_search(i, func(a,b:&Int): a:abs() <> b:abs()) for i in nums] >> [nums:binary_search(i, func(a,b:&Int): a:abs() <> b:abs()) for i in nums[]]
= [1, 2, 3, 4, 5] = [1, 2, 3, 4, 5]
>> ["a", "b", "c"]:find("b") >> ["a", "b", "c"]:find("b")

View File

@ -25,10 +25,10 @@ func main():
= ["AB", "BC", "CD"] = ["AB", "BC", "CD"]
do: do:
result := [:Text] result := @[:Text]
for foo in pairwise(values): for foo in pairwise(values):
result:insert("$(foo.x)$(foo.y)") result:insert("$(foo.x)$(foo.y)")
>> result >> result[]
= ["AB", "BC", "CD"] = ["AB", "BC", "CD"]
>> [i for i in range(5, 10)] >> [i for i in range(5, 10)]

View File

@ -1,7 +1,7 @@
func main(): func main():
>> t1 := {10, 20, 30, 10} >> t1 := @{10, 20, 30, 10}
= {10, 20, 30} = @{10, 20, 30}
>> t1:has(10) >> t1:has(10)
= yes = yes
>> t1:has(-999) >> t1:has(-999)
@ -30,10 +30,10 @@ func main():
>> t1:add_all(t2) >> t1:add_all(t2)
>> t1 >> t1
= {10, 20, 30, 40} = @{10, 20, 30, 40}
>> t1:remove_all(t2) >> t1:remove_all(t2)
>> t1 >> t1
= {10, 20} = @{10, 20}
>> {3, i for i in 5} >> {3, i for i in 5}
= {3, 1, 2, 4, 5} = {3, 1, 2, 4, 5}

View File

@ -57,10 +57,10 @@ func main():
>> {x:10*x for x in y if x > 1 for y in [3, 4, 5] if y < 5} >> {x:10*x for x in y if x > 1 for y in [3, 4, 5] if y < 5}
= {2:20, 3:30, 4:40} = {2:20, 3:30, 4:40}
>> t3 := {1:10, 2:20, 3:30} >> t3 := @{1:10, 2:20, 3:30}
>> t3:remove(3) >> t3:remove(3)
>> t3 >> t3
= {1:10, 2:20} = @{1:10, 2:20}
do: do:
>> plain := {1:10, 2:20, 3:30} >> plain := {1:10, 2:20, 3:30}