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