build/onyxparser.o \
build/onyxsempass.o \
build/onyxsymres.o \
- build/onyxtypecheck.o \
+ build/onyxchecker.o \
build/onyxmsgs.o \
build/onyxutils.o \
build/onyxwasm.o \
typedef struct AstNodeForeign AstNodeForeign;
typedef struct AstNodeGlobal AstNodeGlobal;
typedef struct AstNodeCall AstNodeCall;
+typedef struct AstNodeIntrinsicCall AstNodeIntrinsicCall;
typedef struct AstNodeArgument AstNodeArgument;
typedef struct AstNodeUse AstNodeUse;
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,
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 {
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;
};
typedef struct OnyxProgram {
- bh_arr(AstNodeUse *) uses;
bh_arr(AstNodeGlobal *) globals;
bh_arr(AstNodeFunction *) functions;
bh_arr(AstNodeForeign *) foreigns;
}
sqrt_f32 ::
- proc #intrinsic #inline
+ proc #intrinsic
(val: f32) -> f32 ---
// This is the entry point
}
}
+// 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);
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:
}
}
- 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",
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;
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);
OnyxCompileOptions compile_opts = compile_opts_parse(global_heap_allocator, argc, argv);
CompilerState compile_state = {
.program = {
- .uses = NULL,
.foreigns = NULL,
.globals = NULL,
.functions = NULL
--- /dev/null
+#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);
+}
+++ /dev/null
-#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);
-}
"PARAM",
"ARGUMENT",
"CALL",
+ "INTRINSIC_CALL",
"ASSIGN",
"RETURN",
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);
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;
}
case ONYX_UNARY_OP_NOT:
compile_expression(mod, &code, unop->expr);
+
bh_arr_push(code, ((WasmInstruction){ WI_I32_EQZ, 0x00 }));
break;
*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;
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;
}
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 = {
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++);