From 2f8b2c50e85b1c6b0a8906b0046504de1ed599e2 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Tue, 5 Nov 2024 10:41:31 -0300 Subject: [PATCH] v: allow `map[k]()?` and `map[k]()()` (#22740) --- vlib/v/gen/c/fn.v | 45 +++++++++++++-- vlib/v/parser/expr.v | 79 +++++++++++++++++--------- vlib/v/parser/parser.v | 48 ++-------------- vlib/v/tests/fns/map_index_call_test.v | 39 +++++++++++++ 4 files changed, 133 insertions(+), 78 deletions(-) create mode 100644 vlib/v/tests/fns/map_index_call_test.v diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 0f9d32a00edc81..747f4d864cc198 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -864,16 +864,47 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } else if node.or_block.kind == .absent { g.expr(node.left) } - } else if node.left is ast.IndexExpr && node.name == '' { - g.is_fn_index_call = true - g.expr(node.left) - g.is_fn_index_call = false - } else if !g.inside_curry_call && node.left is ast.CallExpr && node.name == '' { + } else if !g.inside_curry_call && node.left is ast.IndexExpr && node.name == '' { if node.or_block.kind == .absent { + old_is_fn_index_call := g.is_fn_index_call + g.is_fn_index_call = true g.expr(node.left) + g.is_fn_index_call = old_is_fn_index_call } else { + // map1['key']() handling + line := g.go_before_last_stmt() + g.empty_line = true + + // temp var for map1['key'] where value is a fn to be called + left_typ := g.table.value_type(node.left.left_type) + tmp_res := g.new_tmp_var() + fn_sym := g.table.sym(left_typ).info as ast.FnType + fn_type := g.fn_var_signature(fn_sym.func.return_type, fn_sym.func.params.map(it.typ), + tmp_res) + + old_is_fn_index_call := g.is_fn_index_call + g.is_fn_index_call = true + g.write('${fn_type} = ') + g.expr(node.left) + g.is_fn_index_call = old_is_fn_index_call + g.writeln(';') + + tmp_res2 := g.new_tmp_var() + // uses the `tmp_res` as fn name (where it is a ptr to fn var) + g.write('${g.styp(node.return_type)} ${tmp_res2} = ${tmp_res}') + g.last_tmp_call_var << tmp_res2 old_inside_curry_call := g.inside_curry_call g.inside_curry_call = true + // map1['key']()() handling + g.expr(node) + g.inside_curry_call = old_inside_curry_call + g.write2(line, '*(${g.base_type(node.return_type)}*)${tmp_res2}.data') + return + } + } else if !g.inside_curry_call && node.left is ast.CallExpr && node.name == '' { + if node.or_block.kind == .absent { + g.expr(node.left) + } else { ret_typ := node.return_type line := g.go_before_last_stmt() @@ -884,8 +915,10 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { g.last_tmp_call_var << tmp_res g.expr(node.left) - g.expr(node) + old_inside_curry_call := g.inside_curry_call + g.inside_curry_call = true + g.expr(node) g.inside_curry_call = old_inside_curry_call g.write2(line, '*(${g.base_type(ret_typ)}*)${tmp_res}.data') return diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 49388ffe5cc490..d49d759e0f6c41 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -464,35 +464,13 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr { pos := p.tok.pos() args := p.call_args() p.check(.rpar) - mut or_kind := ast.OrKind.absent - mut or_stmts := []ast.Stmt{} // TODO: remove unnecessary allocations by just using .absent - mut or_pos := p.tok.pos() - if p.tok.kind == .key_orelse { - // `foo() or {}`` - or_kind = .block - or_stmts, or_pos = p.or_block(.with_err_var) - } - if p.tok.kind in [.question, .not] { - is_not := p.tok.kind == .not - // `foo()?` - p.next() - if p.inside_defer { - p.error_with_pos('error propagation not allowed inside `defer` blocks', - p.prev_tok.pos()) - } - or_kind = if is_not { .propagate_result } else { .propagate_option } - } - + or_block := p.gen_or_block() node = ast.CallExpr{ name: 'anon' left: node args: args pos: pos - or_block: ast.OrExpr{ - stmts: or_stmts - kind: or_kind - pos: or_pos - } + or_block: or_block scope: p.scope } } @@ -592,13 +570,29 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo pos := p.tok.pos() args := p.call_args() p.check(.rpar) + or_block := p.gen_or_block() node = ast.CallExpr{ - left: node - args: args - pos: pos - scope: p.scope + left: node + args: args + pos: pos + scope: p.scope + or_block: or_block } p.is_stmt_ident = is_stmt_ident + if p.tok.kind == .lpar && p.prev_tok.line_nr == p.tok.line_nr { + p.next() + pos2 := p.tok.pos() + args2 := p.call_args() + p.check(.rpar) + or_block2 := p.gen_or_block() + node = ast.CallExpr{ + left: node + args: args2 + pos: pos2 + scope: p.scope + or_block: or_block2 + } + } } } else if p.tok.kind == .key_as { // sum type as cast `x := SumType as Variant` @@ -699,6 +693,35 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo return node } +fn (mut p Parser) gen_or_block() ast.OrExpr { + if p.tok.kind == .key_orelse { + // `foo() or {}`` + or_stmts, or_pos := p.or_block(.with_err_var) + return ast.OrExpr{ + kind: ast.OrKind.block + stmts: or_stmts + pos: or_pos + } + } else if p.tok.kind in [.question, .not] { + or_pos := p.tok.pos() + is_not := p.tok.kind == .not + // `foo()?` + p.next() + if p.inside_defer { + p.error_with_pos('error propagation not allowed inside `defer` blocks', p.prev_tok.pos()) + } + return ast.OrExpr{ + kind: if is_not { ast.OrKind.propagate_result } else { ast.OrKind.propagate_option } + pos: or_pos + } + } else { + return ast.OrExpr{ + kind: ast.OrKind.absent + pos: p.tok.pos() + } + } +} + fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { prev_inside_infix := p.inside_infix p.inside_infix = true diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index c59c4bc524235b..8ce4645fa4cd37 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2879,33 +2879,13 @@ fn (mut p Parser) name_expr() ast.Expr { pos := p.tok.pos() args := p.call_args() p.check(.rpar) - - mut or_kind := ast.OrKind.absent - mut or_stmts := []ast.Stmt{} - mut or_pos := p.tok.pos() - if p.tok.kind in [.not, .question] { - or_kind = if p.tok.kind == .not { - .propagate_result - } else { - .propagate_option - } - p.next() - } - if p.tok.kind == .key_orelse { - // `foo() or {}`` - or_kind = .block - or_stmts, or_pos = p.or_block(.with_err_var) - } + or_block := p.gen_or_block() node = ast.CallExpr{ left: node args: args pos: pos scope: p.scope - or_block: ast.OrExpr{ - stmts: or_stmts - kind: or_kind - pos: or_pos - } + or_block: or_block } } } @@ -3341,23 +3321,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { p.next() args := p.call_args() p.check(.rpar) - mut or_stmts := []ast.Stmt{} - mut or_kind := ast.OrKind.absent - mut or_pos := p.tok.pos() - if p.tok.kind == .key_orelse { - or_kind = .block - or_stmts, or_pos = p.or_block(.with_err_var) - } - // `foo()?` - if p.tok.kind in [.question, .not] { - is_not := p.tok.kind == .not - p.next() - if p.inside_defer { - p.error_with_pos('error propagation not allowed inside `defer` blocks', - p.prev_tok.pos()) - } - or_kind = if is_not { .propagate_result } else { .propagate_option } - } + or_block := p.gen_or_block() end_pos := p.prev_tok.pos() pos := name_pos.extend(end_pos) comments := p.eat_comments(same_line: true) @@ -3370,11 +3334,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { is_method: true concrete_types: concrete_types concrete_list_pos: concrete_list_pos - or_block: ast.OrExpr{ - stmts: or_stmts - kind: or_kind - pos: or_pos - } + or_block: or_block scope: p.scope comments: comments } diff --git a/vlib/v/tests/fns/map_index_call_test.v b/vlib/v/tests/fns/map_index_call_test.v new file mode 100644 index 00000000000000..5abd7d478d467c --- /dev/null +++ b/vlib/v/tests/fns/map_index_call_test.v @@ -0,0 +1,39 @@ +fn func(arg string) ?int { + return 2 +} + +fn func2(arg string) int { + return 2 +} + +fn func3(arg string) fn (string) int { + return func2 +} + +fn func4(arg string) fn (string) ?int { + return func +} + +fn test_main() { + map1 := { + 'f': func + } + ret := map1['f']('test')? + assert ret == 2 + + map2 := { + 'f': func2 + } + ret2 := map2['f']('test') + assert ret2 == 2 + + map3 := { + 'f': func3 + } + assert map3['f']('test')('test') == 2 + + map4 := { + 'f': func4 + } + assert map4['f']('test')('test')? == 2 +}