From: Brendan Hansen Date: Fri, 25 Dec 2020 19:39:21 +0000 (-0600) Subject: started work on `#solidify` X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=922d57f44fb6d24583daa4d43d3cf3e65772c73a;p=onyx.git started work on `#solidify` --- diff --git a/.vimspector.json b/.vimspector.json index b1d77d0a..d9aef1fd 100644 --- a/.vimspector.json +++ b/.vimspector.json @@ -6,7 +6,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/onyx", - "args": ["--verbose", "progs/odin_example.onyx"], + "args": ["--verbose", "progs/poly_solidify.onyx"], "stopAtEntry": true, "cwd": "~/dev/c/onyx", "environment": [], diff --git a/docs/todo b/docs/todo index d9924b44..7a6faf3b 100644 --- a/docs/todo +++ b/docs/todo @@ -112,6 +112,8 @@ Language Cohesion: [ ] Add macros. + [ ] #solidify polymoprhic procedures. + API Expansion: There are many different places where the standard API for WASI and JS backends could be improved. Here are some of the target areas. diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index 1ecbee00..82a7e3f2 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -26,6 +26,8 @@ typedef struct AstStructLiteral AstStructLiteral; typedef struct AstArrayLiteral AstArrayLiteral; typedef struct AstRangeLiteral AstRangeLiteral; +typedef struct AstDirectiveSolidify AstDirectiveSolidify; + typedef struct AstReturn AstReturn; typedef struct AstJump AstJump; typedef struct AstUse AstUse; @@ -152,6 +154,8 @@ typedef enum AstKind { Ast_Kind_Switch, Ast_Kind_Switch_Case, + Ast_Kind_Directive_Solidify, + Ast_Kind_Count } AstKind; @@ -508,6 +512,15 @@ struct AstIntrinsicCall { VarArgKind va_kind; }; +struct AstDirectiveSolidify { + AstTyped_base; + + AstPolyProc* poly_proc; + bh_arr(AstPolySolution) known_polyvars; + + AstNode* resolved_proc; +}; + // Intruction Node struct AstReturn { AstNode_base; AstTyped* expr; }; struct AstJump { AstNode_base; JumpType jump; u32 count; }; @@ -729,6 +742,9 @@ struct AstPolyParam { struct AstPolySolution { AstNode* poly_sym; Type* type; + + // If `type` is null, it is filled in with this type. + AstType* ast_type; }; struct AstPolyProc { AstNode_base; @@ -736,6 +752,8 @@ struct AstPolyProc { Scope *poly_scope; bh_arr(AstPolyParam) poly_params; + bh_arr(AstPolySolution) known_slns; + AstFunction* base_func; bh_table(AstFunction *) concrete_funcs; }; @@ -911,6 +929,8 @@ typedef enum PolyProcLookupMethod { } PolyProcLookupMethod; AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxFilePos pos); AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxFilePos pos); +AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxFilePos pos); + AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params, OnyxFilePos pos); diff --git a/include/onyxlex.h b/include/onyxlex.h index 3e4cf559..645a5c93 100644 --- a/include/onyxlex.h +++ b/include/onyxlex.h @@ -111,4 +111,6 @@ OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc void onyx_tokenizer_free(OnyxTokenizer* tokenizer); void onyx_lex_tokens(OnyxTokenizer* tokenizer); +b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2); + #endif diff --git a/onyx b/onyx index 722bd518..4777151a 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/poly_solidify.onyx b/progs/poly_solidify.onyx new file mode 100644 index 00000000..f71cb85f --- /dev/null +++ b/progs/poly_solidify.onyx @@ -0,0 +1,18 @@ +#include_file "core/std/wasi" + +use package core + +max_f32 :: #solidify math.max { T = f32 }; + +compose :: proc (a: $A, f: proc (A) -> $B, g: proc (B) -> $C) -> C do return g(f(a)); + +specific_compose :: #solidify compose { A = f32, B = f32, C = u32 }; + +main :: proc (args: [] cstr) { + printf("max(1, 2) = %i\n", math.max(1, 2)); + printf("max_f32(1.0, 2.0) = %f\n", max_f32(1, 2)); + + // printf("max_f32(1, 2) = %i\n", max_f32(cast(u32) 1, cast(u32) 2)); + + +} \ No newline at end of file diff --git a/src/onyxchecker.c b/src/onyxchecker.c index cfdef2e8..8a2610ed 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -1283,6 +1283,10 @@ b32 check_expression(AstTyped** pexpr) { expr->flags |= Ast_Flag_Function_Used; break; + case Ast_Kind_Directive_Solidify: + *pexpr = (AstTyped *) ((AstDirectiveSolidify *) expr)->resolved_proc; + break; + case Ast_Kind_StrLit: break; case Ast_Kind_File_Contents: break; case Ast_Kind_Overloaded_Function: break; diff --git a/src/onyxlex.c b/src/onyxlex.c index 9a678ad9..18f020d5 100644 --- a/src/onyxlex.c +++ b/src/onyxlex.c @@ -437,3 +437,10 @@ void onyx_lex_tokens(OnyxTokenizer* tokenizer) { lexer_lines_processed += tokenizer->line_number - 1; lexer_tokens_processed += bh_arr_length(tokenizer->tokens); } + +b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2) { + if (tkn1->length != tkn2->length) return 0; + fori (i, 0, tkn1->length) + if (tkn1->text[i] != tkn2->text[i]) return 0; + return 1; +} diff --git a/src/onyxparser.c b/src/onyxparser.c index 3066b560..1b034777 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -531,6 +531,39 @@ static AstTyped* parse_factor(OnyxParser* parser) { retval = (AstTyped *) alias; break; } + else if (parse_possible_directive(parser, "solidify")) { + AstDirectiveSolidify* solid = make_node(AstDirectiveSolidify, Ast_Kind_Directive_Solidify); + solid->token = parser->curr - 1; + + solid->poly_proc = (AstPolyProc *) parse_factor(parser); + + solid->known_polyvars = NULL; + bh_arr_new(global_heap_allocator, solid->known_polyvars, 2); + + expect_token(parser, '{'); + while (parser->curr->type != '}') { + if (parser->hit_unexpected_token) break; + + AstNode* poly_var = make_node(AstNode, Ast_Kind_Symbol); + poly_var->token = expect_token(parser, Token_Type_Symbol); + + expect_token(parser, '='); + AstType* poly_type = parse_type(parser); + + bh_arr_push(solid->known_polyvars, ((AstPolySolution) { + .poly_sym = poly_var, + .ast_type = poly_type, + .type = NULL + })); + + if (parser->curr->type != '}') + expect_token(parser, ','); + } + expect_token(parser, '}'); + + retval = (AstTyped *) solid; + break; + } onyx_report_error(parser->curr->pos, "invalid directive in expression."); return NULL; diff --git a/src/onyxsymres.c b/src/onyxsymres.c index 185480c7..e145dab6 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -3,7 +3,7 @@ #include "onyxparser.h" #include "onyxutils.h" #include "onyxastnodes.h" - +#include "onyxerrors.h" static void scope_enter(Scope* new_scope); static void scope_leave(); @@ -21,6 +21,7 @@ static void symres_while(AstIfWhile* whilenode); static void symres_for(AstFor* fornode); static void symres_switch(AstSwitch* switchnode); static void symres_use(AstUse* use); +static void symres_directive_solidify(AstDirectiveSolidify** psolid); static void symres_statement_chain(AstNode** walker); static b32 symres_statement(AstNode** stmt); static void symres_block(AstBlock* block); @@ -468,6 +469,10 @@ static void symres_expression(AstTyped** expr) { symres_array_literal((AstArrayLiteral *)(*expr)); break; + case Ast_Kind_Directive_Solidify: + symres_directive_solidify((AstDirectiveSolidify **) expr); + break; + default: break; } } @@ -593,6 +598,30 @@ cannot_use: onyx_report_error(use->token->pos, "Cannot use this."); } +static void symres_directive_solidify(AstDirectiveSolidify** psolid) { + AstDirectiveSolidify* solid = *psolid; + if (solid->resolved_proc != NULL) + *psolid = (AstDirectiveSolidify *) solid->resolved_proc; + + symres_expression((AstTyped **) &solid->poly_proc); + if (!solid->poly_proc || solid->poly_proc->kind != Ast_Kind_Polymorphic_Proc) { + onyx_report_error(solid->token->pos, "Expected polymorphic procedure in #solidify directive."); + return; + } + + bh_arr_each(AstPolySolution, sln, solid->known_polyvars) { + sln->ast_type = symres_type(sln->ast_type); + sln->type = type_build_from_ast(semstate.node_allocator, sln->ast_type); + if (onyx_has_errors()) return; + } + + solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token->pos); + + // NOTE: Not a DirectiveSolidify. + *psolid = (AstDirectiveSolidify *) solid->resolved_proc; + return; +} + // NOTE: Returns 1 if the statment should be removed static b32 symres_statement(AstNode** stmt) { switch ((*stmt)->kind) { diff --git a/src/onyxutils.c b/src/onyxutils.c index 86562e25..a43d497d 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -536,7 +536,18 @@ AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lo bh_arr(AstPolySolution) slns = NULL; bh_arr_new(global_heap_allocator, slns, bh_arr_length(pp->poly_params)); + bh_arr_each(AstPolySolution, known_sln, pp->known_slns) bh_arr_push(slns, *known_sln); + bh_arr_each(AstPolyParam, param, pp->poly_params) { + b32 already_solved = 0; + bh_arr_each(AstPolySolution, known_sln, pp->known_slns) { + if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) { + already_solved = 1; + break; + } + } + if (already_solved) continue; + Type* actual_type; if (pp_lookup == PPLM_By_Call) { @@ -654,6 +665,55 @@ AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) return func; } +// NOTE: This can return either a AstFunction or an AstPolyProc, depending if enough parameters were +// supplied to remove all the polymorphic variables from the function. +AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxFilePos pos) { + i32 valid_argument_count = 0; + + bh_arr_each(AstPolySolution, sln, slns) { + b32 found_match = 0; + + bh_arr_each(AstPolyParam, param, pp->poly_params) { + if (token_equals(sln->poly_sym->token, param->poly_sym->token)) { + found_match = 1; + break; + } + } + + if (found_match) { + valid_argument_count++; + } else { + onyx_report_error(pos, "'%b' is not a type variable of '%b'.", + sln->poly_sym->token->text, sln->poly_sym->token->length, + pp->token->text, pp->token->length); + return (AstNode *) pp; + } + } + + if (valid_argument_count == bh_arr_length(pp->poly_params)) { + return (AstNode *) polymorphic_proc_solidify(pp, slns, pos); + + } else { + // HACK: Some of these initializations assume that the entity for this polyproc has + // made it through the symbol resolution phase. + // - brendanfh 2020/12/25 + AstPolyProc* new_pp = onyx_ast_node_new(semstate.node_allocator, sizeof(AstPolyProc), Ast_Kind_Polymorphic_Proc); + new_pp->token = pp->token; // TODO: Change this to be the solidify->token + new_pp->base_func = pp->base_func; + new_pp->poly_scope = new_pp->poly_scope; + new_pp->flags = pp->flags; + new_pp->poly_params = pp->poly_params; + + new_pp->known_slns = NULL; + bh_arr_new(global_heap_allocator, new_pp->known_slns, bh_arr_length(pp->known_slns) + bh_arr_length(slns)); + + bh_arr_each(AstPolySolution, sln, pp->known_slns) bh_arr_push(new_pp->known_slns, *sln); + bh_arr_each(AstPolySolution, sln, slns) bh_arr_push(new_pp->known_slns, *sln); + + return (AstNode *) new_pp; + } +} + AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params, OnyxFilePos pos) { // @Cleanup