Added boolean types and while statements
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 25 Jun 2020 19:35:45 +0000 (14:35 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 25 Jun 2020 19:35:45 +0000 (14:35 -0500)
Almost a very usable language

13 files changed:
docs/plan
include/onyxlex.h
include/onyxparser.h
onyx
progs/other.onyx
progs/test.onyx
src/onyx.c
src/onyxlex.c
src/onyxparser.c
src/onyxsempass.c
src/onyxsymres.c
src/onyxtypecheck.c
src/onyxwasm.c

index 649fc86b38fa45e742dc5ed81e689e6ab22c14a0..c7f8e80364a64d2ec9f8c49c2221d5f2ba6f8cbb 100644 (file)
--- 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).
 
index 7c502c7694fca4da5e64762eda848f9f206bd3a5..29c1c73a8eed42b4574c2eb2df45dc7d2ae39d29 100644 (file)
@@ -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;
index 27b903a38dc80eda4c054d0e99215c05b80dd0d0..f9dceade48828efa150e6134f55d95cff3d404a6 100644 (file)
@@ -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 0a10774a060fda1613f33dedddd8a4a609b13473..e335e3561697b6305fb1080a3daecb34eab417b5 100755 (executable)
Binary files a/onyx and b/onyx differ
index 1dadc9e702a1e6e093b8ac465daeb29b81e069a8..b699b444f2241a80cd4f5214f48d2c453d89405f 100644 (file)
@@ -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;
 }
index ad83c20a1590390d8ccb0a9c10cdc537e6970a24..7b492349d9a6dc07b10362d39b290814a4cfcace 100644 (file)
@@ -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);
+    }
 }
index dee3930b4e642032feb83aad85164e91c7ec6f7f..a46145285c363688eec9061ce3bd7fc8b95ddd14 100644 (file)
 
 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 <target file>] [--help] <input files>\n"
+    "\n"
+    "   -o <target_file>        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);
index 6c969bfaa6d57c1f2b8b8a8b1d3148ee142c0ffe..9b0c68f9de88541fcca447f7dd62eb208c259e59 100644 (file)
@@ -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);
index 743333fd472a77d4a56fab63428bfda2e07f925c..9a7dbd51f557a0e7b31c7c3a784ecd3cfe949152 100644 (file)
@@ -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;
 
index d86a30900135550398f30b6684f31978fa815ac7..9ce783a8bdda3aa26e5fbd96649074111a3f2e9d 100644 (file)
@@ -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);
index cfe7f19ddbc67ccca333524ef9d1bd1df41ada8c..a03dd61468441140577b71dc88d67984fc54c5d8 100644 (file)
@@ -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;
index 6949bf57b68c6ceb84d17c40ac86b917613862db..86110e5f75b1c650288c40b572cbf52ffc80da37 100644 (file)
@@ -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;
 
index 2eef58d7e15063957fd6f021d919261db44f3593..a9c6d22a5675a1b3ae0effcb9cd80af56b6e0856 100644 (file)
@@ -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);