From: Brendan Hansen Date: Thu, 9 Jul 2020 02:46:41 +0000 (-0500) Subject: Started working on intrinsic functions X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=57ced833aeeb510a740060d39e9b0fd795767ec9;p=onyx.git Started working on intrinsic functions --- diff --git a/Makefile b/Makefile index 259e4281..36a8fbd2 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ OBJ_FILES=\ build/onyxparser.o \ build/onyxsempass.o \ build/onyxsymres.o \ - build/onyxtypecheck.o \ + build/onyxchecker.o \ build/onyxmsgs.o \ build/onyxutils.o \ build/onyxwasm.o \ diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index cdb7a71c..8b7a4d09 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -19,6 +19,7 @@ typedef struct AstNodeFunction AstNodeFunction; typedef struct AstNodeForeign AstNodeForeign; typedef struct AstNodeGlobal AstNodeGlobal; typedef struct AstNodeCall AstNodeCall; +typedef struct AstNodeIntrinsicCall AstNodeIntrinsicCall; typedef struct AstNodeArgument AstNodeArgument; typedef struct AstNodeUse AstNodeUse; @@ -43,6 +44,7 @@ typedef enum AstNodeKind { AST_NODE_KIND_PARAM, AST_NODE_KIND_ARGUMENT, AST_NODE_KIND_CALL, + AST_NODE_KIND_INTRINSIC_CALL, AST_NODE_KIND_ASSIGNMENT, AST_NODE_KIND_RETURN, @@ -233,10 +235,10 @@ struct AstNodeGlobal { struct AstNodeCall { AstNodeTyped base; - AstNode *callee; // NOTE: Function definition node AstNodeArgument *arguments; // NOTE: Expressions that form the actual param list // They will be chained down using the "next" property // unless this becomes used by something else + AstNode *callee; // NOTE: Function definition node }; struct AstNodeArgument { @@ -245,6 +247,22 @@ struct AstNodeArgument { AstNodeTyped *value; }; +typedef enum OnyxIntrinsic { + ONYX_INTRINSIC_UNDEFINED, + + ONYX_INTRINSIC_FLOAT32_SQRT, + ONYX_INTRINSIC_FLOAT64_SQRT, +} OnyxIntrinsic; + +// NOTE: This needs to have 'arguments' in the +// same position as AstNodeCall +struct AstNodeIntrinsicCall { + AstNodeTyped base; + + AstNodeArgument *arguments; + OnyxIntrinsic intrinsic; +}; + struct AstNodeUse { AstNode base; @@ -252,7 +270,6 @@ struct AstNodeUse { }; typedef struct OnyxProgram { - bh_arr(AstNodeUse *) uses; bh_arr(AstNodeGlobal *) globals; bh_arr(AstNodeFunction *) functions; bh_arr(AstNodeForeign *) foreigns; diff --git a/onyx b/onyx index 06e673ce..0d7801ec 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/test.onyx b/progs/test.onyx index 93c9d88a..47fa4740 100644 --- a/progs/test.onyx +++ b/progs/test.onyx @@ -12,7 +12,7 @@ export in_unit_circle :: proc (x: f32, y: f32) -> bool { } sqrt_f32 :: - proc #intrinsic #inline + proc #intrinsic (val: f32) -> f32 --- // This is the entry point @@ -52,6 +52,18 @@ export main2 :: proc { } } +// Foo :: struct { +// bar : Bar; +// something : i32; +// other : i64; +// } +// +// Bar :: struct { +// x : f32; +// y : f32; +// z : f32; +// } + export main :: proc { print_f32(sqrt_f32(2.0f)); print_i32(5 * 6 + 2 * 3); diff --git a/src/onyx.c b/src/onyx.c index d843c498..f91500c8 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -144,10 +144,12 @@ static CompilerProgress process_source_file(CompilerState* compiler_state, char* bh_arr(AstNode *) top_nodes = parse_source_file(compiler_state, &fc); + bh_arr(AstNodeUse *) uses = NULL; + bh_arr_each(AstNode *, node, top_nodes) { switch ((*node)->kind) { case AST_NODE_KIND_USE: - bh_arr_push(compiler_state->program.uses, (AstNodeUse *) (*node)); + bh_arr_push(uses, (AstNodeUse *) *node); break; case AST_NODE_KIND_GLOBAL: @@ -168,7 +170,7 @@ static CompilerProgress process_source_file(CompilerState* compiler_state, char* } } - bh_arr_each(AstNodeUse *, use_node, compiler_state->program.uses) { + bh_arr_each(AstNodeUse *, use_node, uses) { char* formatted_name = bh_aprintf( global_heap_allocator, "%b.onyx", @@ -177,6 +179,7 @@ static CompilerProgress process_source_file(CompilerState* compiler_state, char* bh_arr_push(compiler_state->queued_files, formatted_name); } + bh_arr_free(uses); if (onyx_message_has_errors(&compiler_state->msgs)) { return ONYX_COMPILER_PROGRESS_FAILED_PARSE; @@ -188,7 +191,6 @@ static CompilerProgress process_source_file(CompilerState* compiler_state, char* static void compiler_state_init(CompilerState* compiler_state, OnyxCompileOptions* opts) { compiler_state->options = opts; - bh_arr_new(global_heap_allocator, compiler_state->program.uses, 4); bh_arr_new(global_heap_allocator, compiler_state->program.foreigns, 4); bh_arr_new(global_heap_allocator, compiler_state->program.globals, 4); bh_arr_new(global_heap_allocator, compiler_state->program.functions, 4); @@ -287,7 +289,6 @@ int main(int argc, char *argv[]) { OnyxCompileOptions compile_opts = compile_opts_parse(global_heap_allocator, argc, argv); CompilerState compile_state = { .program = { - .uses = NULL, .foreigns = NULL, .globals = NULL, .functions = NULL diff --git a/src/onyxchecker.c b/src/onyxchecker.c new file mode 100644 index 00000000..27e4a4c4 --- /dev/null +++ b/src/onyxchecker.c @@ -0,0 +1,375 @@ +#define BH_DEBUG +#include "onyxsempass.h" + +static void check_function(OnyxSemPassState* state, AstNodeFunction* func); +static void check_block(OnyxSemPassState* state, AstNodeBlock* block); +static void check_statement_chain(OnyxSemPassState* state, AstNode* start); +static void check_statement(OnyxSemPassState* state, AstNode* stmt); +static void check_assignment(OnyxSemPassState* state, AstNodeAssign* assign); +static void check_return(OnyxSemPassState* state, AstNodeReturn* retnode); +static void check_if(OnyxSemPassState* state, AstNodeIf* ifnode); +static void check_while(OnyxSemPassState* state, AstNodeWhile* whilenode); +static void check_call(OnyxSemPassState* state, AstNodeCall* call); +static void check_expression(OnyxSemPassState* state, AstNodeTyped* expr); +static void check_global(OnyxSemPassState* state, AstNodeGlobal* global); + +static void check_assignment(OnyxSemPassState* state, AstNodeAssign* assign) { + if (assign->lval->kind == AST_NODE_KIND_SYMBOL) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL, + assign->lval->token->pos, + assign->lval->token->token, assign->lval->token->length); + return; + } + + if ((assign->lval->flags & ONYX_AST_FLAG_CONST) != 0 && assign->lval->type->is_known) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_ASSIGN_CONST, + assign->base.token->pos, + assign->lval->token->token, assign->lval->token->length); + return; + } + + if ((assign->lval->flags & ONYX_AST_FLAG_LVAL) == 0) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_NOT_LVAL, + assign->base.token->pos, + assign->lval->token->token, assign->lval->token->length); + return; + } + + check_expression(state, assign->expr); + + if (!assign->lval->type->is_known) { + assign->lval->type = assign->expr->type; + } else { + if (assign->lval->type != assign->expr->type) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH, + assign->base.token->pos, + assign->lval->type->name, assign->expr->type->name); + return; + } + } +} + +static void check_return(OnyxSemPassState* state, AstNodeReturn* retnode) { + if (retnode->expr) { + check_expression(state, retnode->expr); + + if (retnode->expr->type != state->expected_return_type) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_FUNCTION_RETURN_MISMATCH, + retnode->expr->token->pos, + retnode->expr->type->name, state->expected_return_type->name); + } + } else { + if (state->expected_return_type->size > 0) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + retnode->base.token->pos, + "returning from non-void function without value"); + } + } +} + +static void check_if(OnyxSemPassState* state, AstNodeIf* ifnode) { + check_expression(state, ifnode->cond); + if (ifnode->cond->type != &builtin_types[TYPE_INFO_KIND_BOOL]) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + ifnode->cond->token->pos, + "expected boolean type for condition"); + return; + } + + if (ifnode->true_block.as_if) check_statement(state, (AstNode *) ifnode->true_block.as_block); + if (ifnode->false_block.as_if) check_statement(state, (AstNode *) ifnode->false_block.as_block); +} + +static void check_while(OnyxSemPassState* state, AstNodeWhile* whilenode) { + check_expression(state, whilenode->cond); + if (whilenode->cond->type != &builtin_types[TYPE_INFO_KIND_BOOL]) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + whilenode->cond->token->pos, + "expected boolean type for condition"); + return; + } + + check_block(state, whilenode->body); +} + +static void check_call(OnyxSemPassState* state, AstNodeCall* call) { + AstNodeFunction* callee = (AstNodeFunction *) call->callee; + + if (callee->base.kind == AST_NODE_KIND_SYMBOL) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL, + callee->base.token->pos, + callee->base.token->token, callee->base.token->length); + return; + } + + if (callee->base.kind != AST_NODE_KIND_FUNCTION) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_CALL_NON_FUNCTION, + call->base.token->pos, + callee->base.token->token, callee->base.token->length); + return; + } + + // NOTE: If we calling an intrinsic function, translate the + // call into an intrinsic call node. + if (callee->base.flags & ONYX_AST_FLAG_INTRINSIC) { + call->base.kind = AST_NODE_KIND_INTRINSIC_CALL; + call->callee = NULL; + + onyx_token_null_toggle(callee->base.token); + char* intr_name = callee->base.token->token; + if (!strcmp("sqrt_f32", intr_name)) { + ((AstNodeIntrinsicCall *) call)->intrinsic = ONYX_INTRINSIC_FLOAT32_SQRT; + } + else if (!strcmp("sqrt_f64", intr_name)) { + ((AstNodeIntrinsicCall *) call)->intrinsic = ONYX_INTRINSIC_FLOAT64_SQRT; + } + + onyx_token_null_toggle(callee->base.token); + } + + call->base.type = callee->base.type; + + AstNodeLocal* formal_param = callee->params; + AstNodeArgument* actual_param = call->arguments; + + i32 arg_pos = 0; + while (formal_param != NULL && actual_param != NULL) { + check_expression(state, (AstNodeTyped *) actual_param); + + if (formal_param->base.type != actual_param->base.type) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_FUNCTION_PARAM_TYPE_MISMATCH, + call->base.token->pos, + callee->base.token->token, callee->base.token->length, + formal_param->base.type->name, arg_pos, + actual_param->base.type->name); + return; + } + + arg_pos++; + formal_param = (AstNodeLocal *) formal_param->base.next; + actual_param = (AstNodeArgument *) actual_param->base.next; + } + + if (formal_param != NULL && actual_param == NULL) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + call->base.token->pos, + "too few arguments to function call"); + return; + } + + if (formal_param == NULL && actual_param != NULL) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + call->base.token->pos, + "too many arguments to function call"); + return; + } +} + +static void check_expression(OnyxSemPassState* state, AstNodeTyped* expr) { + switch (expr->kind) { + case AST_NODE_KIND_BIN_OP: + expr->type = &builtin_types[TYPE_INFO_KIND_UNKNOWN]; + + check_expression(state, ((AstNodeBinOp *) expr)->left); + check_expression(state, ((AstNodeBinOp *) expr)->right); + + if (((AstNodeBinOp *) expr)->left->type == NULL) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE, + expr->token->pos, + NULL, 0); + return; + } + + if (((AstNodeBinOp *) expr)->right->type == NULL) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE, + expr->token->pos, + NULL, 0); + return; + } + + if (((AstNodeBinOp *) expr)->left->type != ((AstNodeBinOp *) expr)->right->type) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE, + expr->token->pos, + ((AstNodeBinOp *) expr)->left->type->name, + ((AstNodeBinOp *) expr)->right->type->name); + return; + } + + if (((AstNodeBinOp *) expr)->operation >= ONYX_BINARY_OP_EQUAL + && ((AstNodeBinOp *) expr)->operation <= ONYX_BINARY_OP_GREATER_EQUAL) { + expr->type = &builtin_types[TYPE_INFO_KIND_BOOL]; + } else { + expr->type = ((AstNodeBinOp *) expr)->left->type; + } + + break; + + case AST_NODE_KIND_UNARY_OP: + if (((AstNodeUnaryOp *) expr)->operation != ONYX_UNARY_OP_CAST) { + check_expression(state, ((AstNodeUnaryOp *) expr)->expr); + expr->type = ((AstNodeUnaryOp *) expr)->expr->type; + } + break; + + case AST_NODE_KIND_CALL: + check_call(state, (AstNodeCall *) expr); + break; + + case AST_NODE_KIND_BLOCK: + check_block(state, (AstNodeBlock *) expr); + break; + + case AST_NODE_KIND_SYMBOL: + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL, + expr->token->pos, + expr->token->token, expr->token->length); + break; + + case AST_NODE_KIND_LOCAL: + case AST_NODE_KIND_PARAM: + if (!expr->type->is_known) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + expr->token->pos, + "local variable with unknown type"); + } + break; + + case AST_NODE_KIND_GLOBAL: + if (!expr->type->is_known) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + expr->token->pos, + "global with unknown type"); + } + break; + + case AST_NODE_KIND_ARGUMENT: + check_expression(state, ((AstNodeArgument *) expr)->value); + expr->type = ((AstNodeArgument *) expr)->value->type; + break; + + case AST_NODE_KIND_LITERAL: + // NOTE: Literal types should have been decided + // in the parser (for now). + assert(expr->type->is_known); + break; + + default: + DEBUG_HERE; + break; + } +} + +static void check_global(OnyxSemPassState* state, AstNodeGlobal* global) { + if (global->initial_value) { + check_expression(state, global->initial_value); + + if (global->base.type->is_known) { + if (global->base.type != global->initial_value->type) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_GLOBAL_TYPE_MISMATCH, + global->base.token->pos, + global->base.token->token, global->base.token->length, + global->base.type->name, global->initial_value->type->name); + return; + } + } else { + if (global->initial_value->type) + global->base.type = global->initial_value->type; + } + + } else { + if (!global->base.type || !global->base.type->is_known) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + global->base.token->pos, + "global variable with unknown type"); + } + } +} + +static void check_statement(OnyxSemPassState* state, AstNode* stmt) { + switch (stmt->kind) { + case AST_NODE_KIND_ASSIGNMENT: check_assignment(state, (AstNodeAssign *) stmt); break; + case AST_NODE_KIND_RETURN: check_return(state, (AstNodeReturn *) stmt); break; + case AST_NODE_KIND_IF: check_if(state, (AstNodeIf *) stmt); break; + case AST_NODE_KIND_WHILE: check_while(state, (AstNodeWhile *) stmt); break; + case AST_NODE_KIND_CALL: check_call(state, (AstNodeCall *) stmt); break; + case AST_NODE_KIND_BLOCK: check_block(state, (AstNodeBlock *) stmt); break; + + default: break; + } +} + +static void check_statement_chain(OnyxSemPassState* state, AstNode* start) { + while (start) { + check_statement(state, start); + start = start->next; + } +} + +static void check_block(OnyxSemPassState* state, AstNodeBlock* block) { + check_statement_chain(state, block->body); + + forll(AstNodeLocal, local, block->locals->last_local, prev_local) { + if (!local->base.type->is_known) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE, + local->base.token->pos, + local->base.token->token, local->base.token->length); + return; + } + } +} + +static void check_function(OnyxSemPassState* state, AstNodeFunction* func) { + for (AstNodeLocal *param = func->params; param != NULL; param = (AstNodeLocal *) param->base.next) { + if (!param->base.type->is_known) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + param->base.token->pos, + "function parameter types must be known"); + return; + } + + if (param->base.type->size == 0) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_LITERAL, + param->base.token->pos, + "function parameters must have non-void types"); + return; + } + } + + state->expected_return_type = func->base.type; + if (func->body) { + check_block(state, func->body); + } +} + +void onyx_type_check(OnyxSemPassState* state, OnyxProgram* program) { + + bh_arr_each(AstNodeGlobal *, global, program->globals) + check_global(state, *global); + + bh_arr_each(AstNodeFunction *, function, program->functions) + check_function(state, *function); +} diff --git a/src/onyxtypecheck.c b/src/onyxtypecheck.c deleted file mode 100644 index 869e89af..00000000 --- a/src/onyxtypecheck.c +++ /dev/null @@ -1,357 +0,0 @@ -#define BH_DEBUG -#include "onyxsempass.h" - -static void typecheck_function(OnyxSemPassState* state, AstNodeFunction* func); -static void typecheck_block(OnyxSemPassState* state, AstNodeBlock* block); -static void typecheck_statement_chain(OnyxSemPassState* state, AstNode* start); -static void typecheck_statement(OnyxSemPassState* state, AstNode* stmt); -static void typecheck_assignment(OnyxSemPassState* state, AstNodeAssign* assign); -static void typecheck_return(OnyxSemPassState* state, AstNodeReturn* retnode); -static void typecheck_if(OnyxSemPassState* state, AstNodeIf* ifnode); -static void typecheck_while(OnyxSemPassState* state, AstNodeWhile* whilenode); -static void typecheck_call(OnyxSemPassState* state, AstNodeCall* call); -static void typecheck_expression(OnyxSemPassState* state, AstNodeTyped* expr); -static void typecheck_global(OnyxSemPassState* state, AstNodeGlobal* global); - -static void typecheck_assignment(OnyxSemPassState* state, AstNodeAssign* assign) { - if (assign->lval->kind == AST_NODE_KIND_SYMBOL) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL, - assign->lval->token->pos, - assign->lval->token->token, assign->lval->token->length); - return; - } - - if ((assign->lval->flags & ONYX_AST_FLAG_CONST) != 0 && assign->lval->type->is_known) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_ASSIGN_CONST, - assign->base.token->pos, - assign->lval->token->token, assign->lval->token->length); - return; - } - - if ((assign->lval->flags & ONYX_AST_FLAG_LVAL) == 0) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_NOT_LVAL, - assign->base.token->pos, - assign->lval->token->token, assign->lval->token->length); - return; - } - - typecheck_expression(state, assign->expr); - - if (!assign->lval->type->is_known) { - assign->lval->type = assign->expr->type; - } else { - if (assign->lval->type != assign->expr->type) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH, - assign->base.token->pos, - assign->lval->type->name, assign->expr->type->name); - return; - } - } -} - -static void typecheck_return(OnyxSemPassState* state, AstNodeReturn* retnode) { - if (retnode->expr) { - typecheck_expression(state, retnode->expr); - - if (retnode->expr->type != state->expected_return_type) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_FUNCTION_RETURN_MISMATCH, - retnode->expr->token->pos, - retnode->expr->type->name, state->expected_return_type->name); - } - } else { - if (state->expected_return_type->size > 0) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - retnode->base.token->pos, - "returning from non-void function without value"); - } - } -} - -static void typecheck_if(OnyxSemPassState* state, AstNodeIf* ifnode) { - typecheck_expression(state, ifnode->cond); - if (ifnode->cond->type != &builtin_types[TYPE_INFO_KIND_BOOL]) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - ifnode->cond->token->pos, - "expected boolean type for condition"); - return; - } - - if (ifnode->true_block.as_if) typecheck_statement(state, (AstNode *) ifnode->true_block.as_block); - if (ifnode->false_block.as_if) typecheck_statement(state, (AstNode *) ifnode->false_block.as_block); -} - -static void typecheck_while(OnyxSemPassState* state, AstNodeWhile* whilenode) { - typecheck_expression(state, whilenode->cond); - if (whilenode->cond->type != &builtin_types[TYPE_INFO_KIND_BOOL]) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - whilenode->cond->token->pos, - "expected boolean type for condition"); - return; - } - - typecheck_block(state, whilenode->body); -} - -static void typecheck_call(OnyxSemPassState* state, AstNodeCall* call) { - AstNodeFunction* callee = (AstNodeFunction *) call->callee; - - if (callee->base.kind == AST_NODE_KIND_SYMBOL) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL, - callee->base.token->pos, - callee->base.token->token, callee->base.token->length); - return; - } - - if (callee->base.kind != AST_NODE_KIND_FUNCTION) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_CALL_NON_FUNCTION, - call->base.token->pos, - callee->base.token->token, callee->base.token->length); - return; - } - - call->base.type = callee->base.type; - - AstNodeLocal* formal_param = callee->params; - AstNodeArgument* actual_param = call->arguments; - - i32 arg_pos = 0; - while (formal_param != NULL && actual_param != NULL) { - typecheck_expression(state, (AstNodeTyped *) actual_param); - - if (formal_param->base.type != actual_param->base.type) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_FUNCTION_PARAM_TYPE_MISMATCH, - call->base.token->pos, - callee->base.token->token, callee->base.token->length, - formal_param->base.type->name, arg_pos, - actual_param->base.type->name); - return; - } - - arg_pos++; - formal_param = (AstNodeLocal *) formal_param->base.next; - actual_param = (AstNodeArgument *) actual_param->base.next; - } - - if (formal_param != NULL && actual_param == NULL) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - call->base.token->pos, - "too few arguments to function call"); - return; - } - - if (formal_param == NULL && actual_param != NULL) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - call->base.token->pos, - "too many arguments to function call"); - return; - } -} - -static void typecheck_expression(OnyxSemPassState* state, AstNodeTyped* expr) { - switch (expr->kind) { - case AST_NODE_KIND_BIN_OP: - expr->type = &builtin_types[TYPE_INFO_KIND_UNKNOWN]; - - typecheck_expression(state, ((AstNodeBinOp *) expr)->left); - typecheck_expression(state, ((AstNodeBinOp *) expr)->right); - - if (((AstNodeBinOp *) expr)->left->type == NULL) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE, - expr->token->pos, - NULL, 0); - return; - } - - if (((AstNodeBinOp *) expr)->right->type == NULL) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE, - expr->token->pos, - NULL, 0); - return; - } - - if (((AstNodeBinOp *) expr)->left->type != ((AstNodeBinOp *) expr)->right->type) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE, - expr->token->pos, - ((AstNodeBinOp *) expr)->left->type->name, - ((AstNodeBinOp *) expr)->right->type->name); - return; - } - - if (((AstNodeBinOp *) expr)->operation >= ONYX_BINARY_OP_EQUAL - && ((AstNodeBinOp *) expr)->operation <= ONYX_BINARY_OP_GREATER_EQUAL) { - expr->type = &builtin_types[TYPE_INFO_KIND_BOOL]; - } else { - expr->type = ((AstNodeBinOp *) expr)->left->type; - } - - break; - - case AST_NODE_KIND_UNARY_OP: - if (((AstNodeUnaryOp *) expr)->operation != ONYX_UNARY_OP_CAST) { - typecheck_expression(state, ((AstNodeUnaryOp *) expr)->expr); - expr->type = ((AstNodeUnaryOp *) expr)->expr->type; - } - break; - - case AST_NODE_KIND_CALL: - typecheck_call(state, (AstNodeCall *) expr); - break; - - case AST_NODE_KIND_BLOCK: - typecheck_block(state, (AstNodeBlock *) expr); - break; - - case AST_NODE_KIND_SYMBOL: - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL, - expr->token->pos, - expr->token->token, expr->token->length); - break; - - case AST_NODE_KIND_LOCAL: - case AST_NODE_KIND_PARAM: - if (!expr->type->is_known) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - expr->token->pos, - "local variable with unknown type"); - } - break; - - case AST_NODE_KIND_GLOBAL: - if (!expr->type->is_known) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - expr->token->pos, - "global with unknown type"); - } - break; - - case AST_NODE_KIND_ARGUMENT: - typecheck_expression(state, ((AstNodeArgument *) expr)->value); - expr->type = ((AstNodeArgument *) expr)->value->type; - break; - - case AST_NODE_KIND_LITERAL: - // NOTE: Literal types should have been decided - // in the parser (for now). - assert(expr->type->is_known); - break; - - default: - DEBUG_HERE; - break; - } -} - -static void typecheck_global(OnyxSemPassState* state, AstNodeGlobal* global) { - if (global->initial_value) { - typecheck_expression(state, global->initial_value); - - if (global->base.type->is_known) { - if (global->base.type != global->initial_value->type) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_GLOBAL_TYPE_MISMATCH, - global->base.token->pos, - global->base.token->token, global->base.token->length, - global->base.type->name, global->initial_value->type->name); - return; - } - } else { - if (global->initial_value->type) - global->base.type = global->initial_value->type; - } - - } else { - if (!global->base.type || !global->base.type->is_known) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - global->base.token->pos, - "global variable with unknown type"); - } - } -} - -static void typecheck_statement(OnyxSemPassState* state, AstNode* stmt) { - switch (stmt->kind) { - case AST_NODE_KIND_ASSIGNMENT: typecheck_assignment(state, (AstNodeAssign *) stmt); break; - case AST_NODE_KIND_RETURN: typecheck_return(state, (AstNodeReturn *) stmt); break; - case AST_NODE_KIND_IF: typecheck_if(state, (AstNodeIf *) stmt); break; - case AST_NODE_KIND_WHILE: typecheck_while(state, (AstNodeWhile *) stmt); break; - case AST_NODE_KIND_CALL: typecheck_call(state, (AstNodeCall *) stmt); break; - case AST_NODE_KIND_BLOCK: typecheck_block(state, (AstNodeBlock *) stmt); break; - - default: break; - } -} - -static void typecheck_statement_chain(OnyxSemPassState* state, AstNode* start) { - while (start) { - typecheck_statement(state, start); - start = start->next; - } -} - -static void typecheck_block(OnyxSemPassState* state, AstNodeBlock* block) { - typecheck_statement_chain(state, block->body); - - forll(AstNodeLocal, local, block->locals->last_local, prev_local) { - if (!local->base.type->is_known) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE, - local->base.token->pos, - local->base.token->token, local->base.token->length); - return; - } - } -} - -static void typecheck_function(OnyxSemPassState* state, AstNodeFunction* func) { - for (AstNodeLocal *param = func->params; param != NULL; param = (AstNodeLocal *) param->base.next) { - if (!param->base.type->is_known) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - param->base.token->pos, - "function parameter types must be known"); - return; - } - - if (param->base.type->size == 0) { - onyx_message_add(state->msgs, - ONYX_MESSAGE_TYPE_LITERAL, - param->base.token->pos, - "function parameters must have non-void types"); - return; - } - } - - state->expected_return_type = func->base.type; - if (func->body) { - typecheck_block(state, func->body); - } -} - -void onyx_type_check(OnyxSemPassState* state, OnyxProgram* program) { - - bh_arr_each(AstNodeGlobal *, global, program->globals) - typecheck_global(state, *global); - - bh_arr_each(AstNodeFunction *, function, program->functions) - typecheck_function(state, *function); -} diff --git a/src/onyxutils.c b/src/onyxutils.c index 7fff9577..3e55d696 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -29,6 +29,7 @@ static const char* ast_node_names[] = { "PARAM", "ARGUMENT", "CALL", + "INTRINSIC_CALL", "ASSIGN", "RETURN", diff --git a/src/onyxwasm.c b/src/onyxwasm.c index 7f5a5061..6001f20f 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -218,6 +218,8 @@ static void compile_if(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstN static void compile_while(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeWhile* while_node); static void compile_binop(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeBinOp* binop); static void compile_unaryop(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeUnaryOp* unop); +static void compile_call(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeCall* call); +static void compile_intrinsic_call(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeIntrinsicCall* call); static void compile_expression(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeTyped* expr); static void compile_cast(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeUnaryOp* cast); static void compile_return(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeReturn* ret); @@ -286,9 +288,14 @@ static void compile_statement(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcod case AST_NODE_KIND_WHILE: compile_while(mod, &code, (AstNodeWhile *) stmt); break; case AST_NODE_KIND_BREAK: compile_structured_jump(mod, &code, 0); break; case AST_NODE_KIND_CONTINUE: compile_structured_jump(mod, &code, 1); break; - case AST_NODE_KIND_CALL: compile_expression(mod, &code, (AstNodeTyped *) stmt); break; case AST_NODE_KIND_BLOCK: compile_block(mod, &code, (AstNodeBlock *) stmt); break; + case AST_NODE_KIND_CALL: + case AST_NODE_KIND_INTRINSIC_CALL: + compile_expression(mod, &code, (AstNodeTyped *) stmt); + break; + + default: break; } @@ -493,6 +500,7 @@ static void compile_unaryop(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, case ONYX_UNARY_OP_NOT: compile_expression(mod, &code, unop->expr); + bh_arr_push(code, ((WasmInstruction){ WI_I32_EQZ, 0x00 })); break; @@ -502,6 +510,55 @@ static void compile_unaryop(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, *pcode = code; } +static void compile_call(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeCall* call) { + bh_arr(WasmInstruction) code = *pcode; + + for (AstNodeArgument *arg = call->arguments; + arg != NULL; + arg = (AstNodeArgument *) arg->base.next) { + compile_expression(mod, &code, arg->value); + } + + i32 func_idx = (i32) bh_imap_get(&mod->func_map, (u64) call->callee); + bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx })); + + *pcode = code; +} + +static void compile_intrinsic_call(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeIntrinsicCall* call) { + bh_arr(WasmInstruction) code = *pcode; + + i32 place_arguments_normally = 1; + + // NOTE: Doing this in case there becomes intrinsics that the arguments + // are not placed as they normally would be + if (0) place_arguments_normally = 0; + + if (place_arguments_normally) { + for (AstNodeArgument *arg = call->arguments; + arg != NULL; + arg = (AstNodeArgument *) arg->base.next) { + compile_expression(mod, &code, arg->value); + } + } + + switch (call->intrinsic) { + case ONYX_INTRINSIC_UNDEFINED: + assert(0); + break; + + case ONYX_INTRINSIC_FLOAT32_SQRT: + bh_arr_push(code, ((WasmInstruction){ WI_F32_SQRT, 0x00})); + break; + + case ONYX_INTRINSIC_FLOAT64_SQRT: + bh_arr_push(code, ((WasmInstruction){ WI_F64_SQRT, 0x00 })); + break; + } + + *pcode = code; +} + static void compile_expression(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNodeTyped* expr) { bh_arr(WasmInstruction) code = *pcode; @@ -557,20 +614,14 @@ static void compile_expression(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pco case AST_NODE_KIND_BLOCK: compile_block(mod, &code, (AstNodeBlock *) expr); break; - case AST_NODE_KIND_CALL: - { - AstNodeCall* call = (AstNodeCall *) expr; - for (AstNodeArgument *arg = call->arguments; - arg != NULL; - arg = (AstNodeArgument *) arg->base.next) { - compile_expression(mod, &code, arg->value); - } - i32 func_idx = (i32) bh_imap_get(&mod->func_map, (u64) call->callee); - bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx })); + case AST_NODE_KIND_CALL: + compile_call(mod, &code, (AstNodeCall *) expr); + break; - break; - } + case AST_NODE_KIND_INTRINSIC_CALL: + compile_intrinsic_call(mod, &code, (AstNodeIntrinsicCall *) expr); + break; default: DEBUG_HERE; @@ -683,6 +734,9 @@ static i32 generate_type_idx(OnyxWasmModule* mod, AstNodeFunction* fd) { } static void compile_function(OnyxWasmModule* mod, AstNodeFunction* fd) { + // NOTE: Don't compile intrinsics + if (fd->base.flags & ONYX_AST_FLAG_INTRINSIC) return; + i32 type_idx = generate_type_idx(mod, fd); WasmFunc wasm_func = { @@ -880,8 +934,10 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, OnyxProgram* program) { compile_foreign(module, *foreign); } - bh_arr_each(AstNodeFunction *, function, program->functions) - bh_imap_put(&module->func_map, (u64) *function, module->next_func_idx++); + bh_arr_each(AstNodeFunction *, function, program->functions) { + if (((*function)->base.flags & ONYX_AST_FLAG_INTRINSIC) == 0) + bh_imap_put(&module->func_map, (u64) *function, module->next_func_idx++); + } bh_arr_each(AstNodeGlobal *, global, program->globals) bh_imap_put(&module->global_map, (u64) *global, module->next_global_idx++);