Started working on intrinsic functions
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 9 Jul 2020 02:46:41 +0000 (21:46 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 9 Jul 2020 02:46:41 +0000 (21:46 -0500)
Makefile
include/onyxastnodes.h
onyx
progs/test.onyx
src/onyx.c
src/onyxchecker.c [new file with mode: 0644]
src/onyxtypecheck.c [deleted file]
src/onyxutils.c
src/onyxwasm.c

index 259e42818c0346c5cba4245b85580611de1a1c9f..36a8fbd23084d4d9d23b2c1246ed649b492e47e1 100644 (file)
--- 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 \
index cdb7a71cbdd6a47fe2c4245b6b773851548c05d8..8b7a4d098f170ad9af851e02f630ad06dec5c06b 100644 (file)
@@ -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 06e673ce731eb422696e94b4509f95882b577338..0d7801ec9f78940218113e72d96d622d38e674d9 100755 (executable)
Binary files a/onyx and b/onyx differ
index 93c9d88a28df0fd50cd24778985aa052f4369a92..47fa474015d122f8ea7ae5ee462f3cb2b7dd5cee 100644 (file)
@@ -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);
index d843c498b10882ee993e18d534fd26543ab6acd7..f91500c8bdca0a32cb398e972a5cbe859856a6c5 100644 (file)
@@ -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 (file)
index 0000000..27e4a4c
--- /dev/null
@@ -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 (file)
index 869e89a..0000000
+++ /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);
-}
index 7fff9577eff0b6ec9d58feb4dbc169812f275c7c..3e55d696d8d2476c2d60645071a5053e1a53576d 100644 (file)
@@ -29,6 +29,7 @@ static const char* ast_node_names[] = {
     "PARAM",
     "ARGUMENT",
     "CALL",
+    "INTRINSIC_CALL",
     "ASSIGN",
     "RETURN",
 
index 7f5a50611831dcac5f11c0e62e47c92e521e8e31..6001f20f11c98a828bb9bb53f99ed340b2dba06f 100644 (file)
@@ -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++);