From: Brendan Hansen Date: Thu, 11 Nov 2021 20:59:39 +0000 (-0600) Subject: changed '#add_match' syntax X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b60653b6a91cc9b3cd373fd02253a3acc1487f91;p=onyx.git changed '#add_match' syntax --- diff --git a/docs/interfaces.onyx b/docs/interfaces.onyx index f7b26493..7abe84d4 100644 --- a/docs/interfaces.onyx +++ b/docs/interfaces.onyx @@ -10,13 +10,41 @@ // 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. // diff --git a/examples/14_overloaded_procs.onyx b/examples/14_overloaded_procs.onyx index 3abe4deb..f1e95d7c 100644 --- a/examples/14_overloaded_procs.onyx +++ b/examples/14_overloaded_procs.onyx @@ -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; } diff --git a/include/parser.h b/include/parser.h index 26d34eaa..f837a01a 100644 --- a/include/parser.h +++ b/include/parser.h @@ -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); diff --git a/modules/json/encoder.onyx b/modules/json/encoder.onyx index a3aeab46..d62f281c 100644 --- a/modules/json/encoder.onyx +++ b/modules/json/encoder.onyx @@ -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; // } diff --git a/modules/json/example.onyx b/modules/json/example.onyx index 24558374..cd05d0a6 100644 --- a/modules/json/example.onyx +++ b/modules/json/example.onyx @@ -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 +} diff --git a/modules/vecmath/vector2.onyx b/modules/vecmath/vector2.onyx index d3b5313e..2ebe62e3 100644 --- a/modules/vecmath/vector2.onyx +++ b/modules/vecmath/vector2.onyx @@ -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); diff --git a/modules/wasm_utils/types.onyx b/modules/wasm_utils/types.onyx index d290d8ef..1cf84a84 100644 --- a/modules/wasm_utils/types.onyx +++ b/modules/wasm_utils/types.onyx @@ -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); } diff --git a/src/parser.c b/src/parser.c index ad1388da..093d20a2 100644 --- a/src/parser.c +++ b/src/parser.c @@ -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, diff --git a/src/symres.c b/src/symres.c index d8cd3ce4..5d8af4a8 100644 --- a/src/symres.c +++ b/src/symres.c @@ -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; } diff --git a/tests/aoc-2020/day17.onyx b/tests/aoc-2020/day17.onyx index 44ab1a9a..4b165812 100644 --- a/tests/aoc-2020/day17.onyx +++ b/tests/aoc-2020/day17.onyx @@ -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; } diff --git a/tests/aoc-2020/day24.onyx b/tests/aoc-2020/day24.onyx index efef79d6..60969163 100644 --- a/tests/aoc-2020/day24.onyx +++ b/tests/aoc-2020/day24.onyx @@ -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; } diff --git a/tests/array_struct_robustness.onyx b/tests/array_struct_robustness.onyx index 7c587e2f..f487e8cd 100644 --- a/tests/array_struct_robustness.onyx +++ b/tests/array_struct_robustness.onyx @@ -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); } diff --git a/tests/bugs/macro_auto_return_not_resolved.onyx b/tests/bugs/macro_auto_return_not_resolved.onyx index 573d9f91..3a289d59 100644 --- a/tests/bugs/macro_auto_return_not_resolved.onyx +++ b/tests/bugs/macro_auto_return_not_resolved.onyx @@ -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 +} diff --git a/tests/overload_precedence.onyx b/tests/overload_precedence.onyx index f3f04822..b5dad387 100644 --- a/tests/overload_precedence.onyx +++ b/tests/overload_precedence.onyx @@ -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"); }