mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
206 lines
5.6 KiB
V
206 lines
5.6 KiB
V
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
|
|
// Use of this source code is governed by an MIT license
|
|
// that can be found in the LICENSE file.
|
|
module parser
|
|
|
|
import v.ast
|
|
|
|
fn (mut p Parser) for_stmt() ast.Stmt {
|
|
p.check(.key_for)
|
|
mut pos := p.tok.pos()
|
|
p.open_scope()
|
|
p.inside_for = true
|
|
if p.tok.kind == .key_match {
|
|
return p.error('cannot use `match` in `for` loop')
|
|
}
|
|
// defer { p.close_scope() }
|
|
// Infinite loop
|
|
if p.tok.kind == .lcbr {
|
|
p.inside_for = false
|
|
stmts := p.parse_block_no_scope(false)
|
|
pos.update_last_line(p.prev_tok.line_nr)
|
|
for_stmt := ast.ForStmt{
|
|
stmts: stmts
|
|
pos: pos
|
|
is_inf: true
|
|
scope: p.scope
|
|
}
|
|
p.close_scope()
|
|
return for_stmt
|
|
} else if p.peek_tok.kind in [.decl_assign, .assign, .semicolon]
|
|
|| p.tok.kind == .semicolon || (p.peek_tok.kind == .comma
|
|
&& p.peek_token(2).kind != .key_mut && p.peek_token(3).kind != .key_in) {
|
|
// `for i := 0; i < 10; i++ {` or `for a,b := 0,1; a < 10; a++ {`
|
|
if p.tok.kind == .key_mut {
|
|
return p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`')
|
|
}
|
|
mut init := ast.empty_stmt
|
|
mut cond := p.new_true_expr()
|
|
mut inc := ast.empty_stmt
|
|
mut has_init := false
|
|
mut has_cond := false
|
|
mut has_inc := false
|
|
mut is_multi := p.peek_tok.kind == .comma && p.peek_token(2).kind != .key_mut
|
|
&& p.peek_token(3).kind != .key_in
|
|
if p.peek_tok.kind in [.assign, .decl_assign] || is_multi {
|
|
init = p.assign_stmt()
|
|
has_init = true
|
|
}
|
|
// Allow `for ;; i++ {`
|
|
// Allow `for i = 0; i < ...`
|
|
p.check(.semicolon)
|
|
if p.tok.kind != .semicolon {
|
|
// Disallow `for i := 0; i++; i < ...`
|
|
if p.tok.kind == .name && p.peek_tok.kind in [.inc, .dec] {
|
|
return p.error('cannot use $p.tok.lit$p.peek_tok.kind as value')
|
|
}
|
|
cond = p.expr(0)
|
|
has_cond = true
|
|
}
|
|
p.check(.semicolon)
|
|
if !is_multi {
|
|
is_multi = p.peek_tok.kind == .comma
|
|
}
|
|
if p.tok.kind != .lcbr {
|
|
inc = p.stmt(false)
|
|
has_inc = true
|
|
}
|
|
p.inside_for = false
|
|
stmts := p.parse_block_no_scope(false)
|
|
pos.update_last_line(p.prev_tok.line_nr)
|
|
for_c_stmt := ast.ForCStmt{
|
|
stmts: stmts
|
|
has_init: has_init
|
|
has_cond: has_cond
|
|
has_inc: has_inc
|
|
is_multi: is_multi
|
|
init: init
|
|
cond: cond
|
|
inc: inc
|
|
pos: pos
|
|
scope: p.scope
|
|
}
|
|
p.close_scope()
|
|
return for_c_stmt
|
|
} else if p.peek_tok.kind in [.key_in, .comma]
|
|
|| (p.tok.kind == .key_mut && p.peek_token(2).kind in [.key_in, .comma]) {
|
|
// `for i in vals`, `for i in start .. end`, `for mut user in users`, `for i, mut user in users`
|
|
mut val_is_mut := p.tok.kind == .key_mut
|
|
mut_pos := p.tok.pos()
|
|
if val_is_mut {
|
|
p.next()
|
|
}
|
|
key_var_pos := p.tok.pos()
|
|
mut val_var_pos := p.tok.pos()
|
|
mut key_var_name := ''
|
|
mut val_var_name := p.check_name()
|
|
if p.tok.kind == .comma {
|
|
if val_is_mut {
|
|
p.error_with_pos('index of array or key of map cannot be mutated', mut_pos)
|
|
}
|
|
p.next()
|
|
if p.tok.kind == .key_mut {
|
|
// `for i, mut user in users {`
|
|
p.next()
|
|
val_is_mut = true
|
|
}
|
|
key_var_name = val_var_name
|
|
val_var_pos = p.tok.pos()
|
|
val_var_name = p.check_name()
|
|
if key_var_name == val_var_name && key_var_name != '_' {
|
|
return p.error_with_pos('key and value in a for loop cannot be the same',
|
|
val_var_pos)
|
|
}
|
|
if p.scope.known_var(key_var_name) {
|
|
return p.error('redefinition of key iteration variable `$key_var_name`')
|
|
}
|
|
if p.scope.known_var(val_var_name) {
|
|
return p.error('redefinition of value iteration variable `$val_var_name`')
|
|
}
|
|
p.scope.register(ast.Var{
|
|
name: key_var_name
|
|
typ: ast.int_type
|
|
pos: key_var_pos
|
|
is_tmp: true
|
|
is_stack_obj: true
|
|
})
|
|
} else if p.scope.known_var(val_var_name) {
|
|
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',
|
|
val_var_pos)
|
|
}
|
|
p.check(.key_in)
|
|
if p.tok.kind == .name && p.tok.lit in [key_var_name, val_var_name] {
|
|
return p.error('in a `for x in array` loop, the key or value iteration variable `$p.tok.lit` can not be the same as the array variable')
|
|
}
|
|
// arr_expr
|
|
cond := p.expr(0)
|
|
// 0 .. 10
|
|
// start := p.tok.lit.int()
|
|
// TODO use RangeExpr
|
|
mut high_expr := ast.empty_expr
|
|
mut is_range := false
|
|
if p.tok.kind == .dotdot {
|
|
is_range = true
|
|
p.next()
|
|
high_expr = p.expr(0)
|
|
p.scope.register(ast.Var{
|
|
name: val_var_name
|
|
typ: ast.int_type
|
|
pos: val_var_pos
|
|
is_tmp: true
|
|
is_stack_obj: true
|
|
})
|
|
if key_var_name.len > 0 {
|
|
return p.error_with_pos('cannot declare index variable with range `for`',
|
|
key_var_pos)
|
|
}
|
|
if val_is_mut {
|
|
return p.error_with_pos('variable in range `for` cannot be mut', mut_pos)
|
|
}
|
|
} else {
|
|
// this type will be set in checker
|
|
p.scope.register(ast.Var{
|
|
name: val_var_name
|
|
pos: val_var_pos
|
|
is_mut: val_is_mut
|
|
is_auto_deref: val_is_mut
|
|
is_tmp: true
|
|
is_stack_obj: true
|
|
})
|
|
}
|
|
p.inside_for = false
|
|
stmts := p.parse_block_no_scope(false)
|
|
pos.update_last_line(p.prev_tok.line_nr)
|
|
// println('nr stmts=$stmts.len')
|
|
for_in_stmt := ast.ForInStmt{
|
|
stmts: stmts
|
|
cond: cond
|
|
key_var: key_var_name
|
|
val_var: val_var_name
|
|
high: high_expr
|
|
is_range: is_range
|
|
pos: pos
|
|
val_is_mut: val_is_mut
|
|
scope: p.scope
|
|
}
|
|
p.close_scope()
|
|
return for_in_stmt
|
|
}
|
|
// `for cond {`
|
|
cond := p.expr(0)
|
|
p.inside_for = false
|
|
// extra scope for the body
|
|
p.open_scope()
|
|
stmts := p.parse_block_no_scope(false)
|
|
pos.update_last_line(p.prev_tok.line_nr)
|
|
for_stmt := ast.ForStmt{
|
|
cond: cond
|
|
stmts: stmts
|
|
pos: pos
|
|
scope: p.scope
|
|
}
|
|
p.close_scope()
|
|
p.close_scope()
|
|
return for_stmt
|
|
}
|