gen: if expressions with multiple statements

pull/4895/head
Enzo Baldisserri 2020-05-14 17:15:25 +02:00 committed by GitHub
parent 2a9cbbe157
commit fd0d833e33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 437 additions and 254 deletions

View File

@ -333,6 +333,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
// println('checker: infix expr(op $infix_expr.op.str())') // println('checker: infix expr(op $infix_expr.op.str())')
former_expected_type := c.expected_type
defer {
c.expected_type = former_expected_type
}
c.expected_type = table.void_type c.expected_type = table.void_type
left_type := c.expr(infix_expr.left) left_type := c.expr(infix_expr.left)
infix_expr.left_type = left_type infix_expr.left_type = left_type
@ -1977,64 +1981,56 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol
} }
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
mut expr_required := false
if c.expected_type != table.void_type { if c.expected_type != table.void_type {
// | c.assigned_var_name != '' { // | c.assigned_var_name != '' {
// sym := c.table.get_type_symbol(c.expected_type) // sym := c.table.get_type_symbol(c.expected_type)
// println('$c.file.path $node.pos.line_nr IF is expr: checker exp type = ' + sym.name) // println('$c.file.path $node.pos.line_nr IF is expr: checker exp type = ' + sym.name)
node.is_expr = true expr_required = true
} }
former_expected_type := c.expected_type
node.typ = table.void_type node.typ = table.void_type
mut first_typ := 0
is_ternary := node.is_expr && node.branches.len >= 2 && node.has_else
for i, branch in node.branches { for i, branch in node.branches {
if branch.cond is ast.ParExpr { if branch.cond is ast.ParExpr {
c.error('unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.', c.error('unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.',
branch.pos) branch.pos)
} }
typ := c.expr(branch.cond) if !node.has_else || i < node.branches.len - 1 {
if i < node.branches.len - 1 || !node.has_else { // check condition type is boolean
typ_sym := c.table.get_type_symbol(typ) cond_typ := c.expr(branch.cond)
// if typ_sym.kind != .bool { if cond_typ.idx() != table.bool_type_idx {
if typ.idx() != table.bool_type_idx { typ_sym := c.table.get_type_symbol(cond_typ)
c.error('non-bool (`$typ_sym.name`) used as if condition', node.pos) c.error('non-bool type `$typ_sym.name` used as if condition', branch.pos)
}
}
if is_ternary && i < node.branches.len - 1 && branch.stmts.len > 0 {
last_stmt := branch.stmts[branch.stmts.len - 1]
if last_stmt is ast.ExprStmt {
last_expr := last_stmt as ast.ExprStmt
first_typ = c.expr(last_expr.expr)
} }
} }
c.stmts(branch.stmts) c.stmts(branch.stmts)
} if expr_required {
if node.has_else && node.is_expr { if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
last_branch := node.branches[node.branches.len - 1] last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt
if last_branch.stmts.len > 0 && node.branches[0].stmts.len > 0 { c.expected_type = former_expected_type
match last_branch.stmts[last_branch.stmts.len - 1] { expr_type := c.expr(last_expr.expr)
ast.ExprStmt { if expr_type != node.typ {
// type_sym := p.table.get_type_symbol(it.typ) // first branch of if expression
// p.warn('if expr ret $type_sym.name') if node.typ == table.void_type {
t := c.expr(it.expr) node.is_expr = true
if is_ternary && t != first_typ { node.typ = expr_type
c.error('mismatched types `${c.table.type_to_str(first_typ)}` and `${c.table.type_to_str(t)}`', } else {
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(expr_type)}`',
node.pos) node.pos)
} }
node.typ = t
return t
} }
else {} } else {
c.error('`if` expression requires an expression as the last statement of every branch',
branch.pos)
} }
} else {
c.error('`if` expression needs returns in both branches', node.pos)
} }
} }
// won't yet work due to eg: if true { println('foo') } if expr_required {
/* if !node.has_else {
if node.is_expr && !node.has_else { c.error('`if` expression needs `else` clause', node.pos)
c.error('`if` expression needs `else` clause. remove return values or add `else`', node.pos) }
return node.typ
} }
*/
return table.bool_type return table.bool_type
} }

View File

@ -0,0 +1,14 @@
vlib/v/checker/tests/if_expr_last_stmt.v:4:7: error: `if` expression requires an expression as the last statement of every branch
2 | _ := if true {
3 | 1
4 | } else if false {
| ~~~~~~~~~~~~~
5 | } else {
6 | }
vlib/v/checker/tests/if_expr_last_stmt.v:5:7: error: `if` expression requires an expression as the last statement of every branch
3 | 1
4 | } else if false {
5 | } else {
| ~~~~
6 | }
7 | }

View File

@ -0,0 +1,7 @@
fn main() {
_ := if true {
1
} else if false {
} else {
}
}

View File

@ -1,4 +1,4 @@
vlib/v/checker/tests/ternary_mismatch.v:2:7: error: mismatched types `string` and `int` vlib/v/checker/tests/if_expr_mismatch.v:2:7: error: mismatched types `string` and `int`
1 | fn main() { 1 | fn main() {
2 | s := if true { '12' } else { 12 } 2 | s := if true { '12' } else { 12 }
| ~~ | ~~

View File

@ -0,0 +1,5 @@
vlib/v/checker/tests/if_expr_no_else.v:2:10: error: `if` expression needs `else` clause
1 | fn main() {
2 | _ := if true { 1 }
| ~~
3 | }

View File

@ -0,0 +1,3 @@
fn main() {
_ := if true { 1 }
}

View File

@ -41,11 +41,6 @@ const (
] ]
) )
// 'new'
fn foo(t token.Token) {
util.full_hash()
}
struct Gen { struct Gen {
out strings.Builder out strings.Builder
cheaders strings.Builder cheaders strings.Builder
@ -76,7 +71,9 @@ mut:
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
optionals []string // to avoid duplicates TODO perf, use map optionals []string // to avoid duplicates TODO perf, use map
inside_ternary int // ?: comma separated statements on a single line inside_ternary int // ?: comma separated statements on a single line
stmt_start_pos int ternary_names map[string]string
ternary_level_names map[string][]string
stmt_path_pos []int
right_is_opt bool right_is_opt bool
autofree bool autofree bool
indent int indent int
@ -477,25 +474,35 @@ pub fn (mut g Gen) reset_tmp_count() {
g.tmp_count = 0 g.tmp_count = 0
} }
fn (mut g Gen) decrement_inside_ternary() {
key := g.inside_ternary.str()
for name in g.ternary_level_names[key] {
g.ternary_names.delete(name)
}
g.ternary_level_names.delete(key)
g.inside_ternary--
}
fn (mut g Gen) stmts(stmts []ast.Stmt) { fn (mut g Gen) stmts(stmts []ast.Stmt) {
g.indent++ g.indent++
if g.inside_ternary > 0 { if g.inside_ternary > 0 {
g.write(' ( ') g.writeln('(')
} }
for i, stmt in stmts { for i, stmt in stmts {
g.stmt(stmt) g.stmt(stmt)
if g.inside_ternary > 0 && i < stmts.len - 1 { if g.inside_ternary > 0 && i < stmts.len - 1 {
g.write(', ') g.writeln(',')
} }
} }
if g.inside_ternary > 0 {
g.write(' ) ')
}
g.indent-- g.indent--
if g.inside_ternary > 0 {
g.writeln('')
g.write(')')
}
} }
fn (mut g Gen) stmt(node ast.Stmt) { fn (mut g Gen) stmt(node ast.Stmt) {
g.stmt_start_pos = g.out.len g.stmt_path_pos << g.out.len
// println('cgen.stmt()') // println('cgen.stmt()')
// g.writeln('//// stmt start') // g.writeln('//// stmt start')
match node { match node {
@ -557,15 +564,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} }
ast.ExprStmt { ast.ExprStmt {
g.expr(it.expr) g.expr(it.expr)
expr := it.expr if g.inside_ternary == 0 {
// no ; after an if expression } g.writeln(';')
match expr {
ast.IfExpr {}
else {
if g.inside_ternary == 0 {
g.writeln(';')
}
}
} }
} }
ast.FnDecl { ast.FnDecl {
@ -695,6 +695,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
verror('cgen.stmt(): unhandled node ' + typeof(node)) verror('cgen.stmt(): unhandled node ' + typeof(node))
} }
} }
g.stmt_path_pos.delete(g.stmt_path_pos.len - 1)
} }
fn (mut g Gen) write_defer_stmts() { fn (mut g Gen) write_defer_stmts() {
@ -836,7 +837,7 @@ fn (mut g Gen) gen_assert_stmt(a ast.AssertStmt) {
g.write('if (') g.write('if (')
g.expr(a.expr) g.expr(a.expr)
g.write(')') g.write(')')
g.inside_ternary-- g.decrement_inside_ternary()
s_assertion := a.expr.str().replace('"', "\'") s_assertion := a.expr.str().replace('"', "\'")
mut mod_path := g.file.path mut mod_path := g.file.path
$if windows { $if windows {
@ -862,7 +863,6 @@ fn (mut g Gen) gen_assert_stmt(a ast.AssertStmt) {
} }
fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
// g.write('/*assign_stmt*/')
if assign_stmt.is_static { if assign_stmt.is_static {
g.write('static ') g.write('static ')
} }
@ -873,157 +873,196 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
ast.MatchExpr { return_type = it.return_type } ast.MatchExpr { return_type = it.return_type }
else {} else {}
} }
mut is_multi := false
// json_test failed w/o this check // json_test failed w/o this check
if return_type != table.void_type && return_type != 0 { if return_type != table.void_type && return_type != 0 {
sym := g.table.get_type_symbol(return_type) sym := g.table.get_type_symbol(return_type)
// the left vs. right is ugly and should be removed // the left vs. right is ugly and should be removed
is_multi = sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len || if sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len || assign_stmt.left.len >
assign_stmt.left.len > 1 1 {
} // multi return
if is_multi { // TODO Handle in if_expr
// multi return
mut or_stmts := []ast.Stmt{}
is_optional := return_type.flag_is(.optional)
mr_var_name := 'mr_$assign_stmt.pos.pos'
mr_styp := g.typ(return_type)
g.write('$mr_styp $mr_var_name = ')
g.is_assign_rhs = true
g.expr(assign_stmt.right[0])
g.is_assign_rhs = false
if is_optional {
val := assign_stmt.right[0]
match val {
ast.CallExpr {
or_stmts = it.or_block.stmts
return_type = it.return_type
g.or_block(mr_var_name, or_stmts, return_type)
}
else {}
}
}
g.writeln(';')
for i, ident in assign_stmt.left {
if ident.kind == .blank_ident {
continue
}
ident_var_info := ident.var_info()
styp := g.typ(ident_var_info.typ)
if assign_stmt.op == .decl_assign {
g.write('$styp ')
}
g.expr(ident)
if is_optional {
mr_base_styp := g.base_type(return_type)
g.writeln(' = (*(${mr_base_styp}*)${mr_var_name}.data).arg$i;')
} else {
g.writeln(' = ${mr_var_name}.arg$i;')
}
}
} else {
// `a := 1` | `a,b := 1,2`
for i, ident in assign_stmt.left {
val := assign_stmt.right[i]
ident_var_info := ident.var_info()
styp := g.typ(ident_var_info.typ)
mut is_call := false
mut or_stmts := []ast.Stmt{} mut or_stmts := []ast.Stmt{}
blank_assign := ident.kind == .blank_ident is_optional := return_type.flag_is(.optional)
match val { mr_var_name := 'mr_$assign_stmt.pos.pos'
ast.CallExpr { mr_styp := g.typ(return_type)
is_call = true g.write('$mr_styp $mr_var_name = ')
or_stmts = it.or_block.stmts
return_type = it.return_type
}
// TODO: no buffer fiddling
ast.AnonFn {
if blank_assign {
g.write('{')
}
ret_styp := g.typ(it.decl.return_type)
g.write('$ret_styp (*$ident.name) (')
def_pos := g.definitions.len
g.fn_args(it.decl.args, it.decl.is_variadic)
g.definitions.go_back(g.definitions.len - def_pos)
g.write(') = ')
g.expr(*it)
g.writeln(';')
if blank_assign {
g.write('}')
}
continue
}
else {}
}
gen_or := is_call && return_type.flag_is(.optional)
g.is_assign_rhs = true g.is_assign_rhs = true
if blank_assign { g.expr(assign_stmt.right[0])
if is_call { g.is_assign_rhs = false
g.expr(val) if is_optional {
} else { val := assign_stmt.right[0]
g.write('{$styp _ = ')
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 { match val {
ast.ArrayInit { ast.CallExpr {
is_fixed_array_init = it.is_fixed or_stmts = it.or_block.stmts
has_val = it.has_val return_type = it.return_type
g.or_block(mr_var_name, or_stmts, return_type)
} }
else {} else {}
} }
is_decl := assign_stmt.op == .decl_assign }
// g.write('/*assign_stmt*/') g.writeln(';')
if is_decl && right_sym.kind != .function { for i, ident in assign_stmt.left {
if ident.kind == .blank_ident {
continue
}
ident_var_info := ident.var_info()
styp := g.typ(ident_var_info.typ)
if assign_stmt.op == .decl_assign {
g.write('$styp ') g.write('$styp ')
} }
if right_sym.kind == .function { g.expr(ident)
func := right_sym.info as table.FnType if is_optional {
ret_styp := g.typ(func.func.return_type) mr_base_styp := g.base_type(return_type)
g.write('$ret_styp (*$ident.name) (') g.writeln(' = (*(${mr_base_styp}*)${mr_var_name}.data).arg$i;')
def_pos := g.definitions.len
g.fn_args(func.func.args, func.func.is_variadic)
g.definitions.go_back(g.definitions.len - def_pos)
g.write(')')
} else { } else {
g.ident(ident) g.writeln(' = ${mr_var_name}.arg$i;')
}
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 is_fixed_array_init {
if has_val {
g.write(' = ')
g.expr(val)
} else {
g.write(' = {0}')
}
} else {
g.write(' = ')
if is_decl {
g.expr(val)
} else {
g.expr_with_cast(val, assign_stmt.left_types[i], ident_var_info.typ)
}
}
if gen_or {
g.or_block(ident.name, or_stmts, return_type)
} }
} }
g.is_assign_rhs = false return
}
}
// `a := 1` | `a,b := 1,2`
for i, ident in assign_stmt.left {
val := assign_stmt.right[i]
ident_var_info := ident.var_info()
styp := g.typ(ident_var_info.typ)
mut is_call := false
mut or_stmts := []ast.Stmt{}
blank_assign := ident.kind == .blank_ident
match val {
ast.CallExpr {
is_call = true
or_stmts = it.or_block.stmts
return_type = it.return_type
}
// TODO: no buffer fiddling
ast.AnonFn {
if blank_assign {
g.write('{')
}
ret_styp := g.typ(it.decl.return_type)
g.write('$ret_styp (*$ident.name) (')
def_pos := g.definitions.len
g.fn_args(it.decl.args, it.decl.is_variadic)
g.definitions.go_back(g.definitions.len - def_pos)
g.write(') = ')
g.expr(*it)
g.writeln(';')
if blank_assign {
g.write('}')
}
continue
}
else {}
}
gen_or := is_call && return_type.flag_is(.optional)
g.is_assign_rhs = true
if blank_assign {
if is_call {
g.expr(val)
} else {
g.write('{$styp _ = ')
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 {}
}
is_inside_ternary := g.inside_ternary != 0
cur_line := if is_inside_ternary {
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 {
g.out.write(tabs[g.indent - g.inside_ternary])
}
func := right_sym.info as table.FnType
ret_styp := g.typ(func.func.return_type)
g.write('$ret_styp (*${g.get_ternary_name(ident.name)}) (')
def_pos := g.definitions.len
g.fn_args(func.func.args, func.func.is_variadic)
g.definitions.go_back(g.definitions.len - def_pos)
g.write(')')
} else {
if is_decl {
if is_inside_ternary {
g.out.write(tabs[g.indent - g.inside_ternary])
}
g.write('$styp ')
}
g.ident(ident)
}
if is_inside_ternary {
g.write(';\n$cur_line')
g.out.write(tabs[g.indent])
g.ident(ident)
}
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 is_fixed_array_init {
if has_val {
g.write(' = ')
g.expr(val)
} else {
g.write(' = {0}')
}
} else {
g.write(' = ')
if is_decl {
g.expr(val)
} else {
g.expr_with_cast(val, assign_stmt.left_types[i], ident_var_info.typ)
}
}
if gen_or {
g.or_block(ident.name, or_stmts, return_type)
}
}
g.is_assign_rhs = false
if g.inside_ternary == 0 {
g.writeln(';') g.writeln(';')
} }
} }
} }
fn (mut g Gen) register_ternary_name(name string) {
level_key := g.inside_ternary.str()
if level_key !in g.ternary_level_names {
g.ternary_level_names[level_key] = []string{}
}
new_name := g.new_tmp_var()
g.ternary_names[name] = new_name
g.ternary_level_names[level_key] << name
}
fn (mut g Gen) get_ternary_name(name string) string {
if g.inside_ternary == 0 {
return name
}
if name !in g.ternary_names {
return name
}
return g.ternary_names[name]
}
fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, add_eq bool) bool { fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, add_eq bool) bool {
mut is_ident := false mut is_ident := false
match val { match val {
@ -1771,7 +1810,7 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) {
} }
} }
if is_expr { if is_expr {
g.inside_ternary-- g.decrement_inside_ternary()
} }
} }
@ -1787,7 +1826,7 @@ fn (mut g Gen) ident(node ast.Ident) {
// TODO globals hack // TODO globals hack
g.write('_const_') g.write('_const_')
} }
name := c_name(node.name) mut name := c_name(node.name)
if node.info is ast.IdentVar { if node.info is ast.IdentVar {
ident_var := node.info as ast.IdentVar ident_var := node.info as ast.IdentVar
// x ?int // x ?int
@ -1801,24 +1840,11 @@ fn (mut g Gen) ident(node ast.Ident) {
return return
} }
} }
g.write(name) g.write(g.get_ternary_name(name))
} }
fn (mut g Gen) if_expr(node ast.IfExpr) { fn (mut g Gen) if_expr(node ast.IfExpr) {
// println('if_expr pos=$node.pos.line_nr') if node.is_expr || g.inside_ternary != 0 {
// g.writeln('/* if is_expr=$node.is_expr */')
// If expression? Assign the value to a temp var.
// Previously ?: was used, but it's too unreliable.
type_sym := g.table.get_type_symbol(node.typ)
mut tmp := ''
if type_sym.kind != .void {
tmp = g.new_tmp_var()
// g.writeln('$ti.name $tmp;')
}
// one line ?:
// TODO clean this up once `is` is supported
// TODO: make sure only one stmt in each branch
if node.is_expr && node.branches.len >= 2 && node.has_else && type_sym.kind != .void {
g.inside_ternary++ g.inside_ternary++
g.write('(') g.write('(')
for i, branch in node.branches { for i, branch in node.branches {
@ -1831,43 +1857,43 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
} }
g.stmts(branch.stmts) g.stmts(branch.stmts)
} }
if node.branches.len == 1 {
g.write(': 0')
}
g.write(')') g.write(')')
g.inside_ternary-- g.decrement_inside_ternary()
} else { return
mut is_guard := false
for i, branch in node.branches {
if i == 0 {
match branch.cond {
ast.IfGuardExpr {
is_guard = true
g.write('{ /* if guard */ ${g.typ(it.expr_type)} $it.var_name = ')
g.expr(it.expr)
g.writeln(';')
g.writeln('if (${it.var_name}.ok) {')
}
else {
g.write('if (')
g.expr(branch.cond)
g.writeln(') {')
}
}
} else if i < node.branches.len - 1 || !node.has_else {
g.write('} else if (')
g.expr(branch.cond)
g.writeln(') {')
} else if i == node.branches.len - 1 && node.has_else {
g.writeln('} else {')
}
// Assign ret value
// if i == node.stmts.len - 1 && type_sym.kind != .void {}
// g.writeln('$tmp =')
g.stmts(branch.stmts)
}
if is_guard {
g.write('}')
}
g.writeln('}')
} }
mut is_guard := false
for i, branch in node.branches {
if i == 0 {
match branch.cond {
ast.IfGuardExpr {
is_guard = true
g.write('{ /* if guard */ ${g.typ(it.expr_type)} $it.var_name = ')
g.expr(it.expr)
g.writeln(';')
g.writeln('if (${it.var_name}.ok) {')
}
else {
g.write('if (')
g.expr(branch.cond)
g.writeln(') {')
}
}
} else if i < node.branches.len - 1 || !node.has_else {
g.write('} else if (')
g.expr(branch.cond)
g.writeln(') {')
} else if i == node.branches.len - 1 && node.has_else {
g.writeln('} else {')
}
g.stmts(branch.stmts)
}
if is_guard {
g.write('}')
}
g.writeln('}')
} }
fn (mut g Gen) index_expr(node ast.IndexExpr) { fn (mut g Gen) index_expr(node ast.IndexExpr) {
@ -2710,8 +2736,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
// `nums.map(it % 2 == 0)` // `nums.map(it % 2 == 0)`
fn (mut g Gen) gen_map(node ast.CallExpr) { fn (mut g Gen) gen_map(node ast.CallExpr) {
tmp := g.new_tmp_var() tmp := g.new_tmp_var()
s := g.out.after(g.stmt_start_pos) // the already generated part of current statement s := g.go_before_stmt(0)
g.out.go_back(s.len)
// println('filter s="$s"') // println('filter s="$s"')
ret_typ := g.typ(node.return_type) ret_typ := g.typ(node.return_type)
// inp_typ := g.typ(node.receiver_type) // inp_typ := g.typ(node.receiver_type)
@ -2746,8 +2771,7 @@ fn (mut g Gen) gen_map(node ast.CallExpr) {
// `nums.filter(it % 2 == 0)` // `nums.filter(it % 2 == 0)`
fn (mut g Gen) gen_filter(node ast.CallExpr) { fn (mut g Gen) gen_filter(node ast.CallExpr) {
tmp := g.new_tmp_var() tmp := g.new_tmp_var()
s := g.out.after(g.stmt_start_pos) // the already generated part of current statement s := g.go_before_stmt(0)
g.out.go_back(s.len)
// println('filter s="$s"') // println('filter s="$s"')
sym := g.table.get_type_symbol(node.return_type) sym := g.table.get_type_symbol(node.return_type)
if sym.kind != .array { if sym.kind != .array {
@ -2772,9 +2796,25 @@ fn (mut g Gen) gen_filter(node ast.CallExpr) {
g.write(tmp) g.write(tmp)
} }
fn (mut g Gen) insert_before(s string) { [inline]
cur_line := g.out.after(g.stmt_start_pos) fn (g &Gen) nth_stmt_pos(n int) int {
return g.stmt_path_pos[g.stmt_path_pos.len - (1 + n)]
}
fn (mut g Gen) go_before_stmt(n int) string {
stmt_pos := g.nth_stmt_pos(n)
cur_line := g.out.after(stmt_pos)
g.out.go_back(cur_line.len) g.out.go_back(cur_line.len)
return cur_line
}
[inline]
fn (mut g Gen) go_before_ternary() string {
return g.go_before_stmt(g.inside_ternary)
}
fn (mut g Gen) insert_before_stmt(s string) {
cur_line := g.go_before_stmt(0)
g.writeln(s) g.writeln(s)
g.write(cur_line) g.write(cur_line)
} }

View File

@ -408,7 +408,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
g.gen_json_for_type(node.args[0].typ) g.gen_json_for_type(node.args[0].typ)
json_type_str = g.table.get_type_symbol(node.args[0].typ).name json_type_str = g.table.get_type_symbol(node.args[0].typ).name
} else { } else {
g.insert_before('// json.decode') g.insert_before_stmt('// json.decode')
ast_type := node.args[0].expr as ast.Type ast_type := node.args[0].expr as ast.Type
// `json.decode(User, s)` => json.decode_User(s) // `json.decode(User, s)` => json.decode_User(s)
sym := g.table.get_type_symbol(ast_type.typ) sym := g.table.get_type_symbol(ast_type.typ)
@ -512,7 +512,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
g.call_args(node.args, node.expected_arg_types) g.call_args(node.args, node.expected_arg_types)
g.write(')') g.write(')')
} else { } else {
g.write('${name}(') g.write('${g.get_ternary_name(name)}(')
if is_json_decode { if is_json_decode {
g.write('json__json_parse(') g.write('json__json_parse(')
// Skip the first argument in json.decode which is a type // Skip the first argument in json.decode which is a type

View File

@ -8,10 +8,11 @@ import v.table
import v.token import v.token
fn (mut p Parser) if_expr() ast.IfExpr { fn (mut p Parser) if_expr() ast.IfExpr {
p.inside_if_expr = true was_inside_if_expr := p.inside_if_expr
defer { defer {
p.inside_if_expr = false p.inside_if_expr = was_inside_if_expr
} }
p.inside_if_expr = true
pos := p.tok.position() pos := p.tok.position()
mut branches := []ast.IfBranch{} mut branches := []ast.IfBranch{}
mut has_else := false mut has_else := false

View File

@ -11,3 +11,120 @@ fn test_if_expression_precedence_true_condition() {
res := 1 + if b > c { b } else { c } + 1 res := 1 + if b > c { b } else { c } + 1
assert res == b + 2 assert res == b + 2
} }
fn test_if_expression_with_stmts() {
a := if true {
b := 1
b
} else {
b := 4
b
}
assert a == 1
mut b := 0
b = if false {
42
} else {
24
}
assert b == 24
}
fn noop() {}
fn test_if_expression_with_function_assign() {
a := if true {
my_fn := noop
my_fn()
0
} else {
1
}
assert a == 0
}
fn get_bool_str(b bool) string {
return b.str()
}
fn test_if_expression_mutate_var() {
mut b := false
r := b && if true {
b = true
true
} else {
true
}
assert r == false
// test in function call
assert get_bool_str(b && if true {
b = true
true
} else {
true
}) == 'false'
// test on method call
assert (b && if true {
b = true
true
} else {
true
}).str() == 'false'
// test on array
mut a := [1, 2]
assert a.len == 2 && if true {
a << 3
true
} else {
false
}
}
fn test_simple_nested_if_expressions() {
a := if false {
b := 1
if b == 0 {
0
} else {
b
}
} else {
println('Hello world !')
if 1 == 1 {
t := 12
t + 42
} else {
43
}
}
assert a == 54
}
fn test_complex_nested_if_expressions() {
mut a := false
a = 1 == 2 || true && if true {
g := 6
h := if false { 3 } else { 5 }
mut d := false
if h == 2 {
d = g + 4 == 5
}
if d {
if true {
d = false
} else {
d = true
}
}
d
} else {
x := 6
y := 8
if x + y > 0 {
x > 0
} else {
false
}
}
assert a == false
}