diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index aa6739efc2..3c6d57b291 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -448,7 +448,7 @@ pub fn (mut p Parser) stmt() ast.Stmt { return p.assign_stmt() } else if p.peek_tok.kind == .comma { // `a, b ...` - return p.parse_comma_separated() + return p.parse_multi_expr() } else if p.peek_tok.kind == .colon { // `label:` name := p.check_name() @@ -462,11 +462,7 @@ pub fn (mut p Parser) stmt() ast.Stmt { [.rcbr, .eof] { p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position()) } - epos := p.tok.position() - return ast.ExprStmt{ - expr: p.expr(0) - pos: epos - } + return p.parse_multi_expr() } .comment { return p.comment() @@ -527,8 +523,9 @@ pub fn (mut p Parser) stmt() ast.Stmt { p.error_with_pos('const can only be defined at the top level (outside of functions)', p.tok.position()) } + // literals, 'if', etc. in here else { - return p.parse_comma_separated() + return p.parse_multi_expr() } } } @@ -644,39 +641,47 @@ pub fn (mut p Parser) warn_with_pos(s string, pos token.Position) { } } -fn (mut p Parser) parse_comma_separated() ast.Stmt { +fn (mut p Parser) parse_multi_expr() ast.Stmt { // in here might be 1) multi-expr 2) multi-assign // 1, a, c ... } // multi-expression // a, mut b ... :=/= // multi-assign // collect things upto hard boundaries mut collected := []ast.Expr{} - mut op := p.tok.kind - for op !in [.rcbr, .decl_assign, .assign] { - if op == .name { - collected << p.name_expr() - } else { - collected << p.expr(0) - } + for { + collected << p.expr(0) if p.tok.kind == .comma { p.next() } else { break } - op = p.tok.kind } - is_assignment := p.tok.kind in [.decl_assign, .assign] - if is_assignment { + // TODO: Try to merge assign_expr and assign_stmt + if p.tok.kind == .decl_assign || (p.tok.kind == .assign && collected.len > 1) { mut idents := []ast.Ident{} for c in collected { idents << c as ast.Ident } return p.partial_assign_stmt(idents) + } else if p.tok.kind.is_assign() { + epos := p.tok.position() + if collected.len == 1 { + return ast.ExprStmt { + expr: p.assign_expr(collected[0]) + pos: epos + } + } else { + return ast.ExprStmt { + expr: p.assign_expr(ast.ConcatExpr{ + vals: collected + }) + pos: epos + } + } } else { if collected.len == 1 { - epos := p.tok.position() return ast.ExprStmt{ expr: collected[0] - pos: epos + pos: p.tok.position() } } return ast.ExprStmt{ diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index e5ac6739cd..88415db30d 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -138,7 +138,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { } // Infix for precedence < p.tok.precedence() { - if p.tok.kind.is_assign() { + if p.tok.kind.is_assign() && !p.is_stmt_ident { node = p.assign_expr(node) } else if p.tok.kind == .dot { node = p.dot_expr(node) diff --git a/vlib/v/tests/complex_assign_test.v b/vlib/v/tests/complex_assign_test.v index 2d2c1ad0c1..90de5981f8 100644 --- a/vlib/v/tests/complex_assign_test.v +++ b/vlib/v/tests/complex_assign_test.v @@ -7,25 +7,7 @@ fn multireturner(n int, s string) (int, string) { return n + 1, s } -fn test_assign_multireturn_expression() { - a, b := if true { - multireturner(13, 'awesome') - } else { - multireturner(-1, 'notawesome') - } - assert a == 14 - assert b == 'awesome' - - c, d := if false { - multireturner(-1, 'notawesome') - } else if true { - multireturner(17, 'awesomer') - } else { - multireturner(-1, 'notawesome') - } - assert c == 18 - assert d == 'awesomer' - +fn test_assign_multi_expr_func() { e, f := if false { multireturner(-1, 'notawesome') } else if false { @@ -43,63 +25,84 @@ fn test_assign_multireturn_expression() { } assert g == 1 assert h == 'good' +} - i, j := match true { - false { multireturner(100, 'bad') } - else { multireturner(0, 'good') } - } - assert i == 1 - assert j == 'good' - - k, l, m := if true { - 1, 'awesome', [13] - } else { - 0, 'bad', [0] - } - assert k == 1 - assert l == 'awesome' - assert m == [13] - - n, o, p := if false { - 1, 'awesome', [13] - } else { - 0, 'bad', [0] - } - assert n == 0 - assert o == 'bad' - assert p == [0] - - mut var1 := 17 - var2 := 'awesome' - q, r, s := if true { - 1 + var1, var2, [13] - } else { - 0, 'bad', [0] - } - assert q == 18 - assert r == 'awesome' - assert s == [13] - +fn test_assign_multi_expr() { + // helpers val1 := 1 - val2 := 0 - t, u, v := if true { + val2 := 2 + + // simple case for match + a,b,c := match false { + true { 1,2,3 } + false { 4,5,6 } + else { 7,8,9 } + } + assert a == 4 + assert b == 5 + assert c == 6 + + // test with first value `literal` + d, e, f := if true { + 1, 'awesome', [13] + } else { + 0, 'bad', [0] + } + assert d == 1 + assert e == 'awesome' + assert f == [13] + + // test with first value `literal expr` and statement + awesome := 'awesome' + g, h, i := if true { + 1 + val1, awesome, [13] + } else { + 0, 'bad', [0] + } + assert g == 2 + assert h == 'awesome' + assert i == [13] + + // test with first value `.name` + j, k, l := if true { val1, 'awesome', [13] } else { val2, 'bad', [0] } - assert t == val1 - assert u == 'awesome' - assert v == [13] + assert j == 1 + assert k == 'awesome' + assert l == [13] - val3 := Object { name: 'foo', value: 19 } - x, y, z := if true { - 1 + 1, 'awe' + 'some', { val3 | name: 'bar' } + // test with first value name and peek != .comma + m, n, o := if true { + val1 + 1, val1, val1 + } else { + val2, val2, val2 + } + assert m == val1 + 1 + assert n == val1 + assert o == val1 + + // test practical complex expressions + val3 := Object { name: 'initial', value: 19 } + mut q, mut r, mut s := if true { + 1 + 1, 'awe' + 'some', { val3 | name: 'ok' } } else { 0, '0', Object {} } - assert x == 2 - assert y == 'awesome' - assert z.name == 'bar' - assert z.value == 19 + assert q == 2 + assert r == 'awesome' + assert s.name == 'ok' + assert s.value == 19 + // test assign to existing variables + q, r, s = if false { + 0, '0', Object {} + } else { + 5, '55', { val3 | value: 555 } + } + assert q == 5 + assert r == '55' + assert s.value == 555 + assert s.name == 'initial' }