2020-04-17 18:16:55 +02:00
|
|
|
// 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
|
|
|
|
|
2020-04-23 01:16:58 +02:00
|
|
|
fn (mut p Parser) assign_stmt() ast.Stmt {
|
2020-05-15 23:14:53 +02:00
|
|
|
return p.partial_assign_stmt([])
|
|
|
|
}
|
|
|
|
|
2020-05-25 11:31:04 +02:00
|
|
|
fn (mut p Parser) check_undefined_variables(idents []ast.Ident, expr ast.Expr) {
|
2020-05-24 19:33:53 +02:00
|
|
|
match expr {
|
|
|
|
ast.Ident {
|
|
|
|
for ident in idents {
|
|
|
|
if ident.name == it.name {
|
2020-05-25 23:00:48 +02:00
|
|
|
p.error_with_pos('undefined variable: `$it.name`', it.pos)
|
2020-05-24 19:33:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.InfixExpr {
|
2020-05-25 11:31:04 +02:00
|
|
|
p.check_undefined_variables(idents, it.left)
|
|
|
|
p.check_undefined_variables(idents, it.right)
|
|
|
|
}
|
|
|
|
ast.StringInterLiteral {
|
|
|
|
for expr_ in it.exprs {
|
|
|
|
p.check_undefined_variables(idents, expr_)
|
|
|
|
}
|
2020-05-24 19:33:53 +02:00
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-26 18:00:51 +02:00
|
|
|
fn (mut p Parser) check_cross_variables(idents []ast.Ident, expr ast.Expr) bool {
|
|
|
|
match expr {
|
|
|
|
ast.Ident {
|
|
|
|
for ident in idents {
|
2020-05-28 01:22:09 +02:00
|
|
|
if ident.name == it.name { return true }
|
2020-05-26 18:00:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.InfixExpr {
|
2020-05-28 01:22:09 +02:00
|
|
|
if p.check_cross_variables(idents, it.left) { return true }
|
|
|
|
if p.check_cross_variables(idents, it.right) { return true }
|
2020-05-26 18:00:51 +02:00
|
|
|
}
|
2020-05-28 01:22:09 +02:00
|
|
|
ast.PrefixExpr {
|
|
|
|
if p.check_cross_variables(idents, it.right) { return true }
|
|
|
|
}
|
|
|
|
ast.PostfixExpr {
|
|
|
|
if p.check_cross_variables(idents, it.expr) { return true }
|
2020-05-26 18:00:51 +02:00
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-05-15 23:14:53 +02:00
|
|
|
fn (mut p Parser) partial_assign_stmt(known_lhs []ast.Ident) ast.Stmt {
|
|
|
|
mut idents := known_lhs
|
|
|
|
mut op := p.tok.kind
|
|
|
|
// read (more) idents until assignment sign
|
|
|
|
for op !in [.decl_assign, .assign] {
|
|
|
|
idents << p.parse_assign_ident()
|
|
|
|
if p.tok.kind == .comma {
|
|
|
|
p.next()
|
|
|
|
}
|
|
|
|
op = p.tok.kind
|
2020-04-17 18:16:55 +02:00
|
|
|
}
|
2020-05-15 23:14:53 +02:00
|
|
|
p.next()
|
2020-04-17 18:16:55 +02:00
|
|
|
pos := p.tok.position()
|
|
|
|
exprs := p.parse_assign_rhs()
|
|
|
|
is_decl := op == .decl_assign
|
2020-05-26 18:00:51 +02:00
|
|
|
mut has_cross_var := false
|
2020-05-24 19:33:53 +02:00
|
|
|
if is_decl {
|
|
|
|
// a, b := a + 1, b
|
|
|
|
for expr in exprs {
|
2020-05-25 11:31:04 +02:00
|
|
|
p.check_undefined_variables(idents, expr)
|
2020-05-24 19:33:53 +02:00
|
|
|
}
|
2020-05-26 18:00:51 +02:00
|
|
|
} else if idents.len > 1 {
|
|
|
|
// a, b = b, a
|
|
|
|
for expr in exprs {
|
|
|
|
if p.check_cross_variables(idents, expr) {
|
|
|
|
has_cross_var = true
|
|
|
|
}
|
|
|
|
}
|
2020-05-24 19:33:53 +02:00
|
|
|
}
|
2020-04-17 18:16:55 +02:00
|
|
|
for i, ident in idents {
|
|
|
|
known_var := p.scope.known_var(ident.name)
|
|
|
|
if !is_decl && !known_var {
|
|
|
|
p.error('unknown variable `$ident.name`')
|
|
|
|
}
|
|
|
|
if is_decl && ident.kind != .blank_ident {
|
|
|
|
if p.scope.known_var(ident.name) {
|
|
|
|
p.error('redefinition of `$ident.name`')
|
|
|
|
}
|
|
|
|
if idents.len == exprs.len {
|
|
|
|
p.scope.register(ident.name, ast.Var{
|
|
|
|
name: ident.name
|
|
|
|
expr: exprs[i]
|
|
|
|
is_mut: ident.is_mut || p.inside_for
|
2020-04-27 15:16:31 +02:00
|
|
|
pos: ident.pos
|
2020-04-17 18:16:55 +02:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
p.scope.register(ident.name, ast.Var{
|
|
|
|
name: ident.name
|
|
|
|
is_mut: ident.is_mut || p.inside_for
|
2020-04-27 15:16:31 +02:00
|
|
|
pos: ident.pos
|
2020-04-17 18:16:55 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ast.AssignStmt{
|
|
|
|
left: idents
|
|
|
|
right: exprs
|
|
|
|
op: op
|
|
|
|
pos: pos
|
2020-05-15 23:14:53 +02:00
|
|
|
is_static: false // individual idents may be static
|
2020-05-26 18:00:51 +02:00
|
|
|
has_cross_var: has_cross_var
|
2020-04-17 18:16:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: is it possible to merge with AssignStmt?
|
2020-04-23 01:16:58 +02:00
|
|
|
pub fn (mut p Parser) assign_expr(left ast.Expr) ast.AssignExpr {
|
2020-04-17 18:16:55 +02:00
|
|
|
op := p.tok.kind
|
|
|
|
pos := p.tok.position()
|
2020-04-20 20:56:05 +02:00
|
|
|
p.next()
|
2020-04-17 18:16:55 +02:00
|
|
|
val := p.expr(0)
|
|
|
|
match left {
|
|
|
|
ast.IndexExpr {
|
|
|
|
// it.mark_as_setter()
|
|
|
|
it.is_setter = true
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
node := ast.AssignExpr{
|
|
|
|
left: left
|
|
|
|
val: val
|
|
|
|
op: op
|
|
|
|
pos: pos
|
|
|
|
}
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
2020-05-15 23:14:53 +02:00
|
|
|
fn (mut p Parser) parse_assign_ident() ast.Ident {
|
|
|
|
/// returns a single parsed ident
|
2020-05-19 17:12:47 +02:00
|
|
|
return p.parse_ident(.v)
|
2020-04-17 18:16:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// right hand side of `=` or `:=` in `a,b,c := 1,2,3`
|
2020-04-23 01:16:58 +02:00
|
|
|
fn (mut p Parser) parse_assign_rhs() []ast.Expr {
|
2020-04-26 09:17:13 +02:00
|
|
|
mut exprs := []ast.Expr{}
|
2020-04-17 18:16:55 +02:00
|
|
|
for {
|
|
|
|
expr := p.expr(0)
|
|
|
|
exprs << expr
|
|
|
|
if p.tok.kind == .comma {
|
2020-05-07 06:51:36 +02:00
|
|
|
p.next()
|
2020-04-17 18:16:55 +02:00
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return exprs
|
|
|
|
}
|