From 92453369c5558feaaaa116fbc54968b087e1aeab Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Fri, 27 Jan 2017 17:43:42 +0000 Subject: [PATCH] Remove while loop and readd c-style for loops i.e. all loops are just `for` --- code/demo.odin | 41 ++++------ core/atomic.odin | 4 +- core/fmt.odin | 13 ++- core/hash.odin | 2 +- core/mem.odin | 5 +- core/os_windows.odin | 2 +- core/utf8.odin | 7 +- src/check_stmt.c | 36 +++++---- src/ir.c | 44 ++++++---- src/parser.c | 185 ++++++++++++++++++++++++++++--------------- src/tokenizer.c | 1 - 11 files changed, 201 insertions(+), 139 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index c019faa99..ece779514 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -168,25 +168,25 @@ special_expressions :: proc() { loops :: proc() { - // while loops - while true { + // The C-style for loop + for i := 0; i < 123; i += 1 { break; } - while x := 123; x < 124 { - x += 2; + for i := 0; i < 123; { + break; + } + for false { + break; + } + for { + break; } -/* - This only C-style for loop has now been removed - - for i := 0; i < 123; i += 1 { - } -*/ - for i in 0..<123 { + for i in 0..<123 { // 123 exclusive } - for i in 0..122 { + for i in 0..122 { // 122 inclusive } for val, idx in 12..<16 { @@ -214,26 +214,15 @@ loops :: proc() { } when false { - while i := 0; i < name.count { - r, size := utf8.decode_rune(name[i:]); - i += size; + for i, size := 0; i < name.count; i += size { + r: rune; + r, size = utf8.decode_rune(name[i:]); fmt.printf("%r\n", r); } } - // Emulate a C-style loop (not exactly the same though) - while x := 0; x < 10 { - defer x += 2; - - /* rest of the code */ - // If `break` is used, the `defer` is still called so it's not the same - // as a C-style for loop - } - - - procedure_overloading(); } diff --git a/core/atomic.odin b/core/atomic.odin index cc381300e..3be3c1d40 100644 --- a/core/atomic.odin +++ b/core/atomic.odin @@ -36,7 +36,7 @@ fetch_or :: proc(a: ^i32, operand: i32) -> i32 { spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default old_value := compare_exchange(a, 1, 0); counter := 0; - while old_value != 0 && (time_out < 0 || counter < time_out) { + for old_value != 0 && (time_out < 0 || counter < time_out) { counter += 1; yield_thread(); old_value = compare_exchange(a, 1, 0); @@ -80,7 +80,7 @@ fetch_or :: proc(a: ^i64, operand: i64) -> i64 { spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default old_value := compare_exchange(a, 1, 0); counter := 0; - while old_value != 0 && (time_out < 0 || counter < time_out) { + for old_value != 0 && (time_out < 0 || counter < time_out) { counter += 1; yield_thread(); old_value = compare_exchange(a, 1, 0); diff --git a/core/fmt.odin b/core/fmt.odin index f7c11015e..bd11d05e0 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -460,7 +460,7 @@ fmt_integer :: proc(fi: ^Fmt_Info, u: u64, base: int, signed: bool, digits: stri panic("fmt_integer: unknown base, whoops"); } - while b := cast(u64)base; u >= b { + for b := cast(u64)base; u >= b; { i -= 1; next := u / b; buf[i] = digits[u%b]; @@ -468,7 +468,7 @@ fmt_integer :: proc(fi: ^Fmt_Info, u: u64, base: int, signed: bool, digits: stri } i -= 1; buf[i] = digits[u]; - while i > 0 && prec > buf.count-i { + for i > 0 && prec > buf.count-i { i -= 1; buf[i] = '0'; } @@ -598,7 +598,7 @@ fmt_float :: proc(fi: ^Fmt_Info, v: f64, bit_size: int, verb: rune) { case fi.space: fill = ' '; } - while width > 0 { + for width > 0 { width -= 1; buffer_write_byte(fi.buf, fill); } @@ -885,11 +885,11 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ..any) -> int { end := fmt.count; arg_index := 0; was_prev_index := false; - while i := 0; i < end { + for i := 0; i < end; { fi = Fmt_Info{buf = b, good_arg_index = true}; prev_i := i; - while i < end && fmt[i] != '%' { + for i < end && fmt[i] != '%' { i += 1; } if i > prev_i { @@ -903,7 +903,7 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ..any) -> int { i += 1; - while i < end { + for ; i < end; i += 1 { skip_loop := false; c := fmt[i]; match c { @@ -925,7 +925,6 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ..any) -> int { if skip_loop { break; } - i += 1; } arg_index, i, was_prev_index = arg_number(^fi, arg_index, fmt, i, args.count); diff --git a/core/hash.odin b/core/hash.odin index 7348bef53..b2d6f0226 100644 --- a/core/hash.odin +++ b/core/hash.odin @@ -108,7 +108,7 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 { data := slice_ptr(cast(^u32)data_, len/size_of(u32)); i := 0; - while len >= 8 { + for len >= 8 { k1, k2: u32; k1 = data[i]; i += 1; k1 *= m; diff --git a/core/mem.odin b/core/mem.odin index 1ad80137d..5f1bf378b 100644 --- a/core/mem.odin +++ b/core/mem.odin @@ -75,14 +75,13 @@ allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: i header.size = size; ptr := cast(^int)(header+1); - while i := 0; cast(rawptr)ptr < data { + for i := 0; cast(rawptr)ptr < data; i += 1 { (ptr+i)^ = -1; - i += 1; } } allocation_header :: proc(data: rawptr) -> ^Allocation_Header { p := cast(^int)data; - while (p-1)^ == -1 { + for (p-1)^ == -1 { p = (p-1); } return cast(^Allocation_Header)p-1; diff --git a/core/os_windows.odin b/core/os_windows.odin index acfb02116..4127d6242 100644 --- a/core/os_windows.odin +++ b/core/os_windows.odin @@ -223,7 +223,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) { single_read_length: i32; total_read: i64; - while total_read < length { + for total_read < length { remaining := length - total_read; to_read: u32; MAX :: 1<<32-1; diff --git a/core/utf8.odin b/core/utf8.odin index 7730f94be..8720e1f20 100644 --- a/core/utf8.odin +++ b/core/utf8.odin @@ -143,8 +143,7 @@ valid_rune :: proc(r: rune) -> bool { valid_string :: proc(s: string) -> bool { n := s.count; - i := 0; - while i < n { + for i := 0; i < n; { si := s[i]; if si < RUNE_SELF { // ascii i += 1; @@ -178,8 +177,8 @@ valid_string :: proc(s: string) -> bool { rune_count :: proc(s: string) -> int { count := 0; n := s.count; - i := 0; - while i < n { + + for i := 0; i < n; { defer count += 1; si := s[i]; if si < RUNE_SELF { // ascii diff --git a/src/check_stmt.c b/src/check_stmt.c index fd8e22908..3a1fdf023 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -127,13 +127,13 @@ bool check_is_terminating(AstNode *node) { } case_end; - case_ast_node(ws, WhileStmt, node); - if (ws->cond != NULL && !check_has_break(ws->body, true)) { - return check_is_terminating(ws->body); + case_ast_node(fs, ForStmt, node); + if (!check_has_break(fs->body, true)) { + return check_is_terminating(fs->body); } case_end; - case_ast_node(rs, ForStmt, node); + case_ast_node(rs, RangeStmt, node); if (!check_has_break(rs->body, true)) { return check_is_terminating(rs->body); } @@ -566,27 +566,33 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { } case_end; - case_ast_node(ws, WhileStmt, node); + case_ast_node(fs, ForStmt, node); u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; check_open_scope(c, node); - if (ws->init != NULL) { - check_stmt(c, ws->init, 0); + if (fs->init != NULL) { + check_stmt(c, fs->init, 0); } - if (ws->cond) { - Operand operand = {Addressing_Invalid}; - check_expr(c, &operand, ws->cond); - if (operand.mode != Addressing_Invalid && - !is_type_boolean(operand.type)) { - error_node(ws->cond, "Non-boolean condition in `while` statement"); + if (fs->cond != NULL) { + Operand o = {Addressing_Invalid}; + check_expr(c, &o, fs->cond); + if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) { + error_node(fs->cond, "Non-boolean condition in `for` statement"); } } - check_stmt(c, ws->body, new_flags); + if (fs->post != NULL) { + check_stmt(c, fs->post, 0); + + if (fs->post->kind != AstNode_AssignStmt) { + error_node(fs->post, "`for` statement post statement must be an assignment"); + } + } + check_stmt(c, fs->body, new_flags); check_close_scope(c); case_end; - case_ast_node(rs, ForStmt, node); + case_ast_node(rs, RangeStmt, node); u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; check_open_scope(c, node); diff --git a/src/ir.c b/src/ir.c index 29e81c899..d2f423d4e 100644 --- a/src/ir.c +++ b/src/ir.c @@ -4470,44 +4470,54 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { proc->curr_block = done; case_end; - case_ast_node(ws, WhileStmt, node); - ir_emit_comment(proc, str_lit("WhileStmt")); - if (ws->init != NULL) { - irBlock *init = ir_add_block(proc, node, "while.init"); + case_ast_node(fs, ForStmt, node); + ir_emit_comment(proc, str_lit("ForStmt")); + if (fs->init != NULL) { + irBlock *init = ir_add_block(proc, node, "for.init"); ir_emit_jump(proc, init); proc->curr_block = init; - ir_build_stmt(proc, ws->init); + ir_build_stmt(proc, fs->init); } - irBlock *body = ir_add_block(proc, node, "while.body"); - irBlock *done = ir_add_block(proc, node, "while.done"); // NOTE(bill): Append later - + irBlock *body = ir_add_block(proc, node, "for.body"); + irBlock *done = ir_add_block(proc, node, "for.done"); // NOTE(bill): Append later irBlock *loop = body; - - if (ws->cond != NULL) { - loop = ir_add_block(proc, node, "while.loop"); + if (fs->cond != NULL) { + loop = ir_add_block(proc, node, "for.loop"); + } + irBlock *cont = loop; + if (fs->post != NULL) { + cont = ir_add_block(proc, node, "for.post"); } ir_emit_jump(proc, loop); proc->curr_block = loop; + if (loop != body) { - ir_build_cond(proc, ws->cond, body, done); + ir_build_cond(proc, fs->cond, body, done); proc->curr_block = body; } - ir_push_target_list(proc, done, loop, NULL); + ir_push_target_list(proc, done, cont, NULL); ir_open_scope(proc); - ir_build_stmt(proc, ws->body); + ir_build_stmt(proc, fs->body); ir_close_scope(proc, irDeferExit_Default, NULL); ir_pop_target_list(proc); - ir_emit_jump(proc, loop); + + ir_emit_jump(proc, cont); + + if (fs->post != NULL) { + proc->curr_block = cont; + ir_build_stmt(proc, fs->post); + ir_emit_jump(proc, loop); + } proc->curr_block = done; case_end; - case_ast_node(rs, ForStmt, node); - ir_emit_comment(proc, str_lit("ForStmt")); + case_ast_node(rs, RangeStmt, node); + ir_emit_comment(proc, str_lit("RangeStmt")); Type *val_type = NULL; Type *idx_type = NULL; diff --git a/src/parser.c b/src/parser.c index b3b1822c2..0a9b39ea9 100644 --- a/src/parser.c +++ b/src/parser.c @@ -221,16 +221,18 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ Token token; \ AstNodeArray results; \ }) \ - AST_NODE_KIND(WhileStmt, "while statement", struct { \ + AST_NODE_KIND(ForStmt, "for statement", struct { \ Token token; \ AstNode *init; \ AstNode *cond; \ + AstNode *post; \ AstNode *body; \ }) \ - AST_NODE_KIND(ForStmt, "range statement", struct { \ + AST_NODE_KIND(RangeStmt, "range statement", struct { \ Token token; \ AstNode *value; \ AstNode *index; \ + Token in_token; \ AstNode *expr; \ AstNode *body; \ }) \ @@ -501,8 +503,10 @@ Token ast_node_token(AstNode *node) { return node->WhenStmt.token; case AstNode_ReturnStmt: return node->ReturnStmt.token; - case AstNode_WhileStmt: - return node->WhileStmt.token; + case AstNode_ForStmt: + return node->ForStmt.token; + case AstNode_RangeStmt: + return node->RangeStmt.token; case AstNode_MatchStmt: return node->MatchStmt.token; case AstNode_CaseClause: @@ -904,24 +908,28 @@ AstNode *make_return_stmt(AstFile *f, Token token, AstNodeArray results) { return result; } -AstNode *make_while_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *body) { - AstNode *result = make_node(f, AstNode_WhileStmt); - result->WhileStmt.token = token; - result->WhileStmt.init = init; - result->WhileStmt.cond = cond; - result->WhileStmt.body = body; - return result; -} -AstNode *make_for_stmt(AstFile *f, Token token, AstNode *value, AstNode *index, AstNode *expr, AstNode *body) { + +AstNode *make_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *post, AstNode *body) { AstNode *result = make_node(f, AstNode_ForStmt); result->ForStmt.token = token; - result->ForStmt.value = value; - result->ForStmt.index = index; - result->ForStmt.expr = expr; + result->ForStmt.init = init; + result->ForStmt.cond = cond; + result->ForStmt.post = post; result->ForStmt.body = body; return result; } +AstNode *make_range_stmt(AstFile *f, Token token, AstNode *value, AstNode *index, Token in_token, AstNode *expr, AstNode *body) { + AstNode *result = make_node(f, AstNode_RangeStmt); + result->RangeStmt.token = token; + result->RangeStmt.value = value; + result->RangeStmt.index = index; + result->RangeStmt.in_token = in_token; + result->RangeStmt.expr = expr; + result->RangeStmt.body = body; + return result; +} + AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNode *tag, AstNode *body) { AstNode *result = make_node(f, AstNode_MatchStmt); result->MatchStmt.token = token; @@ -1258,7 +1266,6 @@ void fix_advance_to_next_stmt(AstFile *f) { case Token_if: case Token_when: case Token_return: - case Token_while: case Token_range: case Token_match: case Token_defer: @@ -1626,7 +1633,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, AstNode **foreign_library_token, Str AstNodeArray parse_lhs_expr_list(AstFile *f); AstNodeArray parse_rhs_expr_list(AstFile *f); -AstNode * parse_simple_stmt (AstFile *f); +AstNode * parse_simple_stmt (AstFile *f, bool in_stmt_ok); AstNode * parse_type (AstFile *f); AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) { @@ -1673,7 +1680,7 @@ AstNode *parse_if_expr(AstFile *f) { if (allow_token(f, Token_Semicolon)) { cond = parse_expr(f, false); } else { - init = parse_simple_stmt(f); + init = parse_simple_stmt(f, false); if (allow_token(f, Token_Semicolon)) { cond = parse_expr(f, false); } else { @@ -2261,7 +2268,8 @@ AstNode *parse_value_decl(AstFile *f, AstNodeArray lhs) { return make_value_decl(f, is_mutable, lhs, type, values); } -AstNode *parse_simple_stmt(AstFile *f) { + +AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) { AstNodeArray lhs = parse_lhs_expr_list(f); Token token = f->curr_token; switch (token.kind) { @@ -2293,6 +2301,28 @@ AstNode *parse_simple_stmt(AstFile *f) { return make_assign_stmt(f, token, lhs, rhs); } break; + case Token_in: + if (in_stmt_ok) { + allow_token(f, Token_in); + AstNode *expr = parse_expr(f, false); + switch (f->curr_token.kind) { + case Token_HalfOpenRange: + case Token_Ellipsis: { + Token op = f->curr_token; + next_token(f); + AstNode *right = parse_expr(f, false); + expr = make_interval_expr(f, op, expr, right); + } break; + } + + AstNodeArray rhs = {0}; + array_init_count(&rhs, heap_allocator(), 1); + rhs.e[0] = expr; + + return make_assign_stmt(f, token, lhs, rhs); + } + break; + case Token_Colon: return parse_value_decl(f, lhs); } @@ -2782,7 +2812,7 @@ AstNode *parse_if_stmt(AstFile *f) { if (allow_token(f, Token_Semicolon)) { cond = parse_expr(f, false); } else { - init = parse_simple_stmt(f); + init = parse_simple_stmt(f, false); if (allow_token(f, Token_Semicolon)) { cond = parse_expr(f, false); } else { @@ -2900,47 +2930,78 @@ AstNode *parse_give_stmt(AstFile *f) { return make_expr_stmt(f, ge); } -AstNode *parse_while_stmt(AstFile *f) { - if (f->curr_proc == NULL) { - syntax_error(f->curr_token, "You cannot use a while statement in the file scope"); - return make_bad_stmt(f, f->curr_token, f->curr_token); - } - - Token token = expect_token(f, Token_while); - - AstNode *init = NULL; - AstNode *cond = NULL; - AstNode *body = NULL; - - isize prev_level = f->expr_level; - f->expr_level = -1; - - - cond = parse_simple_stmt(f); - if (is_ast_node_complex_stmt(cond)) { - syntax_error(f->curr_token, "You are not allowed that type of statement in a while statement, it is too complex!"); - } - - if (allow_token(f, Token_Semicolon)) { - init = cond; - cond = parse_simple_stmt(f); - } - f->expr_level = prev_level; - - body = parse_block_stmt(f, false); - - cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression")); - - return make_while_stmt(f, token, init, cond, body); -} - - AstNode *parse_for_stmt(AstFile *f) { if (f->curr_proc == NULL) { syntax_error(f->curr_token, "You cannot use a for statement in the file scope"); return make_bad_stmt(f, f->curr_token, f->curr_token); } + Token token = expect_token(f, Token_for); + + AstNode *init = NULL; + AstNode *cond = NULL; + AstNode *post = NULL; + AstNode *body = NULL; + bool is_range = false; + + if (f->curr_token.kind != Token_OpenBrace) { + isize prev_level = f->expr_level; + f->expr_level = -1; + if (f->curr_token.kind != Token_Semicolon) { + cond = parse_simple_stmt(f, true); + if (cond->kind == AstNode_AssignStmt && + cond->AssignStmt.op.kind == Token_in) { + is_range = true; + } + } + + if (!is_range && f->curr_token.kind == Token_Semicolon) { + next_token(f); + init = cond; + cond = NULL; + if (f->curr_token.kind != Token_Semicolon) { + cond = parse_simple_stmt(f, false); + } + expect_semicolon(f, cond); + if (f->curr_token.kind != Token_OpenBrace) { + post = parse_simple_stmt(f, false); + } + } + + f->expr_level = prev_level; + } + + body = parse_block_stmt(f, false); + + if (is_range) { + GB_ASSERT(cond->kind == AstNode_AssignStmt); + Token in_token = cond->AssignStmt.op; + AstNode *value = NULL; + AstNode *index = NULL; + switch (cond->AssignStmt.lhs.count) { + case 1: + value = cond->AssignStmt.lhs.e[0]; + break; + case 2: + value = cond->AssignStmt.lhs.e[0]; + index = cond->AssignStmt.lhs.e[1]; + break; + default: + error_node(cond, "Expected at 1 or 2 identifiers"); + return make_bad_stmt(f, token, f->curr_token); + } + + AstNode *rhs = NULL; + if (cond->AssignStmt.rhs.count > 0) { + rhs = cond->AssignStmt.rhs.e[0]; + } + return make_range_stmt(f, token, value, index, in_token, rhs, body); + } + + cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression")); + return make_for_stmt(f, token, init, cond, post, body); + +#if 0 Token token = expect_token(f, Token_for); AstNodeArray names = parse_ident_list(f); parse_check_name_list_for_reserves(f, names); @@ -2977,7 +3038,8 @@ AstNode *parse_for_stmt(AstFile *f) { return make_bad_stmt(f, token, f->curr_token); } - return make_for_stmt(f, token, value, index, expr, body); + return make_range_stmt(f, token, value, index, expr, body); +#endif } AstNode *parse_case_clause(AstFile *f) { @@ -3030,7 +3092,7 @@ AstNode *parse_match_stmt(AstFile *f) { AstNode *var = parse_identifier(f); expect_token_after(f, Token_in, "match type name"); - tag = parse_simple_stmt(f); + tag = parse_simple_stmt(f, false); f->expr_level = prev_level; @@ -3052,13 +3114,13 @@ AstNode *parse_match_stmt(AstFile *f) { isize prev_level = f->expr_level; f->expr_level = -1; if (f->curr_token.kind != Token_Semicolon) { - tag = parse_simple_stmt(f); + tag = parse_simple_stmt(f, false); } if (allow_token(f, Token_Semicolon)) { init = tag; tag = NULL; if (f->curr_token.kind != Token_OpenBrace) { - tag = parse_simple_stmt(f); + tag = parse_simple_stmt(f, false); } } @@ -3151,13 +3213,12 @@ AstNode *parse_stmt(AstFile *f) { case Token_Sub: case Token_Xor: case Token_Not: - s = parse_simple_stmt(f); + s = parse_simple_stmt(f, false); expect_semicolon(f, s); return s; case Token_if: return parse_if_stmt(f); case Token_when: return parse_when_stmt(f); - case Token_while: return parse_while_stmt(f); case Token_for: return parse_for_stmt(f); case Token_match: return parse_match_stmt(f); case Token_defer: return parse_defer_stmt(f); diff --git a/src/tokenizer.c b/src/tokenizer.c index c21c09942..8fa836941 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -92,7 +92,6 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_then, "then"), \ TOKEN_KIND(Token_if, "if"), \ TOKEN_KIND(Token_else, "else"), \ - TOKEN_KIND(Token_while, "while"), \ TOKEN_KIND(Token_for, "for"), \ TOKEN_KIND(Token_in, "in"), \ TOKEN_KIND(Token_when, "when"), \