more macro support
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 12 Aug 2021 17:28:09 +0000 (12:28 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 12 Aug 2021 17:28:09 +0000 (12:28 -0500)
bin/onyx
include/onyxastnodes.h
modules/immediate_mode/immediate_renderer.onyx
src/onyxchecker.c
src/onyxparser.c
src/onyxutils.c

index 16ae4856388725f7fc759d7c8bf5e0737322824f..aa21c0e3226919004132157e66201ee23689a301 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 63e52ab3ed5bd6383b8cddd0431adf89f3247f77..f412e13740e3d8a7127435632ce95043a3be70fb 100644 (file)
@@ -1354,6 +1354,8 @@ AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads,
 AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
 void report_unable_to_match_overload(AstCall* call);
 
+void expand_macro(AstCall** pcall);
+
 AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
 
 // NOTE: Useful inlined functions
index 238b718d049575919758e74c1fa4669b1bc3ca31..c0bdcf282f23e4a7333b119c2e7c1ad28ab97ac1 100644 (file)
@@ -521,6 +521,7 @@ apply_transform :: (transform: Transform) do global_renderer->apply_transform(tr
 
 
 save_matrix :: #code {
-    (package immediate_mode).push_matrix();
-    defer (package immediate_mode).pop_matrix();
+    I :: package immediate_mode
+    I.push_matrix();
+    defer I.pop_matrix();
 }
\ No newline at end of file
index 6d753d294090464b182528e3dae6c563fcbc8e54..dfe018022de73af1c672295e6cf544d5c4430c3a 100644 (file)
@@ -407,62 +407,24 @@ static i32 non_baked_argument_count(Arguments* args) {
     return count;
 }
 
-typedef enum ArgState {
-    AS_Expecting_Exact,
-    AS_Expecting_Typed_VA,
-    AS_Expecting_Untyped_VA,
-} ArgState;
+static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
+    if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
 
-CheckStatus check_call(AstCall** pcall) {
-    // All the things that need to be done when checking a call node.
-    //      1. Ensure the callee is not a symbol
-    //      2. Check the callee expression (since it could be a variable or a field access, etc)
-    //      3. Check all arguments
-    //          * Cannot pass overloaded functions (ROBUSTNESS)
-    //      4. If callee is an overloaded function, use the argument types to determine which overload is used.
-    //      5. If callee is polymorphic, use the arguments type to generate a polymorphic function.
-    //      7. Fill in arguments
-    //      8. If callee is an intrinsic, turn call into an Intrinsic_Call node
-    //      9. Check types of formal and actual params against each other, handling varargs
-    AstCall* call = *pcall;
-    
-    if (call->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+    while (call->callee->kind == Ast_Kind_Alias)
+        call->callee = ((AstAlias *) call->callee)->alias;
 
-    u32 current_checking_level_store = current_checking_level;
-    CHECK(expression, &call->callee);
-    CHECK(arguments, &call->args);
-    current_checking_level = current_checking_level_store;
-
-    // SPEED CLEANUP: Keeping an original copy for basically no reason except that sometimes you
-    // need to know the baked argument values in code generation.
-    // This should only be done once, but right now it is being done everytime this is checked,
-    // which can be multiple if we have to yield on a callee's type.
-    arguments_clone(&call->original_args, &call->args);
-
-    while (call->callee->kind == Ast_Kind_Alias) call->callee = ((AstAlias *) call->callee)->alias;
-
-    AstTyped *effective_callee = call->callee;
-
-    // If we are "calling" a macro, the callee on the call node should not be replaced
-    // as then it would remove the macro from the callee, which would turn it into a
-    // normal function call.
+    AstTyped* callee = call->callee;
     b32 calling_a_macro = 0;
 
-    if (effective_callee->kind == Ast_Kind_Macro) {
-        calling_a_macro = 1;
-        if (current_checking_level == EXPRESSION_LEVEL) {
-            onyx_report_error(call->token->pos, "Macros calls are not allowed at the expression level yet.");
-            return Check_Error;
-        }
-
-        effective_callee = ((AstMacro * ) effective_callee)->body;
-    }
-
-    if (effective_callee->kind == Ast_Kind_Overloaded_Function) {
+    if (callee->kind == Ast_Kind_Overloaded_Function) {
         b32 should_yield = 0;
-        AstTyped* new_callee = find_matching_overload_by_arguments(((AstOverloadedFunction *) effective_callee)->overloads, &call->args, &should_yield);
+        AstTyped* new_callee = find_matching_overload_by_arguments(
+            ((AstOverloadedFunction *) callee)->overloads,
+            &call->args,
+            &should_yield);
+
         if (new_callee == NULL) {
-            if (effective_callee->entity->state > Entity_State_Check_Types && !should_yield) {
+            if (callee->entity->state > Entity_State_Check_Types && !should_yield) {
                 report_unable_to_match_overload(call);
                 return Check_Error;
 
@@ -471,21 +433,34 @@ CheckStatus check_call(AstCall** pcall) {
             }
         }
 
-        effective_callee = new_callee;
-        if (!calling_a_macro) call->callee = new_callee;
+        callee = new_callee;
     }
 
-    if (effective_callee->kind == Ast_Kind_Polymorphic_Proc) {
-        AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) effective_callee, PPLM_By_Arguments, &call->args, call->token);
+    if (callee->kind == Ast_Kind_Macro) {
+        if (current_checking_level == EXPRESSION_LEVEL) {
+            onyx_report_error(call->token->pos, "Macros calls are not allowed at the expression level yet.");
+            return Check_Error;
+        }
+
+        calling_a_macro = 1;
+        call->callee = callee;
+        callee = ((AstMacro *) callee)->body;
+
+        if (callee->kind == Ast_Kind_Polymorphic_Proc) {
+            onyx_report_error(call->token->pos, "Cannot call polymorphic macros... yet.");
+            return Check_Error;
+        }
+
+    } else if (callee->kind == Ast_Kind_Polymorphic_Proc) {
+        AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) callee, PPLM_By_Arguments, &call->args, call->token);
         if (new_callee == (AstTyped *) &node_that_signals_a_yield) return Check_Yield_Macro;
         if (new_callee == NULL) return Check_Error;
 
         arguments_remove_baked(&call->args);
-        effective_callee = new_callee;
-        if (!calling_a_macro) call->callee = new_callee;
+        callee = new_callee;
     }
 
-    AstFunction* callee = (AstFunction *) effective_callee;
+    if (!calling_a_macro) call->callee = callee;
 
     // NOTE: Build callee's type
     fill_in_type((AstTyped *) callee);
@@ -498,6 +473,45 @@ CheckStatus check_call(AstCall** pcall) {
         return Check_Error;
     }
 
+    *effective_callee = callee;
+    return Check_Success;
+}
+
+typedef enum ArgState {
+    AS_Expecting_Exact,
+    AS_Expecting_Typed_VA,
+    AS_Expecting_Untyped_VA,
+} ArgState;
+
+CheckStatus check_call(AstCall** pcall) {
+    // All the things that need to be done when checking a call node.
+    //      1. Ensure the callee is not a symbol
+    //      2. Check the callee expression (since it could be a variable or a field access, etc)
+    //      3. Check all arguments
+    //          * Cannot pass overloaded functions (ROBUSTNESS)
+    //      4. If callee is an overloaded function, use the argument types to determine which overload is used.
+    //      5. If callee is polymorphic, use the arguments type to generate a polymorphic function.
+    //      7. Fill in arguments
+    //      8. If callee is an intrinsic, turn call into an Intrinsic_Call node
+    //      9. Check types of formal and actual params against each other, handling varargs
+    AstCall* call = *pcall;
+    
+    if (call->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+    u32 current_checking_level_store = current_checking_level;
+    CHECK(expression, &call->callee);
+    CHECK(arguments, &call->args);
+    current_checking_level = current_checking_level_store;
+
+    // SPEED CLEANUP: Keeping an original copy for basically no reason except that sometimes you
+    // need to know the baked argument values in code generation.
+    // This should only be done once, but right now it is being done everytime this is checked,
+    // which can be multiple if we have to yield on a callee's type.
+    arguments_clone(&call->original_args, &call->args);
+
+    AstFunction* callee=NULL;
+    CHECK(resolve_callee, call, (AstTyped **) &callee);
+
     // CLEANUP maybe make function_get_expected_arguments?
     i32 non_vararg_param_count = (i32) callee->type->Function.param_count;
     if (non_vararg_param_count > 0 &&
@@ -603,7 +617,6 @@ CheckStatus check_call(AstCall** pcall) {
                     continue;
                 }
 
-                // CLEANUP POTENTIAL BUG if the builtin_vararg_type_type is ever rebuilt
                 if ((i16) arg_pos == callee->type->Function.vararg_arg_pos) {
                     arg_state = AS_Expecting_Untyped_VA;
                     continue;
@@ -665,8 +678,7 @@ CheckStatus check_call(AstCall** pcall) {
                 }
 
                 arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
-                break;CheckStatus check_compound(AstCompound* compound);
-
+                break;
             }
         }
 
@@ -687,11 +699,11 @@ type_checking_done:
         return Check_Error;
     }
 
-    if (!calling_a_macro) {
-        callee->flags |= Ast_Flag_Function_Used;
+    callee->flags |= Ast_Flag_Function_Used;
 
-    } else {
-        // Macro expansion
+    if (call->kind == Ast_Kind_Call && call->callee->kind == Ast_Kind_Macro) {
+        expand_macro(pcall);
+        return Check_Return_To_Symres;
     }
 
     return Check_Success;
@@ -1911,8 +1923,9 @@ CheckStatus check_overloaded_function(AstOverloadedFunction* func) {
         if (node->kind == Ast_Kind_Overloaded_Function) continue;
 
         if (   node->kind != Ast_Kind_Function
-            && node->kind != Ast_Kind_Polymorphic_Proc) {
-            onyx_report_error(node->token->pos, "Overload option not procedure. Got '%s'",
+            && node->kind != Ast_Kind_Polymorphic_Proc
+            && node->kind != Ast_Kind_Macro) {
+            onyx_report_error(node->token->pos, "Overload option not procedure or macro. Got '%s'",
                 onyx_ast_node_kind_string(node->kind));
 
             bh_imap_free(&all_overloads);
index d6e615836dd745842295828696c14a929b5343eb..de862194e9473e4a60ba3f10ca78257248c68e4c 100644 (file)
@@ -64,6 +64,7 @@ static b32            parse_possible_function_definition(OnyxParser* parser, Ast
 static AstFunction*   parse_function_definition(OnyxParser* parser, OnyxToken* token);
 static AstTyped*      parse_global_declaration(OnyxParser* parser);
 static AstEnumType*   parse_enum_declaration(OnyxParser* parser);
+static AstMacro*      parse_macro(OnyxParser* parser);
 static AstIf*         parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements);
 static AstTyped*      parse_top_level_expression(OnyxParser* parser);
 static AstBinding*    parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol);
@@ -483,6 +484,11 @@ static AstTyped* parse_factor(OnyxParser* parser) {
             break;
         }
 
+        case Token_Type_Keyword_Macro: {
+            retval = (AstTyped *) parse_macro(parser);
+            break;
+        }
+
         // :TypeValueInterchange
         case '<': {
             AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
index 3f048d315d1226ae82ff885c217a894dbe2fc861..cebbc890d99e0745a595d6898146f98df24e8324 100644 (file)
@@ -1084,6 +1084,7 @@ AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads,
         AstFunction* overload = NULL;
         switch (node->kind) {
             case Ast_Kind_Function:         overload = (AstFunction *) node; break;
+            case Ast_Kind_Macro:            overload = (AstFunction *) ((AstMacro *) node)->body; break;
             case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstPolyProc *) node, PPLM_By_Arguments, param_args); break;
         }
 
@@ -1199,6 +1200,41 @@ void report_unable_to_match_overload(AstCall* call) {
 }
 
 
+//
+// Macros
+//
+//
+// TODO: Write this documentation
+void expand_macro(AstCall** pcall) {
+    AstCall* call = *pcall;
+    AstMacro* macro = (AstMacro *) call->callee;
+    assert(macro->kind == Ast_Kind_Macro);
+
+    AstFunction* template = (AstFunction *) macro->body;
+    assert(template->kind == Ast_Kind_Function);
+    assert(template->type != NULL);
+
+    AstBlock* expansion = (AstBlock *) ast_clone(context.ast_alloc, template->body);
+    expansion->rules = Block_Rule_Macro;
+    expansion->scope = NULL;
+    expansion->next = call->next;
+
+    Scope* argument_scope = scope_create(context.ast_alloc, NULL, call->token->pos);
+    if (expansion->binding_scope != NULL)
+        scope_include(argument_scope, expansion->binding_scope, call->token->pos);
+    expansion->binding_scope = argument_scope;
+
+    // HACK HACK HACK This is probably very wrong. I don't know what guarentees that
+    // the paramters and arguments are going to be in the same order exactly.
+    fori (i, 0, bh_arr_length(call->args.values)) {
+        symbol_introduce(argument_scope,
+            template->params[i].local->token,
+            (AstNode *) ((AstArgument *) call->args.values[i])->value);
+    }
+
+    *(AstBlock **) pcall = expansion;
+    return;
+}
 
 //
 // Polymorphic Structures
@@ -1211,6 +1247,8 @@ void report_unable_to_match_overload(AstCall* call) {
 // that types need to be known completely by the time symbol resolution is done, even though that
 // information shouldn't need to be known until right before the types are checked.
 //
+// The above documentation is very incorrect but I don't want to fix it right now. Basically, polymorphic
+// structures now have a delay instantiation phase and are not forced to be completed immediately.
 
 char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
     char name_buf[256];