Scope *scope;
Scope *binding_scope;
+ Scope *quoted_block_capture_scope;
BlockRule rules;
u32 statement_idx;
AstTyped_base;
AstNode *code;
+ bh_arr(OnyxToken *) binding_symbols;
};
struct AstDirectiveInsert {
AstTyped_base;
AstTyped *code_expr;
+ bh_arr(AstTyped *) binding_exprs;
};
struct AstDirectiveInit {
YIELD(insert->token->pos, "Waiting for resolution to code expression type.");
}
+ bh_arr_each(AstTyped *, pexpr, insert->binding_exprs) {
+ CHECK(expression, pexpr);
+ }
+
Type* code_type = type_build_from_ast(context.ast_alloc, builtin_code_type);
TYPE_CHECK(&insert->code_expr, code_type) {
ERROR(insert->token->pos, "Expected compile-time known expression of type 'Code'.");
}
+ u32 bound_symbol_count = bh_arr_length(code_block->binding_symbols);
+ u32 bound_expr_count = bh_arr_length(insert->binding_exprs);
+ if (bound_symbol_count > bound_expr_count) {
+ onyx_report_error(insert->token->pos, Error_Critical,
+ "Expected at least %d argument%s to unquote code block, only got %d.",
+ bound_symbol_count, bh_num_plural(bound_symbol_count), bound_expr_count);
+ ERROR(code_block->token->pos, "Here is the code block being unquoted.");
+ }
+
AstNode* cloned_block = ast_clone(context.ast_alloc, code_block->code);
cloned_block->next = insert->next;
+
+ if (bound_expr_count > 0) {
+ Scope **scope = NULL;
+
+ if (cloned_block->kind == Ast_Kind_Block) {
+ scope = &((AstBlock *) cloned_block)->quoted_block_capture_scope;
+
+ } else if (bound_symbol_count > 0) {
+ AstReturn* return_node = onyx_ast_node_new(context.ast_alloc, sizeof(AstReturn), Ast_Kind_Return);
+ return_node->token = cloned_block->token;
+ return_node->expr = (AstTyped *) cloned_block;
+
+ AstBlock* body_block = onyx_ast_node_new(context.ast_alloc, sizeof(AstBlock), Ast_Kind_Block);
+ body_block->token = cloned_block->token;
+ body_block->body = (AstNode *) return_node;
+ scope = &((AstBlock *) body_block)->quoted_block_capture_scope;
+
+ AstDoBlock* doblock = (AstDoBlock *) onyx_ast_node_new(context.ast_alloc, sizeof(AstDoBlock), Ast_Kind_Do_Block);
+ doblock->token = cloned_block->token;
+ doblock->block = body_block;
+ doblock->type = &type_auto_return;
+ doblock->next = cloned_block->next;
+
+ cloned_block = (AstNode *) doblock;
+ }
+
+ if (bound_symbol_count > 0) {
+ assert(scope);
+ *scope = scope_create(context.ast_alloc, NULL, code_block->token->pos);
+
+ fori (i, 0, bound_symbol_count) {
+ symbol_introduce(*scope, code_block->binding_symbols[i], (AstNode *) insert->binding_exprs[i]);
+ }
+ }
+ }
+
*(AstNode **) pinsert = cloned_block;
insert->flags |= Ast_Flag_Has_Been_Checked;
case Ast_Kind_Block:
((AstBlock *) nn)->body = ast_clone_list(a, ((AstBlock *) node)->body);
+ ((AstBlock *) nn)->quoted_block_capture_scope = NULL;
break;
case Ast_Kind_Defer:
case Ast_Kind_Directive_Insert:
C(AstDirectiveInsert, code_expr);
+
+ AstDirectiveInsert* id = (AstDirectiveInsert *) nn;
+ AstDirectiveInsert* is = (AstDirectiveInsert *) node;
+ id->binding_exprs = NULL;
+ bh_arr_new(global_heap_allocator, id->binding_exprs, bh_arr_length(is->binding_exprs));
+
+ bh_arr_each(AstTyped *, expr, is->binding_exprs) {
+ bh_arr_push(id->binding_exprs, (AstTyped *) ast_clone(a, (AstNode *) *expr));
+ }
break;
case Ast_Kind_Directive_Defined:
break;
}
- case '?':
case '[': {
+ // HACK CLEANUP
+ // :LinearTokenDependent
+ OnyxToken *matching = find_matching_paren(parser->curr);
+ if (matching->type == ']' &&
+ ((matching + 1)->type == '{' || (matching + 1)->type == '(')) {
+ AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
+ code_block->token = expect_token(parser, '[');
+
+ assert(builtin_code_type != NULL);
+ code_block->type_node = builtin_code_type;
+
+ bh_arr_new(global_heap_allocator, code_block->binding_symbols, 4);
+ while (!consume_token_if_next(parser, ']')) {
+ if (parser->hit_unexpected_token) return (AstTyped *) code_block;
+
+ OnyxToken *symbol = expect_token(parser, Token_Type_Symbol);
+ bh_arr_push(code_block->binding_symbols, symbol);
+
+ if (parser->curr->type != ']')
+ expect_token(parser, ',');
+ }
+
+ if (parser->curr->type == '{') {
+ code_block->code = (AstNode *) parse_block(parser, 1, NULL);
+ ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
+ } else {
+ code_block->code = (AstNode *) parse_expression(parser, 1);
+ }
+
+ retval = (AstTyped *) code_block;
+ break;
+ }
+
+ // :fallthrough
+ }
+
+ case '?': {
AstType *type = parse_type(parser);
retval = (AstTyped *) type;
break;
retval = (AstTyped *) defined;
break;
}
- else if (parse_possible_directive(parser, "quote")) {
- OnyxToken* code_token = parser->curr - 1;
-
- AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
- code_block->token = code_token;
-
- assert(builtin_code_type != NULL);
- code_block->type_node = builtin_code_type;
-
- if (parser->curr->type == '{') {
- code_block->code = (AstNode *) parse_block(parser, 1, NULL);
- ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
-
- } else {
- code_block->code = (AstNode *) parse_expression(parser, 0);
- }
-
- retval = (AstTyped *) code_block;
- break;
- }
- else if (next_tokens_are(parser, 2, '#', '(')) {
- OnyxToken* code_token = expect_token(parser, '#');
- expect_token(parser, '(');
-
- AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
- code_block->token = code_token;
-
- assert(builtin_code_type != NULL);
- code_block->type_node = builtin_code_type;
-
- code_block->code = (AstNode *) parse_expression(parser, 0);
-
- expect_token(parser, ')');
-
- retval = (AstTyped *) code_block;
- break;
- }
else if (parse_possible_directive(parser, "unquote")) {
AstDirectiveInsert* insert = make_node(AstDirectiveInsert, Ast_Kind_Directive_Insert);
insert->token = parser->curr - 1;
insert->code_expr = parse_expression(parser, 0);
parser->parse_calls = 1;
+ if (consume_token_if_next(parser, '(')) {
+ bh_arr_new(global_heap_allocator, insert->binding_exprs, 4);
+ while (!consume_token_if_next(parser, ')')) {
+ if (parser->hit_unexpected_token) break;
+
+ AstTyped *expr = parse_expression(parser, 0);
+ bh_arr_push(insert->binding_exprs, expr);
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+ }
+
retval = (AstTyped *) insert;
break;
}
static SymresStatus symres_directive_insert(AstDirectiveInsert* insert) {
SYMRES(expression, &insert->code_expr);
+ bh_arr_each(AstTyped *, pexpr, insert->binding_exprs) {
+ SYMRES(expression, pexpr);
+ }
return Symres_Success;
}
if (block->binding_scope != NULL)
scope_include(current_scope, block->binding_scope, block->token->pos);
+ if (block->quoted_block_capture_scope != NULL)
+ scope_include(current_scope, block->quoted_block_capture_scope, block->token->pos);
+
if (block->body) {
AstNode** start = &block->body;
fori (i, 0, block->statement_idx) {
}
auto :: #match {
- macro (size := 32 * 1024, $dest: Code = #(context.allocator)) {
+ macro (size := 32 * 1024, $dest: Code = [](context.allocator)) {
use core.alloc {arena, heap_allocator}
a := arena.make(heap_allocator, size);
Use `it` to refer to the current element being tested.
arr := array.make(.[ 1, 2, 3, 4, 5 ]);
- array.filter(&arr, #(it % 2 == 0));
+ array.filter(&arr, [v](v % 2 == 0));
println(arr); // 2, 4
"""
filter :: macro (arr: &[..] $T, body: Code) {
defer i += 1;
it := arr[i];
- if !(#unquote body) do move += 1;
+ if !(#unquote body(it)) do move += 1;
if move != 0 do arr.data[i] = arr.data[i + move];
}
to_list :: slice.to_list
find :: slice.find
find_ptr :: slice.find_ptr
+find_opt :: slice.find_opt
first :: slice.first
count_where :: slice.count_where
windows :: slice.windows
for bucket_index: bucket.count {
it := &bucket.data[bucket_index];
- #unquote body;
+ #unquote body(it);
}
}
}
value: i32;
iterator: Iterator(i32) = ...;
- if #(value) << iterator {
+ if [](value) << iterator {
...iterater closed...
}
"""
#overload
as_iter :: macro (x: &[..] $T) => {
G :: generic_dynamic_array_as_iter
- return G(x, #(arr.data[current]), Iterator(T));
+ return G(x, [](arr.data[current]), Iterator(T));
}
#overload
as_iter :: macro (x: &[..] $T, by_pointer: bool) => {
G :: generic_dynamic_array_as_iter
- return G(x, #(&arr.data[current]), Iterator(&T));
+ return G(x, [](&arr.data[current]), Iterator(&T));
}
return m;
}
+#doc """
+ Collects elements into an array, or a map, depending on if the
+ iterator produces a Pair(K, V) or not.
+"""
+collect :: #match {
+ to_array
+}
+
#doc """
Produces an iterator that first yields all values from the
Python:
results = [it * 2 for it in [1, 2, 3, 4, 5]]
Onyx:
- results := iter.comp(u32.[1, 2, 3, 4, 5], #(it * 2));
+ results := iter.comp(u32.[1, 2, 3, 4, 5], [it](it * 2));
"""
comp :: #match #local {}
for __it: i {
it := *__it;
- a << (#unquote value);
+ a << (#unquote value(it));
}
return a;
}
it: V;
a := make([..] typeof #unquote value);
- for i do a << (#unquote value);
+ for i do a << (#unquote value(it));
return a;
}
*it += 10;
}
or:
- m->update("test", #(*it += 10));
+ m->update("test", [v](*v += 10));
"""
update :: macro (map: ^Map, key: map.Key_Type, body: Code) {
lookup_ :: lookup
if lr.entry_index >= 0 {
it := &map.entries[lr.entry_index].value;
- #unquote body;
+ #unquote body(it);
}
}
}
}
+ with :: macro (o: ?$T, body: Code) {
+ switch o {
+ case .None ---;
+ case .Some => it {
+ #unquote body(it);
+ }
+ }
+ }
+
#doc """
Creates a scope that the `?` operator on an Optional type can
return to, instead of returning from the enclosing function.
// ...
});
"""
- try :: macro (body: Code, catch: Code = #quote {}) -> bool {
+ try :: macro (body: Code, catch: Code = []{}) -> bool {
//
// Using a 'do'-expression block to introduce a new
// 'return' location. This way, when the code in the `body`
#operator ?? macro (opt: ?$T, catch: Code) -> T {
switch value := opt; value {
case .Some => v do return v;
+ case #default ---
}
#unquote catch;
catch :: macro (r: Result($T, $E), on_err: Code) -> T {
switch res := r; res {
case .Ok => v do return v;
- case .Err {
- #unquote on_err;
+ case .Err => err {
+ #unquote on_err(err);
}
}
}
},
macro (arr: [] $T, $cmp: Code) -> bool {
- for it: arr do if #unquote cmp do return true;
+ for it: arr do if #unquote cmp(it) do return true;
return false;
}
}
// OR
- slice.fold(arr, 0, #(it + acc)) |> println();
+ slice.fold(arr, 0, [it](it + acc)) |> println();
"""
fold :: #match #local {}
#overload
fold :: macro (arr: [] $T, init: $R, body: Code) -> R {
acc := init;
- for it: arr do acc = #unquote body;
+ for it: arr do acc = #unquote body(it, acc);
return acc;
}
every :: #match #local {}
#overload
-every :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.every(arr, #(predicate(it)));
+every :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.every(arr, [it](predicate(it)));
#overload
every :: macro (arr: [] $T, predicate_body: Code) -> bool {
for arr {
- if !(#unquote predicate_body) do return false;
+ if !(#unquote predicate_body(it)) do return false;
}
return true;
}
some :: #match #local {}
#overload
-some :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.some(arr, #(predicate(it)));
+some :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.some(arr, [it](predicate(it)));
#overload
some :: macro (arr: [] $T/type_is_struct, predicate_body: Code) -> bool {
for & arr {
- if #unquote predicate_body do return true;
+ if #unquote predicate_body(it) do return true;
}
return false;
}
#overload
some :: macro (arr: [] $T, predicate_body: Code) -> bool {
for arr {
- if #unquote predicate_body do return true;
+ if #unquote predicate_body(it) do return true;
}
return false;
}
find :: macro (arr: [] $T/type_is_struct, pred: Code) -> i32 {
for i: arr.count {
it := &arr[i];
- if #unquote pred do return i;
+ if #unquote pred(it) do return i;
}
return -1;
find :: macro (arr: [] $T, pred: Code) -> i32 {
for i: arr.count {
it := arr[i];
- if #unquote pred do return i;
+ if #unquote pred(it) do return i;
}
return -1;
return null;
}
+#doc """
+
+"""
+find_opt :: #match {
+ macro (arr: [] $T/type_is_struct, cond: Code) -> ? T {
+ for& it: arr {
+ if #unquote cond(it) {
+ return *it;
+ }
+ }
+ return .{};
+ },
+
+ macro (arr: [] $T, cond: Code) -> ? T {
+ for it: arr {
+ if #unquote cond(it) {
+ return it;
+ }
+ }
+ return .{};
+ },
+}
+
first :: #match #locked {
macro (arr: [] $T, predicate: (T) -> bool) -> &T {
first :: first
- return first(arr, #(predicate(it)));
+ return first(arr, [it](predicate(it)));
},
macro (arr: [] $T/type_is_struct, predicate_body: Code) -> &T {
for & arr {
- if #unquote predicate_body do return it;
+ if #unquote predicate_body(it) do return it;
}
return null;
// structure.
for &it_ptr: arr {
it := *it_ptr;
- if #unquote predicate_body do return it_ptr;
+ if #unquote predicate_body(it) do return it_ptr;
}
return null;
count_where :: #match #local {}
#overload
-count_where :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.count_where(arr, #(predicate(it)));
+count_where :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.count_where(arr, [it](predicate(it)));
#overload
count_where :: macro (arr: [] $T, predicate_body: Code) -> u32 {
count: u32 = 0;
for arr {
- if #unquote predicate_body do count += 1;
+ if #unquote predicate_body(it) do count += 1;
}
return count;
}
"""
greatest :: macro (arr: [] $T) -> (i32, T) {
fold_idx_elem :: fold_idx_elem
- return fold_idx_elem(arr, #(*A > *B));
+ return fold_idx_elem(arr, [](*A > *B));
}
#doc """
"""
least :: macro (arr: [] $T) -> (i32, T) {
fold_idx_elem :: fold_idx_elem
- return fold_idx_elem(arr, #(*A < *B));
+ return fold_idx_elem(arr, [](*A < *B));
}
tag_value := *cast(&u32, v.data);
- variant := array.first(u.variants, #(it.tag_value == tag_value));
+ variant := array.first(u.variants, [x](x.tag_value == tag_value));
if !variant {
output->write("unknown_variant");
|> string.strip_trailing_whitespace();
for header: string.split_iter(header_line, #char ",") {
- member := array.first(output_type_info.members, #(do {
- if tag := array.first(it.tags, #(it.type == CSV_Column)); tag {
+ member := array.first(output_type_info.members, [](do {
+ if tag := array.first(it.tags, [t](t.type == CSV_Column)); tag {
return any_as(*tag, CSV_Column).name == header;
}
for &member: output_type_info.members {
if !#first do io.write(writer, ",");
- if tag := array.first(member.tags, #(it.type == CSV_Column)); tag {
+ if tag := array.first(member.tags, [t](t.type == CSV_Column)); tag {
io.write(writer, any_as(*tag, CSV_Column).name);
} else {
io.write(writer, member.name);
output.type = (cast(&info.Type_Info_Pointer) t).to;
}
- alloc.arena.auto(dest=#(context.temp_allocator));
+ alloc.arena.auto(dest=[](context.temp_allocator));
active_item_ptr := null;
active_item_type := void;
for ^member: s.members {
key := member.name;
- if tag := array.first(member.tags, #(it.type == Custom_Key)); tag != null {
+ if tag := array.first(member.tags, [t](t.type == Custom_Key)); tag != null {
key = (cast(^Custom_Key) tag.data).key;
}
- if tag := array.first(member.tags, #(it.type == type_expr)); tag != null {
+ if tag := array.first(member.tags, [t](t.type == type_expr)); tag != null {
if *cast(^type_expr, tag.data) == Ignore {
continue;
}
for^ member: s_info.members {
key := member.name;
- if tag := array.first(member.tags, #(it.type == Custom_Key)); tag != null {
+ if tag := array.first(member.tags, [t](t.type == Custom_Key)); tag != null {
key = (cast(^Custom_Key) tag.data).key;
}
- if tag := array.first(member.tags, #(it.type == type_expr)); tag != null {
+ if tag := array.first(member.tags, [t](t.type == type_expr)); tag != null {
if *cast(^type_expr, tag.data) == Ignore {
continue;
}
for^ member: s_info.members {
key := member.name;
- if tag := array.first(member.tags, #(it.type == Custom_Key)); tag != null {
+ if tag := array.first(member.tags, [t](t.type == Custom_Key)); tag != null {
key = (cast(^Custom_Key) tag.data).key;
}
- if tag := array.first(member.tags, #(it.type == type_expr)); tag != null {
+ if tag := array.first(member.tags, [t](t.type == type_expr)); tag != null {
if *cast(^type_expr, tag.data) == Ignore {
continue;
}
// be a valid rune.
if buffer.length < len do return false;
- return array.every(buffer[1 .. buffer.length], #(it & 0xC0 == 0x80));
+ return array.every(buffer[1 .. buffer.length], [c](c & 0xC0 == 0x80));
}
rune_count :: (s: str) -> i32 {
}
}
- client_count = array.count_where(clients, #(it != null));
+ client_count = array.count_where(clients, [v](v != null));
return server.alive;
}
tcp_server_handle_events :: macro (server: &TCP_Server, handler: Code) {
while server->pulse() {
for iter.as_iter(&server.connection) {
- switch it.kind do #unquote handler;
+ switch it.kind do #unquote handler(it);
}
}
}
file_logger.file = new(File, allocator);
- *file_logger.file = open(filename, mode=.Append)->catch(#quote {
+ *file_logger.file = open(filename, mode=.Append)->catch([] {
raw_free(allocator, file_logger.file);
raw_free(allocator, file_logger);
RANDOM_INCREMENT :: cast(i64) 11
#if #defined(runtime.platform.__random_get) {
- __initial_value :: #(do {
+ __initial_value :: [](do {
v: u64;
runtime.platform.__random_get(.{ ~~&v, sizeof typeof v });
return v;
})
} else {
#if #defined(core.os.time) {
- __initial_value :: #(core.os.time())
+ __initial_value :: [](core.os.time())
} else {
- __initial_value :: #(8675309)
+ __initial_value :: [](8675309)
}
}
}
info := cast(&Type_Info_Struct) get_type_info(type);
if info.kind != .Struct do return null;
- method := core.array.first(info.methods, #(it.name == method_name));
+ method := core.array.first(info.methods, [x](x.name == method_name));
if method != null do return &method.func;
return null;
}
type_info := runtime.info.type_table[it];
type_idx : type_expr = ~~ it;
- #unquote body;
+ #unquote body(type_info, type_idx);
}
}
results := make([..] GPWT_Result(tag_type));
for proc: tagged_procedures {
- if tag := array.first(proc.tags, #(it.type == tag_type)); tag != null {
+ if tag := array.first(proc.tags, [v](v.type == tag_type)); tag != null {
array.push(&results, .{
func = proc.func,
type = proc.type,
__get_env :: (key: str) -> ? str {
__lookup_all_envs();
- return slice.first(all_env, #(it.first == key))
+ return slice.first(all_env, [v](v.first == key))
|> Optional.from_ptr()
|> Optional.transform(x => x.second);
}
join :: (strs: [] str, sep: str, allocator := context.allocator) -> str {
if strs.count == 0 do return "";
- len_sum := array.fold(strs, 0, #(acc + it.length));
+ len_sum := array.fold(strs, 0, [v, acc](acc + v.length));
out := make(str, len_sum + (strs.count - 1) * sep.count);
i := 0;
package core.time
use core {package, *}
-use runtime
Date :: struct {
year: i32;
triple_macro(simple_code);
// As another small shorthand, if a code block only contains one expression (like the code block above),
- // then you can write by wrapping the expression in #(...).
- triple_macro(#(println("Short code block!")));
+ // then you can write by wrapping the expression in [](...).
+ triple_macro([](println("Short code block!")));
// As yet another small piece of syntactic sugar, you can pass a code block to a macro or procedure in
// the following way:
// wasm_externtype_t
-const wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t *externtype) {
+wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t *externtype) {
return externtype->kind;
}
command_procedures := runtime.info.get_procedures_with_tag(Command);
defer delete(&command_procedures);
- command_tag := array.first(command_procedures, #(it.tag.command == command));
+ command_tag := array.first(command_procedures, [p](p.tag.command == command));
if !command_tag {
run_help_command(arguments);
return;
}
for &cell: cells_to_consider {
- map.update(&grid, *cell, #quote { it.alive = it.next; });
+ map.update(&grid, *cell, [v]{ v.alive = v.next; });
}
array.clear(&cells_to_consider);
}
for iter.as_iter(range.{ BITS - 1, 0, -1 }) {
- filter_array(oxygen_array, it, #(A >= B));
- filter_array(co2_array, it, #(A < B));
+ filter_array(oxygen_array, it, [](A >= B));
+ filter_array(co2_array, it, [](A < B));
}
printf("Part 2: {}\n", oxygen_array[0] * co2_array[0]);
}
}
- attempt_add(#(try.x > 0), -1, 0);
- attempt_add(#(try.x < width * 5 - 1), 1, 0);
- attempt_add(#(try.y > 0), 0, -1);
- attempt_add(#(try.y < height * 5 - 1), 0, 1);
+ attempt_add([](try.x > 0), -1, 0);
+ attempt_add([](try.x < width * 5 - 1), 1, 0);
+ attempt_add([](try.y > 0), 0, -1);
+ attempt_add([](try.y < height * 5 - 1), 0, 1);
}
printf("Part 2: {}\n", minimum);
printf("ba[10] is {}.\n", ba[10]);
sum := 0;
- bucket_array.for_each(ba, #quote {
- printf("[{}] -> {}\n", bucket_index, *it);
- sum += *it;
+ bucket_array.for_each(ba, [value] {
+ printf("[{}] -> {}\n", bucket_index, *value);
+ sum += *value;
});
for it: bucket_array.as_iter(&ba) {
Vector2 :: struct { x, y: i32; }
#operator == macro (v1: Vector2, v2: Vector2) => v1.x == v2.x && v1.y == v2.y;
-none_of_the_above :: #quote {
+none_of_the_above :: [] {
case #default {
println("Got default!");
}
#unquote none_of_the_above;
}
-}
\ No newline at end of file
+}