Skip to content

Commit

Permalink
v: allow map[k]()? and map[k]()() (#22740)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp authored Nov 5, 2024
1 parent 0279392 commit 2f8b2c5
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 78 deletions.
45 changes: 39 additions & 6 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down
79 changes: 51 additions & 28 deletions vlib/v/parser/expr.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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
Expand Down
48 changes: 4 additions & 44 deletions vlib/v/parser/parser.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -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
}
Expand Down
39 changes: 39 additions & 0 deletions vlib/v/tests/fns/map_index_call_test.v
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 2f8b2c5

Please sign in to comment.