From: Brendan Hansen Date: Tue, 31 Jan 2023 21:13:10 +0000 (-0600) Subject: added `#this_package`; cleaned up code with new feature X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=dcf478b4cbe532bbaf547bac0069c92e37a3f207;p=onyx.git added `#this_package`; cleaned up code with new feature --- diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index f70ae309..b9b06986 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -44,6 +44,7 @@ NODE(DirectiveRemove) \ NODE(DirectiveFirst) \ NODE(DirectiveExportName) \ + NODE(DirectiveThisPackage) \ \ NODE(Return) \ NODE(Jump) \ @@ -221,6 +222,7 @@ typedef enum AstKind { 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, diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index 2f1e7dde..d35b4d5c 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -99,6 +99,7 @@ static const char* ast_node_names[] = { "REMOVE", "FIRST", "EXPORT NAME", + "THIS PACKAGE", "CALL SITE", "CODE BLOCK", diff --git a/compiler/src/checker.c b/compiler/src/checker.c index c0f58c24..2d35aa34 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -2171,6 +2171,10 @@ CheckStatus check_expression(AstTyped** pexpr) { 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; diff --git a/compiler/src/clone.c b/compiler/src/clone.c index 6b708f8e..62731947 100644 --- a/compiler/src/clone.c +++ b/compiler/src/clone.c @@ -29,6 +29,7 @@ static inline b32 should_clone(AstNode* node) { 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; diff --git a/compiler/src/entities.c b/compiler/src/entities.c index f679db1f..745ae4ae 100644 --- a/compiler/src/entities.c +++ b/compiler/src/entities.c @@ -368,6 +368,7 @@ void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* s 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; diff --git a/compiler/src/parser.c b/compiler/src/parser.c index ba93e0a4..9c8ff9cd 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -791,6 +791,15 @@ static AstTyped* parse_factor(OnyxParser* parser) { 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; diff --git a/compiler/src/symres.c b/compiler/src/symres.c index 38e84895..fbca2f71 100644 --- a/compiler/src/symres.c +++ b/compiler/src/symres.c @@ -7,13 +7,15 @@ // :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; \ @@ -73,21 +75,21 @@ static SymresStatus symres_constraint(AstConstraint* constraint); 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); @@ -261,7 +263,7 @@ static SymresStatus symres_local(AstLocal** local) { 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; } @@ -632,9 +634,9 @@ static SymresStatus symres_expression(AstTyped** expr) { 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; } @@ -691,7 +693,7 @@ static SymresStatus symres_if(AstIfWhile* ifnode) { } 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); @@ -711,7 +713,7 @@ static SymresStatus symres_if(AstIfWhile* ifnode) { 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); @@ -728,7 +730,7 @@ static SymresStatus symres_while(AstIfWhile* whilenode) { } 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); @@ -751,7 +753,7 @@ static SymresStatus symres_case(AstSwitchCase *casenode) { 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); @@ -793,7 +795,7 @@ static SymresStatus symres_use(AstUse* use) { 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); @@ -820,14 +822,14 @@ static SymresStatus symres_use(AstUse* use) { } 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) { @@ -843,7 +845,7 @@ static SymresStatus symres_use(AstUse* use) { } } - symbol_introduce(curr_scope, qu->as_name, thing); + symbol_introduce(current_scope, qu->as_name, thing); } } @@ -871,7 +873,7 @@ static SymresStatus symres_use(AstUse* use) { 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; @@ -993,13 +995,13 @@ static SymresStatus symres_statement_chain(AstNode** walker) { 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; @@ -1044,7 +1046,7 @@ SymresStatus symres_function_header(AstFunction* func) { 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) { @@ -1066,7 +1068,7 @@ SymresStatus symres_function_header(AstFunction* func) { } 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) { @@ -1086,8 +1088,8 @@ SymresStatus symres_function_header(AstFunction* func) { // 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; @@ -1164,7 +1166,7 @@ SymresStatus symres_function(AstFunction* func) { 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; @@ -1238,7 +1240,7 @@ static SymresStatus symres_enum(AstEnumType* enum_node) { 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); } @@ -1359,7 +1361,7 @@ static SymresStatus symres_struct_defaults(AstType* t) { 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; } @@ -1507,6 +1509,13 @@ static SymresStatus symres_process_directive(AstNode* directive) { 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; @@ -1601,7 +1610,7 @@ static SymresStatus symres_polyquery(AstPolyQuery *query) { 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++; @@ -1627,7 +1636,7 @@ static SymresStatus symres_polyquery(AstPolyQuery *query) { 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; @@ -1705,6 +1714,7 @@ static SymresStatus symres_file_contents(AstFileContents* fc) { } void symres_entity(Entity* ent) { + current_entity = ent; if (ent->scope) scope_enter(ent->scope); report_unresolved_symbols = context.cycle_detected; @@ -1714,7 +1724,7 @@ void symres_entity(Entity* ent) { 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); @@ -1777,5 +1787,6 @@ void symres_entity(Entity* ent) { ent->state = next_state; } - curr_scope = NULL; + current_scope = NULL; + current_entity = NULL; } diff --git a/core/container/array.onyx b/core/container/array.onyx index 960016fd..725f021f 100644 --- a/core/container/array.onyx +++ b/core/container/array.onyx @@ -320,6 +320,12 @@ contains :: #match #locked { } } +// 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. @@ -329,12 +335,16 @@ sum :: (arr: [] $T, start: T = 0) -> T { 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; @@ -364,7 +374,8 @@ sort :: (arr: [] $T, cmp: (T, T) -> i32) -> [] T { 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]; @@ -490,23 +501,10 @@ fold :: macro (arr: [] $T, init: $R, body: Code) -> R { 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 { @@ -519,10 +517,7 @@ 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 { @@ -629,17 +624,16 @@ first :: #match #locked { } } -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; } diff --git a/core/container/iter.onyx b/core/container/iter.onyx index 4015d4e3..93d63bd4 100644 --- a/core/container/iter.onyx +++ b/core/container/iter.onyx @@ -527,11 +527,8 @@ const :: (value: $T) -> Iterator(T) { 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)) { @@ -722,10 +719,8 @@ fold :: #match #local {} // @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 { @@ -739,10 +734,8 @@ 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 { @@ -754,11 +747,8 @@ 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 { @@ -769,11 +759,8 @@ 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 { @@ -852,11 +839,8 @@ comp :: macro (i: Iterator($V), value: Code) => { } #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); // @@ -905,11 +889,8 @@ generator_no_copy :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool), close: (^Ctx) -> vo 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) { @@ -952,20 +933,20 @@ generator_no_copy :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool), close: (^Ctx) -> vo #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); diff --git a/core/container/optional.onyx b/core/container/optional.onyx index 0d51e3d2..36283f40 100644 --- a/core/container/optional.onyx +++ b/core/container/optional.onyx @@ -1,5 +1,20 @@ 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; @@ -7,33 +22,52 @@ Optional :: struct (Value_Type: type_expr) { } #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();