added: try operator (?)
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 6 Mar 2023 04:29:22 +0000 (22:29 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 6 Mar 2023 04:29:22 +0000 (22:29 -0600)
compiler/include/astnodes.h
compiler/src/builtins.c
compiler/src/checker.c
compiler/src/parser.c
compiler/src/symres.c
core/container/optional.onyx

index 64be00f7000cfafa75abd5c018f75dfb4050c42f..65741b13c53de1bdb7eb16361760a0189d5bc792 100644 (file)
@@ -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();
index 156a967e7c0045f572698ff9b180c5a4bfbdfbf2..417240100b20224271174900cae4b600f045efcf 100644 (file)
@@ -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);
index 17d4c51a5982608c36bcabb5dbd692f59b87c1f7..559b07cda9692878a48a1aac62ddba474db7a056 100644 (file)
@@ -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
index 4e94e76d47e691aa2ffde8bb549f0df140ae79d9..c1ebbf89c8c0bcfe9c328c4a46881e1fec89073a 100644 (file)
@@ -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")) {
index ddedd2a8ed0cb66572472e7949ab68f30bcab533..5e81c28dbf710540402a93bc957dfe0e06afc9c0 100644 (file)
@@ -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.");
index 87202b9d32995bfff825e8c0c91045071b5b53c4..2db77f0a60d14ec6fe2c3f844486d35e8b20bc51 100644 (file)
@@ -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;