NODE(DirectiveRemove) \
NODE(DirectiveFirst) \
NODE(DirectiveExportName) \
+ NODE(DirectiveThisPackage) \
\
NODE(Return) \
NODE(Jump) \
Ast_Kind_Directive_Remove,
Ast_Kind_Directive_First,
Ast_Kind_Directive_Export_Name,
+ Ast_Kind_Directive_This_Package,
Ast_Kind_Call_Site,
Ast_Kind_Code_Block,
"REMOVE",
"FIRST",
"EXPORT NAME",
+ "THIS PACKAGE",
"CALL SITE",
"CODE BLOCK",
if (expr->type == NULL) YIELD(expr->token->pos, "Waiting to know string literals type. This is a weird one...") ;
break;
+ case Ast_Kind_Directive_This_Package:
+ YIELD(expr->token->pos, "Waiting to resolve #this_package.");
+ break;
+
case Ast_Kind_File_Contents: break;
case Ast_Kind_Overloaded_Function: break;
case Ast_Kind_Enum_Value: break;
case Ast_Kind_Basic_Type:
case Ast_Kind_Enum_Type:
case Ast_Kind_Enum_Value:
+ case Ast_Kind_Directive_This_Package: // Not copied, because it should depend on where it was written, not where a macro/polymorphic function was expanded.
return 0;
default: return 1;
case Ast_Kind_Directive_Operator:
case Ast_Kind_Directive_Init:
case Ast_Kind_Directive_Library:
+ case Ast_Kind_Directive_This_Package:
case Ast_Kind_Injection: {
ent.type = Entity_Type_Process_Directive;
ent.expr = (AstTyped *) node;
retval = (AstTyped *) export_name;
break;
}
+ else if (parse_possible_directive(parser, "this_package")) {
+ AstPackage *this_package = make_node(AstPackage, Ast_Kind_Directive_This_Package);
+ this_package->token = parser->curr - 1;
+ this_package->type_node = builtin_package_id_type;
+ ENTITY_SUBMIT(this_package);
+
+ retval = (AstTyped *) this_package;
+ break;
+ }
onyx_report_error(parser->curr->pos, Error_Critical, "Invalid directive in expression.");
return NULL;
// :EliminatingSymres - notes the places where too much work is being done in symbol resolution
// Variables used during the symbol resolution phase.
-static Scope* curr_scope = NULL;
+static Scope* current_scope = NULL;
static b32 report_unresolved_symbols = 1;
static b32 resolved_a_symbol = 0;
// Everything related to waiting on is imcomplete at the moment.
static Entity* waiting_on = NULL;
+static Entity* current_entity = NULL;
+
#define SYMRES(kind, ...) do { \
SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
if (ss > Symres_Errors_Start) return ss; \
static SymresStatus symres_polyquery(AstPolyQuery *query);
static void scope_enter(Scope* new_scope) {
- curr_scope = new_scope;
+ current_scope = new_scope;
}
static void scope_leave() {
- curr_scope = curr_scope->parent;
+ current_scope = current_scope->parent;
}
static SymresStatus symres_symbol(AstNode** symbol_node) {
OnyxToken* token = (*symbol_node)->token;
- AstNode* res = symbol_resolve(curr_scope, token);
+ AstNode* res = symbol_resolve(current_scope, token);
if (!res) { // :SymresStall
if (report_unresolved_symbols) {
token_toggle_end(token);
- char *closest = find_closest_symbol_in_scope_and_parents(curr_scope, token->text);
+ char *closest = find_closest_symbol_in_scope_and_parents(current_scope, token->text);
token_toggle_end(token);
if (closest) onyx_report_error(token->pos, Error_Critical, "Unable to resolve symbol '%b'. Did you mean '%s'?", token->text, token->length, closest);
SYMRES(type, &(*local)->type_node);
if ((*local)->token != NULL)
- symbol_introduce(curr_scope, (*local)->token, (AstNode *) *local);
+ symbol_introduce(current_scope, (*local)->token, (AstNode *) *local);
return Symres_Success;
}
break;
case Ast_Kind_Do_Block: {
- Scope* old_curr_scope = curr_scope;
+ Scope* old_current_scope = current_scope;
SYMRES(block, ((AstDoBlock *) *expr)->block);
- curr_scope = old_curr_scope;
+ current_scope = old_current_scope;
break;
}
} else {
if (ifnode->initialization != NULL) {
- ifnode->scope = scope_create(context.ast_alloc, curr_scope, ifnode->token->pos);
+ ifnode->scope = scope_create(context.ast_alloc, current_scope, ifnode->token->pos);
scope_enter(ifnode->scope);
SYMRES(statement_chain, &ifnode->initialization);
static SymresStatus symres_while(AstIfWhile* whilenode) {
if (whilenode->initialization != NULL) {
- whilenode->scope = scope_create(context.ast_alloc, curr_scope, whilenode->token->pos);
+ whilenode->scope = scope_create(context.ast_alloc, current_scope, whilenode->token->pos);
scope_enter(whilenode->scope);
SYMRES(statement_chain, &whilenode->initialization);
}
static SymresStatus symres_for(AstFor* fornode) {
- fornode->scope = scope_create(context.ast_alloc, curr_scope, fornode->token->pos);
+ fornode->scope = scope_create(context.ast_alloc, current_scope, fornode->token->pos);
scope_enter(fornode->scope);
SYMRES(expression, &fornode->iter);
SYMRES(local, &fornode->var);
static SymresStatus symres_switch(AstSwitch* switchnode) {
if (switchnode->initialization != NULL) {
- switchnode->scope = scope_create(context.ast_alloc, curr_scope, switchnode->token->pos);
+ switchnode->scope = scope_create(context.ast_alloc, current_scope, switchnode->token->pos);
scope_enter(switchnode->scope);
SYMRES(statement_chain, &switchnode->initialization);
SYMRES(package, package);
if (!use->entity) {
- add_entities_for_node(NULL, (AstNode *) use, curr_scope, NULL);
+ add_entities_for_node(NULL, (AstNode *) use, current_scope, NULL);
}
package_track_use_package(package->package, use->entity);
}
if (used_scope) {
- if (used_scope == curr_scope) return Symres_Success;
+ if (used_scope == current_scope) return Symres_Success;
if (use->only == NULL) {
OnyxFilePos pos = { 0 };
if (use->token != NULL)
pos = use->token->pos;
- scope_include(curr_scope, used_scope, pos);
+ scope_include(current_scope, used_scope, pos);
} else {
bh_arr_each(QualifiedUse, qu, use->only) {
}
}
- symbol_introduce(curr_scope, qu->as_name, thing);
+ symbol_introduce(current_scope, qu->as_name, thing);
}
}
fori (i, 0, shlen(st->Struct.members)) {
StructMember* value = st->Struct.members[i].value;
AstFieldAccess* fa = make_field_access(context.ast_alloc, use_expr, value->name);
- symbol_raw_introduce(curr_scope, value->name, use->token->pos, (AstNode *) fa);
+ symbol_raw_introduce(current_scope, value->name, use->token->pos, (AstNode *) fa);
}
return Symres_Success;
static SymresStatus symres_block(AstBlock* block) {
if (block->rules & Block_Rule_New_Scope) {
if (block->scope == NULL)
- block->scope = scope_create(context.ast_alloc, curr_scope, block->token->pos);
+ block->scope = scope_create(context.ast_alloc, current_scope, block->token->pos);
scope_enter(block->scope);
}
if (block->binding_scope != NULL)
- scope_include(curr_scope, block->binding_scope, block->token->pos);
+ scope_include(current_scope, block->binding_scope, block->token->pos);
if (block->body) {
AstNode** start = &block->body;
func->flags |= Ast_Flag_Comptime;
if (func->scope == NULL)
- func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
+ func->scope = scope_create(context.ast_alloc, current_scope, func->token->pos);
if (func->constraints.constraints != NULL && func->constraints.constraints_met == 0) {
bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
}
bh_arr_each(AstParam, param, func->params) {
- symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+ symbol_introduce(current_scope, param->local->token, (AstNode *) param->local);
}
bh_arr_each(AstParam, param, func->params) {
// what the ramifications of that is.
assert((*node)->kind == Ast_Kind_Static_If || (*node)->kind == Ast_Kind_File_Contents);
- // Need to curr_scope->parent because curr_scope is the function body scope.
- Scope *scope = curr_scope->parent;
+ // Need to current_scope->parent because current_scope is the function body scope.
+ Scope *scope = current_scope->parent;
if ((*node)->kind == Ast_Kind_Static_If) {
AstIf *static_if = (AstIf *) *node;
fori (i, 0, shlen(st->Struct.members)) {
StructMember* value = st->Struct.members[i].value;
AstFieldAccess* fa = make_field_access(context.ast_alloc, (AstTyped *) param->local, value->name);
- symbol_raw_introduce(curr_scope, value->name, param->local->token->pos, (AstNode *) fa);
+ symbol_raw_introduce(current_scope, value->name, param->local->token->pos, (AstNode *) fa);
}
param->use_processed = 1;
if (enum_node->scope == NULL) {
enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
- enum_node->scope = scope_create(context.ast_alloc, curr_scope, enum_node->token->pos);
+ enum_node->scope = scope_create(context.ast_alloc, current_scope, enum_node->token->pos);
type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
}
static SymresStatus symres_polyproc(AstFunction* pp) {
pp->flags |= Ast_Flag_Comptime;
- pp->parent_scope_of_poly_proc = curr_scope;
+ pp->parent_scope_of_poly_proc = current_scope;
return Symres_Success;
}
add_entities_for_node(NULL, (AstNode *) binding, scope, pac);
return Symres_Complete;
}
+
+ case Ast_Kind_Directive_This_Package: {
+ AstPackage *package = (AstPackage *) directive;
+ package->kind = Ast_Kind_Package;
+ package->package = current_entity->package;
+ return Symres_Complete;
+ }
}
return Symres_Success;
if (pp->kind == PPK_Baked_Value && pp->idx == idx) goto skip_introducing_symbol;
}
- symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+ symbol_introduce(current_scope, param->local->token, (AstNode *) param->local);
skip_introducing_symbol:
idx++;
static SymresStatus symres_foreign_block(AstForeignBlock *fb) {
if (fb->scope == NULL)
- fb->scope = scope_create(context.ast_alloc, curr_scope, fb->token->pos);
+ fb->scope = scope_create(context.ast_alloc, current_scope, fb->token->pos);
bh_arr_each(Entity *, pent, fb->captured_entities) {
Entity *ent = *pent;
}
void symres_entity(Entity* ent) {
+ current_entity = ent;
if (ent->scope) scope_enter(ent->scope);
report_unresolved_symbols = context.cycle_detected;
switch (ent->type) {
case Entity_Type_Binding: {
- symbol_introduce(curr_scope, ent->binding->token, ent->binding->node);
+ symbol_introduce(current_scope, ent->binding->token, ent->binding->node);
track_declaration_for_tags((AstNode *) ent->binding);
package_reinsert_use_packages(ent->package);
ent->state = next_state;
}
- curr_scope = NULL;
+ current_scope = NULL;
+ current_entity = NULL;
}
}
}
+// Tests if array is empty.
+// Normally this is unneeded, as arrays have a 'truthiness'
+// that depends on their count. For example, instead of saying:
+// if array.empty(arr) { ... }
+// You can simply say:
+// if !arr { ... }
empty :: (arr: [] $T) => arr.count == 0;
// Uses '+' to sum.
return sum;
}
+// Uses '*' to multiply.
product :: (arr: [] $T, start: T = 1) -> T {
prod := start;
for it: arr do prod *= it;
return prod;
}
+// Uses '+' to add the elements together.
+// Then use '/ i32' to divide by the number of elements.
+// Both of these are assumed to work.
average :: (arr: [] $T) -> T {
sum := cast(T) 0;
for it: *arr do sum += it;
x := arr.data[i];
j := i - 1;
- // @ShortCircuitLogicalOps // This is written this way because '&&' does not short circuit right now.
+ // @ShortCircuitLogicalOps
+ // This is written this way because '&&' does not short circuit right now.
while j >= 0 {
if cmp(arr.data[j], x) > 0 {
arr.data[j + 1] = arr.data[j];
return acc;
}
-/*
-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) -> void) do for ^it: arr do f(it, data);,
- macro (arr: [] $T, data: $R, f: (T, R) -> T) do for ^it: arr do *it = f(*it, data);,
-}
-*/
-
every :: #match #local {}
#overload
-every :: macro (arr: [] $T, predicate: (T) -> bool) -> bool {
- every :: every
- return every(arr, #(predicate(it)));
-}
+every :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.every(arr, #(predicate(it)));
#overload
every :: macro (arr: [] $T, predicate_body: Code) -> bool {
some :: #match #local {}
#overload
-some :: macro (arr: [] $T, predicate: (T) -> bool) -> bool {
- some :: some
- return every(arr, #(predicate(it)));
-}
+some :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.some(arr, #(predicate(it)));
#overload
some :: macro (arr: [] $T/type_is_struct, predicate_body: Code) -> bool {
}
}
-count_where :: #match #locked {
- macro (arr: [] $T, predicate: (T) -> bool) -> u32 {
- count_where :: count_where
- return count_where(arr, #(predicate(it)));
- },
+count_where :: #match #local {}
- macro (arr: [] $T, predicate_body: Code) -> u32 {
- count: u32 = 0;
- for arr {
- if #unquote predicate_body do count += 1;
- }
- return count;
+#overload
+count_where :: macro (arr: [] $T, predicate: (T) -> bool) => #this_package.count_where(arr, #(predicate(it)));
+
+#overload
+count_where :: macro (arr: [] $T, predicate_body: Code) -> u32 {
+ count: u32 = 0;
+ for arr {
+ if #unquote predicate_body do count += 1;
}
+ return count;
}
enumerate :: #match #local {}
#overload
-enumerate :: macro (it: $T/Iterable, start_index: i32 = 0) => {
- as_iter :: as_iter
- enumerate :: enumerate
- return enumerate(as_iter(it), start_index);
-}
+enumerate :: macro (it: $T/Iterable, start_index: i32 = 0) =>
+ #this_package.enumerate(#this_package.as_iter(it), start_index);
#overload
enumerate :: (it: Iterator($T), start_index: i32 = 0) -> Iterator(Enumeration_Value(T)) {
// @Cleanup // some way to shorten this would be nice
#overload
-fold :: macro (it: $T/Iterable, init: $R, combine: $S) => {
- fold :: fold
- return fold(as_iter(it), init, combine);
-}
+fold :: macro (it: $T/Iterable, init: $R, combine: $S) =>
+ #this_package.fold(#this_package.as_iter(it), init, combine);
#overload
fold :: (it: Iterator($T), initial_value: $R, combine: (T, R) -> R) -> R {
count :: #match #local {}
#overload
-count :: macro (it: $T/Iterable, cond: $F) => {
- count :: count
- return count(as_iter(it), cond);
-}
+count :: macro (it: $T/Iterable, cond: $F) =>
+ #this_package.count(#this_package.as_iter(it), cond);
#overload
count :: (it: Iterator($T), cond: (T) -> bool) -> i32 {
some :: #match #local {}
#overload
-some :: macro (it: $T/Iterable, cond: $F) => {
- some :: some
- as_iter :: as_iter
- return some(as_iter(it), cond);
-}
+some :: macro (it: $T/Iterable, cond: $F) =>
+ #this_package.some(#this_package.as_iter(it), cond);
#overload
some :: (it: Iterator($T), cond: (T) -> bool) -> bool {
every :: #match #local {}
#overload
-every :: macro (it: $T/Iterable, cond: $F) => {
- every :: every
- as_iter :: as_iter
- return every(as_iter(it), cond);
-}
+every :: macro (it: $T/Iterable, cond: $F) =>
+ #this_package.every(#this_package.as_iter(it), cond);
#overload
every :: (it: Iterator($T), cond: (T) -> bool) -> bool {
}
#overload
-comp :: macro (i: $I/Iterable, value: Code) => {
- as_iter :: as_iter
- comp :: comp
- return comp(as_iter(i), value);
-}
+comp :: macro (i: $I/Iterable, value: Code) =>
+ #this_package.comp(#this_package.as_iter(i), value);
//
distributor :: #match #local {}
#overload
- distributor :: macro (it: $T/Iterable) => {
- distributor :: distributor;
- as_iter :: as_iter;
- return distributor(as_iter(it));
- }
+ distributor :: macro (it: $T/Iterable) =>
+ #this_package.distributor(#this_package.as_iter(it));
#overload
distributor :: (it: Iterator) -> Iterator(it.Iter_Type) {
#overload
parallel_for :: macro (iterable: $I/Iterable, thread_count: u32, thread_data: ^$Ctx, body: Code) {
- parallel_for :: parallel_for;
- as_iter :: as_iter;
-
- parallel_for(as_iter(iterable), thread_count, thread_data, body);
+ #this_package.parallel_for(
+ #this_package.as_iter(iterable),
+ thread_count,
+ thread_data,
+ body
+ );
}
#overload
parallel_for :: macro (iter: Iterator($T), thread_count: u32, thread_data: ^$Ctx, body: Code) {
use core {thread, alloc}
- distributor :: distributor;
- as_iter :: as_iter;
if thread_count != 0 {
- dist := distributor(iter);
+ dist := #this_package.distributor(iter);
t_data := ^.{iter = ^dist, data = thread_data};
threads := alloc.array_from_stack(thread.Thread, thread_count - 1);
package core
+// Optional is helper type that encapsulates the idea of an empty
+// value, without resorting to null pointers. Optionals are usually
+// provided as a return value from procedures that could fail. There
+// are several helper methods that you can use to make it easier to
+// work with optionals.
+
+// Because Optional is a newer addition to the standard library of Onyx,
+// much of the standard library does not use it. Currently it is only
+// used by Map and Set in their `get_opt` function. In theory, it should
+// be used in many more places, instead of returning `.{}`.
+
+// Optional(T) is a simply a structure with a flag of whether or not
+// the optional contains a value. When the `has_value` is true, the
+// `value` member will be populated with an initialized value.
+// Otherwise, `value` should be a zeroed buffer.
@conv.Custom_Format.{ #solidify format {T=Value_Type} }
Optional :: struct (Value_Type: type_expr) {
has_value: bool;
}
#inject Optional {
- make :: (x: $T) => Optional(T).{ has_value = true, value = x };
+ // Helper procedure for creating an Optional with a value.
+ // Pass a type as the first argument to force the type, otherwise
+ // the type will be inferred from the parameter type.
+ make :: #match #locked {
+ ((x: $T) => Optional(T).{ has_value = true, value = x }),
+ ($T: type_expr, x: T) => (Optional(T).{ has_value = true, value = x })
+ }
+
+ // Create an empty Optional of a certain type. This procedure
+ // is mostly useless, because you can use `.{}` in type inferred
+ // places to avoid having to specify the type.
+ empty :: macro (T: type_expr) => Optional(T).{};
+ // Extracts the value from the Optional, or uses a default if
+ // no value is present.
value_or :: (o: Optional, default: o.Value_Type) -> o.Value_Type {
if !o.has_value do return default;
return o.value;
}
+ // Clears the value in the Optional, zeroing the memory of the value.
reset :: (o: ^Optional) {
o.has_value = false;
core.memory.set(^o.value, 0, sizeof o.Value_Type);
}
+ // Sets the value in the Optional.
set :: (o: ^Optional, value: o.Value_Type) {
o.has_value = true;
o.value = value;
}
+ // Monadic chaining operation.
and_then :: (o: Optional($T), transform: (T) -> Optional($R)) -> Optional(R) {
if !o.has_value do return .{ false };
return transform(o.value);
}
+ // Changes the value inside the optional, if present.
transform :: (o: Optional($T), transform: (T) -> $R) -> Optional(R) {
if !o.has_value do return .{ false };
return Optional.make(transform(o.value));
}
+ // Like `value_or`, but instead of providing a value, you
+ // provide a function to generate a value.
or_else :: (o: Optional($T), generate: () -> Optional(T)) -> Optional(T) {
if o.has_value do return o;
return generate();