From d478b449159e38d82c7f670e2014d14b8767807b Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Tue, 16 Jun 2020 21:20:16 +1000 Subject: [PATCH] parser/checker/gen: merge AssignExpr & AssignStmt into just AssignStmt --- vlib/v/ast/ast.v | 32 +- vlib/v/ast/str.v | 13 +- vlib/v/checker/checker.v | 249 +++++++-------- .../checker/tests/assign_expr_type_err_i.out | 2 +- vlib/v/checker/tests/blank_modify.out | 2 +- vlib/v/checker/tests/cannot_assign_array.out | 2 +- vlib/v/checker/tests/unreachable_code.out | 4 +- .../tests/void_function_assign_to_string.out | 4 +- vlib/v/fmt/fmt.v | 27 +- vlib/v/gen/cgen.v | 285 +++++++----------- vlib/v/gen/fn.v | 6 - vlib/v/gen/js/js.v | 87 +++--- vlib/v/gen/x64/gen.v | 30 +- vlib/v/gen/x64/tests/general.vv | 2 +- vlib/v/parser/assign.v | 191 +++++------- vlib/v/parser/for.v | 6 +- vlib/v/parser/parser.v | 136 ++++----- vlib/v/parser/pratt.v | 11 +- 18 files changed, 455 insertions(+), 634 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 5cd81406ff..f1a5173c70 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -9,7 +9,7 @@ import v.errors pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl -pub type Expr = AnonFn | ArrayInit | AsCast | AssignExpr | Assoc | BoolLiteral | CallExpr | +pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | @@ -42,10 +42,13 @@ pub: // Stand-alone expression in a statement list. pub struct ExprStmt { pub: - expr Expr - pos token.Position + expr Expr + pos token.Position + // treat like expr (dont add trailing `;`) + // is used for `x++` in `for x:=1; ; x++` + is_expr bool pub mut: - typ table.Type + typ table.Type } pub struct IntegerLiteral { @@ -510,7 +513,7 @@ pub: has_init bool cond Expr // i < 10; has_cond bool - inc Expr // i++; + inc Stmt // i++; i += 2 has_inc bool stmts []Stmt pos token.Position @@ -543,10 +546,11 @@ pub: op token.Kind pos token.Position pub mut: - left []Ident + left []Expr left_types []table.Type right_types []table.Type is_static bool // for translated code only + is_simple bool // `x+=2` in `for x:=1; ; x+=2` has_cross_var bool } @@ -637,17 +641,6 @@ pub: expr Expr } -pub struct AssignExpr { -pub: - op token.Kind - pos token.Position - left Expr - val Expr -pub mut: - left_type table.Type - right_type table.Type -} - pub struct GoStmt { pub: call_expr Expr @@ -812,7 +805,7 @@ pub struct SqlExpr { } [inline] -pub fn expr_is_blank_ident(expr Expr) bool { +pub fn (expr Expr) is_blank_ident() bool { match expr { Ident { return it.kind == .blank_ident } else { return false } @@ -829,9 +822,6 @@ pub fn (expr Expr) position() token.Position { return it.pos } // ast.Ident { } - AssignExpr { - return it.pos - } CastExpr { return it.pos } diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index a61e5d4661..dd0f31885a 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -224,12 +224,15 @@ pub fn (node Stmt) str() string { match node { AssignStmt { mut out := '' - for i, ident in it.left { - var_info := ident.var_info() - if var_info.is_mut { - out += 'mut ' + for i, left in it.left { + if left is Ident { + ident := left as Ident + var_info := ident.var_info() + if var_info.is_mut { + out += 'mut ' + } } - out += ident.name + out += left.str() if i < it.left.len - 1 { out += ',' } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 80f7391489..8c1b6f4dd6 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -667,91 +667,6 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) { } } -fn (mut c Checker) assign_expr(mut assign_expr ast.AssignExpr) { - c.expected_type = table.void_type - left_type := c.unwrap_generic(c.expr(assign_expr.left)) - c.expected_type = left_type - if ast.expr_is_blank_ident(assign_expr.left) { - c.expected_type = table.Type(0) - } - assign_expr.left_type = left_type - // println('setting exp type to $c.expected_type $t.name') - right_type := c.check_expr_opt_call(assign_expr.val, c.unwrap_generic(c.expr(assign_expr.val))) - assign_expr.right_type = right_type - right := c.table.get_type_symbol(right_type) - left := c.table.get_type_symbol(left_type) - match assign_expr.left { - ast.Ident { - if it.kind == .blank_ident { - if assign_expr.op != .assign { - c.error('cannot modify blank `_` variable', it.pos) - } - return - } - } - ast.PrefixExpr { - // Do now allow `*x = y` outside `unsafe` - if it.op == .mul && !c.inside_unsafe { - c.error('modifying variables via deferencing can only be done in `unsafe` blocks', - assign_expr.pos) - } - } - else {} - } - // Make sure the variable is mutable - c.fail_if_immutable(assign_expr.left) - // Single side check - match assign_expr.op { - .assign {} // No need to do single side check for =. But here put it first for speed. - .plus_assign { - if !left.is_number() && left_type != table.string_type && !left.is_pointer() { - c.error('operator += not defined on left operand type `$left.name`', assign_expr.left.position()) - } else if !right.is_number() && right_type != table.string_type && !right.is_pointer() { - c.error('operator += not defined on right operand type `$right.name`', assign_expr.val.position()) - } - if assign_expr.val is ast.IntegerLiteral && assign_expr.val.str().int() == 1 { - c.error('use `++` instead of `+= 1`', assign_expr.pos) - } - } - .minus_assign { - if !left.is_number() && !left.is_pointer() { - c.error('operator -= not defined on left operand type `$left.name`', assign_expr.left.position()) - } else if !right.is_number() && !right.is_pointer() { - c.error('operator -= not defined on right operand type `$right.name`', assign_expr.val.position()) - } - if assign_expr.val is ast.IntegerLiteral && assign_expr.val.str().int() == 1 { - c.error('use `--` instead of `-= 1`', assign_expr.pos) - } - } - .mult_assign, .div_assign { - if !left.is_number() { - c.error('operator ${assign_expr.op.str()} not defined on left operand type `$left.name`', - assign_expr.left.position()) - } else if !right.is_number() { - c.error('operator ${assign_expr.op.str()} not defined on right operand type `$right.name`', - assign_expr.val.position()) - } - } - .and_assign, .or_assign, .xor_assign, .mod_assign, .left_shift_assign, .right_shift_assign { - if !left.is_int() { - c.error('operator ${assign_expr.op.str()} not defined on left operand type `$left.name`', - assign_expr.left.position()) - } else if !right.is_int() { - c.error('operator ${assign_expr.op.str()} not defined on right operand type `$right.name`', - assign_expr.val.position()) - } - } - else {} - } - // Dual sides check (compatibility check) - if !c.check_types(right_type, left_type) { - left_type_sym := c.table.get_type_symbol(left_type) - right_type_sym := c.table.get_type_symbol(right_type) - c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name`', - assign_expr.val.position()) - } -} - pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type { c.stmts(call_expr.or_block.stmts) if call_expr.is_method { @@ -1170,7 +1085,9 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) t c.check_or_expr(call_expr.or_block, ret_type) } // remove optional flag - return ret_type.clear_flag(.optional) + // return ret_type.clear_flag(.optional) + // TODO: currently unwrapped in assign, would need to refactor assign to unwrap here + return ret_type } else if call_expr.or_block.kind == .block { c.error('unexpected `or` block, the function `$call_expr.name` does not return an optional', call_expr.pos) @@ -1378,63 +1295,128 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { mut right_len := assign_stmt.right.len if right_first is ast.CallExpr || right_first is ast.IfExpr || right_first is ast.MatchExpr { right_type0 := c.expr(right_first) - assign_stmt.right_types = [right_type0] + assign_stmt.right_types = [c.check_expr_opt_call(right_first, right_type0)] right_type_sym0 := c.table.get_type_symbol(right_type0) - if right_type0 == table.void_type { - right_len = 0 - } else if right_type_sym0.kind == .multi_return { + if right_type_sym0.kind == .multi_return { assign_stmt.right_types = right_type_sym0.mr_info().types right_len = assign_stmt.right_types.len } - if assign_stmt.left.len != right_len { - if right_first is ast.CallExpr { - call_expr := assign_stmt.right[0] as ast.CallExpr - c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${call_expr.name}()` returns $right_len value(s)', - assign_stmt.pos) - } else { - c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)', - assign_stmt.pos) - } - return + else if right_type0 == table.void_type { right_len=0 } + } + if assign_stmt.left.len != right_len { + if right_first is ast.CallExpr { + call_expr := assign_stmt.right[0] as ast.CallExpr + c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${call_expr.name}()` returns $right_len value(s)', + assign_stmt.pos) + } else { + c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)', + assign_stmt.pos) } - } else if assign_stmt.left.len != right_len { - c.error('assignment mismatch: $assign_stmt.left.len variable(s) $assign_stmt.right.len value(s)', - assign_stmt.pos) return } - mut scope := c.file.scope.innermost(assign_stmt.pos.pos) - for i, _ in assign_stmt.left { - mut ident := assign_stmt.left[i] - if assign_stmt.right_types.len < right_len { + is_decl := assign_stmt.op == .decl_assign + for i, left in assign_stmt.left { + is_blank_ident := left.is_blank_ident() + mut left_type := table.void_type + if !is_decl && !is_blank_ident { + left_type = c.expr(left) + c.expected_type = c.unwrap_generic(left_type) + } + if assign_stmt.right_types.len < assign_stmt.left.len { // first type or multi return types added above right_type := c.expr(assign_stmt.right[i]) assign_stmt.right_types << c.check_expr_opt_call(assign_stmt.right[i], right_type) - } else if i < assign_stmt.right.len { // only once for multi return - c.check_expr_opt_call(assign_stmt.right[i], assign_stmt.right_types[i]) } - mut val_type := assign_stmt.right_types[i] - mut ident_var_info := ident.var_info() - is_decl := assign_stmt.op == .decl_assign + right := if i < assign_stmt.right.len { assign_stmt.right[i] } else { assign_stmt.right[0] } + right_type := assign_stmt.right_types[i] if is_decl { - if ident.kind != .blank_ident { - // check variable name for beginning with capital letter 'Abc' - c.check_valid_snake_case(ident.name, 'variable name', ident.pos) - } - val_type = c.table.mktyp(val_type) + left_type = c.table.mktyp(right_type) + // we are unwrapping here instead if check_expr_opt_call currently + if left_type.has_flag(.optional) { left_type = left_type.clear_flag(.optional) } } else { - c.fail_if_immutable(ident) - var_type := c.expr(ident) - assign_stmt.left_types << var_type - if ident.kind != .blank_ident && !c.check_types(val_type, var_type) { - val_type_sym := c.table.get_type_symbol(val_type) - var_type_sym := c.table.get_type_symbol(var_type) - c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', - assign_stmt.pos) - } + // Make sure the variable is mutable + c.fail_if_immutable(left) + // left_type = c.expr(left) + } + assign_stmt.left_types << left_type + match left { + ast.Ident { + if it.kind == .blank_ident { + left_type = right_type + assign_stmt.left_types[i] = right_type + if assign_stmt.op !in [.assign, .decl_assign] { + c.error('cannot modify blank `_` identifier', it.pos) + } + } + else { + if is_decl { c.check_valid_snake_case(it.name, 'variable name', it.pos) } + mut scope := c.file.scope.innermost(assign_stmt.pos.pos) + mut ident_var_info := it.var_info() + ident_var_info.typ = left_type + it.info = ident_var_info + scope.update_var_type(it.name, left_type) + } + } + ast.PrefixExpr { + // Do now allow `*x = y` outside `unsafe` + if it.op == .mul && !c.inside_unsafe { + c.error('modifying variables via deferencing can only be done in `unsafe` blocks', assign_stmt.pos) + } + } + else {} + } + + left_type_unwrapped := c.unwrap_generic(left_type) + right_type_unwrapped := c.unwrap_generic(right_type) + left_sym := c.table.get_type_symbol(left_type_unwrapped) + right_sym := c.table.get_type_symbol(right_type_unwrapped) + // Single side check + match assign_stmt.op { + .assign {} // No need to do single side check for =. But here put it first for speed. + .plus_assign { + if !left_sym.is_number() && left_type != table.string_type && !left_sym.is_pointer() { + c.error('operator += not defined on left operand type `$left_sym.name`', left.position()) + } else if !right_sym.is_number() && right_type != table.string_type && !right_sym.is_pointer() { + c.error('operator += not defined on right operand type `$right_sym.name`', right.position()) + } + if right is ast.IntegerLiteral && right.str().int() == 1 { + c.error('use `++` instead of `+= 1`', assign_stmt.pos) + } + } + .minus_assign { + if !left_sym.is_number() && !left_sym.is_pointer() { + c.error('operator -= not defined on left operand type `$left_sym.name`', left.position()) + } else if !right_sym.is_number() && !right_sym.is_pointer() { + c.error('operator -= not defined on right operand type `$right_sym.name`', right.position()) + } + if right is ast.IntegerLiteral && right.str().int() == 1 { + c.error('use `--` instead of `-= 1`', assign_stmt.pos) + } + } + .mult_assign, .div_assign { + if !left_sym.is_number() { + c.error('operator ${assign_stmt.op.str()} not defined on left operand type `$left_sym.name`', + left.position()) + } else if !right_sym.is_number() { + c.error('operator ${assign_stmt.op.str()} not defined on right operand type `$right_sym.name`', + right.position()) + } + } + .and_assign, .or_assign, .xor_assign, .mod_assign, .left_shift_assign, .right_shift_assign { + if !left_sym.is_int() { + c.error('operator ${assign_stmt.op.str()} not defined on left operand type `$left_sym.name`', + left.position()) + } else if !right_sym.is_int() { + c.error('operator ${assign_stmt.op.str()} not defined on right operand type `$right_sym.name`', + right.position()) + } + } + else {} + } + // Dual sides check (compatibility check) + if !is_blank_ident && !c.check_types(right_type_unwrapped, left_type_unwrapped) { + c.error('cannot assign `$right_sym.name` to `${left.str()}` of type `$left_sym.name`', + right.position()) } - ident_var_info.typ = val_type - ident.info = ident_var_info - assign_stmt.left[i] = ident - scope.update_var_type(ident.name, val_type) } c.expected_type = table.void_type } @@ -1678,8 +1660,8 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.in_for_count++ c.stmt(it.init) c.expr(it.cond) - // c.stmt(it.inc) - c.expr(it.inc) + c.stmt(it.inc) + // c.expr(it.inc) c.stmts(it.stmts) c.in_for_count-- } @@ -1845,9 +1827,6 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { return it.typ.to_ptr() // return it.typ } - ast.AssignExpr { - c.assign_expr(mut it) - } ast.Assoc { scope := c.file.scope.innermost(it.pos.pos) v := scope.find_var(it.var_name) or { diff --git a/vlib/v/checker/tests/assign_expr_type_err_i.out b/vlib/v/checker/tests/assign_expr_type_err_i.out index dff3efe002..cc6bb4fe67 100644 --- a/vlib/v/checker/tests/assign_expr_type_err_i.out +++ b/vlib/v/checker/tests/assign_expr_type_err_i.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/assign_expr_type_err_i.v:3:9: error: cannot assign `string` to variable `foo` of type `f64` +vlib/v/checker/tests/assign_expr_type_err_i.v:3:9: error: cannot assign `string` to `foo` of type `f64` 1 | fn main() { 2 | mut foo := 1.5 3 | foo += 'hello' diff --git a/vlib/v/checker/tests/blank_modify.out b/vlib/v/checker/tests/blank_modify.out index 12b7cc9552..bbf627cb0d 100644 --- a/vlib/v/checker/tests/blank_modify.out +++ b/vlib/v/checker/tests/blank_modify.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/blank_modify.v:2:2: error: cannot modify blank `_` variable +vlib/v/checker/tests/blank_modify.v:2:2: error: cannot modify blank `_` identifier 1 | fn main() { 2 | _ += 1 | ^ diff --git a/vlib/v/checker/tests/cannot_assign_array.out b/vlib/v/checker/tests/cannot_assign_array.out index 19a9f8c668..fadf5e887f 100644 --- a/vlib/v/checker/tests/cannot_assign_array.out +++ b/vlib/v/checker/tests/cannot_assign_array.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/cannot_assign_array.v:9:11: error: cannot assign `array_fixed_f64_8` to variable `ctx.vb` of type `string` +vlib/v/checker/tests/cannot_assign_array.v:9:11: error: cannot assign `array_fixed_f64_8` to `ctx.vb` of type `string` 7 | mut ctx := Context{} 8 | x := 2.32 9 | ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]!! diff --git a/vlib/v/checker/tests/unreachable_code.out b/vlib/v/checker/tests/unreachable_code.out index 7fbe4da619..d068f8e622 100644 --- a/vlib/v/checker/tests/unreachable_code.out +++ b/vlib/v/checker/tests/unreachable_code.out @@ -1,7 +1,7 @@ -vlib/v/checker/tests/unreachable_code.v:3:7: error: unreachable code +vlib/v/checker/tests/unreachable_code.v:3:4: error: unreachable code 1 | fn foo() int { 2 | return if 1 == 1 { 1 } else { 2 } 3 | a := 1 - | ^ + | ~~ 4 | println(a) 5 | } diff --git a/vlib/v/checker/tests/void_function_assign_to_string.out b/vlib/v/checker/tests/void_function_assign_to_string.out index 35b5e75201..c1483c74e0 100644 --- a/vlib/v/checker/tests/void_function_assign_to_string.out +++ b/vlib/v/checker/tests/void_function_assign_to_string.out @@ -1,7 +1,7 @@ -vlib/v/checker/tests/void_function_assign_to_string.v:6:6: error: cannot assign `void` to variable `a` of type `string` +vlib/v/checker/tests/void_function_assign_to_string.v:6:4: error: assignment mismatch: 1 variable(s) but `x()` returns 0 value(s) 4 | fn main(){ 5 | mut a := '' 6 | a = x(1,2) // hello - | ~~~~~~ + | ^ 7 | eprintln('a: $a') 8 | } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index a10d22da5e..3a21cd35c7 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -160,14 +160,20 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { } match node { ast.AssignStmt { - for i, ident in it.left { - var_info := ident.var_info() - if var_info.is_mut { - f.write('mut ') + for i, left in it.left { + if left is ast.Ident { + ident := left as ast.Ident + var_info := ident.var_info() + if var_info.is_mut { + f.write('mut ') + } + f.expr(left) + if i < it.left.len - 1 { + f.write(', ') + } } - f.expr(ident) - if i < it.left.len - 1 { - f.write(', ') + else { + f.expr(left) } } f.is_assign = true @@ -260,7 +266,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { f.write('; ') f.expr(it.cond) f.write('; ') - f.expr(it.inc) + f.stmt(it.inc) f.writeln(' {') f.stmts(it.stmts) f.writeln('}') @@ -543,11 +549,6 @@ pub fn (mut f Fmt) expr(node ast.Expr) { f.expr(it.expr) f.write(' as $type_str') } - ast.AssignExpr { - f.expr(it.left) - f.write(' $it.op.str() ') - f.expr(it.val) - } ast.Assoc { f.writeln('{') // f.indent++ diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 7c5303bfab..6b8a96fbd4 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -646,7 +646,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { } ast.ExprStmt { g.expr(it.expr) - if g.inside_ternary == 0 && !(it.expr is ast.IfExpr) { + if g.inside_ternary == 0 && !it.is_expr && !(it.expr is ast.IfExpr) { g.writeln(';') } } @@ -700,7 +700,8 @@ fn (mut g Gen) stmt(node ast.Stmt) { } g.write('; ') if it.has_inc { - g.expr(it.inc) + + g.stmt(it.inc) } g.writeln(') {') g.stmts(it.stmts) @@ -1007,6 +1008,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.write('static ') } mut return_type := table.void_type + op := if assign_stmt.op == .decl_assign { token.Kind.assign } else { assign_stmt.op } + is_decl := assign_stmt.op == .decl_assign match assign_stmt.right[0] { ast.CallExpr { return_type = it.return_type } ast.IfExpr { return_type = it.typ } @@ -1016,9 +1019,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { // json_test failed w/o this check if return_type != table.void_type && return_type != 0 { sym := g.table.get_type_symbol(return_type) - // the left vs. right is ugly and should be removed - if sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len || assign_stmt.left.len > - 1 { + if sym.kind == .multi_return { // multi return // TODO Handle in if_expr is_optional := return_type.has_flag(.optional) @@ -1028,22 +1029,19 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.is_assign_rhs = true g.expr(assign_stmt.right[0]) g.is_assign_rhs = false - if is_optional && assign_stmt.right[0] is ast.CallExpr { - val := assign_stmt.right[0] as ast.CallExpr - return_type = val.return_type - g.or_block(mr_var_name, val.or_block, return_type) - } g.writeln(';') - for i, ident in assign_stmt.left { - if ident.kind == .blank_ident { - continue + for i, lx in assign_stmt.left { + match lx { + ast.Ident { + if it.kind == .blank_ident { continue } + } + else {} } - ident_var_info := ident.var_info() - styp := g.typ(ident_var_info.typ) + styp := g.typ(assign_stmt.left_types[i]) if assign_stmt.op == .decl_assign { g.write('$styp ') } - g.expr(ident) + g.expr(lx) if is_optional { mr_base_styp := g.base_type(return_type) g.writeln(' = (*(${mr_base_styp}*)${mr_var_name}.data).arg$i;') @@ -1054,20 +1052,40 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { return } } + // TODO: non idents on left (exprs) if assign_stmt.has_cross_var { - for ident in assign_stmt.left { - type_str := g.typ(ident.var_info().typ) - g.writeln('$type_str _var_$ident.pos.pos = $ident.name;') + for i,left in assign_stmt.left { + match left { + ast.Ident { + styp := g.typ(assign_stmt.left_types[i]) + g.writeln('$styp _var_${it.pos.pos} = $it.name;') + } + else {} + } } } // `a := 1` | `a,b := 1,2` - for i, ident in assign_stmt.left { + for i, left in assign_stmt.left { + mut var_type := assign_stmt.left_types[i] + val_type := assign_stmt.right_types[i] val := assign_stmt.right[i] - ident_var_info := ident.var_info() - styp := g.typ(ident_var_info.typ) mut is_call := false - blank_assign := ident.kind == .blank_ident + mut blank_assign := false + mut ident := ast.Ident{} + if left is ast.Ident { + ident = left as ast.Ident + // id_info := ident.var_info() + // var_type = id_info.typ + blank_assign = ident.kind == .blank_ident + } + styp := g.typ(var_type) + mut is_fixed_array_init := false + mut has_val := false match val { + ast.ArrayInit { + is_fixed_array_init = it.is_fixed + has_val = it.has_val + } ast.CallExpr { is_call = true return_type = it.return_type @@ -1092,7 +1110,11 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } else {} } - g.is_assign_rhs = true + right_sym := g.table.get_type_symbol(val_type) + g.is_assign_lhs = true + if val_type.has_flag(.optional) { + g.right_is_opt = true + } if blank_assign { if is_call { g.expr(val) @@ -1101,28 +1123,35 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.expr(val) g.writeln(';}') } - } else { - right_sym := g.table.get_type_symbol(assign_stmt.right_types[i]) - mut is_fixed_array_init := false - mut has_val := false - match val { - ast.ArrayInit { - is_fixed_array_init = it.is_fixed - has_val = it.has_val - } - else {} + } + else if right_sym.kind == .array_fixed && assign_stmt.op == .assign { + right := val as ast.ArrayInit + for j, expr in right.exprs { + g.expr(left) + g.write('[$j] = ') + g.expr(expr) + g.writeln(';') } + } else { + g.assign_op = assign_stmt.op is_inside_ternary := g.inside_ternary != 0 - cur_line := if is_inside_ternary { + cur_line := if is_inside_ternary && is_decl { g.register_ternary_name(ident.name) g.empty_line = false g.go_before_ternary() } else { '' } - is_decl := assign_stmt.op == .decl_assign - if right_sym.kind == .function { - if is_inside_ternary { + mut str_add := false + if var_type == table.string_type_idx && assign_stmt.op == .plus_assign { + // str += str2 => `str = string_add(str, str2)` + g.expr(left) + g.write(' = /*f*/string_add(') + str_add = true + } + + if right_sym.kind == .function && is_decl { + if is_inside_ternary && is_decl { g.out.write(tabs[g.indent - g.inside_ternary]) } func := right_sym.info as table.FnType @@ -1139,56 +1168,69 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } g.write('$styp ') } - g.ident(ident) + g.expr(left) } - if is_inside_ternary { + if is_inside_ternary && is_decl { g.write(';\n$cur_line') g.out.write(tabs[g.indent]) - g.ident(ident) + g.expr(left) } + g.is_assign_lhs = false + g.is_assign_rhs = true + + if !g.is_array_set && !str_add { + g.write(' $op ') + } else if str_add { + g.write(', ') + } + mut cloned := false if g.autofree && right_sym.kind in [.array, .string] { - if g.gen_clone_assignment(val, right_sym, true) { - g.writeln(';') - // g.expr_var_name = '' - return + if g.gen_clone_assignment(val, right_sym, false) { + cloned = true } } - if is_fixed_array_init { - if has_val { - g.write(' = ') - g.expr(val) - } else { - g.write(' = {0}') - } - } else { - g.write(' = ') + unwrap_optional := !var_type.has_flag(.optional) && val_type.has_flag(.optional) + if unwrap_optional { g.write('*($styp*)') } + if !cloned { if is_decl { - g.expr(val) + if is_fixed_array_init && !has_val { g.write('{0}') } + else { g.expr(val) } } else { if assign_stmt.has_cross_var { g.gen_cross_tmp_variable(assign_stmt.left, val) } else { - g.expr_with_cast(val, assign_stmt.left_types[i], ident_var_info.typ) + g.expr_with_cast(val, val_type, var_type) } } } + if unwrap_optional { g.write('.data') } + if g.is_array_set { + g.write(' })') + g.is_array_set = false + } else if str_add { + g.write(')') + } } + g.right_is_opt = false g.is_assign_rhs = false - if g.inside_ternary == 0 { + if g.inside_ternary == 0 && !assign_stmt.is_simple { g.writeln(';') } } } -fn (mut g Gen) gen_cross_tmp_variable(idents []ast.Ident, val ast.Expr) { +fn (mut g Gen) gen_cross_tmp_variable(left []ast.Expr, val ast.Expr) { match val { ast.Ident { mut has_var := false - for ident in idents { - if it.name == ident.name { - g.write('_var_${ident.pos.pos}') - has_var = true - break + for lx in left { + if lx is ast.Ident { + ident := lx as ast.Ident + if it.name == ident.name { + g.write('_var_${ident.pos.pos}') + has_var = true + break + } } } if !has_var { @@ -1196,16 +1238,16 @@ fn (mut g Gen) gen_cross_tmp_variable(idents []ast.Ident, val ast.Expr) { } } ast.InfixExpr { - g.gen_cross_tmp_variable(idents, it.left) + g.gen_cross_tmp_variable(left, it.left) g.write(it.op.str()) - g.gen_cross_tmp_variable(idents, it.right) + g.gen_cross_tmp_variable(left, it.right) } ast.PrefixExpr { g.write(it.op.str()) - g.gen_cross_tmp_variable(idents, it.right) + g.gen_cross_tmp_variable(left, it.right) } ast.PostfixExpr { - g.gen_cross_tmp_variable(idents, it.expr) + g.gen_cross_tmp_variable(left, it.expr) g.write(it.op.str()) } else { @@ -1360,9 +1402,6 @@ fn (mut g Gen) expr(node ast.Expr) { ast.AsCast { g.as_cast(it) } - ast.AssignExpr { - g.assign_expr(it) - } ast.Assoc { g.assoc(it) } @@ -1638,112 +1677,6 @@ fn (mut g Gen) enum_expr(node ast.Expr) { } } -fn (mut g Gen) assign_expr(node ast.AssignExpr) { - // g.write('/*assign_expr*/') - mut is_call := false - mut or_block := ast.OrExpr{} - mut return_type := table.void_type - match node.val { - ast.CallExpr { - is_call = true - or_block = it.or_block - return_type = it.return_type - } - else {} - } - gen_or := is_call && return_type.has_flag(.optional) - tmp_opt := if gen_or { g.new_tmp_var() } else { '' } - if gen_or { - rstyp := g.typ(return_type) - g.write('/*q*/ $rstyp $tmp_opt = ') - } - g.is_assign_rhs = true - if ast.expr_is_blank_ident(node.left) { - if is_call { - g.expr(node.val) - } else { - // g.write('{${g.typ(node.left_type)} _ = ') - g.write('{') - g.expr(node.val) - g.writeln(';}') - } - } else { - g.is_assign_lhs = true - if node.right_type.has_flag(.optional) { - g.right_is_opt = true - } - mut str_add := false - if node.left_type == table.string_type_idx && node.op == .plus_assign { - // str += str2 => `str = string_add(str, str2)` - g.expr(node.left) - g.write(' = /*f*/string_add(') - str_add = true - } - right_sym := g.table.get_type_symbol(node.right_type) - if right_sym.kind == .array_fixed && node.op == .assign { - right := node.val as ast.ArrayInit - for j, expr in right.exprs { - g.expr(node.left) - g.write('[$j] = ') - g.expr(expr) - g.writeln(';') - } - } else { - g.assign_op = node.op - if !gen_or { - // Don't need to generate `var = ` in `or {}` expressions, since we are doing - // `Option_X tmp = ...; var = *(X*)tmp.data;` - g.expr(node.left) - // arr[i] = val => `array_set(arr, i, val)`, not `array_get(arr, i) = val` - if !g.is_array_set && !str_add { - g.write(' $node.op.str() ') - } else if str_add { - g.write(', ') - } - } - g.is_assign_lhs = false - // right_sym := g.table.get_type_symbol(node.right_type) - // left_sym := g.table.get_type_symbol(node.left_type) - mut cloned := false - // !g.is_array_set - if g.autofree && right_sym.kind in [.array, .string] { - if g.gen_clone_assignment(node.val, right_sym, false) { - cloned = true - } - } - if !cloned { - g.expr_with_cast(node.val, node.right_type, node.left_type) - } - if g.is_array_set { - g.write(' })') - g.is_array_set = false - } else if str_add { - g.write(')') - } - } - g.right_is_opt = false - } - if gen_or { - // g.write('/*777 $tmp_opt*/') - g.or_block(tmp_opt, or_block, return_type) - unwrapped_type_str := g.typ(return_type.clear_flag(.optional)) - ident := node.left as ast.Ident - 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 - // `var = tmp;` - g.write('\n$ident.name = $tmp_opt') - } else { - // var = *(X*)tmp.data;` - g.write('\n$ident.name = *($unwrapped_type_str*)${tmp_opt}.data') - } - } - // g.expr(node.left) - } - g.is_assign_rhs = false -} - fn (mut g Gen) infix_expr(node ast.InfixExpr) { // println('infix_expr() op="$node.op.str()" line_nr=$node.pos.line_nr') // g.write('/*infix*/') diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index f89fb8def2..28fc2a5524 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -449,9 +449,6 @@ fn (mut g Gen) method_call(node ast.CallExpr) { // /////// g.call_args(node.args, node.expected_arg_types) g.write(')') - // if node.or_block.stmts.len > 0 { - // g.or_block(node.or_block.stmts, node.return_type) - // } } fn (mut g Gen) fn_call(node ast.CallExpr) { @@ -589,9 +586,6 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { } g.write(')') } - // if node.or_block.stmts.len > 0 { - // g.or_block(node.or_block.stmts, node.return_type) - // } g.is_c_call = false if g.is_json_fn { g.write(')') diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 589a197ec5..669d2eb879 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -530,9 +530,6 @@ fn (mut g JsGen) expr(node ast.Expr) { // skip: JS has no types, so no need to cast // TODO: Is jsdoc needed here for TS support? } - ast.AssignExpr { - g.gen_assign_expr(it) - } ast.Assoc { // TODO } @@ -679,11 +676,9 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) { if it.left.len > it.right.len { // multi return g.write('const [') - for i, ident in it.left { - if ident.name in ['', '_'] { - g.write('') - } else { - g.write(g.js_name(ident.name)) + for i, left in it.left { + if !left.is_blank_ident() { + g.expr(left) } if i < it.left.len - 1 { g.write(', ') @@ -694,33 +689,47 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) { g.writeln(';') } else { // `a := 1` | `a,b := 1,2` - for i, ident in it.left { + for i, left in it.left { + mut op := it.op + if it.op == .decl_assign { op = .assign } val := it.right[i] - - if ident.kind == .blank_ident || ident.name in ['', '_'] { - tmp_var := g.new_tmp_var() - // TODO: Can the tmp_var declaration be omitted? - g.write('const $tmp_var = ') - g.expr(val) - g.writeln(';') - continue + mut is_mut := false + if left is ast.Ident { + ident := left as ast.Ident + is_mut = ident.is_mut + if ident.kind == .blank_ident || ident.name in ['', '_'] { + tmp_var := g.new_tmp_var() + // TODO: Can the tmp_var declaration be omitted? + g.write('const $tmp_var = ') + g.expr(val) + g.writeln(';') + continue + } } - ident_var_info := ident.var_info() - mut styp := g.typ(ident_var_info.typ) + mut styp := g.typ(it.left_types[i]) if !g.inside_loop && styp.len > 0 { g.doc.gen_typ(styp) } - if g.inside_loop || ident.is_mut { - g.write('let ') - } else { - g.write('const ') + if it.op == .decl_assign { + if g.inside_loop || is_mut { + g.write('let ') + } else { + g.write('const ') + } + } + g.expr(left) + if g.inside_map_set && op == .assign { + g.inside_map_set = false + g.write(', ') + g.expr(val) + g.write(')') + } else { + g.write(' $op ') + g.expr(val) } - - g.write('${g.js_name(ident.name)} = ') - g.expr(val) if g.inside_loop { g.write('; ') @@ -793,7 +802,7 @@ fn (mut g JsGen) gen_expr_stmt(it ast.ExprStmt) { g.expr(it.expr) expr := it.expr if expr is ast.IfExpr { } // no ; after an if expression - else if !g.inside_ternary { g.writeln(';') } + else if !g.inside_ternary && !it.is_expr { g.writeln(';') } } fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { @@ -909,7 +918,7 @@ fn (mut g JsGen) gen_for_c_stmt(it ast.ForCStmt) { } g.write('; ') if it.has_inc { - g.expr(it.inc) + g.stmt(it.inc) } g.writeln(') {') g.stmts(it.stmts) @@ -1095,28 +1104,6 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { } else {} } -fn (mut g JsGen) gen_assign_expr(it ast.AssignExpr) { - if it.left_type == table.void_type && it.op == .assign { - // _ = 1 - tmp_var := g.new_tmp_var() - g.write('const $tmp_var = ') - g.expr(it.val) - return - } - - // NB: The expr has to go *before* inside_map_set as it's defined there - g.expr(it.left) - if g.inside_map_set && it.op == .assign { - g.inside_map_set = false - g.write(', ') - g.expr(it.val) - g.write(')') - } else { - g.write(' $it.op ') - g.expr(it.val) - } -} - fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { mut name := '' if it.name.starts_with('JS.') { diff --git a/vlib/v/gen/x64/gen.v b/vlib/v/gen/x64/gen.v index 4232000cf8..32ab915523 100644 --- a/vlib/v/gen/x64/gen.v +++ b/vlib/v/gen/x64/gen.v @@ -561,7 +561,6 @@ fn (mut g Gen) expr(node ast.Expr) { // println('cgen expr()') match node { ast.ArrayInit {} - ast.AssignExpr {} ast.CallExpr { if it.name in ['println', 'print', 'eprintln', 'eprint'] { expr := it.args[0].expr @@ -626,19 +625,24 @@ fn (mut g Gen) allocate_var(name string, size, initial_val int) { fn (mut g Gen) assign_stmt(node ast.AssignStmt) { // `a := 1` | `a,b := 1,2` - for ident in node.left { - match node.right[0] { - ast.IntegerLiteral { - g.allocate_var(ident.name, 4, it.val.int()) + for i, left in node.left { + right := node.right[i] + name := left.str() + // if left is ast.Ident { + // ident := left as ast.Ident + match right { + ast.IntegerLiteral { + g.allocate_var(name, 4, it.val.int()) + } + ast.InfixExpr { + g.infix_expr(it) + g.allocate_var(name, 4, 0) + } + else { + g.error_with_pos('assign_stmt unhandled expr: ' + typeof(right), right.position()) + } } - ast.InfixExpr { - g.infix_expr(it) - g.allocate_var(ident.name, 4, 0) - } - else { - g.error_with_pos('assign_stmt unhandled expr: ' + typeof(node.right[0]), node.right[0].position()) - } - } + // } } } diff --git a/vlib/v/gen/x64/tests/general.vv b/vlib/v/gen/x64/tests/general.vv index a80da817ee..b1b5e4a25b 100644 --- a/vlib/v/gen/x64/tests/general.vv +++ b/vlib/v/gen/x64/tests/general.vv @@ -1,5 +1,5 @@ fn if_test() { - a := 1 + mut a := 1 if a == 1 { println('a == 1') b := 2 diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index 46d77827de..138a17594c 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -5,161 +5,122 @@ module parser import v.ast + fn (mut p Parser) assign_stmt() ast.Stmt { - return p.partial_assign_stmt([]) + return p.partial_assign_stmt(p.expr_list()) } -fn (mut p Parser) check_undefined_variables(idents []ast.Ident, expr ast.Expr) { - match expr { +fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) { + match val { ast.Ident { - for ident in idents { - if ident.name == it.name { - p.error_with_pos('undefined variable: `$it.name`', it.pos) + for expr in exprs { + if expr is ast.Ident { + ident := expr as ast.Ident + if ident.name == it.name { + p.error_with_pos('undefined variable: `$it.name`', it.pos) + } } } } ast.InfixExpr { - p.check_undefined_variables(idents, it.left) - p.check_undefined_variables(idents, it.right) + p.check_undefined_variables(exprs, it.left) + p.check_undefined_variables(exprs, it.right) } ast.ParExpr { - p.check_undefined_variables(idents, it.expr) + p.check_undefined_variables(exprs, it.expr) } ast.PostfixExpr { - p.check_undefined_variables(idents, it.expr) + p.check_undefined_variables(exprs, it.expr) } ast.PrefixExpr { - p.check_undefined_variables(idents, it.right) + p.check_undefined_variables(exprs, it.right) } ast.StringInterLiteral { for expr_ in it.exprs { - p.check_undefined_variables(idents, expr_) + p.check_undefined_variables(exprs, expr_) } } else {} } } -fn (mut p Parser) check_cross_variables(idents []ast.Ident, expr ast.Expr) bool { - match expr { +fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool { + match val { ast.Ident { - for ident in idents { - if ident.name == it.name { return true } + for expr in exprs { + if expr is ast.Ident { + ident := expr as ast.Ident + if ident.name == it.name { return true } + } } } ast.InfixExpr { - if p.check_cross_variables(idents, it.left) { return true } - if p.check_cross_variables(idents, it.right) { return true } + return p.check_cross_variables(exprs, it.left) || p.check_cross_variables(exprs, it.right) } ast.PrefixExpr { - if p.check_cross_variables(idents, it.right) { return true } + return p.check_cross_variables(exprs, it.right) } ast.PostfixExpr { - if p.check_cross_variables(idents, it.expr) { return true } + return p.check_cross_variables(exprs, it.expr) } else {} } return false } -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 - } - p.next() - pos := p.tok.position() - exprs := p.parse_assign_rhs() - is_decl := op == .decl_assign - mut has_cross_var := false - if is_decl { - // a, b := a + 1, b - for expr in exprs { - p.check_undefined_variables(idents, expr) - } - } else if idents.len > 1 { - // a, b = b, a - for expr in exprs { - if p.check_cross_variables(idents, expr) { - has_cross_var = true - } - } - } - for i, ident in idents { - if !is_decl && ident.kind != .blank_ident && !p.scope.known_var(ident.name) { - 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 - pos: ident.pos - }) - } else { - p.scope.register(ident.name, ast.Var{ - name: ident.name - is_mut: ident.is_mut || p.inside_for - pos: ident.pos - }) - } - } - } - return ast.AssignStmt{ - left: idents - right: exprs - op: op - pos: pos - is_static: false // individual idents may be static - has_cross_var: has_cross_var - } -} - -// TODO: is it possible to merge with AssignStmt? -pub fn (mut p Parser) assign_expr(left ast.Expr) ast.AssignExpr { +fn (mut p Parser) partial_assign_stmt(left []ast.Expr) ast.Stmt { + p.is_stmt_ident = false op := p.tok.kind pos := p.tok.position() p.next() - val := p.expr(0) - if left is ast.IndexExpr { - mut index_expr := left as ast.IndexExpr - index_expr.is_setter = true - } - node := ast.AssignExpr{ - left: left - val: val - op: op - pos: pos - } - return node -} - -fn (mut p Parser) parse_assign_ident() ast.Ident { - /// returns a single parsed ident - return p.parse_ident(.v) -} - -// right hand side of `=` or `:=` in `a,b,c := 1,2,3` -fn (mut p Parser) parse_assign_rhs() []ast.Expr { - mut exprs := []ast.Expr{} - for { - expr := p.expr(0) - exprs << expr - if p.tok.kind == .comma { - p.next() - } else { - break + right := p.expr_list() + mut has_cross_var := false + if op == .decl_assign { + // a, b := a + 1, b + for r in right { + p.check_undefined_variables(left, r) } } - return exprs + else if left.len > 1 { + // a, b = b, a + for r in right { + has_cross_var = p.check_cross_variables(left, r) + } + } + for i, lx in left { + match lx { + ast.Ident { + if op == .decl_assign { + if left.len == right.len { + p.scope.register(it.name, ast.Var{ + name: it.name + expr: right[i] + is_mut: it.is_mut || p.inside_for + pos: it.pos + }) + } else { + p.scope.register(it.name, ast.Var{ + name: it.name + is_mut: it.is_mut || p.inside_for + pos: it.pos + }) + } + } + } + ast.IndexExpr { + it.is_setter = true + } + ast.ParExpr {} + ast.PrefixExpr {} + ast.SelectorExpr { + if op == .decl_assign { + p.error_with_pos('struct fields can only be declared during the initialization', it.pos) + } + } + else {} + // TODO: parexpr ( check vars) + // else { p.error_with_pos('unexpected `${typeof(lx)}`', lx.position()) } + } + } + return ast.AssignStmt{op: op, left: left, right: right, pos: pos, has_cross_var: has_cross_var, is_simple: p.inside_for && p.tok.kind == .lcbr} } diff --git a/vlib/v/parser/for.v b/vlib/v/parser/for.v index bd3caaae1e..788162904b 100644 --- a/vlib/v/parser/for.v +++ b/vlib/v/parser/for.v @@ -28,8 +28,7 @@ fn (mut p Parser) for_stmt() ast.Stmt { // `for i := 0; i < 10; i++ {` mut init := ast.Stmt{} mut cond := p.new_true_expr() - // mut inc := ast.Stmt{} - mut inc := ast.Expr{} + mut inc := ast.Stmt{} mut has_init := false mut has_cond := false mut has_inc := false @@ -47,8 +46,7 @@ fn (mut p Parser) for_stmt() ast.Stmt { } p.check(.semicolon) if p.tok.kind != .lcbr { - // inc = p.stmt() - inc = p.expr(0) + inc = p.stmt(false) has_inc = true } p.inside_for = false diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 232ed44b70..3d8a61592a 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -518,31 +518,26 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { pos: assert_pos } } - .key_mut, .key_static { - return p.assign_stmt() - } .key_for { return p.for_stmt() } - .name { - if p.peek_tok.kind == .decl_assign { - // `x := ...` - return p.assign_stmt() - } else if p.peek_tok.kind == .comma { - // `a, b ...` - return p.parse_multi_expr(is_top_level) - } else if p.peek_tok.kind == .colon { - // `label:` - name := p.check_name() - p.next() - return ast.GotoLabel{ - name: name + .name, .key_mut, .key_static, .mul { + if p.tok.kind == .name { + if p.peek_tok.kind == .colon { + // `label:` + name := p.check_name() + p.next() + return ast.GotoLabel{ + name: name + } + } + else if p.peek_tok.kind == .name { + p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position()) + } + else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr && p.peek_tok.kind in + [.rcbr, .eof] { + p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position()) } - } else if p.peek_tok.kind == .name { - p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position()) - } else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr && p.peek_tok.kind in - [.rcbr, .eof] { - p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position()) } return p.parse_multi_expr(is_top_level) } @@ -618,6 +613,19 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { } } + +fn (mut p Parser) expr_list() []ast.Expr { + mut exprs := []ast.Expr{} + for { + exprs << p.expr(0) + if p.tok.kind != .comma { + break + } + p.next() + } + return exprs +} + fn (mut p Parser) attributes() []ast.Attr { mut attrs := []ast.Attr{} p.check(.lsbr) @@ -738,60 +746,30 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { // 1, a, c ... } // multi-expression // a, mut b ... :=/= // multi-assign // collect things upto hard boundaries - tok_kind := p.tok.kind - mut collected := []ast.Expr{} - for { - collected << p.expr(0) - if p.tok.kind == .comma { - p.next() - } else { - break + tok := p.tok + left := p.expr_list() + left0 := left[0] + if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { + return p.partial_assign_stmt(left) + } + else if is_top_level && left.len > 0 && + left0 !is ast.CallExpr && left0 !is ast.PostfixExpr && + !(left0 is ast.InfixExpr && (left0 as ast.InfixExpr).op == .left_shift) && + left0 !is ast.ComptimeCall && tok.kind !in [.key_if, .key_match] { + p.error_with_pos('expression evaluated but not used', left0.position()) + } + if left.len == 1 { + return ast.ExprStmt{ + expr: left0 + pos: tok.position() + is_expr: p.inside_for } } - // 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 { - match c { - ast.Ident { idents << it } - ast.SelectorExpr { p.error_with_pos('struct fields can only be declared during the initialization', - it.pos) } - else { p.error_with_pos('unexpected `${typeof(c)}`', c.position()) } - } - } - 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 - } - } - return ast.ExprStmt{ - expr: p.assign_expr(ast.ConcatExpr{ - vals: collected - }) - pos: epos - } - } else if is_top_level && collected.len > 0 && collected[0] !is ast.AssignExpr && - collected[0] !is ast.CallExpr && collected[0] !is ast.PostfixExpr && - !(collected[0] is ast.InfixExpr && (collected[0] as ast.InfixExpr).op == .left_shift) && - collected[0] !is ast.ComptimeCall && tok_kind !in [.key_if, .key_match] { - p.error_with_pos('expression evaluated but not used', collected[0].position()) - } else { - if collected.len == 1 { - return ast.ExprStmt{ - expr: collected[0] - pos: p.tok.position() - } - } - return ast.ExprStmt{ - expr: ast.ConcatExpr{ - vals: collected - } - pos: p.tok.position() + return ast.ExprStmt{ + expr: ast.ConcatExpr{ + vals: left } + pos: tok.position() } } @@ -1406,22 +1384,14 @@ fn (mut p Parser) const_decl() ast.ConstDecl { fn (mut p Parser) return_stmt() ast.Return { first_pos := p.tok.position() p.next() - // return expressions - mut exprs := []ast.Expr{} + // no return if p.tok.kind == .rcbr { return ast.Return{ pos: first_pos } } - for { - expr := p.expr(0) - exprs << expr - if p.tok.kind == .comma { - p.next() - } else { - break - } - } + // return exprs + exprs := p.expr_list() end_pos := exprs.last().position() return ast.Return{ exprs: exprs diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 6db833a6f6..fa0f4ed1e9 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -16,7 +16,8 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { // Prefix match p.tok.kind { .key_mut, .key_static { - node = p.parse_assign_ident() + node = p.name_expr() + p.is_stmt_ident = is_stmt_ident } .name { if p.tok.lit == 'sql' && p.peek_tok.kind == .name { @@ -158,13 +159,12 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { } // Infix for precedence < p.tok.precedence() { - if p.tok.kind.is_assign() && !p.is_stmt_ident { - node = p.assign_expr(node) - } else if p.tok.kind == .dot { + if p.tok.kind == .dot { node = p.dot_expr(node) p.is_stmt_ident = is_stmt_ident } else if p.tok.kind == .lsbr { node = p.index_expr(node) + p.is_stmt_ident = is_stmt_ident } else if p.tok.kind == .key_as { pos := p.tok.position() p.next() @@ -186,7 +186,8 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { op: tok.kind pos: pos } - } else if p.tok.kind.is_infix() { + } + else if p.tok.kind.is_infix() { // return early for deref assign `*x = 2` goes to prefix expr if p.tok.kind == .mul && p.tok.line_nr != p.prev_tok.line_nr && p.peek_tok2.kind == .assign {