added precedence option to overloads
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 18 Jun 2021 17:43:46 +0000 (12:43 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 18 Jun 2021 17:43:46 +0000 (12:43 -0500)
bin/onyx
core/io/stream.onyx
include/onyxastnodes.h
include/onyxutils.h
src/onyxbuiltins.c
src/onyxparser.c
src/onyxsymres.c
src/onyxutils.c
tests/overload_precedence [new file with mode: 0644]
tests/overload_precedence.onyx [new file with mode: 0644]

index 3d3597925d285b05d50929b590419e2b92345e8e..28ba7ff10dee859e629db557fbefe9c81b26b5b7 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 987c322058fff9e938579af91437c3475abe3b7c..d94abde3062f748d0edab9432450e03cb48d58ec 100644 (file)
@@ -266,6 +266,10 @@ dynamic_string_stream_make :: (init_size := 128, a := context.allocator) -> Dyna
     };
 }
 
+dynamic_string_stream_free :: (use dds: ^DynamicStringStream) {
+    array.free(^data);
+}
+
 dynamic_string_stream_to_str :: (use dds: ^DynamicStringStream) -> str {
     return data.data[0 .. curr_pos];
 }
index eb9a399f49c6f1664f4cdf38e1e310778f02ddfd..490976c92561931bff336035f1849ac9d4f574c3 100644 (file)
@@ -830,10 +830,20 @@ struct AstFunction {
     struct Entity* entity_body;
 };
 
+typedef struct OverloadOption OverloadOption;
+struct OverloadOption {
+    // This is u64 because padding will make it that anyway.
+    // Consider: would there be any practical benefit to having the precedence setting
+    // be a compile-time known value? as opposed to a hardcoded value?
+    u64 precedence;
+
+    AstTyped* option;
+};
+
 struct AstOverloadedFunction {
     AstTyped_base;
 
-    bh_arr(AstTyped *) overloads;
+    bh_arr(OverloadOption) overloads;
     
     // CLEANUP: This is unused right now, but should be used to cache
     // the complete set of overloads that can be used by an overloaded
@@ -937,6 +947,8 @@ struct AstDirectiveAddOverload {
     // then resolved to an overloaded function.
     AstNode *overloaded_function;
 
+    // See note in OverloadOption. This could be refactored into an OverloadOption?
+    u64 precedence;
     AstTyped *overload;
 };
 
@@ -1207,7 +1219,7 @@ typedef struct IntrinsicMap {
 
 extern bh_table(OnyxIntrinsic) intrinsic_table;
 
-extern bh_arr(AstTyped *) operator_overloads[Binary_Op_Count];
+extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
 
 void initialize_builtins(bh_allocator a);
 void introduce_build_options(bh_allocator a);
@@ -1261,8 +1273,9 @@ AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution)
 AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
 AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual);
 
-AstTyped* find_matching_overload_by_arguments(bh_arr(AstTyped *) overloads, Arguments* args);
-AstTyped* find_matching_overload_by_type(bh_arr(AstTyped *) overloads, Type* type);
+void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
+AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args);
+AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
 void report_unable_to_match_overload(AstCall* call);
 
 AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
index a67833c95d6120544fecbd9b2a4b689e3f999fb8..cd72671171fdabb7e3c6e08ce0bfde0bfc29d126 100644 (file)
@@ -26,7 +26,7 @@ AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn);
 AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol);
 AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token);
 
-void build_all_overload_options(bh_arr(AstTyped *) overloads, bh_imap* all_overloads);
+void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads);
 
 u32 char_to_base16_value(char x);
 
index 0a8abaf0d8931ef9bc9f08da885eef32ba35a4f0..54475b59ae067ecf4a5be94efd0f075b83799f1a 100644 (file)
@@ -329,7 +329,7 @@ static IntrinsicMap builtin_intrinsics[] = {
     { NULL, ONYX_INTRINSIC_UNDEFINED },
 };
 
-bh_arr(AstTyped *) operator_overloads[Binary_Op_Count] = { 0 };
+bh_arr(OverloadOption) operator_overloads[Binary_Op_Count] = { 0 };
 
 void initialize_builtins(bh_allocator a) {
     // HACK
index 08a5256fe38e734c5a80322f732d3e05f617b638..b5c7d61f3d6d98552c634f5bc8895f8ff793fecb 100644 (file)
@@ -1918,12 +1918,19 @@ static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, Onyx
 
     bh_arr_new(global_heap_allocator, ofunc->overloads, 4);
 
+    u64 precedence = 0;
     while (!consume_token_if_next(parser, '}')) {
         if (parser->hit_unexpected_token) return ofunc;
 
-        AstTyped* o_node = parse_expression(parser, 0);
+        if (parse_possible_directive(parser, "precedence")) {
+            AstNumLit* pre = parse_int_literal(parser);
+            if (parser->hit_unexpected_token) return ofunc;
 
-        bh_arr_push(ofunc->overloads, o_node);
+            precedence = bh_max(pre->value.l, 0);
+        }
+
+        AstTyped* option = parse_expression(parser, 0);
+        add_overload_option(&ofunc->overloads, precedence++, option);
 
         if (parser->curr->type != '}')
             expect_token(parser, ',');
@@ -2370,6 +2377,16 @@ static void parse_top_level_statement(OnyxParser* parser) {
                 add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
 
                 expect_token(parser, ',');
+
+                if (parse_possible_directive(parser, "precedence")) {
+                    AstNumLit* pre = parse_int_literal(parser);
+                    if (parser->hit_unexpected_token) return;
+
+                    add_overload->precedence = bh_max(pre->value.l, 0);
+                } else {
+                    add_overload->precedence = 0;
+                }
+
                 add_overload->overload = parse_expression(parser, 0);
 
                 ENTITY_SUBMIT(add_overload);
index deb1bbfeda80cf47a1cdcd6d472dd01b3e64efce..76e328cad82e4aecffdf58c3f0ee5ddcfcec1375 100644 (file)
@@ -877,8 +877,8 @@ static SymresStatus symres_global(AstGlobal* global) {
 }
 
 static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc) {
-    bh_arr_each(AstTyped *, node, ofunc->overloads) {
-        SYMRES(expression, node);
+    bh_arr_each(OverloadOption, overload, ofunc->overloads) {
+        SYMRES(expression, &overload->option);
     }
     return Symres_Success;
 }
@@ -1023,7 +1023,7 @@ static SymresStatus symres_process_directive(AstNode* directive) {
             } else {
                 AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
                 SYMRES(expression, (AstTyped **) &add_overload->overload);
-                bh_arr_push(ofunc->overloads, (AstTyped *) add_overload->overload);
+                add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload);
             }
 
             break;
@@ -1050,7 +1050,7 @@ static SymresStatus symres_process_directive(AstNode* directive) {
                 return Symres_Error;
             }
 
-            bh_arr_push(operator_overloads[operator->operator], operator->overload);
+            add_overload_option(&operator_overloads[operator->operator], 0, operator->overload);
             break;
         }
 
index cca3892d1b19a92af7f26bbb3c06d5010da5fc8b..e737e804d61c97ebad3f2ee789b003a50b0f459b 100644 (file)
@@ -978,6 +978,32 @@ AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupM
 //  * Resolving an overload from a TypeFunction (so an overloaded procedure can be passed as a parameter)
 //
 
+void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload) {
+    bh_arr(OverloadOption) overloads = *poverloads;
+
+    i32 index = -1;
+    fori (i, 0, bh_arr_length(overloads)) {
+        if (overloads[i].precedence > precedence) {
+            index = i;
+            break;
+        }
+    }
+
+    if (index < 0) {
+        bh_arr_push(overloads, ((OverloadOption) {
+            .precedence = precedence,
+            .option     = overload,
+        }));
+
+    } else {
+        bh_arr_insertn(overloads, index, 1);
+        overloads[index].precedence = precedence;
+        overloads[index].option     = overload;
+    }
+
+    *poverloads = overloads;
+}
+
 // NOTE: The job of this function is to take a set of overloads, and traverse it to add all possible
 // overloads that are reachable. This is slightly more difficult than it may seem. In this language,
 // overloaded procedures have a strict ordering to their overloads, which determines how the correct
@@ -996,20 +1022,20 @@ AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupM
 // an "entries" array that, so long as nothing is ever removed from it, will maintain the order in
 // which entries were put into the map. This is useful because a simple recursive algorithm can
 // collect all the overloads into the map, and also use the map to provide a base case.
-void build_all_overload_options(bh_arr(AstTyped *) overloads, bh_imap* all_overloads) {
-    bh_arr_each(AstTyped *, node, overloads) {
-        if (bh_imap_has(all_overloads, (u64) *node)) continue;
+void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads) {
+    bh_arr_each(OverloadOption, overload, overloads) {
+        if (bh_imap_has(all_overloads, (u64) overload->option)) continue;
 
-        bh_imap_put(all_overloads, (u64) *node, 1);
+        bh_imap_put(all_overloads, (u64) overload->option, 1);
 
-        if ((*node)->kind == Ast_Kind_Overloaded_Function) {
-            AstOverloadedFunction* sub_overload = (AstOverloadedFunction *) *node;
+        if (overload->option->kind == Ast_Kind_Overloaded_Function) {
+            AstOverloadedFunction* sub_overload = (AstOverloadedFunction *) overload->option;
             build_all_overload_options(sub_overload->overloads, all_overloads);
         }
     }
 }
 
-AstTyped* find_matching_overload_by_arguments(bh_arr(AstTyped *) overloads, Arguments* param_args) {
+AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* param_args) {
     Arguments args;
     arguments_clone(&args, param_args);
     arguments_ensure_length(&args, bh_arr_length(args.values) + bh_arr_length(args.named_values));
@@ -1077,7 +1103,7 @@ AstTyped* find_matching_overload_by_arguments(bh_arr(AstTyped *) overloads, Argu
     return matched_overload;
 }
 
-AstTyped* find_matching_overload_by_type(bh_arr(AstTyped *) overloads, Type* type) {
+AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type) {
     if (type->kind != Type_Kind_Function) return NULL;
 
     bh_imap all_overloads;
diff --git a/tests/overload_precedence b/tests/overload_precedence
new file mode 100644 (file)
index 0000000..7f67e0c
--- /dev/null
@@ -0,0 +1,4 @@
+Option M
+Option X
+Option R
+Option Z
diff --git a/tests/overload_precedence.onyx b/tests/overload_precedence.onyx
new file mode 100644 (file)
index 0000000..f3f0482
--- /dev/null
@@ -0,0 +1,20 @@
+#load "core/std"
+
+use package core
+
+overloaded :: #match {
+    #precedence 10 (x: i32) { println("Option X"); },
+    #precedence 5  (y: i32) { println("Option Y"); },
+    #precedence 4  (z: i32) { println("Option Z"); },
+}
+
+main :: (args: [] cstr) {
+    overloaded(10);
+    overloaded(x=10);
+    overloaded(r=10);
+    overloaded(z=10);
+}
+
+#add_match overloaded, #precedence 3 (q: i32) { println("Option Q"); }
+#add_match overloaded, #precedence 2 (r: i32) { println("Option R"); }
+#add_match overloaded, #precedence 1 (m: i32) { println("Option M"); }