mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
gen: if expressions with multiple statements
This commit is contained in:
parent
2a9cbbe157
commit
fd0d833e33
@ -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 {
|
||||
// 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
|
||||
left_type := c.expr(infix_expr.left)
|
||||
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 {
|
||||
mut expr_required := false
|
||||
if c.expected_type != table.void_type {
|
||||
// | c.assigned_var_name != '' {
|
||||
// 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)
|
||||
node.is_expr = true
|
||||
expr_required = true
|
||||
}
|
||||
former_expected_type := c.expected_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 {
|
||||
if branch.cond is ast.ParExpr {
|
||||
c.error('unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.',
|
||||
branch.pos)
|
||||
}
|
||||
typ := c.expr(branch.cond)
|
||||
if i < node.branches.len - 1 || !node.has_else {
|
||||
typ_sym := c.table.get_type_symbol(typ)
|
||||
// if typ_sym.kind != .bool {
|
||||
if typ.idx() != table.bool_type_idx {
|
||||
c.error('non-bool (`$typ_sym.name`) used as if condition', node.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)
|
||||
if !node.has_else || i < node.branches.len - 1 {
|
||||
// check condition type is boolean
|
||||
cond_typ := c.expr(branch.cond)
|
||||
if cond_typ.idx() != table.bool_type_idx {
|
||||
typ_sym := c.table.get_type_symbol(cond_typ)
|
||||
c.error('non-bool type `$typ_sym.name` used as if condition', branch.pos)
|
||||
}
|
||||
}
|
||||
c.stmts(branch.stmts)
|
||||
}
|
||||
if node.has_else && node.is_expr {
|
||||
last_branch := node.branches[node.branches.len - 1]
|
||||
if last_branch.stmts.len > 0 && node.branches[0].stmts.len > 0 {
|
||||
match last_branch.stmts[last_branch.stmts.len - 1] {
|
||||
ast.ExprStmt {
|
||||
// type_sym := p.table.get_type_symbol(it.typ)
|
||||
// p.warn('if expr ret $type_sym.name')
|
||||
t := c.expr(it.expr)
|
||||
if is_ternary && t != first_typ {
|
||||
c.error('mismatched types `${c.table.type_to_str(first_typ)}` and `${c.table.type_to_str(t)}`',
|
||||
if expr_required {
|
||||
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
|
||||
last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt
|
||||
c.expected_type = former_expected_type
|
||||
expr_type := c.expr(last_expr.expr)
|
||||
if expr_type != node.typ {
|
||||
// first branch of if expression
|
||||
if node.typ == table.void_type {
|
||||
node.is_expr = true
|
||||
node.typ = expr_type
|
||||
} else {
|
||||
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(expr_type)}`',
|
||||
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 node.is_expr && !node.has_else {
|
||||
c.error('`if` expression needs `else` clause. remove return values or add `else`', node.pos)
|
||||
if expr_required {
|
||||
if !node.has_else {
|
||||
c.error('`if` expression needs `else` clause', node.pos)
|
||||
}
|
||||
return node.typ
|
||||
}
|
||||
*/
|
||||
return table.bool_type
|
||||
}
|
||||
|
||||
|
14
vlib/v/checker/tests/if_expr_last_stmt.out
Normal file
14
vlib/v/checker/tests/if_expr_last_stmt.out
Normal 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 | }
|
7
vlib/v/checker/tests/if_expr_last_stmt.vv
Normal file
7
vlib/v/checker/tests/if_expr_last_stmt.vv
Normal file
@ -0,0 +1,7 @@
|
||||
fn main() {
|
||||
_ := if true {
|
||||
1
|
||||
} else if false {
|
||||
} else {
|
||||
}
|
||||
}
|
@ -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() {
|
||||
2 | s := if true { '12' } else { 12 }
|
||||
| ~~
|
5
vlib/v/checker/tests/if_expr_no_else.out
Normal file
5
vlib/v/checker/tests/if_expr_no_else.out
Normal 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 | }
|
3
vlib/v/checker/tests/if_expr_no_else.vv
Normal file
3
vlib/v/checker/tests/if_expr_no_else.vv
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
_ := if true { 1 }
|
||||
}
|
@ -41,11 +41,6 @@ const (
|
||||
]
|
||||
)
|
||||
|
||||
// 'new'
|
||||
fn foo(t token.Token) {
|
||||
util.full_hash()
|
||||
}
|
||||
|
||||
struct Gen {
|
||||
out 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
|
||||
optionals []string // to avoid duplicates TODO perf, use map
|
||||
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
|
||||
autofree bool
|
||||
indent int
|
||||
@ -477,25 +474,35 @@ pub fn (mut g Gen) reset_tmp_count() {
|
||||
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) {
|
||||
g.indent++
|
||||
if g.inside_ternary > 0 {
|
||||
g.write(' ( ')
|
||||
g.writeln('(')
|
||||
}
|
||||
for i, stmt in stmts {
|
||||
g.stmt(stmt)
|
||||
if g.inside_ternary > 0 && i < stmts.len - 1 {
|
||||
g.write(', ')
|
||||
g.writeln(',')
|
||||
}
|
||||
}
|
||||
if g.inside_ternary > 0 {
|
||||
g.write(' ) ')
|
||||
}
|
||||
g.indent--
|
||||
if g.inside_ternary > 0 {
|
||||
g.writeln('')
|
||||
g.write(')')
|
||||
}
|
||||
}
|
||||
|
||||
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()')
|
||||
// g.writeln('//// stmt start')
|
||||
match node {
|
||||
@ -557,15 +564,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||
}
|
||||
ast.ExprStmt {
|
||||
g.expr(it.expr)
|
||||
expr := it.expr
|
||||
// no ; after an if expression }
|
||||
match expr {
|
||||
ast.IfExpr {}
|
||||
else {
|
||||
if g.inside_ternary == 0 {
|
||||
g.writeln(';')
|
||||
}
|
||||
}
|
||||
if g.inside_ternary == 0 {
|
||||
g.writeln(';')
|
||||
}
|
||||
}
|
||||
ast.FnDecl {
|
||||
@ -695,6 +695,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||
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() {
|
||||
@ -836,7 +837,7 @@ fn (mut g Gen) gen_assert_stmt(a ast.AssertStmt) {
|
||||
g.write('if (')
|
||||
g.expr(a.expr)
|
||||
g.write(')')
|
||||
g.inside_ternary--
|
||||
g.decrement_inside_ternary()
|
||||
s_assertion := a.expr.str().replace('"', "\'")
|
||||
mut mod_path := g.file.path
|
||||
$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) {
|
||||
// g.write('/*assign_stmt*/')
|
||||
if assign_stmt.is_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 }
|
||||
else {}
|
||||
}
|
||||
mut is_multi := false
|
||||
// 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
|
||||
is_multi = sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len ||
|
||||
assign_stmt.left.len > 1
|
||||
}
|
||||
if is_multi {
|
||||
// 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
|
||||
if sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len || assign_stmt.left.len >
|
||||
1 {
|
||||
// multi return
|
||||
// TODO Handle in if_expr
|
||||
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)
|
||||
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
|
||||
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
|
||||
g.expr(assign_stmt.right[0])
|
||||
g.is_assign_rhs = false
|
||||
if is_optional {
|
||||
val := assign_stmt.right[0]
|
||||
match val {
|
||||
ast.ArrayInit {
|
||||
is_fixed_array_init = it.is_fixed
|
||||
has_val = it.has_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 {}
|
||||
}
|
||||
is_decl := assign_stmt.op == .decl_assign
|
||||
// g.write('/*assign_stmt*/')
|
||||
if is_decl && right_sym.kind != .function {
|
||||
}
|
||||
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 ')
|
||||
}
|
||||
if right_sym.kind == .function {
|
||||
func := right_sym.info as table.FnType
|
||||
ret_styp := g.typ(func.func.return_type)
|
||||
g.write('$ret_styp (*$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(')')
|
||||
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.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.writeln(' = ${mr_var_name}.arg$i;')
|
||||
}
|
||||
}
|
||||
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(';')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
mut is_ident := false
|
||||
match val {
|
||||
@ -1771,7 +1810,7 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) {
|
||||
}
|
||||
}
|
||||
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
|
||||
g.write('_const_')
|
||||
}
|
||||
name := c_name(node.name)
|
||||
mut name := c_name(node.name)
|
||||
if node.info is ast.IdentVar {
|
||||
ident_var := node.info as ast.IdentVar
|
||||
// x ?int
|
||||
@ -1801,24 +1840,11 @@ fn (mut g Gen) ident(node ast.Ident) {
|
||||
return
|
||||
}
|
||||
}
|
||||
g.write(name)
|
||||
g.write(g.get_ternary_name(name))
|
||||
}
|
||||
|
||||
fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||
// println('if_expr pos=$node.pos.line_nr')
|
||||
// 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 {
|
||||
if node.is_expr || g.inside_ternary != 0 {
|
||||
g.inside_ternary++
|
||||
g.write('(')
|
||||
for i, branch in node.branches {
|
||||
@ -1831,43 +1857,43 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||
}
|
||||
g.stmts(branch.stmts)
|
||||
}
|
||||
if node.branches.len == 1 {
|
||||
g.write(': 0')
|
||||
}
|
||||
g.write(')')
|
||||
g.inside_ternary--
|
||||
} else {
|
||||
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('}')
|
||||
g.decrement_inside_ternary()
|
||||
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 {')
|
||||
}
|
||||
g.stmts(branch.stmts)
|
||||
}
|
||||
if is_guard {
|
||||
g.write('}')
|
||||
}
|
||||
g.writeln('}')
|
||||
}
|
||||
|
||||
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)`
|
||||
fn (mut g Gen) gen_map(node ast.CallExpr) {
|
||||
tmp := g.new_tmp_var()
|
||||
s := g.out.after(g.stmt_start_pos) // the already generated part of current statement
|
||||
g.out.go_back(s.len)
|
||||
s := g.go_before_stmt(0)
|
||||
// println('filter s="$s"')
|
||||
ret_typ := g.typ(node.return_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)`
|
||||
fn (mut g Gen) gen_filter(node ast.CallExpr) {
|
||||
tmp := g.new_tmp_var()
|
||||
s := g.out.after(g.stmt_start_pos) // the already generated part of current statement
|
||||
g.out.go_back(s.len)
|
||||
s := g.go_before_stmt(0)
|
||||
// println('filter s="$s"')
|
||||
sym := g.table.get_type_symbol(node.return_type)
|
||||
if sym.kind != .array {
|
||||
@ -2772,9 +2796,25 @@ fn (mut g Gen) gen_filter(node ast.CallExpr) {
|
||||
g.write(tmp)
|
||||
}
|
||||
|
||||
fn (mut g Gen) insert_before(s string) {
|
||||
cur_line := g.out.after(g.stmt_start_pos)
|
||||
[inline]
|
||||
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)
|
||||
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.write(cur_line)
|
||||
}
|
||||
|
@ -408,7 +408,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||
g.gen_json_for_type(node.args[0].typ)
|
||||
json_type_str = g.table.get_type_symbol(node.args[0].typ).name
|
||||
} else {
|
||||
g.insert_before('// json.decode')
|
||||
g.insert_before_stmt('// json.decode')
|
||||
ast_type := node.args[0].expr as ast.Type
|
||||
// `json.decode(User, s)` => json.decode_User(s)
|
||||
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.write(')')
|
||||
} else {
|
||||
g.write('${name}(')
|
||||
g.write('${g.get_ternary_name(name)}(')
|
||||
if is_json_decode {
|
||||
g.write('json__json_parse(')
|
||||
// Skip the first argument in json.decode which is a type
|
||||
|
@ -8,10 +8,11 @@ import v.table
|
||||
import v.token
|
||||
|
||||
fn (mut p Parser) if_expr() ast.IfExpr {
|
||||
p.inside_if_expr = true
|
||||
was_inside_if_expr := p.inside_if_expr
|
||||
defer {
|
||||
p.inside_if_expr = false
|
||||
p.inside_if_expr = was_inside_if_expr
|
||||
}
|
||||
p.inside_if_expr = true
|
||||
pos := p.tok.position()
|
||||
mut branches := []ast.IfBranch{}
|
||||
mut has_else := false
|
||||
|
@ -11,3 +11,120 @@ fn test_if_expression_precedence_true_condition() {
|
||||
res := 1 + if b > c { b } else { c } + 1
|
||||
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user