gen: if expressions with multiple statements
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 {
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 | }
|
|
@ -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() {
|
1 | fn main() {
|
||||||
2 | s := if true { '12' } else { 12 }
|
2 | s := if true { '12' } else { 12 }
|
||||||
| ~~
|
| ~~
|
|
@ -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 | }
|
|
@ -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 {
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue