// ---------------------------------
// Dynamic Arrays
// ---------------------------------
-make :: #match {
+make :: #match #locked {
($T: type_expr, capacity := 4, allocator := context.allocator) -> [..] T {
arr : [..] T;
init(^arr, capacity, allocator);
(package core.array).free(x);
}
-copy :: #match {
+copy :: #match #locked {
(arr: ^[..] $T, allocator := context.allocator) -> [..] T {
new_arr : [..] T;
init(^new_arr, arr.count, allocator);
arr.data[idx] = value;
}
-contains :: #match {
+contains :: #match #locked {
macro (arr: [] $T, $cmp: Code) -> bool {
for it: arr do if #unquote cmp do return true;
return false;
// Simple insertion sort
// cmp should return >0 if left > right
//
-sort :: #match {
+sort :: #match #locked {
(arr: [] $T, cmp: (T, T) -> i32) {
for i: 1 .. arr.count {
x := arr.data[i];
}
}
-quicksort :: #match {
+quicksort :: #match #locked {
(arr: [] $T, cmp: ( T, T) -> i32) do quicksort_impl(arr, cmp, 0, arr.count - 1); ,
(arr: [] $T, cmp: (^T, ^T) -> i32) do quicksort_impl(arr, cmp, 0, arr.count - 1); ,
}
quicksort_impl(arr, cmp, pivot + 1, hi);
}
- quicksort_partition :: #match {
+ quicksort_partition :: #match #locked {
(arr: [] $T, cmp: (T, T) -> i32, lo, hi: i32) -> i32 {
pivot := arr[hi];
i := lo - 1;
return val;
}
-map :: #match {
+map :: #match #locked {
macro (arr: [] $T, f: (^T) -> void) do for ^it: arr do f(it);,
macro (arr: [] $T, f: (T) -> T) do for ^it: arr do *it = f(*it);,
macro (arr: [] $T, body: Code) do for ^it: arr do #unquote body;,
macro (arr: [] $T, data: $R, f: (T, R) -> T) do for ^it: arr do *it = f(*it, data);,
}
-every :: #match {}
-#match every macro (arr: [] $T, predicate: (T) -> bool) -> bool {
- every :: every
- return every(arr, #(predicate(it)));
-}
+every :: #match #locked {
+ macro (arr: [] $T, predicate: (T) -> bool) -> bool {
+ every :: every
+ return every(arr, #(predicate(it)));
+ },
-#match every macro (arr: [] $T, predicate_body: Code) -> bool {
- for arr {
- if !(#unquote predicate_body) do return false;
+ macro (arr: [] $T, predicate_body: Code) -> bool {
+ for arr {
+ if !(#unquote predicate_body) do return false;
+ }
+ return true;
}
- return true;
}
-some :: #match {}
-#match some macro (arr: [] $T, predicate: (T) -> bool) -> bool {
- some :: some
- return every(arr, #(predicate(it)));
-}
+some :: #match #locked {
+ macro (arr: [] $T, predicate: (T) -> bool) -> bool {
+ some :: some
+ return every(arr, #(predicate(it)));
+ },
-#match some macro (arr: [] $T, predicate_body: Code) -> bool {
- for arr {
- if #unquote predicate_body do return false;
+ macro (arr: [] $T, predicate_body: Code) -> bool {
+ for arr {
+ if #unquote predicate_body do return false;
+ }
+ return true;
}
- return true;
}
fill :: (arr: [] $T, value: T) {
return null;
}
-first :: #match {}
-#match first macro (arr: [] $T, predicate: (T) -> bool) -> ^T {
- first :: first
- return first(arr, #(predicate(it)));
-}
+first :: #match #locked {
+ macro (arr: [] $T, predicate: (T) -> bool) -> ^T {
+ first :: first
+ return first(arr, #(predicate(it)));
+ },
-#match first macro (arr: [] $T, predicate_body: Code) -> ^T {
- // This is to preserve the semantics that "it" is
- // not a pointer (same as contains)
- for ^it_ptr: arr {
- it := *it_ptr;
- if #unquote predicate_body do return it_ptr;
- }
+ macro (arr: [] $T, predicate_body: Code) -> ^T {
+ // This is to preserve the semantics that "it" is
+ // not a pointer (same as contains)
+ for ^it_ptr: arr {
+ it := *it_ptr;
+ if #unquote predicate_body do return it_ptr;
+ }
- return null;
+ return null;
+ }
}
-count_where :: #match {}
-#match count_where macro (arr: [] $T, predicate: (T) -> bool) -> u32 {
- count_where :: count_where
- return count_where(arr, #(predicate(it)));
-}
+count_where :: #match #locked {
+ macro (arr: [] $T, predicate: (T) -> bool) -> u32 {
+ count_where :: count_where
+ return count_where(arr, #(predicate(it)));
+ },
-#match count_where macro (arr: [] $T, predicate_body: Code) -> u32 {
- count: u32 = 0;
- for arr {
- if #unquote predicate_body do count += 1;
+ macro (arr: [] $T, predicate_body: Code) -> u32 {
+ count: u32 = 0;
+ for arr {
+ if #unquote predicate_body do count += 1;
+ }
+ return count;
}
- return count;
}
as_iterator :: #match {}
#local __iterable_test :: macro (i: Iterator($V)) {}
-Iterable :: interface (t: $T) {
+Iterable :: interface (T as t) {
as_iterator(t) |> __iterable_test();
}
}
#local {
- ValidKey :: interface (t: $T) {
+ ValidKey :: interface (T as t) {
// In order to use a certain type as a key in a Map, you must
// provide an implementation of core.hash.to_u32() for that type,
// and you must provide an operator overload for ==.
math :: package core.math
}
-#local SetValue :: interface (t: $T) {
+#local SetValue :: interface (T as t) {
{ hash.to_u32(t) } -> u32;
{ t == t } -> bool;
}
(key: type_expr) -> u32 do return to_u32(cast(u32) key);
}
-Hashable :: interface (t: $T) {
+Hashable :: interface (T as t) {
{ to_u32(t) } -> u32;
}
info := cast(^Type_Info_Struct) get_type_info(type);
if info.kind != .Struct do return null;
- for ^method: info.methods {
- if method.name == method_name {
- return ^method.func;
- }
- }
-
+ method := core.array.first(info.methods, #(it.name == method_name));
+ if method != null do return ^method.func;
return null;
}
auto_flush_stdio := true
-print :: #match {
+print :: #match #locked {
(x: str) {
io.write(^stdio.print_writer, x);
if x[x.count - 1] == #char "\n" && auto_flush_stdio do __flush_stdio();
// expressions do not type check correctly, then the set of parameters does not
// satisfy the interface.
- CanAdd :: interface (t: $T) {
+ CanAdd :: interface (T as t) {
t + t;
}
// These have to be defined out here because #operator and #match are only allowed
// as top level expressions currently.
-NumberLike :: interface (t: $T) {
+NumberLike :: interface (T as t) {
// The constraints here are a little stricter than before. This syntax
// allows you to specify the expected type of the expression. If the type
// of the expression and the type after the arrow do not match, then the
// when the are combined with overloaded procedures.
// Take this example. Here is the same CanAdd interface from before.
- CanAdd :: interface (t: $T) {
+ CanAdd :: interface (T as t) {
t + t;
}
AstBlock *true_stmt;
AstBlock *false_stmt;
- // Used by Static_If
- Scope *defined_in_scope;
- bh_arr(struct Entity *) true_entities;
- bh_arr(struct Entity *) false_entities;
+ union {
+ // Used by Static_If
+ struct {
+ Scope *defined_in_scope;
+ bh_arr(struct Entity *) true_entities;
+ bh_arr(struct Entity *) false_entities;
+ };
+
+ // Used by While
+ b32 bottom_test;
+ };
};
typedef struct AstIfWhile AstIf;
typedef struct AstIfWhile AstWhile;
// the complete set of overloads that can be used by an overloaded
// function.
bh_imap all_overloads;
+
+ b32 locked : 1;
};
// @CLEANUP: Is this really necessary?
typedef struct InterfaceParam {
OnyxToken *value_token;
- AstType *type_node;
+ OnyxToken *type_token;
} InterfaceParam;
typedef struct InterfaceConstraint {
}
if (whilenode->true_stmt) CHECK(statement, (AstNode **) &whilenode->true_stmt);
- if (whilenode->false_stmt) CHECK(statement, (AstNode **) &whilenode->false_stmt);
+ if (whilenode->false_stmt) {
+ if (whilenode->bottom_test) {
+ ERROR(whilenode->token->pos, "while-loops with an 'else' clause cannot be bottom tested.");
+ }
+
+ CHECK(statement, (AstNode **) &whilenode->false_stmt);
+ }
return Check_Success;
}
sentinel->token = ip->value_token;
sentinel->type_node = constraint->type_args[i];
- assert(ip->type_node);
AstAlias *type_alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstAlias), Ast_Kind_Alias);
- type_alias->token = ip->type_node->token;
+ type_alias->token = ip->type_token;
type_alias->alias = (AstTyped *) constraint->type_args[i];
symbol_introduce(constraint->scope, ip->value_token, (AstNode *) sentinel);
- symbol_introduce(constraint->scope, ip->type_node->token, (AstNode *) type_alias);
+ symbol_introduce(constraint->scope, ip->type_token, (AstNode *) type_alias);
}
assert(constraint->entity);
AstIfWhile* while_node = make_node(AstIfWhile, Ast_Kind_While);
while_node->token = while_token;
+ if (parse_possible_directive(parser, "bottom_test")) {
+ while_node->bottom_test = 1;
+ }
+
AstTyped* cond;
AstNode* initialization_or_cond=NULL;
b32 had_initialization = 0;
if (parser->hit_unexpected_token) return interface;
InterfaceParam ip;
+ ip.type_token = expect_token(parser, Token_Type_Symbol);
+ expect_token(parser, Token_Type_Keyword_As);
ip.value_token = expect_token(parser, Token_Type_Symbol);
- expect_token(parser, ':');
- expect_token(parser, '$');
-
- OnyxToken* type_sym = expect_token(parser, Token_Type_Symbol);
- ip.type_node = (AstType *) make_symbol(parser->allocator, type_sym);
bh_arr_push(interface->params, ip);
}
static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
+ b32 locked = 0;
+ if (parse_possible_directive(parser, "locked")) {
+ locked = 1;
+ }
+
expect_token(parser, '{');
AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
ofunc->token = token;
ofunc->flags |= Ast_Flag_Comptime;
+ ofunc->locked = locked;
bh_arr_new(global_heap_allocator, ofunc->overloads, 4);
if (add_overload->overloaded_function == NULL) return Symres_Error; // NOTE: Error message will already be generated
if (add_overload->overloaded_function->kind != Ast_Kind_Overloaded_Function) {
- onyx_report_error(add_overload->token->pos, Error_Critical, "#add_overload directive did not resolve to an overloaded function.");
+ onyx_report_error(add_overload->token->pos, Error_Critical, "#match directive expects a matched procedure.");
+ return Symres_Error;
+ }
- } else {
- AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
- SYMRES(expression, (AstTyped **) &add_overload->overload);
- add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload);
+ AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
+ if (ofunc->locked) {
+ onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as the original #match was declared as #locked.");
+ onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match.");
+ return Symres_Error;
}
+ SYMRES(expression, (AstTyped **) &add_overload->overload);
+ add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload);
break;
}
emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
- emit_expression(mod, &code, while_node->cond);
- WI(WI_I32_EQZ);
- WID(WI_COND_JUMP, 0x01);
+ if (!while_node->bottom_test) {
+ emit_expression(mod, &code, while_node->cond);
+ WI(WI_I32_EQZ);
+ WID(WI_COND_JUMP, 0x01);
+ }
emit_block(mod, &code, while_node->true_stmt, 0);
- if (bh_arr_last(code).type != WI_JUMP)
- WID(WI_JUMP, 0x00);
+ if (while_node->bottom_test) {
+ emit_expression(mod, &code, while_node->cond);
+ WID(WI_COND_JUMP, 0x00);
+
+ } else {
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(WI_JUMP, 0x00);
+ }
emit_leave_structured_block(mod, &code);
emit_leave_structured_block(mod, &code);
return g(f(a));
}
- Number :: interface (t: $T) {
+ Number :: interface (T as t) {
{ t * t } -> T;
}
use package core
-Hashable :: interface (t: $T) {
+Hashable :: interface (T as t) {
{ hash.to_u32(t) } -> u32;
}
},
}
-CanCastTo :: interface (t: $T, d: $D) {
+CanCastTo :: interface (T as t, D as d) {
{ cast(D) t } -> D;
}
return x + y + x * y;
}
-SemiRing :: interface (t: $T) {
+SemiRing :: interface (T as t) {
{t + t} -> T;
{t * t} -> T;
}
return (x & y) | (x ^ y);
}
-BitField :: interface (t: $T) {
+BitField :: interface (T as t) {
{ ~t } -> T;
{ t & t } -> T;
{ t | t } -> T;