| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module parser |
| 5 | |
| 6 | import v.ast |
| 7 | |
| 8 | fn (mut p Parser) for_stmt() ast.Stmt { |
| 9 | p.check(.key_for) |
| 10 | mut pos := p.tok.pos() |
| 11 | p.open_scope() |
| 12 | p.inside_for = true |
| 13 | if p.tok.kind == .key_match { |
| 14 | return p.error('cannot use `match` in `for` loop') |
| 15 | } |
| 16 | // defer { p.close_scope() } |
| 17 | // Infinite loop |
| 18 | mut comments := []ast.Comment{} |
| 19 | comments << p.eat_comments() |
| 20 | if p.tok.kind == .lcbr { |
| 21 | p.inside_for = false |
| 22 | stmts := p.parse_block_no_scope(false) |
| 23 | pos.update_last_line(p.prev_tok.line_nr) |
| 24 | for_stmt := ast.ForStmt{ |
| 25 | stmts: stmts |
| 26 | pos: pos |
| 27 | comments: comments |
| 28 | is_inf: true |
| 29 | scope: p.scope |
| 30 | } |
| 31 | p.close_scope() |
| 32 | return for_stmt |
| 33 | } else if p.peek_tok.kind == .semicolon |
| 34 | || (p.peek_tok.kind in [.inc, .dec] && p.peek_token(2).kind in [.semicolon, .comma]) |
| 35 | || p.peek_tok.kind.is_assign() || p.tok.kind == .semicolon |
| 36 | || (p.peek_tok.kind == .comma && p.peek_token(2).kind != .key_mut |
| 37 | && p.peek_token(3).kind != .key_in) { |
| 38 | // `for i := 0; i < 10; i++ {` or `for a,b := 0,1; a < 10; a++ {` |
| 39 | if p.tok.kind == .key_mut { |
| 40 | return p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`') |
| 41 | } |
| 42 | mut init := ast.empty_stmt |
| 43 | mut cond := p.new_true_expr() |
| 44 | mut inc := ast.empty_stmt |
| 45 | mut has_init := false |
| 46 | mut has_cond := false |
| 47 | mut has_inc := false |
| 48 | mut is_multi := p.peek_tok.kind == .comma && p.peek_token(2).kind != .key_mut |
| 49 | && p.peek_token(3).kind != .key_in |
| 50 | if p.peek_tok.kind.is_assign() || is_multi { |
| 51 | init = p.assign_stmt() |
| 52 | has_init = true |
| 53 | } else if p.peek_tok.kind in [.inc, .dec] { |
| 54 | init = p.stmt(false) |
| 55 | has_init = true |
| 56 | } |
| 57 | comments << p.eat_comments() |
| 58 | // Allow `for ;; i++ {` |
| 59 | // Allow `for i = 0; i < ...` |
| 60 | p.check(.semicolon) |
| 61 | if p.tok.kind != .semicolon { |
| 62 | // Disallow `for i := 0; i++; i < ...` |
| 63 | if p.tok.kind == .name && p.peek_tok.kind in [.inc, .dec] { |
| 64 | return p.error('cannot use ${p.tok.lit}${p.peek_tok.kind} as value') |
| 65 | } |
| 66 | comments << p.eat_comments() |
| 67 | cond = p.expr(0) |
| 68 | has_cond = true |
| 69 | } |
| 70 | comments << p.eat_comments() |
| 71 | p.check(.semicolon) |
| 72 | if !is_multi { |
| 73 | is_multi = p.peek_tok.kind == .comma |
| 74 | } |
| 75 | comments << p.eat_comments() |
| 76 | if p.tok.kind != .lcbr { |
| 77 | inc = p.stmt(false) |
| 78 | has_inc = true |
| 79 | } |
| 80 | comments << p.eat_comments() |
| 81 | p.inside_for = false |
| 82 | stmts := p.parse_block_no_scope(false) |
| 83 | pos.update_last_line(p.prev_tok.line_nr) |
| 84 | for_c_stmt := ast.ForCStmt{ |
| 85 | stmts: stmts |
| 86 | has_init: has_init |
| 87 | has_cond: has_cond |
| 88 | has_inc: has_inc |
| 89 | is_multi: is_multi |
| 90 | init: init |
| 91 | cond: cond |
| 92 | inc: inc |
| 93 | pos: pos |
| 94 | comments: comments |
| 95 | scope: p.scope |
| 96 | } |
| 97 | p.close_scope() |
| 98 | return for_c_stmt |
| 99 | } else if p.peek_tok.kind in [.key_in, .comma] |
| 100 | || (p.tok.kind == .key_mut && p.peek_token(2).kind in [.key_in, .comma]) { |
| 101 | // `for i in vals`, `for i in start .. end`, `for mut user in users`, `for i, mut user in users` |
| 102 | mut val_is_mut := p.tok.kind == .key_mut |
| 103 | mut_pos := p.tok.pos() |
| 104 | if val_is_mut { |
| 105 | p.next() |
| 106 | } |
| 107 | key_var_pos := p.tok.pos() |
| 108 | mut val_var_pos := p.tok.pos() |
| 109 | mut key_var_name := '' |
| 110 | mut val_var_name := p.check_name() |
| 111 | if p.tok.kind == .comma { |
| 112 | if val_is_mut { |
| 113 | p.error_with_pos('index of array or key of map cannot be mutated', mut_pos) |
| 114 | } |
| 115 | p.next() |
| 116 | if p.tok.kind == .key_mut { |
| 117 | // `for i, mut user in users {` |
| 118 | p.next() |
| 119 | val_is_mut = true |
| 120 | } |
| 121 | key_var_name = val_var_name |
| 122 | val_var_pos = p.tok.pos() |
| 123 | val_var_name = p.check_name() |
| 124 | if key_var_name == val_var_name && key_var_name != '_' { |
| 125 | return p.error_with_pos('key and value in a for loop cannot be the same', |
| 126 | val_var_pos) |
| 127 | } |
| 128 | if p.scope.known_var(key_var_name) { |
| 129 | return p.error_with_pos('redefinition of key iteration variable `${key_var_name}`', |
| 130 | key_var_pos) |
| 131 | } |
| 132 | if p.scope.known_var(val_var_name) { |
| 133 | return p.error_with_pos('redefinition of value iteration variable `${val_var_name}`', |
| 134 | val_var_pos) |
| 135 | } |
| 136 | p.scope.register(ast.Var{ |
| 137 | name: key_var_name |
| 138 | typ: ast.int_type |
| 139 | pos: key_var_pos |
| 140 | is_tmp: true |
| 141 | is_stack_obj: true |
| 142 | }) |
| 143 | } else if p.scope.known_var(val_var_name) { |
| 144 | return p.error_with_pos('redefinition of value iteration variable `${val_var_name}`, use `for (${val_var_name} in array) {` if you want to check for a condition instead', |
| 145 | val_var_pos) |
| 146 | } |
| 147 | comments << p.eat_comments() |
| 148 | p.check(.key_in) |
| 149 | comments << p.eat_comments() |
| 150 | // arr_expr |
| 151 | p.inside_for_expr = true |
| 152 | cond := p.expr(0) |
| 153 | p.inside_for_expr = false |
| 154 | // 0 .. 10 |
| 155 | // start := p.tok.lit.int() |
| 156 | // TODO: use RangeExpr |
| 157 | mut high_expr := ast.empty_expr |
| 158 | mut is_range := false |
| 159 | if p.tok.kind == .ellipsis { |
| 160 | p.error_with_pos('for loop only supports exclusive (`..`) ranges, not inclusive (`...`)', |
| 161 | p.tok.pos()) |
| 162 | } else if p.tok.kind == .dotdot { |
| 163 | is_range = true |
| 164 | p.next() |
| 165 | high_expr = p.expr(0) |
| 166 | p.scope.register(ast.Var{ |
| 167 | name: val_var_name |
| 168 | typ: ast.int_type |
| 169 | pos: val_var_pos |
| 170 | is_tmp: true |
| 171 | is_stack_obj: true |
| 172 | }) |
| 173 | if key_var_name != '' { |
| 174 | return p.error_with_pos('cannot declare index variable with range `for`', |
| 175 | key_var_pos) |
| 176 | } |
| 177 | if val_is_mut { |
| 178 | return p.error_with_pos('variable in range `for` cannot be mut', mut_pos) |
| 179 | } |
| 180 | } else { |
| 181 | // this type will be set in checker |
| 182 | p.scope.register(ast.Var{ |
| 183 | name: val_var_name |
| 184 | pos: val_var_pos |
| 185 | is_mut: val_is_mut |
| 186 | is_auto_deref: val_is_mut |
| 187 | is_tmp: true |
| 188 | is_stack_obj: true |
| 189 | }) |
| 190 | } |
| 191 | comments << p.eat_comments() |
| 192 | p.inside_for = false |
| 193 | stmts := p.parse_block_no_scope(false) |
| 194 | pos.update_last_line(p.prev_tok.line_nr) |
| 195 | // println('nr stmts=${stmts.len}') |
| 196 | for_in_stmt := ast.ForInStmt{ |
| 197 | stmts: stmts |
| 198 | cond: cond |
| 199 | key_var: key_var_name |
| 200 | val_var: val_var_name |
| 201 | high: high_expr |
| 202 | is_range: is_range |
| 203 | pos: pos |
| 204 | kv_pos: key_var_pos |
| 205 | vv_pos: val_var_pos |
| 206 | comments: comments |
| 207 | val_is_mut: val_is_mut |
| 208 | scope: p.scope |
| 209 | } |
| 210 | p.close_scope() |
| 211 | return for_in_stmt |
| 212 | } |
| 213 | // `for cond {` |
| 214 | cond := p.expr(0) |
| 215 | p.inside_for = false |
| 216 | // extra scope for the body |
| 217 | p.open_scope() |
| 218 | stmts := p.parse_block_no_scope(false) |
| 219 | pos.update_last_line(p.prev_tok.line_nr) |
| 220 | for_stmt := ast.ForStmt{ |
| 221 | cond: cond |
| 222 | stmts: stmts |
| 223 | pos: pos |
| 224 | scope: p.scope |
| 225 | } |
| 226 | p.close_scope() |
| 227 | p.close_scope() |
| 228 | return for_stmt |
| 229 | } |
| 230 | |