From: Brendan Hansen Date: Thu, 25 Jun 2020 19:35:45 +0000 (-0500) Subject: Added boolean types and while statements X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=9c709a93f91a71a6f865bcbd0888ef80dc4a902d;p=onyx.git Added boolean types and while statements Almost a very usable language --- diff --git a/docs/plan b/docs/plan index 649fc86b..c7f8e803 100644 --- a/docs/plan +++ b/docs/plan @@ -57,7 +57,7 @@ HOW: [X] Numeric literals have the minimum type detected [X] Foreign imports (functions only) [X] Comparison operators - [ ] Proper boolean type + [X] Proper boolean type [X] Conditional branching works as expected [ ] Simple while loop is functioning as expected [ ] break and continue semantics @@ -84,6 +84,12 @@ HOW: - Changing output location - Viewing help screen + [ ] 'use' statements work + - Adds '.onyx' to the end of the file name list + - Only searches in current directory for now + + [ ] Output 'drop' instruction for functions whose return value isn't used + [ ] Devise and implement a simple set of implicit type casting rules. - Numeric literals always type cast to whatever type is needed (very flexible). diff --git a/include/onyxlex.h b/include/onyxlex.h index 7c502c76..29c1c73a 100644 --- a/include/onyxlex.h +++ b/include/onyxlex.h @@ -22,6 +22,7 @@ typedef enum OnyxTokenType { TOKEN_TYPE_KEYWORD_PROC, TOKEN_TYPE_KEYWORD_GLOBAL, TOKEN_TYPE_KEYWORD_CAST, + TOKEN_TYPE_KEYWORD_WHILE, TOKEN_TYPE_RIGHT_ARROW, TOKEN_TYPE_LEFT_ARROW, @@ -57,6 +58,8 @@ typedef enum OnyxTokenType { TOKEN_TYPE_SYMBOL, TOKEN_TYPE_LITERAL_STRING, TOKEN_TYPE_LITERAL_NUMERIC, + TOKEN_TYPE_LITERAL_BOOL_TRUE, + TOKEN_TYPE_LITERAL_BOOL_FALSE, TOKEN_TYPE_COUNT } OnyxTokenType; diff --git a/include/onyxparser.h b/include/onyxparser.h index 27b903a3..f9dceade 100644 --- a/include/onyxparser.h +++ b/include/onyxparser.h @@ -12,6 +12,7 @@ typedef struct OnyxAstNodeLocal OnyxAstNodeLocal; typedef struct OnyxAstNodeScope OnyxAstNodeScope; typedef struct OnyxAstNodeBlock OnyxAstNodeBlock; typedef struct OnyxAstNodeIf OnyxAstNodeIf; +typedef struct OnyxAstNodeWhile OnyxAstNodeWhile; typedef struct OnyxAstNodeParam OnyxAstNodeParam; typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef; typedef struct OnyxAstNodeForeign OnyxAstNodeForeign; @@ -60,14 +61,14 @@ typedef enum OnyxAstNodeKind { ONYX_AST_NODE_KIND_EQUAL, ONYX_AST_NODE_KIND_NOT_EQUAL, - ONYX_AST_NODE_KIND_GREATER, - ONYX_AST_NODE_KIND_GREATER_EQUAL, ONYX_AST_NODE_KIND_LESS, ONYX_AST_NODE_KIND_LESS_EQUAL, + ONYX_AST_NODE_KIND_GREATER, + ONYX_AST_NODE_KIND_GREATER_EQUAL, ONYX_AST_NODE_KIND_NOT, ONYX_AST_NODE_KIND_IF, - ONYX_AST_NODE_KIND_LOOP, + ONYX_AST_NODE_KIND_WHILE, ONYX_AST_NODE_KIND_COUNT } OnyxAstNodeKind; @@ -167,6 +168,16 @@ struct OnyxAstNodeIf { OnyxAstNode *true_block; }; +struct OnyxAstNodeWhile { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; // NOTE: UNUSED + OnyxTypeInfo *type; + OnyxAstNode *next; + OnyxAstNode *cond; + OnyxAstNodeBlock *body; +}; + struct OnyxAstNodeFuncDef { OnyxAstNodeKind kind; u32 flags; @@ -230,6 +241,7 @@ union OnyxAstNode { OnyxAstNodeNumLit as_numlit; OnyxAstNodeForeign as_foreign; OnyxAstNodeIf as_if; + OnyxAstNodeWhile as_while; OnyxAstNodeFile as_file; }; diff --git a/onyx b/onyx index 0a10774a..e335e356 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/other.onyx b/progs/other.onyx index 1dadc9e7..b699b444 100644 --- a/progs/other.onyx +++ b/progs/other.onyx @@ -1,4 +1,38 @@ -other_value :: proc -> i32 { - return 8675309 + something_else(); +other_value :: proc (n i32) -> i32 { + return 8675309 + something_else(n); +} + +export fib :: proc (n i32) -> i32 { + if n == 0 { return 1; } + if n == 1 { return 1; } + + a := 0; + b := 1; + + count := n; + + while count >= 0 { + count = count - 1; + + c :: a + b; + b = a; + a = c; + } + + return a; +} + +export factorial :: proc (n i32) -> i32 { + if n <= 1 { return 1; } + + f := 1; + i := 2; + + while i <= n { + f = f * i; + i = i + 1; + } + + return f; } diff --git a/progs/test.onyx b/progs/test.onyx index ad83c20a..7b492349 100644 --- a/progs/test.onyx +++ b/progs/test.onyx @@ -2,20 +2,54 @@ // Foreign functions are included this way: // sym_name :: foreign "module" "name" proc ... -print :: foreign "host" "print" proc (value i32) --- +// TODO: Make this work +// use "progs/other"; -something_else :: proc -> i32 { - return 100; +print_i32 :: foreign "host" "print" proc (value i32) --- +print_f32 :: foreign "host" "print" proc (value f32) --- +print_i64 :: foreign "host" "print" proc (value i64) --- +print_f64 :: foreign "host" "print" proc (value f64) --- + +something_else :: proc (n i32) -> i32 { + return 100 * n; } // This is the entry point export main :: proc { - nineteen :: 5 * 3 + 4; - thirty_five :: 5 * (3 + 4); + i := 0; + while i < 10 { + print_i32(fib(i)); + i = i + 1; + } + + i = 0; + while i < 10 { + print_i32(factorial(i)); + i = i + 1; + } + + x : i32; + y := 0; + while y < 10 { + + x = 0; + while x < 10 { + print_i32(x + y * 10); + x = x + 1; + } + + y = y + 1; + } +} + +export main2 :: proc { + big_num := fib(factorial(4)); + something :: other_value(3); - something :: other_value(); + condition := big_num < something; - print(nineteen); - print(thirty_five); - print(something); + if condition { + print_i32(big_num); + print_i32(something); + } } diff --git a/src/onyx.c b/src/onyx.c index dee3930b..a4614528 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -13,7 +13,12 @@ static const char* docstring = "Onyx compiler version " VERSION "\n" "\n" - "The standard compiler for the Onyx programming language.\n"; + "The standard compiler for the Onyx programming language.\n" + "\n" + " $ onyx [-o ] [--help] \n" + "\n" + " -o Specify the target file\n" + " --help Print this help message\n"; typedef enum CompileAction { ONYX_COMPILE_ACTION_COMPILE, @@ -39,7 +44,7 @@ typedef enum CompilerProgress { } CompilerProgress; typedef struct CompilerState { - bh_arena token_arena, ast_arena, msg_arena, sp_arena; + bh_arena ast_arena, msg_arena, sp_arena; bh_allocator token_alloc, ast_alloc, msg_alloc, sp_alloc; bh_table(bh_file_contents) loaded_files; @@ -83,8 +88,10 @@ void compile_opts_free(OnyxCompileOptions* opts) { OnyxAstNodeFile* parse_source_file(bh_file_contents* file_contents, CompilerState* compiler_state) { // NOTE: Maybe don't want to recreate the tokenizer and parser for every file OnyxTokenizer tokenizer = onyx_tokenizer_create(compiler_state->token_alloc, file_contents); + bh_printf("Lexing %s\n", file_contents->filename); onyx_lex_tokens(&tokenizer); + bh_printf("Parsing %s\n", file_contents->filename); OnyxParser parser = onyx_parser_create(compiler_state->ast_alloc, &tokenizer, &compiler_state->msgs); return onyx_parse(&parser); } @@ -96,9 +103,7 @@ i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) { onyx_message_create(compiler_state->msg_alloc, &compiler_state->msgs); - // NOTE: Create the arena for tokens from the lexer - bh_arena_init(&compiler_state->token_arena, opts->allocator, 16 * 1024 * 1024); // 16 MB - compiler_state->token_alloc = bh_arena_allocator(&compiler_state->token_arena); + compiler_state->token_alloc = opts->allocator; // NOTE: Create the arena where AST nodes will exist // Prevents nodes from being scattered across memory due to fragmentation @@ -115,10 +120,11 @@ i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) { bh_file_error err = bh_file_open(&file, *filename); if (err != BH_FILE_ERROR_NONE) { - bh_printf_err("Failed to open file %s\n", filename); + bh_printf_err("Failed to open file %s\n", *filename); return ONYX_COMPILER_PROGRESS_FAILED_READ; } + bh_printf("Reading %s\n", file.filename); bh_file_contents fc = bh_file_read_contents(compiler_state->token_alloc, &file); bh_file_close(&file); @@ -145,6 +151,7 @@ i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) { return ONYX_COMPILER_PROGRESS_FAILED_PARSE; } + bh_printf("Checking semantics and types\n"); OnyxSemPassState sp_state = onyx_sempass_create( compiler_state->sp_alloc, compiler_state->ast_alloc, &compiler_state->msgs); onyx_sempass(&sp_state, root_file); @@ -152,6 +159,7 @@ i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) { return ONYX_COMPILER_PROGRESS_FAILED_SEMPASS; } + bh_printf("Creating WASM code\n"); compiler_state->wasm_mod = onyx_wasm_module_create(opts->allocator); onyx_wasm_module_compile(&compiler_state->wasm_mod, root_file); @@ -164,15 +172,17 @@ i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) { return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT; } + bh_printf("Writing WASM to %s\n", output_file.filename); onyx_wasm_module_write_to_file(&compiler_state->wasm_mod, output_file); return ONYX_COMPILER_PROGRESS_SUCCESS; } void compiler_state_free(CompilerState* cs) { + // NOTE: There is a memory leak here because the token's aren't freed + bh_arena_free(&cs->ast_arena); bh_arena_free(&cs->msg_arena); - bh_arena_free(&cs->token_arena); bh_arena_free(&cs->sp_arena); bh_table_free(cs->loaded_files); onyx_wasm_module_free(&cs->wasm_mod); diff --git a/src/onyxlex.c b/src/onyxlex.c index 6c969bfa..9b0c68f9 100644 --- a/src/onyxlex.c +++ b/src/onyxlex.c @@ -20,6 +20,7 @@ static const char* onyx_token_type_names[] = { "proc", //"TOKEN_TYPE_KEYWORD_PROC", "global", //"TOKEN_TYPE_KEYWORD_GLOBAL", "as", //"TOKEN_TYPE_KEYWORD_CAST", + "while", //"TOKEN_TYPE_KEYWORD_WHILE", "->", //"TOKEN_TYPE_RIGHT_ARROW", "<-", //"TOKEN_TYPE_LEFT_ARROW", @@ -55,6 +56,8 @@ static const char* onyx_token_type_names[] = { "TOKEN_TYPE_SYMBOL", "TOKEN_TYPE_LITERAL_STRING", "TOKEN_TYPE_LITERAL_NUMERIC", + "true", + "false", "TOKEN_TYPE_COUNT" }; @@ -141,7 +144,7 @@ OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) { LITERAL_TOKEN("export", 1, TOKEN_TYPE_KEYWORD_EXPORT); LITERAL_TOKEN("use", 1, TOKEN_TYPE_KEYWORD_USE); LITERAL_TOKEN("if", 1, TOKEN_TYPE_KEYWORD_IF); - LITERAL_TOKEN("elseif", 1, TOKEN_TYPE_KEYWORD_ELSEIF); + LITERAL_TOKEN("elseif", 1, TOKEN_TYPE_KEYWORD_ELSEIF); LITERAL_TOKEN("else", 1, TOKEN_TYPE_KEYWORD_ELSE); LITERAL_TOKEN("foreign", 1, TOKEN_TYPE_KEYWORD_FOREIGN); LITERAL_TOKEN("for", 1, TOKEN_TYPE_KEYWORD_FOR); @@ -150,6 +153,9 @@ OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) { LITERAL_TOKEN("proc", 1, TOKEN_TYPE_KEYWORD_PROC); LITERAL_TOKEN("global", 1, TOKEN_TYPE_KEYWORD_GLOBAL); LITERAL_TOKEN("as", 1, TOKEN_TYPE_KEYWORD_CAST); + LITERAL_TOKEN("while", 1, TOKEN_TYPE_KEYWORD_WHILE); + LITERAL_TOKEN("true", 1, TOKEN_TYPE_LITERAL_BOOL_TRUE); + LITERAL_TOKEN("false", 1, TOKEN_TYPE_LITERAL_BOOL_FALSE); LITERAL_TOKEN("->", 0, TOKEN_TYPE_RIGHT_ARROW); LITERAL_TOKEN("<-", 0, TOKEN_TYPE_RIGHT_ARROW); LITERAL_TOKEN("<=", 0, TOKEN_TYPE_SYM_LESS_EQUAL); diff --git a/src/onyxparser.c b/src/onyxparser.c index 743333fd..9a7dbd51 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -39,7 +39,7 @@ static const char* ast_node_names[] = { "NOT", "IF", - "LOOP", + "WHILE", "ONYX_AST_NODE_KIND_COUNT", }; @@ -48,7 +48,7 @@ struct OnyxTypeInfo builtin_types[] = { { ONYX_TYPE_INFO_KIND_UNKNOWN, 0, "unknown" }, { ONYX_TYPE_INFO_KIND_VOID, 0, "void", 0, 0, 0, 0, 1 }, - { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 0, 0, 1, 1 }, + { ONYX_TYPE_INFO_KIND_BOOL, 1, "bool", 0, 1, 0, 1, 1 }, { ONYX_TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0, 1 }, { ONYX_TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0, 1 }, @@ -79,6 +79,7 @@ static OnyxAstNode* parse_factor(OnyxParser* parser); static OnyxAstNode* parse_bin_op(OnyxParser* parser, OnyxAstNode* left); static OnyxAstNode* parse_expression(OnyxParser* parser); static OnyxAstNodeIf* parse_if_stmt(OnyxParser* parser); +static OnyxAstNodeWhile* parse_while_stmt(OnyxParser* parser); static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret); static OnyxAstNode* parse_return_statement(OnyxParser* parser); static OnyxAstNodeBlock* parse_block(OnyxParser* parser); @@ -249,6 +250,24 @@ static OnyxAstNode* parse_factor(OnyxParser* parser) { case TOKEN_TYPE_LITERAL_NUMERIC: return (OnyxAstNode *) parse_numeric_literal(parser); + case TOKEN_TYPE_LITERAL_BOOL_TRUE: + { + OnyxAstNodeNumLit* bool_node = (OnyxAstNodeNumLit *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LITERAL); + bool_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_BOOL]; + bool_node->token = expect(parser, TOKEN_TYPE_LITERAL_BOOL_TRUE); + bool_node->value.i = 1; + return (OnyxAstNode *) bool_node; + } + + case TOKEN_TYPE_LITERAL_BOOL_FALSE: + { + OnyxAstNodeNumLit* bool_node = (OnyxAstNodeNumLit *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LITERAL); + bool_node->type = &builtin_types[ONYX_TYPE_INFO_KIND_BOOL]; + bool_node->token = expect(parser, TOKEN_TYPE_LITERAL_BOOL_FALSE); + bool_node->value.i = 0; + return (OnyxAstNode *) bool_node; + } + default: onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN, @@ -392,6 +411,20 @@ static OnyxAstNodeIf* parse_if_stmt(OnyxParser* parser) { return root_if; } +static OnyxAstNodeWhile* parse_while_stmt(OnyxParser* parser) { + OnyxToken* while_token = expect(parser, TOKEN_TYPE_KEYWORD_WHILE); + + OnyxAstNode* cond = parse_expression(parser); + OnyxAstNodeBlock* body = parse_block(parser); + + OnyxAstNodeWhile* while_node = (OnyxAstNodeWhile *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_WHILE); + while_node->token = while_token; + while_node->cond = cond; + while_node->body = body; + + return while_node; +} + // Returns 1 if the symbol was consumed. Returns 0 otherwise // ret is set to the statement to insert static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret) { @@ -520,6 +553,11 @@ static OnyxAstNode* parse_statement(OnyxParser* parser) { retval = (OnyxAstNode *) parse_if_stmt(parser); break; + case TOKEN_TYPE_KEYWORD_WHILE: + needs_semicolon = 0; + retval = (OnyxAstNode *) parse_while_stmt(parser); + break; + default: break; } @@ -619,6 +657,7 @@ static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) { curr_param = (OnyxAstNodeParam *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM); curr_param->token = symbol; curr_param->type = parse_type(parser); + curr_param->flags |= ONYX_AST_FLAG_CONST; if (first_param == NULL) first_param = curr_param; diff --git a/src/onyxsempass.c b/src/onyxsempass.c index d86a3090..9ce783a8 100644 --- a/src/onyxsempass.c +++ b/src/onyxsempass.c @@ -65,6 +65,9 @@ static void collapse_scopes(OnyxAstNodeFile* root_node) { if (walker->kind == ONYX_AST_NODE_KIND_BLOCK) { bh_arr_push(traversal_queue, (OnyxAstNodeBlock *) walker); + } else if (walker->kind == ONYX_AST_NODE_KIND_WHILE) { + bh_arr_push(traversal_queue, walker->as_while.body); + } else if (walker->kind == ONYX_AST_NODE_KIND_IF) { if (walker->as_if.true_block) bh_arr_push(traversal_queue, (OnyxAstNodeBlock *) walker->as_if.true_block); diff --git a/src/onyxsymres.c b/src/onyxsymres.c index cfe7f19d..a03dd614 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -13,6 +13,7 @@ static void symres_expression(OnyxSemPassState* state, OnyxAstNode** expr); static void symres_assignment(OnyxSemPassState* state, OnyxAstNode* assign); static void symres_return(OnyxSemPassState* state, OnyxAstNode* ret); static void symres_if(OnyxSemPassState* state, OnyxAstNodeIf* ifnode); +static void symres_while(OnyxSemPassState* state, OnyxAstNodeWhile* whilenode); static void symres_statement_chain(OnyxSemPassState* state, OnyxAstNode* walker, OnyxAstNode** trailer); static b32 symres_statement(OnyxSemPassState* state, OnyxAstNode* stmt); static void symres_block(OnyxSemPassState* state, OnyxAstNodeBlock* block); @@ -212,6 +213,11 @@ static void symres_if(OnyxSemPassState* state, OnyxAstNodeIf* ifnode) { } } +static void symres_while(OnyxSemPassState* state, OnyxAstNodeWhile* whilenode) { + symres_expression(state, &whilenode->cond); + symres_block(state, whilenode->body); +} + // NOTE: Returns 1 if the statment should be removed static b32 symres_statement(OnyxSemPassState* state, OnyxAstNode* stmt) { switch (stmt->kind) { @@ -219,6 +225,7 @@ static b32 symres_statement(OnyxSemPassState* state, OnyxAstNode* stmt) { case ONYX_AST_NODE_KIND_ASSIGNMENT: symres_assignment(state, stmt); return 0; case ONYX_AST_NODE_KIND_RETURN: symres_return(state, stmt); return 0; case ONYX_AST_NODE_KIND_IF: symres_if(state, &stmt->as_if); return 0; + case ONYX_AST_NODE_KIND_WHILE: symres_while(state, &stmt->as_while); return 0; case ONYX_AST_NODE_KIND_CALL: symres_call(state, stmt); return 0; case ONYX_AST_NODE_KIND_ARGUMENT: symres_expression(state, (OnyxAstNode **) &stmt->left); return 0; case ONYX_AST_NODE_KIND_BLOCK: symres_block(state, &stmt->as_block); return 0; diff --git a/src/onyxtypecheck.c b/src/onyxtypecheck.c index 6949bf57..86110e5f 100644 --- a/src/onyxtypecheck.c +++ b/src/onyxtypecheck.c @@ -8,6 +8,7 @@ static void typecheck_statement(OnyxSemPassState* state, OnyxAstNode* stmt); static void typecheck_assignment(OnyxSemPassState* state, OnyxAstNode* assign); static void typecheck_return(OnyxSemPassState* state, OnyxAstNode* retnode); static void typecheck_if(OnyxSemPassState* state, OnyxAstNodeIf* ifnode); +static void typecheck_while(OnyxSemPassState* state, OnyxAstNodeWhile* whilenode); static void typecheck_call(OnyxSemPassState* state, OnyxAstNodeCall* call); static void typecheck_expression(OnyxSemPassState* state, OnyxAstNode* expr); @@ -73,11 +74,11 @@ static void typecheck_return(OnyxSemPassState* state, OnyxAstNode* retnode) { static void typecheck_if(OnyxSemPassState* state, OnyxAstNodeIf* ifnode) { typecheck_expression(state, ifnode->cond); - if (ifnode->cond->type != &builtin_types[ONYX_TYPE_INFO_KIND_INT32]) { + if (ifnode->cond->type != &builtin_types[ONYX_TYPE_INFO_KIND_BOOL]) { onyx_message_add(state->msgs, ONYX_MESSAGE_TYPE_LITERAL, ifnode->cond->token->pos, - "expected integer type for condition"); + "expected boolean type for condition"); return; } @@ -85,6 +86,19 @@ static void typecheck_if(OnyxSemPassState* state, OnyxAstNodeIf* ifnode) { if (ifnode->false_block) typecheck_statement(state, ifnode->false_block); } +static void typecheck_while(OnyxSemPassState* state, OnyxAstNodeWhile* whilenode) { + typecheck_expression(state, whilenode->cond); + if (whilenode->cond->type != &builtin_types[ONYX_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, OnyxAstNodeCall* call) { OnyxAstNodeFuncDef* callee = (OnyxAstNodeFuncDef *) call->callee; @@ -188,7 +202,13 @@ static void typecheck_expression(OnyxSemPassState* state, OnyxAstNode* expr) { return; } - expr->type = expr->left->type; + if (expr->kind >= ONYX_AST_NODE_KIND_EQUAL + && expr->kind <= ONYX_AST_NODE_KIND_GREATER_EQUAL) { + expr->type = &builtin_types[ONYX_TYPE_INFO_KIND_BOOL]; + } else { + expr->type = expr->left->type; + } + break; case ONYX_AST_NODE_KIND_NEGATE: @@ -248,6 +268,7 @@ static void typecheck_statement(OnyxSemPassState* state, OnyxAstNode* stmt) { case ONYX_AST_NODE_KIND_ASSIGNMENT: typecheck_assignment(state, stmt); break; case ONYX_AST_NODE_KIND_RETURN: typecheck_return(state, stmt); break; case ONYX_AST_NODE_KIND_IF: typecheck_if(state, &stmt->as_if); break; + case ONYX_AST_NODE_KIND_WHILE: typecheck_while(state, &stmt->as_while); break; case ONYX_AST_NODE_KIND_CALL: typecheck_call(state, &stmt->as_call); break; case ONYX_AST_NODE_KIND_BLOCK: typecheck_block(state, &stmt->as_block); break; diff --git a/src/onyxwasm.c b/src/onyxwasm.c index 2eef58d7..a9c6d22a 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -214,6 +214,7 @@ static void compile_statement(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* static void compile_assign_lval(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* lval); static void compile_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* assign); static void compile_if(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeIf* if_node); +static void compile_while(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeWhile* while_node); static void compile_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* expr); static void compile_cast(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* cast); static void compile_return(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* ret); @@ -244,8 +245,10 @@ static void compile_statement(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* case ONYX_AST_NODE_KIND_RETURN: compile_return(mod, func, stmt); break; case ONYX_AST_NODE_KIND_ASSIGNMENT: compile_assignment(mod, func, stmt); break; case ONYX_AST_NODE_KIND_IF: compile_if(mod, func, (OnyxAstNodeIf *) stmt); break; + case ONYX_AST_NODE_KIND_WHILE: compile_while(mod, func, (OnyxAstNodeWhile *) stmt); break; case ONYX_AST_NODE_KIND_CALL: compile_expression(mod, func, stmt); break; case ONYX_AST_NODE_KIND_BLOCK: compile_block(mod, func, (OnyxAstNodeBlock *) stmt); break; + default: break; } } @@ -295,6 +298,24 @@ static void compile_if(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeIf* if_no bh_arr_push(func->code, ((WasmInstruction){ WI_IF_END, 0x00 })); } +static void compile_while(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeWhile* while_node) { + bh_arr_push(func->code, ((WasmInstruction){ WI_BLOCK_START, 0x40 })); + bh_arr_push(func->code, ((WasmInstruction){ WI_LOOP_START, 0x40 })); + + compile_expression(mod, func, while_node->cond); + bh_arr_push(func->code, ((WasmInstruction){ WI_I32_EQZ, 0x00 })); + bh_arr_push(func->code, ((WasmInstruction){ WI_COND_JUMP, 0x01 })); + + forll (OnyxAstNode, stmt, while_node->body->body, next) { + compile_statement(mod, func, stmt); + } + + bh_arr_push(func->code, ((WasmInstruction){ WI_JUMP, 0x00 })); + + bh_arr_push(func->code, ((WasmInstruction){ WI_LOOP_END, 0x00 })); + bh_arr_push(func->code, ((WasmInstruction){ WI_BLOCK_END, 0x00 })); +} + static void compile_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* assign) { compile_expression(mod, func, assign->right); compile_assign_lval(mod, func, assign->left); @@ -304,7 +325,8 @@ static void compile_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* case ONYX_AST_NODE_KIND_##ast_binop: \ { \ WasmInstructionType instr_type; \ - switch (expr->type->kind) { \ + switch (expr->left->type->kind) { \ + case ONYX_TYPE_INFO_KIND_BOOL: \ case ONYX_TYPE_INFO_KIND_UINT32: \ case ONYX_TYPE_INFO_KIND_INT32: instr_type = WI_I32_##wasm_binop; break; \ case ONYX_TYPE_INFO_KIND_UINT64: \ @@ -324,15 +346,16 @@ static void compile_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* case ONYX_AST_NODE_KIND_##ast_binop: \ { \ WasmInstructionType instr_type; \ - switch (expr->type->kind) { \ + switch (expr->left->type->kind) { \ + case ONYX_TYPE_INFO_KIND_BOOL: \ case ONYX_TYPE_INFO_KIND_UINT32: \ case ONYX_TYPE_INFO_KIND_INT32: \ - if (expr->type->is_unsigned) instr_type = WI_I32_##wasm_binop##_U; \ + if (expr->left->type->is_unsigned) instr_type = WI_I32_##wasm_binop##_U; \ else instr_type = WI_I32_##wasm_binop##_S; \ break; \ case ONYX_TYPE_INFO_KIND_UINT64: \ case ONYX_TYPE_INFO_KIND_INT64: \ - if (expr->type->is_unsigned) instr_type = WI_I64_##wasm_binop##_U; \ + if (expr->left->type->is_unsigned) instr_type = WI_I64_##wasm_binop##_U; \ else instr_type = WI_I64_##wasm_binop##_S; \ break; \ case ONYX_TYPE_INFO_KIND_FLOAT32: instr_type = WI_F32_##wasm_binop; break; \ @@ -1000,6 +1023,9 @@ static void output_instruction(WasmInstruction* instr, bh_buffer* buff) { case WI_LOCAL_SET: case WI_CALL: case WI_BLOCK_START: + case WI_LOOP_START: + case WI_JUMP: + case WI_COND_JUMP: case WI_IF_START: leb = uint_to_uleb128((u64) instr->data.i1, &leb_len); bh_buffer_append(buff, leb, leb_len);