From: Brendan Hansen Date: Thu, 12 Aug 2021 17:28:09 +0000 (-0500) Subject: more macro support X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=2ccfdc3f266494c2f340b81d0f1966c49c75ec51;p=onyx.git more macro support --- diff --git a/bin/onyx b/bin/onyx index 16ae4856..aa21c0e3 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index 63e52ab3..f412e137 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -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 diff --git a/modules/immediate_mode/immediate_renderer.onyx b/modules/immediate_mode/immediate_renderer.onyx index 238b718d..c0bdcf28 100644 --- a/modules/immediate_mode/immediate_renderer.onyx +++ b/modules/immediate_mode/immediate_renderer.onyx @@ -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 diff --git a/src/onyxchecker.c b/src/onyxchecker.c index 6d753d29..dfe01802 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -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); diff --git a/src/onyxparser.c b/src/onyxparser.c index d6e61583..de862194 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -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); diff --git a/src/onyxutils.c b/src/onyxutils.c index 3f048d31..cebbc890 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -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];