1// Copyright (c) 2019-2020 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
7import v.table
8
9fn (mut p Parser) assign_stmt() ast.Stmt {
10	exprs, comments := p.expr_list()
11	return p.partial_assign_stmt(exprs, comments)
12}
13
14fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) {
15	match val {
16		ast.Ident {
17			for expr in exprs {
18				if expr is ast.Ident {
19					if expr.name == val.name {
20						p.error_with_pos('undefined variable: `$val.name`', val.pos)
21					}
22				}
23			}
24		}
25		ast.InfixExpr {
26			p.check_undefined_variables(exprs, val.left)
27			p.check_undefined_variables(exprs, val.right)
28		}
29		ast.ParExpr {
30			p.check_undefined_variables(exprs, val.expr)
31		}
32		ast.PostfixExpr {
33			p.check_undefined_variables(exprs, val.expr)
34		}
35		ast.PrefixExpr {
36			p.check_undefined_variables(exprs, val.right)
37		}
38		ast.StringInterLiteral {
39			for expr_ in val.exprs {
40				p.check_undefined_variables(exprs, expr_)
41			}
42		}
43		else {}
44	}
45}
46
47fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool {
48	val_ := val
49	match val_ {
50		ast.Ident {
51			for expr in exprs {
52				if expr is ast.Ident {
53					if expr.name == val_.name {
54						return true
55					}
56				}
57			}
58		}
59		ast.IndexExpr {
60			for expr in exprs {
61				if expr.str() == val.str() {
62					return true
63				}
64			}
65		}
66		ast.InfixExpr { return p.check_cross_variables(exprs, val_.left) || p.check_cross_variables(exprs, val_.right) }
67		ast.PrefixExpr { return p.check_cross_variables(exprs, val_.right) }
68		ast.PostfixExpr { return p.check_cross_variables(exprs, val_.expr) }
69		ast.SelectorExpr {
70			for expr in exprs {
71				if expr.str() == val.str() {
72					return true
73				}
74			}
75		}
76		else {}
77	}
78	return false
79}
80
81fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comment) ast.Stmt {
82	p.is_stmt_ident = false
83	op := p.tok.kind
84	pos := p.tok.position()
85	p.next()
86	right, right_comments := p.expr_list()
87	mut comments := []ast.Comment{cap: left_comments.len + right_comments.len}
88	comments << left_comments
89	comments << right_comments
90	mut has_cross_var := false
91	if op == .decl_assign {
92		// a, b := a + 1, b
93		for r in right {
94			p.check_undefined_variables(left, r)
95		}
96	} else if left.len > 1 {
97		// a, b = b, a
98		for r in right {
99			has_cross_var = p.check_cross_variables(left, r)
100			if op !in [.assign, .decl_assign] {
101				p.error('unexpected $op.str(), expecting := or = or comma')
102			}
103			if has_cross_var {
104				break
105			}
106		}
107	}
108	for i, lx in left {
109		match mut lx {
110			ast.Ident {
111				if op == .decl_assign {
112					if p.scope.known_var(lx.name) {
113						p.error_with_pos('redefinition of `$lx.name`', lx.pos)
114					}
115					mut share := table.ShareType(0)
116					if lx.info is ast.IdentVar {
117						share = (lx.info as ast.IdentVar).share
118					}
119					mut v := ast.Var{
120						name: lx.name
121						expr: if left.len == right.len { right[i] } else { ast.Expr{} }
122						share: share
123						is_mut: lx.is_mut || p.inside_for
124						pos: lx.pos
125					}
126					obj := ast.ScopeObject(v)
127					lx.obj = obj
128					p.scope.register(lx.name, obj)
129				}
130			}
131			ast.IndexExpr {
132				if op == .decl_assign {
133					p.error_with_pos('non-name `$lx.left[$lx.index]` on left side of `:=`',
134						lx.pos)
135				}
136				lx.is_setter = true
137			}
138			ast.ParExpr {}
139			ast.PrefixExpr {}
140			ast.SelectorExpr {
141				if op == .decl_assign {
142					p.error_with_pos('struct fields can only be declared during the initialization',
143						lx.pos)
144				}
145			}
146			else {
147				// TODO: parexpr ( check vars)
148				// else { p.error_with_pos('unexpected `${typeof(lx)}`', lx.position()) }
149			}
150		}
151	}
152	return ast.AssignStmt{
153		op: op
154		left: left
155		right: right
156		comments: comments
157		pos: pos
158		has_cross_var: has_cross_var
159		is_simple: p.inside_for && p.tok.kind == .lcbr
160	}
161}
162