changed '#add_match' syntax
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 11 Nov 2021 20:59:39 +0000 (14:59 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 11 Nov 2021 20:59:39 +0000 (14:59 -0600)
14 files changed:
docs/interfaces.onyx
examples/14_overloaded_procs.onyx
include/parser.h
modules/json/encoder.onyx
modules/json/example.onyx
modules/vecmath/vector2.onyx
modules/wasm_utils/types.onyx
src/parser.c
src/symres.c
tests/aoc-2020/day17.onyx
tests/aoc-2020/day24.onyx
tests/array_struct_robustness.onyx
tests/bugs/macro_auto_return_not_resolved.onyx
tests/overload_precedence.onyx

index f7b2649366634fc7902775bb5c9f6994db1568d8..7abe84d418f11a3a5eb4c6264944e8fea9ac9aa9 100644 (file)
 // or a Set out of a type, you need to declare a match option for hash.to_u32
 // and define '==' for the type. This is kind of probablimatic and doesn't
 // make for great self explanatory or maintainable code. Also I don't like
-// #add_match at all, as it feels like a complete and utter hack.
+// #match at all, as it feels like a complete and utter hack.
 //
+
+// Ignore everything that is said below. It all sounded like a good idea
+// at one point, until I started implementing it when I realized that not
+// only is it WAY too complicated and adds too much complexity in the compiler
+// but it doesn't feel like a natural extention to Onyx.
+//
+// Instead, 'interfaces' are going to be much much simpler, but to write
+// and for the compiler to deal with. Effectively, they are compile time checks
+// that assert that a particular set of expressions is valid, and optionally
+// return the correct type. They are offered as an extension to overloaded functions
+// and polymorphic functions, instead of being a replacement for them. To remedy
+// the "#add_match" syntax, some work has gone into the parser to make the following
+// legal syntax:
+
+#match hash.to_u32 (x: Vec2) -> u32 { /* .. */ }
+
+// This feels a lot nicer to write and doesn't have the weird line noise of the
+// _ and the ,.
+
+
+
+ValidKey :: interface (T: type_expr) {
+    hash.to_u32(T);
+    T == T;
+}
+
+
+
 // To remedy all of this, I think Onyx should have interfaces (or constraints,
 // I'm not sure what I want to call them yet). The idea is much like
 // an type class in Haskell where you must define a set of procedure types
 // that the implementer must define in order to provide the interface.
-// With these interfaces, I would get rid of #add_match and only provide
+// With these interfaces, I would get rid of #match and only provide
 // locally overloaded procedures (like print right now), as these are still
 // useful.
 //
index 3abe4debd8bd5cac8e59161a485ec15b7611674d..f1e95d7cb2a808b9fc46e29c13ee2eeba81e23d9 100644 (file)
@@ -72,17 +72,16 @@ print_type :: #match {
 // type knowledge at runtime. To rectify this, let's add a new case that makes
 // this work for any type:
 
-#add_match print_type, #precedence 10 (x: $T) {
+#match print_type #precedence 10 (x: $T) {
     printf("Fallback case: called with a {} ({})\n", T, x);
 }
 
-// There are a couple things going on here. The #add_match directive takes two "parameters" separated
-// by a comma: the overloaded procedure to add a match option to, and the option itself. The other
+// There are a couple things going on here. The #match directive takes two "parameters": the overloaded procedure to add a match option to, and the option itself. The other
 // thing to notice is the "#precedence 10" specification. Onyx tries to match the arguments you provided
 // with each of the options. The first one that matches perfectly, is the one that is used. The order in
 // which it does this process is important, and that is why you can control it with the #precedence
 // directive. The #precedence directive is allowed in two places: right before the option in the original
-// #match directive, or in front of the option in an #add_match directive, as seen above. Currently,
+// #match directive, or in front of the option in an #match directive, as seen above. Currently,
 // the options are sorted in ascending order according to the precedence. I feel like this is backwards,
 // so that may change in the future. Either way, setting the precedence allows you to place the options
 // in the correct order so something like (x: $T), which will match ANY single argument, is placed last
@@ -94,7 +93,7 @@ print_type :: #match {
 Person :: struct { name: str; age: i32; }
 
 // The overload option for hashing a person:
-#add_match (package core).hash.to_u32, (use p: Person) -> u32 {
+#match (package core).hash.to_u32 (use p: Person) -> u32 {
     return hash.to_u32(name) * 16239563 + age;
 }
 
index 26d34eaaff8e0a6c20a4a4e1042abfd55c3690f0..f837a01afd610a72208d44aa29f728bc0bd02294 100644 (file)
@@ -32,6 +32,11 @@ typedef struct OnyxParser {
     bh_arr(AstFlags) scope_flags;
 
     b32 hit_unexpected_token : 1;
+
+    // If this is 1, then things that look like calls with immediately be parsed as calls.
+    // However, if this is 0 then things that look like procedure definitions after expressions,
+    // will be parsed as procedure definitions.
+    b32 greedily_consume_calls : 1;
 } OnyxParser;
 
 const char* onyx_ast_node_kind_string(AstKind kind);
index a3aeab464b7908df7ae475080be5a431aa83accc..d62f281ce854a4b5e2f8ce16466035bce05240b7 100644 (file)
@@ -135,7 +135,7 @@ encode :: #match {
     // This is disabled because I would prefer to have a compile time error for an unsupported type,
     // as opposed to a error to check programatically.
     //
-    // // Inserted after any of the #add_match directives
+    // // Inserted after any of the #match directives
     // #precedence 1000 (w: ^io.Writer, v: $T) -> Encoding_Error {
     //     return .Unsupported_Type;
     // }
index 245583746426fbe880aed0fefd1dceedd918ddc0..cd05d0a6f3ef954db44ac6fb78a627cd6737fbe8 100644 (file)
@@ -51,8 +51,8 @@ Vector2 :: struct {
     x, y: f32;
 }
 
-#add_match json.encode, (w: ^io.Writer, v: Vector2) -> json.Encoding_Error {
+#match json.encode (w: ^io.Writer, v: Vector2) -> json.Encoding_Error {
     io.write_format(w, "{{\"x\":{},\"y\":{}}}", v.x, v.y);
 
     return .None;
-}
\ No newline at end of file
+}
index d3b5313e0f484d6764ab71a9c9feab87dbbaddab..2ebe62e3e30f4cd7e5385f345e9751bba33552ac 100644 (file)
@@ -43,7 +43,7 @@ vector2_equal :: (a: Vector2($T), b: Vector2(T)) -> bool {
     return a.x == b.x && a.y == b.y;
 }
 
-#add_match io.write, vector2_write
+#match io.write vector2_write
 vector2_write :: (writer: ^io.Writer, v: Vector2($T)) {
     io.write(writer, "Vector2(");
     io.write(writer, v.x);
index d290d8ef1f2a8a0f112841e0d3f8a83c41755368..1cf84a844d5fd84bad2209e5ddcd8450a6af9824 100644 (file)
@@ -16,7 +16,7 @@ WasmSection :: enum {
     DataCount :: 0x0c;
 }
 
-#add_match hash.to_u32, macro (w: WasmSection) -> u32 {
+#match hash.to_u32 macro (w: WasmSection) -> u32 {
     return hash.to_u32(cast(u32) w);
 }
 
index ad1388da137adef70b098dc4eebecdfdba1cdcc5..093d20a280e96cf593fa0f5c1eebdd3fd8db7a0a 100644 (file)
@@ -56,9 +56,12 @@ static AstNode*       parse_statement(OnyxParser* parser);
 static AstType*       parse_type(OnyxParser* parser);
 static AstTypeOf*     parse_typeof(OnyxParser* parser);
 static AstStructType* parse_struct(OnyxParser* parser);
+static AstInterface*  parse_interface(OnyxParser* parser);
 static void           parse_function_params(OnyxParser* parser, AstFunction* func);
 static b32            parse_possible_directive(OnyxParser* parser, const char* dir);
+static b32            parse_possible_function_definition_no_consume(OnyxParser* parser);
 static b32            parse_possible_function_definition(OnyxParser* parser, AstTyped** ret);
+static b32            parse_possible_quick_function_definition_no_consume(OnyxParser* parser);
 static b32            parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret);
 static AstFunction*   parse_function_definition(OnyxParser* parser, OnyxToken* token);
 static AstTyped*      parse_global_declaration(OnyxParser* parser);
@@ -725,6 +728,11 @@ static AstTyped* parse_factor(OnyxParser* parser) {
             }
 
             case '(': {
+                if (!parser->greedily_consume_calls) {
+                    if (parse_possible_function_definition_no_consume(parser)) goto factor_parsed;
+                    if (parse_possible_quick_function_definition_no_consume(parser)) goto factor_parsed;
+                }
+
                 AstCall* call_node = make_node(AstCall, Ast_Kind_Call);
                 call_node->token = expect_token(parser, '(');
                 call_node->callee = retval;
@@ -2271,7 +2279,7 @@ function_defined:
     }
 }
 
-static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
+static b32 parse_possible_function_definition_no_consume(OnyxParser* parser) {
     if (parser->curr->type == '(') {
         OnyxToken* matching_paren = find_matching_paren(parser->curr);
         if (matching_paren == NULL) return 0;
@@ -2298,9 +2306,18 @@ static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret
 
         if (!is_params) return 0;
 
+        return 1;
+    }
+
+    return 0;
+}
+
+static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
+    if (parse_possible_function_definition_no_consume(parser)) {
         OnyxToken* proc_token = parser->curr;
         AstFunction* func_node = parse_function_definition(parser, proc_token);
         *ret = (AstTyped *) func_node;
+
         return 1;
     }
 
@@ -2312,7 +2329,7 @@ typedef struct QuickParam {
     b32 is_baked;
 } QuickParam;
 
-static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
+static b32 parse_possible_quick_function_definition_no_consume(OnyxParser* parser) {
     if (parser->curr->type != '(') return 0;
 
     OnyxToken* matching_paren = find_matching_paren(parser->curr);
@@ -2323,6 +2340,12 @@ static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped
     if (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>')
         return 0;
 
+    return 1;
+}
+
+static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
+    if (!parse_possible_quick_function_definition_no_consume(parser)) return 0;
+
     OnyxToken* proc_token = expect_token(parser, '(');
 
     bh_arr(QuickParam) params=NULL;
@@ -2854,12 +2877,13 @@ static void parse_top_level_statement(OnyxParser* parser) {
                 ENTITY_SUBMIT(operator);
                 return;
             }
-            else if (parse_possible_directive(parser, "add_overload") || parse_possible_directive(parser, "add_match")) {
+            else if (parse_possible_directive(parser, "match")) {
                 AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
                 add_overload->token = dir_token;
-                add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
 
-                expect_token(parser, ',');
+                parser->greedily_consume_calls = 0;
+                add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
+                parser->greedily_consume_calls = 1;
 
                 if (parse_possible_directive(parser, "precedence")) {
                     AstNumLit* pre = parse_int_literal(parser);
@@ -3048,6 +3072,7 @@ OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer) {
     parser.alternate_entity_placement_stack = NULL;
     parser.current_symbol_stack = NULL;
     parser.scope_flags = NULL;
+    parser.greedily_consume_calls = 1;
 
     parser.polymorph_context = (PolymorphicContext) {
         .root_node = NULL,
index d8cd3ce4e6a0eeac0e420ac51d0abc660e994beb..5d8af4a8dec7fadff57398a5d31b362420bf5f10 100644 (file)
@@ -751,7 +751,7 @@ static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
     }
 
     solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
-    if (solid->resolved_proc == (AstTyped *) &node_that_signals_a_yield) {
+    if (solid->resolved_proc == (AstNode *) &node_that_signals_a_yield) {
         solid->resolved_proc = NULL;
         return Symres_Yield_Macro;
     }
index 44ab1a9a869735c18b7760d8e3097bd006e35e62..4b165812e61fe2a090767cf3b7406feea266b6d0 100644 (file)
@@ -12,7 +12,7 @@ CubeState :: struct {
     next  := false;
 }
 
-#add_match hash.to_u32, (c: CubePos) -> u32 {
+#match hash.to_u32 (c: CubePos) -> u32 {
     return 17 * c.x + 13 * c.y + 11 * c.z + 19 * c.w;
 }
 
index efef79d614fa6a0388a9c805f720e78278eabebd..60969163948323dc9c1c9dc310594634f27d72b6 100644 (file)
@@ -7,7 +7,7 @@ Vec2 :: struct {
        y: i32 = 0;
 }
 
-#add_match hash.to_u32, (v: Vec2) -> u32 {
+#match hash.to_u32 (v: Vec2) -> u32 {
        return v.x * 11 + v.y * 17;
 }
 
index 7c587e2fb543c8c3310b08fff114dd65f84a4887..f487e8cd0c5ecedd2064fa0792dd93fbd86015ae 100644 (file)
@@ -5,7 +5,7 @@ use package core
 Vec2 :: struct { x: i32; y: i32; }
 
 // Overload print() to print Vec2's.
-#add_match io.write, (use writer: ^io.Writer, use v: Vec2) {
+#match io.write (use writer: ^io.Writer, use v: Vec2) {
     io.write_format(writer, "Vec2({}, {})", x, y);
 }
 
index 573d9f91b9176f58af6729cd7795a5a03aeaafec..3a289d59d7726df5ca40c1b13adbcb89529ec622 100644 (file)
@@ -4,7 +4,7 @@ use package core
 
 Point :: struct { x, y: i32; }
 
-#add_match hash.to_u32, macro (p: Point) -> #auto do return p.x * 1000 + p.y;
+#match hash.to_u32 macro (p: Point) -> #auto do return p.x * 1000 + p.y;
 
 #operator == macro (p1: Point, p2: Point) -> #auto {
     return p1.x == p2.x && p1.y == p2.y;
@@ -18,4 +18,4 @@ main :: (args) => {
     S << Point.{ 1, 2 };
 
     set.has(^S, Point.{ 1, 2 }) |> println();
-}
\ No newline at end of file
+}
index f3f04822c68fb7f70a7c4db1946acbc71ac84380..b5dad3874f7f1c18b842f648248c5be67083d5a4 100644 (file)
@@ -15,6 +15,6 @@ main :: (args: [] cstr) {
     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"); }
+#match overloaded #precedence 3 (q: i32) { println("Option Q"); }
+#match overloaded #precedence 2 (r: i32) { println("Option R"); }
+#match overloaded #precedence 1 (m: i32) { println("Option M"); }