gen: fix nested `or`
parent
d3ce6fd2e7
commit
1633675c11
|
@ -2,7 +2,7 @@ import json
|
|||
|
||||
struct Employee {
|
||||
name string
|
||||
age int
|
||||
age int
|
||||
}
|
||||
|
||||
fn test_simple() {
|
||||
|
@ -11,23 +11,24 @@ fn test_simple() {
|
|||
assert s == '{"name":"Peter","age":28}'
|
||||
y := json.decode(Employee, s) or {
|
||||
assert false
|
||||
Employee{}
|
||||
}
|
||||
assert y.name == 'Peter'
|
||||
assert y.age == 28
|
||||
}
|
||||
|
||||
struct User2 {
|
||||
age int
|
||||
nums []int
|
||||
age int
|
||||
nums []int
|
||||
}
|
||||
|
||||
struct User {
|
||||
age int
|
||||
nums []int
|
||||
last_name string [json:lastName]
|
||||
is_registered bool [json:IsRegistered]
|
||||
typ int [json:'type']
|
||||
pets string [raw; json:'pet_animals']
|
||||
age int
|
||||
nums []int
|
||||
last_name string [json:lastName]
|
||||
is_registered bool [json:IsRegistered]
|
||||
typ int [json:'type']
|
||||
pets string [raw; json:'pet_animals']
|
||||
}
|
||||
|
||||
fn test_parse_user() {
|
||||
|
@ -51,8 +52,15 @@ fn test_parse_user() {
|
|||
assert u.pets == '{"name":"Bob","animal":"Dog"}'
|
||||
}
|
||||
|
||||
fn test_encode_user(){
|
||||
usr := User{ age: 10, nums: [1,2,3], last_name: 'Johnson', is_registered: true, typ: 0, pets: 'foo'}
|
||||
fn test_encode_user() {
|
||||
usr := User{
|
||||
age: 10
|
||||
nums: [1, 2, 3]
|
||||
last_name: 'Johnson'
|
||||
is_registered: true
|
||||
typ: 0
|
||||
pets: 'foo'
|
||||
}
|
||||
expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"foo"}'
|
||||
out := json.encode(usr)
|
||||
println(out)
|
||||
|
@ -60,17 +68,17 @@ fn test_encode_user(){
|
|||
}
|
||||
|
||||
struct Color {
|
||||
space string
|
||||
point string [raw]
|
||||
space string
|
||||
point string [raw]
|
||||
}
|
||||
|
||||
fn test_raw_json_field() {
|
||||
color := json.decode(Color, '{"space": "YCbCr", "point": {"Y": 123}}') or {
|
||||
println('text')
|
||||
return
|
||||
}
|
||||
assert color.point == '{"Y":123}'
|
||||
assert color.space == 'YCbCr'
|
||||
color := json.decode(Color, '{"space": "YCbCr", "point": {"Y": 123}}') or {
|
||||
println('text')
|
||||
return
|
||||
}
|
||||
assert color.point == '{"Y":123}'
|
||||
assert color.space == 'YCbCr'
|
||||
}
|
||||
|
||||
struct City {
|
||||
|
@ -79,7 +87,7 @@ struct City {
|
|||
|
||||
struct Country {
|
||||
cities []City
|
||||
name string
|
||||
name string
|
||||
}
|
||||
|
||||
fn test_struct_in_struct() {
|
||||
|
@ -92,6 +100,4 @@ fn test_struct_in_struct() {
|
|||
assert country.cities[0].name == 'London'
|
||||
assert country.cities[1].name == 'Manchester'
|
||||
println(country.cities)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -43,8 +43,9 @@ pub:
|
|||
pub struct ExprStmt {
|
||||
pub:
|
||||
expr Expr
|
||||
typ table.Type
|
||||
pos token.Position
|
||||
pub mut:
|
||||
typ table.Type
|
||||
}
|
||||
|
||||
pub struct IntegerLiteral {
|
||||
|
|
|
@ -848,7 +848,9 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
c.error('json.decode: second argument needs to be a string', call_expr.pos)
|
||||
}
|
||||
typ := expr as ast.Type
|
||||
return typ.typ.set_flag(.optional)
|
||||
ret_type := typ.typ.set_flag(.optional)
|
||||
call_expr.return_type = ret_type
|
||||
return ret_type
|
||||
}
|
||||
// look for function in format `mod.fn` or `fn` (main/builtin)
|
||||
mut f := table.Fn{}
|
||||
|
@ -1012,17 +1014,15 @@ fn (mut c Checker) type_implements(typ, inter_typ table.Type, pos token.Position
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) check_expr_opt_call(x ast.Expr, xtype table.Type, is_return_used bool) {
|
||||
match x {
|
||||
ast.CallExpr {
|
||||
if it.return_type.flag_is(.optional) {
|
||||
c.check_or_block(it, xtype, is_return_used)
|
||||
} else if it.or_block.is_used && it.name != 'json.decode' { // TODO remove decode hack
|
||||
c.error('unexpected `or` block, the function `$it.name` does not return an optional',
|
||||
it.pos)
|
||||
}
|
||||
pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, xtype table.Type, is_return_used bool) {
|
||||
if expr is ast.CallExpr {
|
||||
call_expr := expr as ast.CallExpr
|
||||
if call_expr.return_type.flag_is(.optional) {
|
||||
c.check_or_block(call_expr, xtype, is_return_used)
|
||||
} else if call_expr.or_block.is_used {
|
||||
c.error('unexpected `or` block, the function `$call_expr.name` does not return an optional',
|
||||
call_expr.pos)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1052,12 +1052,13 @@ pub fn (mut c Checker) check_or_block(mut call_expr ast.CallExpr, ret_type table
|
|||
}
|
||||
match last_stmt {
|
||||
ast.ExprStmt {
|
||||
type_fits := c.table.check(c.expr(it.expr), ret_type)
|
||||
it.typ = c.expr(it.expr)
|
||||
type_fits := c.table.check(it.typ, ret_type)
|
||||
is_panic_or_exit := is_expr_panic_or_exit(it.expr)
|
||||
if type_fits || is_panic_or_exit {
|
||||
return
|
||||
}
|
||||
type_name := c.table.get_type_symbol(c.expr(it.expr)).name
|
||||
type_name := c.table.get_type_symbol(it.typ).name
|
||||
expected_type_name := c.table.get_type_symbol(ret_type).name
|
||||
c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`',
|
||||
it.pos)
|
||||
|
@ -1509,9 +1510,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
|||
c.enum_decl(it)
|
||||
}
|
||||
ast.ExprStmt {
|
||||
etype := c.expr(it.expr)
|
||||
it.typ = c.expr(it.expr)
|
||||
c.expected_type = table.void_type
|
||||
c.check_expr_opt_call(it.expr, etype, false)
|
||||
c.check_expr_opt_call(it.expr, it.typ, false)
|
||||
}
|
||||
ast.FnDecl {
|
||||
c.fn_decl(it)
|
||||
|
@ -1986,6 +1987,7 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type {
|
|||
match branch.stmts[branch.stmts.len - 1] {
|
||||
ast.ExprStmt {
|
||||
ret_type = c.expr(it.expr)
|
||||
it.typ = ret_type
|
||||
}
|
||||
else {
|
||||
// TODO: ask alex about this
|
||||
|
@ -2106,14 +2108,14 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
|||
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
|
||||
last_expr.typ = c.expr(last_expr.expr)
|
||||
if last_expr.typ != node.typ {
|
||||
if node.typ == table.void_type {
|
||||
// first branch of if expression
|
||||
node.is_expr = true
|
||||
node.typ = expr_type
|
||||
node.typ = last_expr.typ
|
||||
} else {
|
||||
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(expr_type)}`',
|
||||
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(last_expr.typ)}`',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -903,16 +903,11 @@ 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 {
|
||||
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 {}
|
||||
}
|
||||
if is_optional && assign_stmt.right[0] is ast.CallExpr {
|
||||
val := assign_stmt.right[0] as ast.CallExpr
|
||||
or_stmts = val.or_block.stmts
|
||||
return_type = val.return_type
|
||||
g.or_block(mr_var_name, or_stmts, return_type)
|
||||
}
|
||||
g.writeln(';')
|
||||
for i, ident in assign_stmt.left {
|
||||
|
@ -969,7 +964,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
|||
}
|
||||
else {}
|
||||
}
|
||||
gen_or := is_call && return_type.flag_is(.optional)
|
||||
g.is_assign_rhs = true
|
||||
if blank_assign {
|
||||
if is_call {
|
||||
|
@ -1060,9 +1054,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
|||
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 {
|
||||
|
@ -2945,6 +2936,7 @@ fn (mut g Gen) insert_before_stmt(s string) {
|
|||
// If the user is not using the optional return value. We need to pass a temp var
|
||||
// to access its fields (`.ok`, `.error` etc)
|
||||
// `os.cp(...)` => `Option bool tmp = os__cp(...); if (!tmp.ok) { ... }`
|
||||
// Returns the type of the last stmt
|
||||
fn (mut g Gen) or_block(var_name string, stmts []ast.Stmt, return_type table.Type) {
|
||||
cvar_name := c_name(var_name)
|
||||
mr_styp := g.base_type(return_type)
|
||||
|
@ -2952,50 +2944,37 @@ fn (mut g Gen) or_block(var_name string, stmts []ast.Stmt, return_type table.Typ
|
|||
g.writeln('if (!${cvar_name}.ok) {')
|
||||
g.writeln('\tstring err = ${cvar_name}.v_error;')
|
||||
g.writeln('\tint errcode = ${cvar_name}.ecode;')
|
||||
last_type, type_of_last_expression := g.type_of_last_statement(stmts)
|
||||
if last_type == 'v.ast.ExprStmt' && type_of_last_expression != 'void' {
|
||||
if stmts.len > 0 && stmts[stmts.len - 1] is ast.ExprStmt && (stmts[stmts.len - 1] as ast.ExprStmt).typ !=
|
||||
table.void_type {
|
||||
g.indent++
|
||||
for i, stmt in stmts {
|
||||
if i == stmts.len - 1 {
|
||||
g.indent--
|
||||
g.write('\t*(${mr_styp}*) ${cvar_name}.data = ')
|
||||
expr_stmt := stmt as ast.ExprStmt
|
||||
g.stmt_path_pos << g.out.len
|
||||
g.write('*(${mr_styp}*) ${cvar_name}.data = ')
|
||||
is_opt_call := expr_stmt.expr is ast.CallExpr && expr_stmt.typ.flag_is(.optional)
|
||||
if is_opt_call {
|
||||
g.write('*(${mr_styp}*) ')
|
||||
}
|
||||
g.expr(expr_stmt.expr)
|
||||
if is_opt_call {
|
||||
g.write('.data')
|
||||
}
|
||||
if g.inside_ternary == 0 && !(expr_stmt.expr is ast.IfExpr) {
|
||||
g.writeln(';')
|
||||
}
|
||||
g.stmt_path_pos.delete(g.stmt_path_pos.len - 1)
|
||||
} else {
|
||||
g.stmt(stmt)
|
||||
}
|
||||
g.stmt(stmt)
|
||||
}
|
||||
g.indent--
|
||||
} else {
|
||||
g.stmts(stmts)
|
||||
}
|
||||
g.write('}')
|
||||
}
|
||||
|
||||
fn (mut g Gen) type_of_last_statement(stmts []ast.Stmt) (string, string) {
|
||||
mut last_type := ''
|
||||
mut last_expr_result_type := ''
|
||||
if stmts.len > 0 {
|
||||
last_stmt := stmts[stmts.len - 1]
|
||||
last_type = typeof(last_stmt)
|
||||
if last_type == 'v.ast.ExprStmt' {
|
||||
match last_stmt {
|
||||
ast.ExprStmt {
|
||||
it_expr_type := typeof(it.expr)
|
||||
if it_expr_type == 'v.ast.CallExpr' {
|
||||
g.writeln('\t // typeof it_expr_type: $it_expr_type')
|
||||
last_expr_result_type = g.type_of_call_expr(it.expr)
|
||||
} else {
|
||||
last_expr_result_type = it_expr_type
|
||||
}
|
||||
}
|
||||
else {
|
||||
last_expr_result_type = last_type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
g.writeln('\t// last_type: $last_type')
|
||||
g.writeln('\t// last_expr_result_type: $last_expr_result_type')
|
||||
return last_type, last_expr_result_type
|
||||
}
|
||||
|
||||
fn (mut g Gen) type_of_call_expr(node ast.Expr) string {
|
||||
match node {
|
||||
ast.CallExpr { return g.typ(it.return_type) }
|
||||
|
|
|
@ -284,7 +284,14 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
|||
if node.should_be_skipped {
|
||||
return
|
||||
}
|
||||
gen_or := !g.is_assign_rhs && node.or_block.stmts.len > 0
|
||||
gen_or := node.or_block.stmts.len > 0
|
||||
cur_line := if gen_or && g.is_assign_rhs {
|
||||
line := g.go_before_stmt(0)
|
||||
g.out.write(tabs[g.indent])
|
||||
line
|
||||
} else {
|
||||
''
|
||||
}
|
||||
tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
|
||||
if gen_or {
|
||||
styp := g.typ(node.return_type)
|
||||
|
@ -297,6 +304,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
|||
}
|
||||
if gen_or {
|
||||
g.or_block(tmp_opt, node.or_block.stmts, node.return_type)
|
||||
g.write('\n${cur_line}${tmp_opt}')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
|||
// `foo() or {}``
|
||||
mut or_stmts := []ast.Stmt{}
|
||||
if p.tok.kind == .key_orelse {
|
||||
was_inside_or_expr := p.inside_or_expr
|
||||
p.inside_or_expr = true
|
||||
p.next()
|
||||
p.open_scope()
|
||||
|
@ -63,7 +64,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
|||
is_or_block_used = true
|
||||
or_stmts = p.parse_block_no_scope()
|
||||
p.close_scope()
|
||||
p.inside_or_expr = false
|
||||
p.inside_or_expr = was_inside_or_expr
|
||||
}
|
||||
if p.tok.kind == .question {
|
||||
// `foo()?`
|
||||
|
|
|
@ -668,13 +668,12 @@ fn (mut p Parser) parse_multi_expr() ast.Stmt {
|
|||
expr: p.assign_expr(collected[0])
|
||||
pos: epos
|
||||
}
|
||||
} else {
|
||||
return ast.ExprStmt{
|
||||
expr: p.assign_expr(ast.ConcatExpr{
|
||||
vals: collected
|
||||
})
|
||||
pos: epos
|
||||
}
|
||||
}
|
||||
return ast.ExprStmt{
|
||||
expr: p.assign_expr(ast.ConcatExpr{
|
||||
vals: collected
|
||||
})
|
||||
pos: epos
|
||||
}
|
||||
} else {
|
||||
if collected.len == 1 {
|
||||
|
@ -1000,12 +999,10 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
|||
is_used: is_or_block_used
|
||||
}
|
||||
}
|
||||
mut node := ast.Expr{}
|
||||
node = mcall_expr
|
||||
if is_filter {
|
||||
p.close_scope()
|
||||
}
|
||||
return node
|
||||
return mcall_expr
|
||||
}
|
||||
sel_expr := ast.SelectorExpr{
|
||||
expr: left
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
struct Abc {
|
||||
x int
|
||||
}
|
||||
|
@ -24,12 +23,18 @@ fn string_0(x int) ?string {
|
|||
return '$x'
|
||||
}
|
||||
|
||||
fn b_0(b bool) ?bool {
|
||||
if b == false {
|
||||
return error('my error 4')
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
fn return_a_string() string {
|
||||
return 'abcdef'
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fn test_optional_int() {
|
||||
a := i_0(0) or {
|
||||
4999
|
||||
|
@ -41,6 +46,18 @@ fn test_optional_int() {
|
|||
assert b == 4123
|
||||
}
|
||||
|
||||
/*
|
||||
fn test_optional_bool() {
|
||||
a := true && b_0(false) {
|
||||
true
|
||||
}
|
||||
assert a == true
|
||||
b := false || b_0(true) {
|
||||
false
|
||||
}
|
||||
assert b == true
|
||||
}
|
||||
*/
|
||||
fn test_optional_struct() {
|
||||
sa := struct_0(0) or {
|
||||
Abc{7999}
|
||||
|
@ -58,6 +75,10 @@ fn test_optional_with_statements_before_last_expression() {
|
|||
Abc{12345}
|
||||
}
|
||||
assert s.x == 12345
|
||||
b := b_0(true) or {
|
||||
false
|
||||
}
|
||||
assert b == true
|
||||
}
|
||||
|
||||
fn test_optional_with_fn_call_as_last_expression() {
|
||||
|
@ -75,3 +96,61 @@ fn test_optional_with_fn_call_last_expr_and_preceding_statements() {
|
|||
}
|
||||
assert s == 'abcdef'
|
||||
}
|
||||
|
||||
fn test_nested_optional() {
|
||||
a := i_0(1) or {
|
||||
b := i_0(0) or {
|
||||
3
|
||||
}
|
||||
b
|
||||
}
|
||||
assert a == 1
|
||||
b := i_0(0) or {
|
||||
c := i_0(1) or {
|
||||
3
|
||||
}
|
||||
c
|
||||
}
|
||||
assert b == 1
|
||||
c := i_0(0) or {
|
||||
d := i_0(0) or {
|
||||
3
|
||||
}
|
||||
d
|
||||
}
|
||||
assert c == 3
|
||||
}
|
||||
|
||||
fn test_nested_optional_with_opt_fn_call_as_last_value() {
|
||||
a := i_0(1) or {
|
||||
i_0(0) or {
|
||||
3
|
||||
}
|
||||
}
|
||||
assert a == 1
|
||||
b := i_0(0) or {
|
||||
i_0(1) or {
|
||||
3
|
||||
}
|
||||
}
|
||||
assert b == 1
|
||||
c := i_0(0) or {
|
||||
i_0(0) or {
|
||||
3
|
||||
}
|
||||
}
|
||||
assert c == 3
|
||||
// TODO Enable once optional in boolean expressions are working
|
||||
// d := b_0(true) or {
|
||||
// false && b_0(true) or {
|
||||
// true
|
||||
// }
|
||||
// }
|
||||
// assert d == false
|
||||
// e := b_0(true) or {
|
||||
// true && b_0(true) or {
|
||||
// false
|
||||
// }
|
||||
// }
|
||||
// assert e == false
|
||||
}
|
||||
|
|
|
@ -97,6 +97,41 @@ fn test_q() {
|
|||
// assert foo_ok()? == true
|
||||
}
|
||||
|
||||
fn or_return_val() int {
|
||||
a := ret_none() or {
|
||||
return 1
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
fn or_return_error() ?int {
|
||||
a := ret_none() or {
|
||||
return error('Nope')
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
fn or_return_none() ?int {
|
||||
a := ret_none() or {
|
||||
return none
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
fn test_or_return() {
|
||||
assert or_return_val() == 1
|
||||
if _ := or_return_error() {
|
||||
assert false
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
if _ := or_return_none() {
|
||||
assert false
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
}
|
||||
|
||||
fn test_reassignment() {
|
||||
mut x2 := foo_ok() or {
|
||||
assert false
|
||||
|
@ -107,7 +142,7 @@ fn test_reassignment() {
|
|||
assert x2 == 100
|
||||
x2 += 1
|
||||
assert x2 == 101
|
||||
///
|
||||
//
|
||||
mut x3 := 0
|
||||
x3 = foo_ok() or {
|
||||
assert false
|
||||
|
@ -170,11 +205,9 @@ fn opt_ptr(a &int) ?&int {
|
|||
|
||||
fn test_opt_ptr() {
|
||||
if true {
|
||||
|
||||
}
|
||||
//
|
||||
else{
|
||||
|
||||
else {
|
||||
}
|
||||
a := 3
|
||||
mut r := opt_ptr(&a) or {
|
||||
|
@ -207,7 +240,6 @@ fn test_multi_return_opt() {
|
|||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn foo() ?void {
|
||||
return error('something')
|
||||
}
|
||||
|
@ -220,7 +252,6 @@ fn test_optional_void() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fn bar() ? {
|
||||
return error('bar error')
|
||||
}
|
||||
|
@ -232,3 +263,13 @@ fn test_optional_void_only_question() {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
fn test_optional_void_with_empty_or() {
|
||||
foo() or {}
|
||||
assert true
|
||||
}
|
||||
|
||||
fn test_optional_val_with_empty_or() {
|
||||
ret_none() or {}
|
||||
assert true
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue