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

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

View File

@ -9,7 +9,7 @@ import v.errors
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type 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
}

View File

@ -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 += ','
}

View File

@ -667,91 +667,6 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) {
}
}
fn (mut c Checker) assign_expr(mut assign_expr ast.AssignExpr) {
c.expected_type = table.void_type
left_type := c.unwrap_generic(c.expr(assign_expr.left))
c.expected_type = left_type
if ast.expr_is_blank_ident(assign_expr.left) {
c.expected_type = table.Type(0)
}
assign_expr.left_type = left_type
// println('setting exp type to $c.expected_type $t.name')
right_type := c.check_expr_opt_call(assign_expr.val, c.unwrap_generic(c.expr(assign_expr.val)))
assign_expr.right_type = right_type
right := c.table.get_type_symbol(right_type)
left := c.table.get_type_symbol(left_type)
match assign_expr.left {
ast.Ident {
if it.kind == .blank_ident {
if assign_expr.op != .assign {
c.error('cannot modify blank `_` variable', it.pos)
}
return
}
}
ast.PrefixExpr {
// Do now allow `*x = y` outside `unsafe`
if it.op == .mul && !c.inside_unsafe {
c.error('modifying variables via deferencing can only be done in `unsafe` blocks',
assign_expr.pos)
}
}
else {}
}
// Make sure the variable is mutable
c.fail_if_immutable(assign_expr.left)
// Single side check
match assign_expr.op {
.assign {} // No need to do single side check for =. But here put it first for speed.
.plus_assign {
if !left.is_number() && left_type != table.string_type && !left.is_pointer() {
c.error('operator += not defined on left operand type `$left.name`', assign_expr.left.position())
} else if !right.is_number() && right_type != table.string_type && !right.is_pointer() {
c.error('operator += not defined on right operand type `$right.name`', assign_expr.val.position())
}
if assign_expr.val is ast.IntegerLiteral && assign_expr.val.str().int() == 1 {
c.error('use `++` instead of `+= 1`', assign_expr.pos)
}
}
.minus_assign {
if !left.is_number() && !left.is_pointer() {
c.error('operator -= not defined on left operand type `$left.name`', assign_expr.left.position())
} else if !right.is_number() && !right.is_pointer() {
c.error('operator -= not defined on right operand type `$right.name`', assign_expr.val.position())
}
if assign_expr.val is ast.IntegerLiteral && assign_expr.val.str().int() == 1 {
c.error('use `--` instead of `-= 1`', assign_expr.pos)
}
}
.mult_assign, .div_assign {
if !left.is_number() {
c.error('operator ${assign_expr.op.str()} not defined on left operand type `$left.name`',
assign_expr.left.position())
} else if !right.is_number() {
c.error('operator ${assign_expr.op.str()} not defined on right operand type `$right.name`',
assign_expr.val.position())
}
}
.and_assign, .or_assign, .xor_assign, .mod_assign, .left_shift_assign, .right_shift_assign {
if !left.is_int() {
c.error('operator ${assign_expr.op.str()} not defined on left operand type `$left.name`',
assign_expr.left.position())
} else if !right.is_int() {
c.error('operator ${assign_expr.op.str()} not defined on right operand type `$right.name`',
assign_expr.val.position())
}
}
else {}
}
// Dual sides check (compatibility check)
if !c.check_types(right_type, left_type) {
left_type_sym := c.table.get_type_symbol(left_type)
right_type_sym := c.table.get_type_symbol(right_type)
c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name`',
assign_expr.val.position())
}
}
pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
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 {

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/assign_expr_type_err_i.v:3:9: error: cannot assign `string` to variable `foo` of type `f64`
vlib/v/checker/tests/assign_expr_type_err_i.v:3:9: error: cannot assign `string` to `foo` of type `f64`
1 | fn main() {
2 | mut foo := 1.5
3 | foo += 'hello'

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/blank_modify.v:2:2: error: cannot modify blank `_` variable
vlib/v/checker/tests/blank_modify.v:2:2: error: cannot modify blank `_` identifier
1 | fn main() {
2 | _ += 1
| ^

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/cannot_assign_array.v:9:11: error: cannot assign `array_fixed_f64_8` to variable `ctx.vb` of type `string`
vlib/v/checker/tests/cannot_assign_array.v:9:11: error: cannot assign `array_fixed_f64_8` to `ctx.vb` of type `string`
7 | mut ctx := Context{}
8 | x := 2.32
9 | ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]!!

View File

@ -1,7 +1,7 @@
vlib/v/checker/tests/unreachable_code.v:3:7: error: unreachable code
vlib/v/checker/tests/unreachable_code.v:3:4: error: unreachable code
1 | fn foo() int {
2 | return if 1 == 1 { 1 } else { 2 }
3 | a := 1
| ^
| ~~
4 | println(a)
5 | }

View File

@ -1,7 +1,7 @@
vlib/v/checker/tests/void_function_assign_to_string.v:6:6: error: cannot assign `void` to variable `a` of type `string`
vlib/v/checker/tests/void_function_assign_to_string.v:6:4: error: assignment mismatch: 1 variable(s) but `x()` returns 0 value(s)
4 | fn main(){
5 | mut a := ''
6 | a = x(1,2) // hello
| ~~~~~~
| ^
7 | eprintln('a: $a')
8 | }

View File

@ -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++

View File

@ -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*/')

View File

@ -449,9 +449,6 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
// ///////
g.call_args(node.args, node.expected_arg_types)
g.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(')')

View File

@ -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.') {

View File

@ -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())
}
}
// }
}
}

View File

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

View File

@ -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}
}

View File

@ -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

View File

@ -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

View File

@ -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 {