From: Brendan Hansen Date: Sun, 19 Jul 2020 19:39:56 +0000 (-0500) Subject: Added basic for loops X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=89cc1ba7c749004d1c574a4eb607cc5198d6e870;p=onyx.git Added basic for loops Just syntactic sugar for certain kinds of while loops --- diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index 72dbe704..25791afb 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -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; diff --git a/include/onyxlex.h b/include/onyxlex.h index 24168862..c54fdbbc 100644 --- a/include/onyxlex.h +++ b/include/onyxlex.h @@ -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 b87f677f..c238b47f 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/arrays.onyx b/progs/arrays.onyx index f2f61384..eab2ac03 100644 --- a/progs/arrays.onyx +++ b/progs/arrays.onyx @@ -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; diff --git a/progs/print_funcs.onyx b/progs/print_funcs.onyx index 3aadfc9d..e232cb25 100644 --- a/progs/print_funcs.onyx +++ b/progs/print_funcs.onyx @@ -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, } diff --git a/src/onyxchecker.c b/src/onyxchecker.c index 05b17017..dd521cf1 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -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); diff --git a/src/onyxlex.c b/src/onyxlex.c index 8a1da57b..afc0b6c5 100644 --- a/src/onyxlex.c +++ b/src/onyxlex.c @@ -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); diff --git a/src/onyxparser.c b/src/onyxparser.c index 0bee666a..097af9cb 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -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 // : = @@ -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); diff --git a/src/onyxsymres.c b/src/onyxsymres.c index 00b81658..f504daa3 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -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; diff --git a/src/onyxutils.c b/src/onyxutils.c index 06ed78a5..541accf5 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -43,6 +43,7 @@ static const char* ast_node_names[] = { "ARRAY_ACCESS", "IF", + "FOR", "WHILE", "BREAK", "CONTINUE", diff --git a/src/onyxwasm.c b/src/onyxwasm.c index 536a0c8a..972db89f 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -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: