v2 / vlib / v / parser / for.v
229 lines · 227 sloc · 6.4 KB · 2332ecff4811b8c97dfda8e825170e9397962519
Raw
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.
4module parser
5
6import v.ast
7
8fn (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