parser/checker/gen: merge AssignExpr & AssignStmt into just AssignStmt
parent
651a203ecb
commit
d478b44915
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 += ','
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -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]!!
|
||||||
|
|
|
@ -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 | }
|
||||||
|
|
|
@ -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 | }
|
||||||
|
|
|
@ -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++
|
||||||
|
|
|
@ -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*/')
|
||||||
|
|
|
@ -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(')')
|
||||||
|
|
|
@ -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.') {
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue