// Copyright (c) 2019-2020 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 import v.table fn (mut p Parser) for_stmt() ast.Stmt { p.check(.key_for) pos := p.tok.position() p.open_scope() p.inside_for = true // defer { p.close_scope() } // Infinite loop if p.tok.kind == .lcbr { p.inside_for = false stmts := p.parse_block() p.close_scope() return ast.ForStmt{ stmts: stmts pos: pos is_inf: true } } else if p.tok.kind == .key_mut { p.error('`mut` is not needed in for loops') } else if p.peek_tok.kind in [.decl_assign, .assign, .semicolon] || p.tok.kind == .semicolon { // `for i := 0; i < 10; i++ {` mut init := ast.Stmt{} mut cond := p.new_true_expr() mut inc := ast.Stmt{} mut has_init := false mut has_cond := false mut has_inc := false if p.peek_tok.kind in [.assign, .decl_assign] { 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] { p.error('cannot use $p.tok.lit$p.peek_tok.kind as value') } cond = p.expr(0) has_cond = true } p.check(.semicolon) if p.tok.kind != .lcbr { inc = p.stmt(false) has_inc = true } p.inside_for = false stmts := p.parse_block() p.close_scope() return ast.ForCStmt{ stmts: stmts has_init: has_init has_cond: has_cond has_inc: has_inc init: init cond: cond inc: inc pos: pos } } else if p.peek_tok.kind in [.key_in, .comma] { // `for i in vals`, `for i in start .. end` key_var_pos := p.tok.position() mut val_var_pos := p.tok.position() mut key_var_name := '' mut val_var_name := p.check_name() if p.tok.kind == .comma { p.next() key_var_name = val_var_name val_var_pos = p.tok.position() val_var_name = p.check_name() if key_var_name == val_var_name && key_var_name != '_' { 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) { p.error('redefinition of key iteration variable `$key_var_name`') } if p.scope.known_var(val_var_name) { p.error('redefinition of value iteration variable `$val_var_name`') } p.scope.register(key_var_name, ast.Var{ name: key_var_name typ: table.int_type pos: key_var_pos }) } else if p.scope.known_var(val_var_name) { p.error('redefinition of value iteration variable `$val_var_name`') } p.check(.key_in) if p.tok.kind == .name && p.tok.lit in [key_var_name, val_var_name] { 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.Expr{} mut is_range := false if p.tok.kind == .dotdot { is_range = true p.next() high_expr = p.expr(0) p.scope.register(val_var_name, ast.Var{ name: val_var_name typ: table.int_type pos: val_var_pos }) } else { // this type will be set in checker p.scope.register(val_var_name, ast.Var{ name: val_var_name pos: val_var_pos }) } p.inside_for = false stmts := p.parse_block() // println('nr stmts=$stmts.len') p.close_scope() return 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 } } // `for cond {` cond := p.expr(0) p.inside_for = false stmts := p.parse_block() p.close_scope() return ast.ForStmt{ cond: cond stmts: stmts pos: pos } }