Added basic for loops
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 19 Jul 2020 19:39:56 +0000 (14:39 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 19 Jul 2020 19:39:56 +0000 (14:39 -0500)
Just syntactic sugar for certain kinds of while loops

include/onyxastnodes.h
include/onyxlex.h
onyx
progs/arrays.onyx
progs/print_funcs.onyx
src/onyxchecker.c
src/onyxlex.c
src/onyxparser.c
src/onyxsymres.c
src/onyxutils.c
src/onyxwasm.c

index 72dbe704fc231c500cf6c907a37aa0e6b3f29a75..25791afb9d39b687a03315c59b23279b7182a453 100644 (file)
@@ -25,6 +25,7 @@ typedef struct AstReturn AstReturn;
 
 typedef struct AstBlock AstBlock;
 typedef struct AstIf AstIf;
+typedef struct AstFor AstFor;
 typedef struct AstWhile AstWhile;
 
 typedef struct AstType AstType;
@@ -84,6 +85,7 @@ typedef enum AstKind {
     Ast_Kind_Array_Access,
 
     Ast_Kind_If,
+    Ast_Kind_For,
     Ast_Kind_While,
     Ast_Kind_Break,
     Ast_Kind_Continue,
@@ -205,7 +207,7 @@ struct AstLocal         { AstTyped_base; AstLocal *prev_local; };
 struct AstCall          { AstTyped_base; AstArgument *arguments; u64 arg_count; AstNode *callee; };
 struct AstIntrinsicCall { AstTyped_base; AstArgument *arguments; u64 arg_count; OnyxIntrinsic intrinsic; };
 struct AstArgument      { AstTyped_base; AstTyped *value; };
-struct AstAddressOf     { AstTyped_base; AstTyped *expr; }; 
+struct AstAddressOf     { AstTyped_base; AstTyped *expr; };
 struct AstDereference   { AstTyped_base; AstTyped *expr; };
 struct AstArrayAccess   { AstTyped_base; AstTyped *addr; AstTyped *expr; u64 elem_size; };
 
@@ -215,6 +217,19 @@ struct AstReturn        { AstNode_base;  AstTyped* expr; };
 // Structure Nodes
 struct AstBlock         { AstNode_base;  AstNode *body; Scope *scope; };
 struct AstWhile         { AstNode_base;  AstTyped *cond; AstNode *stmt; };
+struct AstFor           {
+    AstNode_base;
+
+    // NOTE: Stores the iteration variable
+    Scope *scope;
+
+    // NOTE: Local defining the iteration variable
+    AstLocal* var;
+
+    AstTyped *start, *end, *step;
+
+    AstNode *stmt;
+};
 struct AstIf {
     AstNode_base;
     AstTyped *cond;
index 24168862ebf78088dbc67a92f033a94289728911..c54fdbbc5d2cb349ed18854fc06a37f15244c36f 100644 (file)
@@ -21,37 +21,38 @@ typedef enum TokenType {
     Token_Type_Keyword_Proc         = 267,
     Token_Type_Keyword_Cast         = 268,
     Token_Type_Keyword_While        = 269,
-    Token_Type_Keyword_Break        = 270,
-    Token_Type_Keyword_Continue     = 271,
-
-    Token_Type_Right_Arrow          = 272,
-    Token_Type_Left_Arrow           = 273,
-    Token_Type_Empty_Block          = 274,
-
-    Token_Type_Greater_Equal        = 275,
-    Token_Type_Less_Equal           = 276,
-    Token_Type_Equal_Equal          = 277,
-    Token_Type_Not_Equal            = 278,
-    Token_Type_Plus_Equal           = 279,
-    Token_Type_Minus_Equal          = 280,
-    Token_Type_Star_Equal           = 281,
-    Token_Type_Fslash_Equal         = 282,
-    Token_Type_Percent_Equal        = 283,
-
-    Token_Type_Symbol               = 284,
-    Token_Type_Literal_String       = 285,
-    Token_Type_Literal_Numeric      = 286,
-    Token_Type_Literal_True         = 287,
-    Token_Type_Literal_False        = 288,
-
-    Token_Type_Count                = 289,
+    Token_Type_Keyword_For          = 270,
+    Token_Type_Keyword_Break        = 271,
+    Token_Type_Keyword_Continue     = 272,
+
+    Token_Type_Right_Arrow          = 273,
+    Token_Type_Left_Arrow           = 274,
+    Token_Type_Empty_Block          = 275,
+
+    Token_Type_Greater_Equal        = 276,
+    Token_Type_Less_Equal           = 277,
+    Token_Type_Equal_Equal          = 278,
+    Token_Type_Not_Equal            = 279,
+    Token_Type_Plus_Equal           = 280,
+    Token_Type_Minus_Equal          = 281,
+    Token_Type_Star_Equal           = 282,
+    Token_Type_Fslash_Equal         = 283,
+    Token_Type_Percent_Equal        = 284,
+
+    Token_Type_Symbol               = 285,
+    Token_Type_Literal_String       = 286,
+    Token_Type_Literal_Numeric      = 287,
+    Token_Type_Literal_True         = 288,
+    Token_Type_Literal_False        = 289,
+
+    Token_Type_Count                = 290,
 } TokenType;
 
 typedef struct OnyxFilePos {
     const char* filename;
     char* line_start;
     u32 line;
-    
+
     // NOTE: This assumes that no line is no longer than 2^16 chars
     u16 column, length;
 } OnyxFilePos;
diff --git a/onyx b/onyx
index b87f677f797237435b562b046eee5ccb7c096b6a..c238b47f754bf11bc6e08b7fae2c9154cffc2db5 100755 (executable)
Binary files a/onyx and b/onyx differ
index f2f61384e0cdd8c76a7ecccb3ac4eabf8687c391..eab2ac0319d426456d8e9a5a74b70d2f0cc07b89 100644 (file)
@@ -39,19 +39,26 @@ sort :: proc (src: ^i32, len: i32) {
     }
 }
 
-other_str :: "I can't believe this \n actually fricken worked!";
+for_test :: proc {
+    // 0 to 9
+    for i: 0, 10 print(i);
+}
 
-str_test :: proc {
+str_test :: proc #export "main" {
     hello_str :: "Hello World!";
 
     // Address of and dereference cancel each other out
     print(^*hello_str);
 
-    print("This is a test"[10] as i32);
+    print(hello_str, 5);
+
+    for i: 0, 10, 2 print(i);
+
+    for y: 0, 5 for x: 0, 5 print(x + y * 5);
 }
 
 // Don't need to bind this function to a symbol
-proc #export "main" {
+proc #export "main2" {
     print(min(10.0, 12.0));
 
     global_arr = 128 as ^i32;
index 3aadfc9dc39d12a83061b3cfd15a7a44cef00a99..e232cb25c22cd490eed7ae8fccb86cbd434e2589 100644 (file)
@@ -45,6 +45,10 @@ print_str :: proc (str: ^u8) {
     }
 }
 
+print_str_len :: proc (str: ^u8, len: i32) {
+    for i: 0, len print(str[i] as i32);
+}
+
 print :: proc #overloaded {
     print_bool,
     print_i32,
@@ -58,4 +62,5 @@ print :: proc #overloaded {
     print_f64arr,
 
     print_str,
+    print_str_len,
 }
index 05b17017d25d36e7f3efc166aa9067e389a44eea..dd521cf1f6fe8dfcda181339a0bd00bde3f0e1ff 100644 (file)
@@ -10,6 +10,7 @@ CHECK(statement, AstNode* stmt);
 CHECK(return, AstReturn* retnode);
 CHECK(if, AstIf* ifnode);
 CHECK(while, AstWhile* whilenode);
+CHECK(for, AstFor* fornode);
 CHECK(call, AstCall* call);
 CHECK(binaryop, AstBinaryOp* binop);
 CHECK(expression, AstTyped* expr);
@@ -77,6 +78,31 @@ CHECK(while, AstWhile* whilenode) {
     return check_statement(whilenode->stmt);
 }
 
+CHECK(for, AstFor* fornode) {
+    check_expression(fornode->start);
+    check_expression(fornode->end);
+    if (fornode->step) check_expression(fornode->step);
+
+    // HACK
+    if (fornode->start->type != &basic_types[Basic_Kind_I32]) {
+        onyx_message_add(Msg_Type_Literal,
+                fornode->start->token->pos,
+                "expected expression of type i32 for start");
+        return 1;
+    }
+
+    if (fornode->end->type != &basic_types[Basic_Kind_I32]) {
+        onyx_message_add(Msg_Type_Literal,
+                fornode->end->token->pos,
+                "expected expression of type i32 for end");
+        return 1;
+    }
+
+    check_statement(fornode->stmt);
+
+    return 0;
+}
+
 static AstTyped* match_overloaded_function(AstCall* call, AstOverloadedFunction* ofunc) {
     bh_arr_each(AstTyped *, node, ofunc->overloads) {
         AstFunction* overload = (AstFunction *) *node;
@@ -503,6 +529,7 @@ CHECK(statement, AstNode* stmt) {
         case Ast_Kind_Return:     return check_return((AstReturn *) stmt);
         case Ast_Kind_If:         return check_if((AstIf *) stmt);
         case Ast_Kind_While:      return check_while((AstWhile *) stmt);
+        case Ast_Kind_For:        return check_for((AstFor *) stmt);
         case Ast_Kind_Call:       return check_call((AstCall *) stmt);
         case Ast_Kind_Block:      return check_block((AstBlock *) stmt);
 
index 8a1da57b1b3ae13755e298201646159be489f1fa..afc0b6c5b36781777f8509a6fbdfa83a4785434c 100644 (file)
@@ -138,6 +138,7 @@ OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) {
     LITERAL_TOKEN("proc",       1, Token_Type_Keyword_Proc);
     LITERAL_TOKEN("as",         1, Token_Type_Keyword_Cast);
     LITERAL_TOKEN("while",      1, Token_Type_Keyword_While);
+    LITERAL_TOKEN("for",        1, Token_Type_Keyword_For);
     LITERAL_TOKEN("break",      1, Token_Type_Keyword_Break);
     LITERAL_TOKEN("continue",   1, Token_Type_Keyword_Continue);
     LITERAL_TOKEN("true",       1, Token_Type_Literal_True);
index 0bee666a21f72af782caa4e5fccc10b530f51ca8..097af9cbadb916aef4ad09e35f5f3514a5bc5b83 100644 (file)
@@ -33,6 +33,7 @@ static AstTyped*    parse_factor(OnyxParser* parser);
 static AstTyped*    parse_expression(OnyxParser* parser);
 static AstIf*       parse_if_stmt(OnyxParser* parser);
 static AstWhile*    parse_while_stmt(OnyxParser* parser);
+static AstFor*      parse_for_stmt(OnyxParser* parser);
 static b32          parse_symbol_declaration(OnyxParser* parser, AstNode** ret);
 static AstReturn*   parse_return_statement(OnyxParser* parser);
 static AstBlock*    parse_block(OnyxParser* parser);
@@ -484,6 +485,31 @@ static AstWhile* parse_while_stmt(OnyxParser* parser) {
     return while_node;
 }
 
+static AstFor* parse_for_stmt(OnyxParser* parser) {
+    AstFor* for_node = make_node(AstFor, Ast_Kind_For);
+    for_node->token = expect_token(parser, Token_Type_Keyword_For);
+
+    AstLocal* var_node = make_node(AstLocal, Ast_Kind_Local);
+    var_node->token = expect_token(parser, Token_Type_Symbol);
+    var_node->type_node = (AstType *) &basic_type_i32;
+
+    for_node->var = var_node;
+
+    expect_token(parser, ':');
+    for_node->start = parse_expression(parser);
+    expect_token(parser, ',');
+    for_node->end = parse_expression(parser);
+
+    if (parser->curr->type == ',') {
+        consume_token(parser);
+        for_node->step = parse_expression(parser);
+    }
+
+    for_node->stmt = parse_statement(parser);
+
+    return for_node;
+}
+
 // Returns 1 if the symbol was consumed. Returns 0 otherwise
 // ret is set to the statement to insert
 // <symbol> : <type> = <expr>
@@ -605,6 +631,11 @@ static AstNode* parse_statement(OnyxParser* parser) {
             retval = (AstNode *) parse_while_stmt(parser);
             break;
 
+        case Token_Type_Keyword_For:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_for_stmt(parser);
+            break;
+
         case Token_Type_Keyword_Break:
             retval = make_node(AstNode, Ast_Kind_Break);
             retval->token = expect_token(parser, Token_Type_Keyword_Break);
index 00b81658a5a15e460bad2948a0806118c307c43a..f504daa3d6b450103cfac7efb109eeada849230c 100644 (file)
@@ -14,6 +14,7 @@ static void symres_expression(AstTyped** expr);
 static void symres_return(AstReturn* ret);
 static void symres_if(AstIf* ifnode);
 static void symres_while(AstWhile* whilenode);
+static void symres_for(AstFor* fornode);
 static void symres_statement_chain(AstNode* walker, AstNode** trailer);
 static b32  symres_statement(AstNode* stmt);
 static void symres_block(AstBlock* block);
@@ -214,6 +215,21 @@ static void symres_while(AstWhile* whilenode) {
     symres_statement(whilenode->stmt);
 }
 
+static void symres_for(AstFor* fornode) {
+    fornode->scope = scope_create(semstate.node_allocator, semstate.curr_scope);
+    scope_enter(fornode->scope);
+
+    symbol_introduce(fornode->var->token, (AstNode *) fornode->var);
+
+    symres_expression(&fornode->start);
+    symres_expression(&fornode->end);
+    if (fornode->step) symres_expression(&fornode->step);
+
+    symres_statement(fornode->stmt);
+
+    scope_leave();
+}
+
 // NOTE: Returns 1 if the statment should be removed
 static b32 symres_statement(AstNode* stmt) {
     switch (stmt->kind) {
@@ -221,6 +237,7 @@ static b32 symres_statement(AstNode* stmt) {
         case Ast_Kind_Return:     symres_return((AstReturn *) stmt);                return 0;
         case Ast_Kind_If:         symres_if((AstIf *) stmt);                        return 0;
         case Ast_Kind_While:      symres_while((AstWhile *) stmt);                  return 0;
+        case Ast_Kind_For:        symres_for((AstFor *) stmt);                      return 0;
         case Ast_Kind_Call:       symres_call((AstCall *) stmt);                    return 0;
         case Ast_Kind_Argument:   symres_expression((AstTyped **) &((AstArgument *)stmt)->value); return 0;
         case Ast_Kind_Block:      symres_block((AstBlock *) stmt);                  return 0;
index 06ed78a540e63d5a879c705110d5dd403b7d31df..541accf5011b333a6a86fc32897a6cd72a563057 100644 (file)
@@ -43,6 +43,7 @@ static const char* ast_node_names[] = {
     "ARRAY_ACCESS",
 
     "IF",
+    "FOR",
     "WHILE",
     "BREAK",
     "CONTINUE",
index 536a0c8a205933bbeaf34989b5e32be540158f79..972db89f738e68d1d67a68bea3a871e09b39d1c2 100644 (file)
@@ -232,6 +232,7 @@ COMPILE_FUNC(statement,      AstNode* stmt);
 COMPILE_FUNC(assignment,     AstBinaryOp* assign);
 COMPILE_FUNC(if,             AstIf* if_node);
 COMPILE_FUNC(while,          AstWhile* while_node);
+COMPILE_FUNC(for,            AstFor* for_node);
 COMPILE_FUNC(binop,          AstBinaryOp* binop);
 COMPILE_FUNC(unaryop,        AstUnaryOp* unop);
 COMPILE_FUNC(call,           AstCall* call);
@@ -303,6 +304,7 @@ COMPILE_FUNC(statement, AstNode* stmt) {
         case Ast_Kind_Return:     compile_return(mod, &code, (AstReturn *) stmt); break;
         case Ast_Kind_If:         compile_if(mod, &code, (AstIf *) stmt); break;
         case Ast_Kind_While:      compile_while(mod, &code, (AstWhile *) stmt); break;
+        case Ast_Kind_For:        compile_for(mod, &code, (AstFor *) stmt); break;
         case Ast_Kind_Break:      compile_structured_jump(mod, &code, 0); break;
         case Ast_Kind_Continue:   compile_structured_jump(mod, &code, 1); break;
         case Ast_Kind_Block:      compile_block(mod, &code, (AstBlock *) stmt); break;
@@ -431,6 +433,52 @@ COMPILE_FUNC(while, AstWhile* while_node) {
     *pcode = code;
 }
 
+COMPILE_FUNC(for, AstFor* for_node) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    i32 it_idx = (i32) bh_imap_get(&mod->local_map, (u64) for_node->var);
+
+    compile_expression(mod, &code, for_node->start);
+    WID(WI_LOCAL_SET, it_idx);
+
+    WID(WI_BLOCK_START, 0x40);
+    WID(WI_LOOP_START, 0x40);
+
+    bh_arr_push(mod->structured_jump_target, 1);
+    bh_arr_push(mod->structured_jump_target, 2);
+
+    WID(WI_LOCAL_GET, it_idx);
+    compile_expression(mod, &code, for_node->end);
+    WI(WI_I32_GE_S);
+    WID(WI_COND_JUMP, 0x01);
+
+    if (for_node->stmt->kind == Ast_Kind_Block) {
+        forll (AstNode, stmt, ((AstBlock *) for_node->stmt)->body, next) {
+            compile_statement(mod, &code, stmt);
+        }
+    } else {
+        compile_statement(mod, &code, for_node->stmt);
+    }
+
+    if (for_node->step == NULL)
+        WID(WI_I32_CONST, 0x01);
+    else
+        compile_expression(mod, &code, for_node->step);
+    WID(WI_LOCAL_GET, it_idx);
+    WI(WI_I32_ADD);
+    WID(WI_LOCAL_SET, it_idx);
+
+    bh_arr_pop(mod->structured_jump_target);
+    bh_arr_pop(mod->structured_jump_target);
+
+    WID(WI_JUMP, 0x00);
+
+    WI(WI_LOOP_END);
+    WI(WI_BLOCK_END);
+
+    *pcode = code;
+}
+
 // NOTE: These need to be in the same order as
 // the OnyxBinaryOp enum
 static const WasmInstructionType binop_map[][4] = {
@@ -1517,6 +1565,7 @@ static void output_instruction(WasmInstruction* instr, bh_buffer* buff) {
     switch (instr->type) {
         case WI_LOCAL_GET:
         case WI_LOCAL_SET:
+        case WI_LOCAL_TEE:
         case WI_GLOBAL_GET:
         case WI_GLOBAL_SET:
         case WI_CALL: