parser/checker/gen: merge AssignExpr & AssignStmt into just AssignStmt

pull/5388/head
joe-conigliaro 2020-06-16 21:20:16 +10:00 committed by GitHub
parent 651a203ecb
commit d478b44915
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 455 additions and 634 deletions

View File

@ -9,7 +9,7 @@ import v.errors
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl 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 | CastExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr |
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | MapInit | MatchExpr | None | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | MapInit | MatchExpr | None |
OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr |
@ -44,6 +44,9 @@ pub struct ExprStmt {
pub: pub:
expr Expr expr Expr
pos token.Position pos token.Position
// treat like expr (dont add trailing `;`)
// is used for `x++` in `for x:=1; ; x++`
is_expr bool
pub mut: pub mut:
typ table.Type typ table.Type
} }
@ -510,7 +513,7 @@ pub:
has_init bool has_init bool
cond Expr // i < 10; cond Expr // i < 10;
has_cond bool has_cond bool
inc Expr // i++; inc Stmt // i++; i += 2
has_inc bool has_inc bool
stmts []Stmt stmts []Stmt
pos token.Position pos token.Position
@ -543,10 +546,11 @@ pub:
op token.Kind op token.Kind
pos token.Position pos token.Position
pub mut: pub mut:
left []Ident left []Expr
left_types []table.Type left_types []table.Type
right_types []table.Type right_types []table.Type
is_static bool // for translated code only is_static bool // for translated code only
is_simple bool // `x+=2` in `for x:=1; ; x+=2`
has_cross_var bool has_cross_var bool
} }
@ -637,17 +641,6 @@ pub:
expr Expr 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 struct GoStmt {
pub: pub:
call_expr Expr call_expr Expr
@ -812,7 +805,7 @@ pub struct SqlExpr {
} }
[inline] [inline]
pub fn expr_is_blank_ident(expr Expr) bool { pub fn (expr Expr) is_blank_ident() bool {
match expr { match expr {
Ident { return it.kind == .blank_ident } Ident { return it.kind == .blank_ident }
else { return false } else { return false }
@ -829,9 +822,6 @@ pub fn (expr Expr) position() token.Position {
return it.pos return it.pos
} }
// ast.Ident { } // ast.Ident { }
AssignExpr {
return it.pos
}
CastExpr { CastExpr {
return it.pos return it.pos
} }

View File

@ -224,12 +224,15 @@ pub fn (node Stmt) str() string {
match node { match node {
AssignStmt { AssignStmt {
mut out := '' mut out := ''
for i, ident in it.left { for i, left in it.left {
if left is Ident {
ident := left as Ident
var_info := ident.var_info() var_info := ident.var_info()
if var_info.is_mut { if var_info.is_mut {
out += 'mut ' out += 'mut '
} }
out += ident.name }
out += left.str()
if i < it.left.len - 1 { if i < it.left.len - 1 {
out += ',' out += ','
} }

View File

@ -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 { pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
c.stmts(call_expr.or_block.stmts) c.stmts(call_expr.or_block.stmts)
if call_expr.is_method { 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) c.check_or_expr(call_expr.or_block, ret_type)
} }
// remove optional flag // 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 { } else if call_expr.or_block.kind == .block {
c.error('unexpected `or` block, the function `$call_expr.name` does not return an optional', c.error('unexpected `or` block, the function `$call_expr.name` does not return an optional',
call_expr.pos) call_expr.pos)
@ -1378,14 +1295,14 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
mut right_len := assign_stmt.right.len mut right_len := assign_stmt.right.len
if right_first is ast.CallExpr || right_first is ast.IfExpr || right_first is ast.MatchExpr { if right_first is ast.CallExpr || right_first is ast.IfExpr || right_first is ast.MatchExpr {
right_type0 := c.expr(right_first) 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) right_type_sym0 := c.table.get_type_symbol(right_type0)
if right_type0 == table.void_type { if right_type_sym0.kind == .multi_return {
right_len = 0
} else if right_type_sym0.kind == .multi_return {
assign_stmt.right_types = right_type_sym0.mr_info().types assign_stmt.right_types = right_type_sym0.mr_info().types
right_len = assign_stmt.right_types.len right_len = assign_stmt.right_types.len
} }
else if right_type0 == table.void_type { right_len=0 }
}
if assign_stmt.left.len != right_len { if assign_stmt.left.len != right_len {
if right_first is ast.CallExpr { if right_first is ast.CallExpr {
call_expr := assign_stmt.right[0] as ast.CallExpr call_expr := assign_stmt.right[0] as ast.CallExpr
@ -1397,44 +1314,109 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
} }
return return
} }
} else if assign_stmt.left.len != right_len { is_decl := assign_stmt.op == .decl_assign
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $assign_stmt.right.len value(s)', for i, left in assign_stmt.left {
assign_stmt.pos) is_blank_ident := left.is_blank_ident()
return 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)
} }
mut scope := c.file.scope.innermost(assign_stmt.pos.pos) if assign_stmt.right_types.len < assign_stmt.left.len { // first type or multi return types added above
for i, _ in assign_stmt.left {
mut ident := assign_stmt.left[i]
if assign_stmt.right_types.len < right_len {
right_type := c.expr(assign_stmt.right[i]) right_type := c.expr(assign_stmt.right[i])
assign_stmt.right_types << c.check_expr_opt_call(assign_stmt.right[i], right_type) 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] right := if i < assign_stmt.right.len { assign_stmt.right[i] } else { assign_stmt.right[0] }
mut ident_var_info := ident.var_info() right_type := assign_stmt.right_types[i]
is_decl := assign_stmt.op == .decl_assign
if is_decl { if is_decl {
if ident.kind != .blank_ident { left_type = c.table.mktyp(right_type)
// check variable name for beginning with capital letter 'Abc' // we are unwrapping here instead if check_expr_opt_call currently
c.check_valid_snake_case(ident.name, 'variable name', ident.pos) if left_type.has_flag(.optional) { left_type = left_type.clear_flag(.optional) }
}
val_type = c.table.mktyp(val_type)
} else { } else {
c.fail_if_immutable(ident) // Make sure the variable is mutable
var_type := c.expr(ident) c.fail_if_immutable(left)
assign_stmt.left_types << var_type // left_type = c.expr(left)
if ident.kind != .blank_ident && !c.check_types(val_type, var_type) { }
val_type_sym := c.table.get_type_symbol(val_type) assign_stmt.left_types << left_type
var_type_sym := c.table.get_type_symbol(var_type) match left {
c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', ast.Ident {
assign_stmt.pos) 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)
} }
} }
ident_var_info.typ = val_type else {
ident.info = ident_var_info if is_decl { c.check_valid_snake_case(it.name, 'variable name', it.pos) }
assign_stmt.left[i] = ident mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
scope.update_var_type(ident.name, val_type) 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())
}
} }
c.expected_type = table.void_type c.expected_type = table.void_type
} }
@ -1678,8 +1660,8 @@ fn (mut c Checker) stmt(node ast.Stmt) {
c.in_for_count++ c.in_for_count++
c.stmt(it.init) c.stmt(it.init)
c.expr(it.cond) c.expr(it.cond)
// c.stmt(it.inc) c.stmt(it.inc)
c.expr(it.inc) // c.expr(it.inc)
c.stmts(it.stmts) c.stmts(it.stmts)
c.in_for_count-- 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.to_ptr()
// return it.typ // return it.typ
} }
ast.AssignExpr {
c.assign_expr(mut it)
}
ast.Assoc { ast.Assoc {
scope := c.file.scope.innermost(it.pos.pos) scope := c.file.scope.innermost(it.pos.pos)
v := scope.find_var(it.var_name) or { v := scope.find_var(it.var_name) or {

View File

@ -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() { 1 | fn main() {
2 | mut foo := 1.5 2 | mut foo := 1.5
3 | foo += 'hello' 3 | foo += 'hello'

View File

@ -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() { 1 | fn main() {
2 | _ += 1 2 | _ += 1
| ^ | ^

View File

@ -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{} 7 | mut ctx := Context{}
8 | x := 2.32 8 | x := 2.32
9 | ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]!! 9 | ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]!!

View File

@ -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 { 1 | fn foo() int {
2 | return if 1 == 1 { 1 } else { 2 } 2 | return if 1 == 1 { 1 } else { 2 }
3 | a := 1 3 | a := 1
| ^ | ~~
4 | println(a) 4 | println(a)
5 | } 5 | }

View File

@ -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(){ 4 | fn main(){
5 | mut a := '' 5 | mut a := ''
6 | a = x(1,2) // hello 6 | a = x(1,2) // hello
| ~~~~~~ | ^
7 | eprintln('a: $a') 7 | eprintln('a: $a')
8 | } 8 | }

View File

@ -160,16 +160,22 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
} }
match node { match node {
ast.AssignStmt { ast.AssignStmt {
for i, ident in it.left { for i, left in it.left {
if left is ast.Ident {
ident := left as ast.Ident
var_info := ident.var_info() var_info := ident.var_info()
if var_info.is_mut { if var_info.is_mut {
f.write('mut ') f.write('mut ')
} }
f.expr(ident) f.expr(left)
if i < it.left.len - 1 { if i < it.left.len - 1 {
f.write(', ') f.write(', ')
} }
} }
else {
f.expr(left)
}
}
f.is_assign = true f.is_assign = true
f.write(' $it.op.str() ') f.write(' $it.op.str() ')
for i, val in it.right { for i, val in it.right {
@ -260,7 +266,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
f.write('; ') f.write('; ')
f.expr(it.cond) f.expr(it.cond)
f.write('; ') f.write('; ')
f.expr(it.inc) f.stmt(it.inc)
f.writeln(' {') f.writeln(' {')
f.stmts(it.stmts) f.stmts(it.stmts)
f.writeln('}') f.writeln('}')
@ -543,11 +549,6 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
f.expr(it.expr) f.expr(it.expr)
f.write(' as $type_str') f.write(' as $type_str')
} }
ast.AssignExpr {
f.expr(it.left)
f.write(' $it.op.str() ')
f.expr(it.val)
}
ast.Assoc { ast.Assoc {
f.writeln('{') f.writeln('{')
// f.indent++ // f.indent++

View File

@ -646,7 +646,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} }
ast.ExprStmt { ast.ExprStmt {
g.expr(it.expr) 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(';') g.writeln(';')
} }
} }
@ -700,7 +700,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} }
g.write('; ') g.write('; ')
if it.has_inc { if it.has_inc {
g.expr(it.inc)
g.stmt(it.inc)
} }
g.writeln(') {') g.writeln(') {')
g.stmts(it.stmts) g.stmts(it.stmts)
@ -1007,6 +1008,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.write('static ') g.write('static ')
} }
mut return_type := table.void_type 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] { match assign_stmt.right[0] {
ast.CallExpr { return_type = it.return_type } ast.CallExpr { return_type = it.return_type }
ast.IfExpr { return_type = it.typ } 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 // json_test failed w/o this check
if return_type != table.void_type && return_type != 0 { if return_type != table.void_type && return_type != 0 {
sym := g.table.get_type_symbol(return_type) sym := g.table.get_type_symbol(return_type)
// the left vs. right is ugly and should be removed if sym.kind == .multi_return {
if sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len || assign_stmt.left.len >
1 {
// multi return // multi return
// TODO Handle in if_expr // TODO Handle in if_expr
is_optional := return_type.has_flag(.optional) 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.is_assign_rhs = true
g.expr(assign_stmt.right[0]) g.expr(assign_stmt.right[0])
g.is_assign_rhs = false 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(';') g.writeln(';')
for i, ident in assign_stmt.left { for i, lx in assign_stmt.left {
if ident.kind == .blank_ident { match lx {
continue ast.Ident {
if it.kind == .blank_ident { continue }
} }
ident_var_info := ident.var_info() else {}
styp := g.typ(ident_var_info.typ) }
styp := g.typ(assign_stmt.left_types[i])
if assign_stmt.op == .decl_assign { if assign_stmt.op == .decl_assign {
g.write('$styp ') g.write('$styp ')
} }
g.expr(ident) g.expr(lx)
if is_optional { if is_optional {
mr_base_styp := g.base_type(return_type) mr_base_styp := g.base_type(return_type)
g.writeln(' = (*(${mr_base_styp}*)${mr_var_name}.data).arg$i;') 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 return
} }
} }
// TODO: non idents on left (exprs)
if assign_stmt.has_cross_var { if assign_stmt.has_cross_var {
for ident in assign_stmt.left { for i,left in assign_stmt.left {
type_str := g.typ(ident.var_info().typ) match left {
g.writeln('$type_str _var_$ident.pos.pos = $ident.name;') 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` // `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] val := assign_stmt.right[i]
ident_var_info := ident.var_info()
styp := g.typ(ident_var_info.typ)
mut is_call := false 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 { match val {
ast.ArrayInit {
is_fixed_array_init = it.is_fixed
has_val = it.has_val
}
ast.CallExpr { ast.CallExpr {
is_call = true is_call = true
return_type = it.return_type return_type = it.return_type
@ -1092,7 +1110,11 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} }
else {} 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 blank_assign {
if is_call { if is_call {
g.expr(val) g.expr(val)
@ -1101,28 +1123,35 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.expr(val) g.expr(val)
g.writeln(';}') g.writeln(';}')
} }
}
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 { } else {
right_sym := g.table.get_type_symbol(assign_stmt.right_types[i]) g.assign_op = assign_stmt.op
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 {}
}
is_inside_ternary := g.inside_ternary != 0 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.register_ternary_name(ident.name)
g.empty_line = false g.empty_line = false
g.go_before_ternary() g.go_before_ternary()
} else { } else {
'' ''
} }
is_decl := assign_stmt.op == .decl_assign mut str_add := false
if right_sym.kind == .function { if var_type == table.string_type_idx && assign_stmt.op == .plus_assign {
if is_inside_ternary { // 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]) g.out.write(tabs[g.indent - g.inside_ternary])
} }
func := right_sym.info as table.FnType func := right_sym.info as table.FnType
@ -1139,73 +1168,86 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} }
g.write('$styp ') 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.write(';\n$cur_line')
g.out.write(tabs[g.indent]) 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.autofree && right_sym.kind in [.array, .string] {
if g.gen_clone_assignment(val, right_sym, true) { if g.gen_clone_assignment(val, right_sym, false) {
g.writeln(';') cloned = true
// g.expr_var_name = ''
return
} }
} }
if is_fixed_array_init { unwrap_optional := !var_type.has_flag(.optional) && val_type.has_flag(.optional)
if has_val { if unwrap_optional { g.write('*($styp*)') }
g.write(' = ') if !cloned {
g.expr(val)
} else {
g.write(' = {0}')
}
} else {
g.write(' = ')
if is_decl { if is_decl {
g.expr(val) if is_fixed_array_init && !has_val { g.write('{0}') }
else { g.expr(val) }
} else { } else {
if assign_stmt.has_cross_var { if assign_stmt.has_cross_var {
g.gen_cross_tmp_variable(assign_stmt.left, val) g.gen_cross_tmp_variable(assign_stmt.left, val)
} else { } 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 g.is_assign_rhs = false
if g.inside_ternary == 0 { if g.inside_ternary == 0 && !assign_stmt.is_simple {
g.writeln(';') 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 { match val {
ast.Ident { ast.Ident {
mut has_var := false mut has_var := false
for ident in idents { for lx in left {
if lx is ast.Ident {
ident := lx as ast.Ident
if it.name == ident.name { if it.name == ident.name {
g.write('_var_${ident.pos.pos}') g.write('_var_${ident.pos.pos}')
has_var = true has_var = true
break break
} }
} }
}
if !has_var { if !has_var {
g.expr(val) g.expr(val)
} }
} }
ast.InfixExpr { ast.InfixExpr {
g.gen_cross_tmp_variable(idents, it.left) g.gen_cross_tmp_variable(left, it.left)
g.write(it.op.str()) g.write(it.op.str())
g.gen_cross_tmp_variable(idents, it.right) g.gen_cross_tmp_variable(left, it.right)
} }
ast.PrefixExpr { ast.PrefixExpr {
g.write(it.op.str()) g.write(it.op.str())
g.gen_cross_tmp_variable(idents, it.right) g.gen_cross_tmp_variable(left, it.right)
} }
ast.PostfixExpr { ast.PostfixExpr {
g.gen_cross_tmp_variable(idents, it.expr) g.gen_cross_tmp_variable(left, it.expr)
g.write(it.op.str()) g.write(it.op.str())
} }
else { else {
@ -1360,9 +1402,6 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.AsCast { ast.AsCast {
g.as_cast(it) g.as_cast(it)
} }
ast.AssignExpr {
g.assign_expr(it)
}
ast.Assoc { ast.Assoc {
g.assoc(it) 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) { fn (mut g Gen) infix_expr(node ast.InfixExpr) {
// println('infix_expr() op="$node.op.str()" line_nr=$node.pos.line_nr') // println('infix_expr() op="$node.op.str()" line_nr=$node.pos.line_nr')
// g.write('/*infix*/') // g.write('/*infix*/')

View File

@ -449,9 +449,6 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
// /////// // ///////
g.call_args(node.args, node.expected_arg_types) g.call_args(node.args, node.expected_arg_types)
g.write(')') 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) { fn (mut g Gen) fn_call(node ast.CallExpr) {
@ -589,9 +586,6 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
} }
g.write(')') g.write(')')
} }
// if node.or_block.stmts.len > 0 {
// g.or_block(node.or_block.stmts, node.return_type)
// }
g.is_c_call = false g.is_c_call = false
if g.is_json_fn { if g.is_json_fn {
g.write(')') g.write(')')

View File

@ -530,9 +530,6 @@ fn (mut g JsGen) expr(node ast.Expr) {
// skip: JS has no types, so no need to cast // skip: JS has no types, so no need to cast
// TODO: Is jsdoc needed here for TS support? // TODO: Is jsdoc needed here for TS support?
} }
ast.AssignExpr {
g.gen_assign_expr(it)
}
ast.Assoc { ast.Assoc {
// TODO // TODO
} }
@ -679,11 +676,9 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) {
if it.left.len > it.right.len { if it.left.len > it.right.len {
// multi return // multi return
g.write('const [') g.write('const [')
for i, ident in it.left { for i, left in it.left {
if ident.name in ['', '_'] { if !left.is_blank_ident() {
g.write('') g.expr(left)
} else {
g.write(g.js_name(ident.name))
} }
if i < it.left.len - 1 { if i < it.left.len - 1 {
g.write(', ') g.write(', ')
@ -694,9 +689,14 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) {
g.writeln(';') g.writeln(';')
} else { } else {
// `a := 1` | `a,b := 1,2` // `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] val := it.right[i]
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 ['', '_'] { if ident.kind == .blank_ident || ident.name in ['', '_'] {
tmp_var := g.new_tmp_var() tmp_var := g.new_tmp_var()
// TODO: Can the tmp_var declaration be omitted? // TODO: Can the tmp_var declaration be omitted?
@ -705,22 +705,31 @@ fn (mut g JsGen) gen_assign_stmt(it ast.AssignStmt) {
g.writeln(';') g.writeln(';')
continue continue
} }
}
ident_var_info := ident.var_info() mut styp := g.typ(it.left_types[i])
mut styp := g.typ(ident_var_info.typ)
if !g.inside_loop && styp.len > 0 { if !g.inside_loop && styp.len > 0 {
g.doc.gen_typ(styp) g.doc.gen_typ(styp)
} }
if g.inside_loop || ident.is_mut { if it.op == .decl_assign {
if g.inside_loop || is_mut {
g.write('let ') g.write('let ')
} else { } else {
g.write('const ') g.write('const ')
} }
}
g.write('${g.js_name(ident.name)} = ') g.expr(left)
if g.inside_map_set && op == .assign {
g.inside_map_set = false
g.write(', ')
g.expr(val) g.expr(val)
g.write(')')
} else {
g.write(' $op ')
g.expr(val)
}
if g.inside_loop { if g.inside_loop {
g.write('; ') g.write('; ')
@ -793,7 +802,7 @@ fn (mut g JsGen) gen_expr_stmt(it ast.ExprStmt) {
g.expr(it.expr) g.expr(it.expr)
expr := it.expr expr := it.expr
if expr is ast.IfExpr { } // no ; after an if expression 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) { 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('; ') g.write('; ')
if it.has_inc { if it.has_inc {
g.expr(it.inc) g.stmt(it.inc)
} }
g.writeln(') {') g.writeln(') {')
g.stmts(it.stmts) g.stmts(it.stmts)
@ -1095,28 +1104,6 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) {
} else {} } 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) { fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
mut name := '' mut name := ''
if it.name.starts_with('JS.') { if it.name.starts_with('JS.') {

View File

@ -561,7 +561,6 @@ fn (mut g Gen) expr(node ast.Expr) {
// println('cgen expr()') // println('cgen expr()')
match node { match node {
ast.ArrayInit {} ast.ArrayInit {}
ast.AssignExpr {}
ast.CallExpr { ast.CallExpr {
if it.name in ['println', 'print', 'eprintln', 'eprint'] { if it.name in ['println', 'print', 'eprintln', 'eprint'] {
expr := it.args[0].expr 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) { fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
// `a := 1` | `a,b := 1,2` // `a := 1` | `a,b := 1,2`
for ident in node.left { for i, left in node.left {
match node.right[0] { right := node.right[i]
name := left.str()
// if left is ast.Ident {
// ident := left as ast.Ident
match right {
ast.IntegerLiteral { ast.IntegerLiteral {
g.allocate_var(ident.name, 4, it.val.int()) g.allocate_var(name, 4, it.val.int())
} }
ast.InfixExpr { ast.InfixExpr {
g.infix_expr(it) g.infix_expr(it)
g.allocate_var(ident.name, 4, 0) g.allocate_var(name, 4, 0)
} }
else { else {
g.error_with_pos('assign_stmt unhandled expr: ' + typeof(node.right[0]), node.right[0].position()) g.error_with_pos('assign_stmt unhandled expr: ' + typeof(right), right.position())
} }
} }
// }
} }
} }

View File

@ -1,5 +1,5 @@
fn if_test() { fn if_test() {
a := 1 mut a := 1
if a == 1 { if a == 1 {
println('a == 1') println('a == 1')
b := 2 b := 2

View File

@ -5,161 +5,122 @@ module parser
import v.ast import v.ast
fn (mut p Parser) assign_stmt() ast.Stmt { 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) { fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) {
match expr { match val {
ast.Ident { ast.Ident {
for ident in idents { for expr in exprs {
if expr is ast.Ident {
ident := expr as ast.Ident
if ident.name == it.name { if ident.name == it.name {
p.error_with_pos('undefined variable: `$it.name`', it.pos) p.error_with_pos('undefined variable: `$it.name`', it.pos)
} }
} }
} }
}
ast.InfixExpr { ast.InfixExpr {
p.check_undefined_variables(idents, it.left) p.check_undefined_variables(exprs, it.left)
p.check_undefined_variables(idents, it.right) p.check_undefined_variables(exprs, it.right)
} }
ast.ParExpr { ast.ParExpr {
p.check_undefined_variables(idents, it.expr) p.check_undefined_variables(exprs, it.expr)
} }
ast.PostfixExpr { ast.PostfixExpr {
p.check_undefined_variables(idents, it.expr) p.check_undefined_variables(exprs, it.expr)
} }
ast.PrefixExpr { ast.PrefixExpr {
p.check_undefined_variables(idents, it.right) p.check_undefined_variables(exprs, it.right)
} }
ast.StringInterLiteral { ast.StringInterLiteral {
for expr_ in it.exprs { for expr_ in it.exprs {
p.check_undefined_variables(idents, expr_) p.check_undefined_variables(exprs, expr_)
} }
} }
else {} else {}
} }
} }
fn (mut p Parser) check_cross_variables(idents []ast.Ident, expr ast.Expr) bool { fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool {
match expr { match val {
ast.Ident { ast.Ident {
for ident in idents { for expr in exprs {
if expr is ast.Ident {
ident := expr as ast.Ident
if ident.name == it.name { return true } if ident.name == it.name { return true }
} }
} }
}
ast.InfixExpr { ast.InfixExpr {
if p.check_cross_variables(idents, it.left) { return true } return p.check_cross_variables(exprs, it.left) || p.check_cross_variables(exprs, it.right)
if p.check_cross_variables(idents, it.right) { return true }
} }
ast.PrefixExpr { ast.PrefixExpr {
if p.check_cross_variables(idents, it.right) { return true } return p.check_cross_variables(exprs, it.right)
} }
ast.PostfixExpr { ast.PostfixExpr {
if p.check_cross_variables(idents, it.expr) { return true } return p.check_cross_variables(exprs, it.expr)
} }
else {} else {}
} }
return false return false
} }
fn (mut p Parser) partial_assign_stmt(known_lhs []ast.Ident) ast.Stmt { fn (mut p Parser) partial_assign_stmt(left []ast.Expr) ast.Stmt {
mut idents := known_lhs p.is_stmt_ident = false
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 {
op := p.tok.kind op := p.tok.kind
pos := p.tok.position() pos := p.tok.position()
p.next() p.next()
val := p.expr(0) right := p.expr_list()
if left is ast.IndexExpr { mut has_cross_var := false
mut index_expr := left as ast.IndexExpr if op == .decl_assign {
index_expr.is_setter = true // a, b := a + 1, b
for r in right {
p.check_undefined_variables(left, r)
} }
node := ast.AssignExpr{
left: left
val: val
op: op
pos: pos
} }
return node else if left.len > 1 {
// a, b = b, a
for r in right {
has_cross_var = p.check_cross_variables(left, r)
} }
fn (mut p Parser) parse_assign_ident() ast.Ident {
/// returns a single parsed ident
return p.parse_ident(.v)
} }
for i, lx in left {
// right hand side of `=` or `:=` in `a,b,c := 1,2,3` match lx {
fn (mut p Parser) parse_assign_rhs() []ast.Expr { ast.Ident {
mut exprs := []ast.Expr{} if op == .decl_assign {
for { if left.len == right.len {
expr := p.expr(0) p.scope.register(it.name, ast.Var{
exprs << expr name: it.name
if p.tok.kind == .comma { expr: right[i]
p.next() is_mut: it.is_mut || p.inside_for
pos: it.pos
})
} else { } else {
break p.scope.register(it.name, ast.Var{
name: it.name
is_mut: it.is_mut || p.inside_for
pos: it.pos
})
} }
} }
return exprs }
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}
} }

View File

@ -28,8 +28,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
// `for i := 0; i < 10; i++ {` // `for i := 0; i < 10; i++ {`
mut init := ast.Stmt{} mut init := ast.Stmt{}
mut cond := p.new_true_expr() mut cond := p.new_true_expr()
// mut inc := ast.Stmt{} mut inc := ast.Stmt{}
mut inc := ast.Expr{}
mut has_init := false mut has_init := false
mut has_cond := false mut has_cond := false
mut has_inc := false mut has_inc := false
@ -47,8 +46,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
} }
p.check(.semicolon) p.check(.semicolon)
if p.tok.kind != .lcbr { if p.tok.kind != .lcbr {
// inc = p.stmt() inc = p.stmt(false)
inc = p.expr(0)
has_inc = true has_inc = true
} }
p.inside_for = false p.inside_for = false

View File

@ -518,32 +518,27 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
pos: assert_pos pos: assert_pos
} }
} }
.key_mut, .key_static {
return p.assign_stmt()
}
.key_for { .key_for {
return p.for_stmt() return p.for_stmt()
} }
.name { .name, .key_mut, .key_static, .mul {
if p.peek_tok.kind == .decl_assign { if p.tok.kind == .name {
// `x := ...` if p.peek_tok.kind == .colon {
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:` // `label:`
name := p.check_name() name := p.check_name()
p.next() p.next()
return ast.GotoLabel{ return ast.GotoLabel{
name: name name: name
} }
} else if p.peek_tok.kind == .name { }
else if p.peek_tok.kind == .name {
p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position()) 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 }
else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr && p.peek_tok.kind in
[.rcbr, .eof] { [.rcbr, .eof] {
p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position()) p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position())
} }
}
return p.parse_multi_expr(is_top_level) return p.parse_multi_expr(is_top_level)
} }
.comment { .comment {
@ -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 { fn (mut p Parser) attributes() []ast.Attr {
mut attrs := []ast.Attr{} mut attrs := []ast.Attr{}
p.check(.lsbr) 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 // 1, a, c ... } // multi-expression
// a, mut b ... :=/= // multi-assign // a, mut b ... :=/= // multi-assign
// collect things upto hard boundaries // collect things upto hard boundaries
tok_kind := p.tok.kind tok := p.tok
mut collected := []ast.Expr{} left := p.expr_list()
for { left0 := left[0]
collected << p.expr(0) if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() {
if p.tok.kind == .comma { return p.partial_assign_stmt(left)
p.next()
} else {
break
} }
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())
} }
// TODO: Try to merge assign_expr and assign_stmt if left.len == 1 {
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{ return ast.ExprStmt{
expr: p.assign_expr(collected[0]) expr: left0
pos: epos pos: tok.position()
} is_expr: p.inside_for
}
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{ return ast.ExprStmt{
expr: ast.ConcatExpr{ expr: ast.ConcatExpr{
vals: collected vals: left
}
pos: p.tok.position()
} }
pos: tok.position()
} }
} }
@ -1406,22 +1384,14 @@ fn (mut p Parser) const_decl() ast.ConstDecl {
fn (mut p Parser) return_stmt() ast.Return { fn (mut p Parser) return_stmt() ast.Return {
first_pos := p.tok.position() first_pos := p.tok.position()
p.next() p.next()
// return expressions // no return
mut exprs := []ast.Expr{}
if p.tok.kind == .rcbr { if p.tok.kind == .rcbr {
return ast.Return{ return ast.Return{
pos: first_pos pos: first_pos
} }
} }
for { // return exprs
expr := p.expr(0) exprs := p.expr_list()
exprs << expr
if p.tok.kind == .comma {
p.next()
} else {
break
}
}
end_pos := exprs.last().position() end_pos := exprs.last().position()
return ast.Return{ return ast.Return{
exprs: exprs exprs: exprs

View File

@ -16,7 +16,8 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
// Prefix // Prefix
match p.tok.kind { match p.tok.kind {
.key_mut, .key_static { .key_mut, .key_static {
node = p.parse_assign_ident() node = p.name_expr()
p.is_stmt_ident = is_stmt_ident
} }
.name { .name {
if p.tok.lit == 'sql' && p.peek_tok.kind == .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 // Infix
for precedence < p.tok.precedence() { for precedence < p.tok.precedence() {
if p.tok.kind.is_assign() && !p.is_stmt_ident { if p.tok.kind == .dot {
node = p.assign_expr(node)
} else if p.tok.kind == .dot {
node = p.dot_expr(node) node = p.dot_expr(node)
p.is_stmt_ident = is_stmt_ident p.is_stmt_ident = is_stmt_ident
} else if p.tok.kind == .lsbr { } else if p.tok.kind == .lsbr {
node = p.index_expr(node) node = p.index_expr(node)
p.is_stmt_ident = is_stmt_ident
} else if p.tok.kind == .key_as { } else if p.tok.kind == .key_as {
pos := p.tok.position() pos := p.tok.position()
p.next() p.next()
@ -186,7 +186,8 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
op: tok.kind op: tok.kind
pos: pos 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 // 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 == if p.tok.kind == .mul && p.tok.line_nr != p.prev_tok.line_nr && p.peek_tok2.kind ==
.assign { .assign {