From: Brendan Hansen Date: Mon, 6 Mar 2023 04:29:22 +0000 (-0600) Subject: added: try operator (?) X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=7599195c5ad01c8c4b1ca9222a00676ccb18ede3;p=onyx.git added: try operator (?) --- diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index 64be00f7..65741b13 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -293,6 +293,9 @@ typedef enum UnaryOp { Unary_Op_Bitwise_Not, Unary_Op_Cast, Unary_Op_Auto_Cast, + Unary_Op_Try, + + Unary_Op_Count, } UnaryOp; typedef enum BinaryOp { @@ -602,7 +605,6 @@ struct AstTyped { AstTyped_base; }; // Expression Nodes struct AstNamedValue { AstTyped_base; AstTyped* value; }; -struct AstUnaryOp { AstTyped_base; UnaryOp operation; AstTyped *expr; }; struct AstStrLit { AstTyped_base; u64 data_id; u64 length; b32 is_cstr: 1; }; struct AstLocal { AstTyped_base; }; struct AstDereference { AstTyped_base; AstTyped *expr; }; @@ -620,6 +622,14 @@ struct AstNumLit { b32 was_hex_literal : 1; b32 was_char_literal : 1; }; +struct AstUnaryOp { + AstTyped_base; + UnaryOp operation; + + AstTyped *expr; + + Arguments *overload_args; +}; struct AstBinaryOp { AstTyped_base; BinaryOp operation; @@ -1319,6 +1329,7 @@ struct AstDirectiveOperator { AstNode_base; BinaryOp operator; + OnyxToken *operator_token; u64 order; AstTyped *overload; @@ -1724,6 +1735,7 @@ typedef Table(OnyxIntrinsic) IntrinsicTable; extern IntrinsicTable intrinsic_table; extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count]; +extern bh_arr(OverloadOption) unary_operator_overloads[Unary_Op_Count]; void initialize_builtins(bh_allocator a); void initalize_special_globals(); diff --git a/compiler/src/builtins.c b/compiler/src/builtins.c index 156a967e..41724010 100644 --- a/compiler/src/builtins.c +++ b/compiler/src/builtins.c @@ -369,6 +369,7 @@ static IntrinsicMap builtin_intrinsics[] = { }; bh_arr(OverloadOption) operator_overloads[Binary_Op_Count] = { 0 }; +bh_arr(OverloadOption) unary_operator_overloads[Unary_Op_Count] = { 0 }; void initialize_builtins(bh_allocator a) { // HACK @@ -491,6 +492,10 @@ void initialize_builtins(bh_allocator a) { bh_arr_new(global_heap_allocator, operator_overloads[i], 4); } + fori (i, 0, Unary_Op_Count) { + bh_arr_new(global_heap_allocator, unary_operator_overloads[i], 4); + } + IntrinsicMap* intrinsic = &builtin_intrinsics[0]; while (intrinsic->name != NULL) { shput(intrinsic_table, intrinsic->name, intrinsic->intrinsic); diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 17d4c51a..559b07cd 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -117,7 +117,7 @@ b32 all_checks_are_final = 1; b32 inside_for_iterator = 0; bh_arr(AstFor *) for_node_stack = NULL; static bh_imap __binop_impossible_cache[Binary_Op_Count]; -static AstCall __binop_maybe_overloaded; +static AstCall __op_maybe_overloaded; #define STATEMENT_LEVEL 1 @@ -817,7 +817,7 @@ static void report_bad_binaryop(AstBinaryOp* binop) { } static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* third_argument) { - if (bh_arr_length(operator_overloads[binop->operation]) == 0) return &__binop_maybe_overloaded; + if (bh_arr_length(operator_overloads[binop->operation]) == 0) return &__op_maybe_overloaded; if (binop->overload_args == NULL || binop->overload_args->values[1] == NULL) { if (binop->overload_args == NULL) { @@ -845,7 +845,6 @@ static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* thi binop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, binop->left); } - binop->overload_args->values[1] = (AstTyped *) make_argument(context.ast_alloc, binop->right); if (third_argument != NULL) binop->overload_args->values[2] = (AstTyped *) make_argument(context.ast_alloc, third_argument); } @@ -862,6 +861,32 @@ static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop, AstTyped* thi return implicit_call; } +static AstCall* unaryop_try_operator_overload(AstUnaryOp* unop) { + if (bh_arr_length(unary_operator_overloads[unop->operation]) == 0) return &__op_maybe_overloaded; + + if (unop->overload_args == NULL || unop->overload_args->values[0] == NULL) { + if (unop->overload_args == NULL) { + unop->overload_args = bh_alloc_item(context.ast_alloc, Arguments); + bh_arr_new(context.ast_alloc, unop->overload_args->values, 1); + bh_arr_set_length(unop->overload_args->values, 1); + } + + unop->overload_args->values[0] = (AstTyped *) make_argument(context.ast_alloc, unop->expr); + } + + AstTyped* overload = find_matching_overload_by_arguments(unary_operator_overloads[unop->operation], unop->overload_args); + if (overload == NULL || overload == (AstTyped *) &node_that_signals_a_yield) return (AstCall *) overload; + + AstCall* implicit_call = onyx_ast_node_new(context.ast_alloc, sizeof(AstCall), Ast_Kind_Call); + implicit_call->token = unop->token; + implicit_call->callee = overload; + implicit_call->va_kind = VA_Kind_Not_VA; + + arguments_clone(&implicit_call->args, unop->overload_args); + return implicit_call; +} + + static CheckStatus assign_type_or_check(AstTyped **node, Type *type, OnyxToken *report_loc) { if (node && (*node)->type == NULL) { @@ -1232,7 +1257,7 @@ CheckStatus check_binaryop(AstBinaryOp** pbinop) { if (implicit_call == (AstCall *) &node_that_signals_a_yield) YIELD(binop->token->pos, "Trying to resolve operator overload."); - if (implicit_call != NULL && implicit_call != &__binop_maybe_overloaded) { + if (implicit_call != NULL && implicit_call != &__op_maybe_overloaded) { // NOTE: Not a binary op implicit_call->next = binop->next; *pbinop = (AstBinaryOp *) implicit_call; @@ -1241,7 +1266,7 @@ CheckStatus check_binaryop(AstBinaryOp** pbinop) { return Check_Success; } - if (cache_key && implicit_call != &__binop_maybe_overloaded) { + if (cache_key && implicit_call != &__op_maybe_overloaded) { bh_imap_put(&__binop_impossible_cache[binop->operation], cache_key, 1); } } @@ -1374,6 +1399,21 @@ CheckStatus check_unaryop(AstUnaryOp** punop) { unaryop->type = unaryop->expr->type; } + if (unaryop->operation == Unary_Op_Try) { + AstCall* call = unaryop_try_operator_overload(unaryop); + if (call == (AstCall *) &node_that_signals_a_yield) YIELD(unaryop->token->pos, "Waiting on potential operator overload."); + if (call != NULL && call != &__op_maybe_overloaded) { + call->next = unaryop->next; + *(AstCall **) punop = call; + + CHECK(call, (AstCall **) punop); + return Check_Success; + } + + ERROR_(unaryop->token->pos, "'%s' does not support '?' operator.", type_get_name(unaryop->expr->type)); + } + + if (unaryop->expr->flags & Ast_Flag_Comptime) { unaryop->flags |= Ast_Flag_Comptime; // NOTE: Not a unary op diff --git a/compiler/src/parser.c b/compiler/src/parser.c index 4e94e76d..c1ebbf89 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -889,6 +889,17 @@ static AstTyped* parse_factor(OnyxParser* parser) { break; } + case '?': { + AstUnaryOp* unop = make_node(AstUnaryOp, Ast_Kind_Unary_Op); + unop->token = expect_token(parser, '?'); + unop->operation = Unary_Op_Try; + + unop->expr = retval; + + retval = (AstTyped *) unop; + break; + } + default: goto factor_parsed; } } @@ -3300,6 +3311,7 @@ static void parse_top_level_statement(OnyxParser* parser) { else if (parse_possible_directive(parser, "operator")) { AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator); operator->token = dir_token; + operator->operator_token = parser->curr; // These cases have to happen first because these are not necessarily "binary operators", // they are just things that I want to be able to overload. []= is technically a ternary @@ -3322,11 +3334,7 @@ static void parse_top_level_statement(OnyxParser* parser) { consume_token(parser); if (op == Binary_Op_Subscript) expect_token(parser, ']'); // #operator [] ... needs to consume the other ']' - if (op == Binary_Op_Count) { - onyx_report_error(parser->curr->pos, Error_Critical, "Invalid binary operator."); - } else { - operator->operator = op; - } + operator->operator = op; operator_determined: if (parse_possible_directive(parser, "order")) { diff --git a/compiler/src/symres.c b/compiler/src/symres.c index ddedd2a8..5e81c28d 100644 --- a/compiler/src/symres.c +++ b/compiler/src/symres.c @@ -1442,6 +1442,28 @@ static SymresStatus symres_process_directive(AstNode* directive) { onyx_report_error(operator->token->pos, Error_Critical, "This cannot be used as an operator overload."); return Symres_Error; } + + // First try unary operator overloading + // CLEANUP This is not written well at all... + if (operator->operator == Binary_Op_Count) { + if (bh_arr_length(overload->params) != 1) { + onyx_report_error(operator->token->pos, Error_Critical, "Expected exactly 1 argument for unary operator overload."); + return Symres_Error; + } + + UnaryOp unop = Unary_Op_Count; + switch (operator->operator_token->type) { + case '?': unop = Unary_Op_Try; break; + } + + if (unop == Unary_Op_Count) { + onyx_report_error(operator->token->pos, Error_Critical, "Unknown operator."); + return Symres_Error; + } + + add_overload_option(&unary_operator_overloads[unop], operator->order, operator->overload); + return Symres_Success; + } if (operator->operator != Binary_Op_Subscript_Equals && bh_arr_length(overload->params) != 2) { onyx_report_error(operator->token->pos, Error_Critical, "Expected exactly 2 arguments for binary operator overload."); diff --git a/core/container/optional.onyx b/core/container/optional.onyx index 87202b9d..2db77f0a 100644 --- a/core/container/optional.onyx +++ b/core/container/optional.onyx @@ -121,6 +121,13 @@ package core return default; } +#operator ? macro (opt: ?$T) -> T { + value := opt; + if value do return value.value; + + return #from_enclosing .{}; +} + #overload __implicit_bool_cast :: macro (o: ?$T) => o.has_value;