diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 95dfb04baf..bc17664c23 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -755,6 +755,8 @@ pub: pub struct ConcatExpr { pub: vals []Expr +pub mut: + return_type table.Type } pub struct None { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index feed2eb30a..a5097eb501 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1588,6 +1588,13 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) { pub fn (mut c Checker) expr(node ast.Expr) table.Type { match mut node { + ast.AnonFn { + keep_ret_type := c.fn_return_type + c.fn_return_type = it.decl.return_type + c.stmts(it.decl.stmts) + c.fn_return_type = keep_ret_type + return it.typ + } ast.ArrayInit { return c.array_init(mut it) } @@ -1645,6 +1652,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { ast.CharLiteral { return table.byte_type } + ast.ConcatExpr { + return c.concat_expr(mut it) + } ast.EnumVal { return c.enum_val(mut it) } @@ -1730,13 +1740,6 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { it.expr_type = c.expr(it.expr) return table.string_type } - ast.AnonFn { - keep_ret_type := c.fn_return_type - c.fn_return_type = it.decl.return_type - c.stmts(it.decl.stmts) - c.fn_return_type = keep_ret_type - return it.typ - } else { tnode := typeof(node) if tnode != 'unknown v.ast.Expr' { @@ -1858,6 +1861,23 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type { return table.void_type } +pub fn (mut c Checker) concat_expr(concat_expr mut ast.ConcatExpr) table.Type { + mut mr_types := []table.Type{} + for expr in concat_expr.vals { + mr_types << c.expr(expr) + } + if concat_expr.vals.len == 1 { + typ := mr_types[0] + concat_expr.return_type = typ + return typ + } else { + typ := c.table.find_or_register_multi_return(mr_types) + table.new_type(typ) + concat_expr.return_type = typ + return typ + } +} + pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type { node.is_expr = c.expected_type != table.void_type node.expected_type = c.expected_type diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index c66b0857d2..ccf87e0434 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1163,6 +1163,18 @@ fn (g &Gen) autofree_var_call(free_fn_name string, v ast.Var) string { fn (mut g Gen) expr(node ast.Expr) { // println('cgen expr() line_nr=$node.pos.line_nr') match node { + ast.AnonFn { + // TODO: dont fiddle with buffers + pos := g.out.len + def_pos := g.definitions.len + g.stmt(it.decl) + fn_body := g.out.after(pos) + g.out.go_back(fn_body.len) + g.definitions.go_back(g.definitions.len - def_pos) + g.definitions.write(fn_body) + fsym := g.table.get_type_symbol(it.typ) + g.write('&${fsym.name}') + } ast.ArrayInit { g.array_init(it) } @@ -1226,6 +1238,9 @@ fn (mut g Gen) expr(node ast.Expr) { ast.CharLiteral { g.write("'$it.val'") } + ast.ConcatExpr { + g.concat_expr(it) + } ast.EnumVal { // g.write('${it.mod}${it.enum_name}_$it.val') styp := g.typ(it.typ) @@ -1375,18 +1390,6 @@ fn (mut g Gen) expr(node ast.Expr) { ast.TypeOf { g.typeof_expr(it) } - ast.AnonFn { - // TODO: dont fiddle with buffers - pos := g.out.len - def_pos := g.definitions.len - g.stmt(it.decl) - fn_body := g.out.after(pos) - g.out.go_back(fn_body.len) - g.definitions.go_back(g.definitions.len - def_pos) - g.definitions.write(fn_body) - fsym := g.table.get_type_symbol(it.typ) - g.write('&${fsym.name}') - } else { // #printf("node=%d\n", node.typ); println(term.red('cgen.expr(): bad node ' + typeof(node))) @@ -1524,7 +1527,7 @@ fn (mut g Gen) assign_expr(node ast.AssignExpr) { g.or_block(tmp_opt, or_stmts, return_type) unwrapped_type_str := g.typ(return_type.set_flag(.unset)) ident := node.left as ast.Ident - if ident.info is ast.IdentVar { + if ident.kind != .blank_ident && ident.info is ast.IdentVar { ident_var := ident.info as ast.IdentVar if ident_var.is_optional { // var is already an optional, just copy the value @@ -1847,6 +1850,26 @@ fn (mut g Gen) ident(node ast.Ident) { g.write(g.get_ternary_name(name)) } +fn (mut g Gen) concat_expr(node ast.ConcatExpr) { + styp := g.typ(node.return_type) + sym := g.table.get_type_symbol(node.return_type) + is_multi := sym.kind == .multi_return + + if !is_multi { + g.expr(node.vals[0]) + } else { + g.write('($styp){') + for i, expr in node.vals { + g.write('.arg$i=') + g.expr(expr) + if i < node.vals.len - 1 { + g.write(',') + } + } + g.write('}') + } +} + fn (mut g Gen) if_expr(node ast.IfExpr) { if node.is_expr || g.inside_ternary != 0 { g.inside_ternary++ diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index fb4c77d3b8..cba046420f 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -6,13 +6,21 @@ module parser import v.ast fn (mut p Parser) assign_stmt() ast.Stmt { - is_static := p.tok.kind == .key_static - if is_static { - p.next() + return p.partial_assign_stmt([]) +} + +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 } - idents := p.parse_assign_lhs() - op := p.tok.kind - p.next() // :=, = + p.next() pos := p.tok.position() exprs := p.parse_assign_rhs() is_decl := op == .decl_assign @@ -46,7 +54,7 @@ fn (mut p Parser) assign_stmt() ast.Stmt { right: exprs op: op pos: pos - is_static: is_static + is_static: false // individual idents may be static } } @@ -72,31 +80,9 @@ pub fn (mut p Parser) assign_expr(left ast.Expr) ast.AssignExpr { return node } -fn (mut p Parser) parse_assign_lhs() []ast.Ident { - mut idents := []ast.Ident{} - for { - is_mut := p.tok.kind == .key_mut - if is_mut { - p.next() - } - is_static := p.tok.kind == .key_static - if is_static { - p.next() - } - mut ident := p.parse_ident(false, false) - ident.is_mut = is_mut - ident.info = ast.IdentVar{ - is_mut: is_mut - is_static: is_static - } - idents << ident - if p.tok.kind == .comma { - p.next() - } else { - break - } - } - return idents +fn (mut p Parser) parse_assign_ident() ast.Ident { + /// returns a single parsed ident + return p.parse_ident(false, false) } // right hand side of `=` or `:=` in `a,b,c := 1,2,3` diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 1cceb0ca13..e62a64f1c7 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -447,9 +447,12 @@ pub fn (mut p Parser) stmt() ast.Stmt { return p.for_stmt() } .name { - if p.peek_tok.kind in [.decl_assign, .comma] { + if p.peek_tok.kind == .decl_assign { // `x := ...` return p.assign_stmt() + } else if p.peek_tok.kind == .comma { + // `a, b ...` + return p.parse_comma_separated() } else if p.peek_tok.kind == .colon { // `label:` name := p.check_name() @@ -524,15 +527,11 @@ pub fn (mut p Parser) stmt() ast.Stmt { name: name } } + .key_const { + p.error_with_pos('const can only be defined at the top level (outside of functions)', p.tok.position()) + } else { - if p.tok.kind == .key_const { - p.error_with_pos('const can only be defined at the top level (outside of functions)', p.tok.position()) - } - epos := p.tok.position() - return ast.ExprStmt{ - expr: p.expr(0) - pos: epos - } + return p.parse_comma_separated() } } } @@ -645,27 +644,93 @@ pub fn (mut p Parser) warn_with_pos(s string, pos token.Position) { } } -pub fn (mut p Parser) parse_ident(is_c, is_js bool) ast.Ident { - // p.warn('name ') - pos := p.tok.position() - mut name := p.check_name() - if name == '_' { - return ast.Ident{ - name: '_' - kind: .blank_ident - pos: pos +fn (mut p Parser) parse_comma_separated() 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) + } + 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 { + mut idents := []ast.Ident{} + for c in collected { + idents << c as ast.Ident + } + return p.partial_assign_stmt(idents) + } else { + if collected.len == 1 { + epos := p.tok.position() + return ast.ExprStmt{ + expr: collected[0] + pos: epos + } + } + return ast.ExprStmt{ + expr: ast.ConcatExpr { + vals: collected + } + pos: p.tok.position() } } - if p.expr_mod.len > 0 { - name = '${p.expr_mod}.$name' +} + +pub fn (mut p Parser) parse_ident(is_c, is_js bool) ast.Ident { + // p.warn('name ') + is_mut := p.tok.kind == .key_mut + if is_mut { + p.next() } - return ast.Ident{ - kind: .unresolved - name: name - is_c: is_c - is_js: is_js - mod: p.mod - pos: pos + is_static := p.tok.kind == .key_static + if is_static { + p.next() + } + if p.tok.kind == .name { + pos := p.tok.position() + mut name := p.check_name() + if name == '_' { + return ast.Ident{ + name: '_' + kind: .blank_ident + pos: pos + info: ast.IdentVar { + is_mut: false + is_static: false + } + } + } + if p.expr_mod.len > 0 { + name = '${p.expr_mod}.$name' + } + mut ident := ast.Ident{ + kind: .unresolved + name: name + is_c: is_c + is_js: is_js + mod: p.mod + pos: pos + } + ident.is_mut = is_mut + ident.info = ast.IdentVar{ + is_mut: is_mut + is_static: is_static + } + return ident + } else { + p.error('unexpected token `$p.tok.lit`') } } diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 4001c093c9..e5ac6739cd 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -15,6 +15,9 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { p.is_stmt_ident = false // Prefix match p.tok.kind { + .key_mut, .key_static { + node = p.parse_assign_ident() + } .name { node = p.name_expr() p.is_stmt_ident = is_stmt_ident diff --git a/vlib/v/tests/complex_assign_test.v b/vlib/v/tests/complex_assign_test.v index c675960d02..2d2c1ad0c1 100644 --- a/vlib/v/tests/complex_assign_test.v +++ b/vlib/v/tests/complex_assign_test.v @@ -1,3 +1,7 @@ +struct Object { + name string + value int +} fn multireturner(n int, s string) (int, string) { return n + 1, s @@ -47,12 +51,55 @@ fn test_assign_multireturn_expression() { assert i == 1 assert j == 'good' - // TODO: returning non-function calls does not work yet due to parsing issues - /* - e, f := if true { - 1, 'awesome' + k, l, m := if true { + 1, 'awesome', [13] } else { - 0, 'bad' + 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] + + val1 := 1 + val2 := 0 + t, u, v := if true { + val1, 'awesome', [13] + } else { + val2, 'bad', [0] + } + assert t == val1 + assert u == 'awesome' + assert v == [13] + + val3 := Object { name: 'foo', value: 19 } + x, y, z := if true { + 1 + 1, 'awe' + 'some', { val3 | name: 'bar' } + } else { + 0, '0', Object {} + } + assert x == 2 + assert y == 'awesome' + assert z.name == 'bar' + assert z.value == 19 + }