forked from aniani/vim
patch 8.1.1834: cannot use a lambda as a method
Problem: Cannot use a lambda as a method. Solution: Implement ->{lambda}(). (closes #4768)
This commit is contained in:
parent
0c779e8e48
commit
22a0c0c4ec
@ -1217,7 +1217,8 @@ expr8(expr1, ...) |Funcref| function call
|
|||||||
When expr8 is a |Funcref| type variable, invoke the function it refers to.
|
When expr8 is a |Funcref| type variable, invoke the function it refers to.
|
||||||
|
|
||||||
|
|
||||||
expr8->name([args]) method call *method*
|
expr8->name([args]) method call *method* *->*
|
||||||
|
expr8->{lambda}([args])
|
||||||
|
|
||||||
For methods that are also available as global functions this is the same as: >
|
For methods that are also available as global functions this is the same as: >
|
||||||
name(expr8 [, args])
|
name(expr8 [, args])
|
||||||
@ -1227,6 +1228,9 @@ This allows for chaining, passing the value that one method returns to the
|
|||||||
next method: >
|
next method: >
|
||||||
mylist->filter(filterexpr)->map(mapexpr)->sort()->join()
|
mylist->filter(filterexpr)->map(mapexpr)->sort()->join()
|
||||||
<
|
<
|
||||||
|
Example of using a lambda: >
|
||||||
|
GetPercentage->{x -> x * 100}()->printf('%d%%')
|
||||||
|
|
||||||
*E274*
|
*E274*
|
||||||
"->name(" must not contain white space. There can be white space before the
|
"->name(" must not contain white space. There can be white space before the
|
||||||
"->" and after the "(", thus you can split the lines like this: >
|
"->" and after the "(", thus you can split the lines like this: >
|
||||||
|
150
src/eval.c
150
src/eval.c
@ -32,6 +32,7 @@ static char *e_cannot_mod = N_("E995: Cannot modify existing variable");
|
|||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_FLOAT
|
||||||
static char *e_float_as_string = N_("E806: using Float as a String");
|
static char *e_float_as_string = N_("E806: using Float as a String");
|
||||||
#endif
|
#endif
|
||||||
|
static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis");
|
||||||
|
|
||||||
#define NAMESPACE_CHAR (char_u *)"abglstvw"
|
#define NAMESPACE_CHAR (char_u *)"abglstvw"
|
||||||
|
|
||||||
@ -3693,7 +3694,6 @@ eval_func(
|
|||||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||||
funcexe.firstline = curwin->w_cursor.lnum;
|
funcexe.firstline = curwin->w_cursor.lnum;
|
||||||
funcexe.lastline = curwin->w_cursor.lnum;
|
funcexe.lastline = curwin->w_cursor.lnum;
|
||||||
funcexe.doesrange = &len;
|
|
||||||
funcexe.evaluate = evaluate;
|
funcexe.evaluate = evaluate;
|
||||||
funcexe.partial = partial;
|
funcexe.partial = partial;
|
||||||
funcexe.basetv = basetv;
|
funcexe.basetv = basetv;
|
||||||
@ -4821,6 +4821,95 @@ eval7(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the function referred to in "rettv".
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
call_func_rettv(
|
||||||
|
char_u **arg,
|
||||||
|
typval_T *rettv,
|
||||||
|
int evaluate,
|
||||||
|
dict_T *selfdict,
|
||||||
|
typval_T *basetv)
|
||||||
|
{
|
||||||
|
partial_T *pt = NULL;
|
||||||
|
funcexe_T funcexe;
|
||||||
|
typval_T functv;
|
||||||
|
char_u *s;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// need to copy the funcref so that we can clear rettv
|
||||||
|
if (evaluate)
|
||||||
|
{
|
||||||
|
functv = *rettv;
|
||||||
|
rettv->v_type = VAR_UNKNOWN;
|
||||||
|
|
||||||
|
/* Invoke the function. Recursive! */
|
||||||
|
if (functv.v_type == VAR_PARTIAL)
|
||||||
|
{
|
||||||
|
pt = functv.vval.v_partial;
|
||||||
|
s = partial_name(pt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s = functv.vval.v_string;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s = (char_u *)"";
|
||||||
|
|
||||||
|
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||||
|
funcexe.firstline = curwin->w_cursor.lnum;
|
||||||
|
funcexe.lastline = curwin->w_cursor.lnum;
|
||||||
|
funcexe.evaluate = evaluate;
|
||||||
|
funcexe.partial = pt;
|
||||||
|
funcexe.selfdict = selfdict;
|
||||||
|
funcexe.basetv = basetv;
|
||||||
|
ret = get_func_tv(s, -1, rettv, arg, &funcexe);
|
||||||
|
|
||||||
|
/* Clear the funcref afterwards, so that deleting it while
|
||||||
|
* evaluating the arguments is possible (see test55). */
|
||||||
|
if (evaluate)
|
||||||
|
clear_tv(&functv);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate "->method()".
|
||||||
|
* "*arg" points to the '-'.
|
||||||
|
* Returns FAIL or OK. "*arg" is advanced to after the ')'.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
eval_lambda(
|
||||||
|
char_u **arg,
|
||||||
|
typval_T *rettv,
|
||||||
|
int evaluate,
|
||||||
|
int verbose) /* give error messages */
|
||||||
|
{
|
||||||
|
typval_T base = *rettv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// Skip over the ->.
|
||||||
|
*arg += 2;
|
||||||
|
rettv->v_type = VAR_UNKNOWN;
|
||||||
|
|
||||||
|
ret = get_lambda_tv(arg, rettv, evaluate);
|
||||||
|
if (ret == NOTDONE)
|
||||||
|
return FAIL;
|
||||||
|
else if (**arg != '(')
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
{
|
||||||
|
if (*skipwhite(*arg) == '(')
|
||||||
|
semsg(_(e_nowhitespace));
|
||||||
|
else
|
||||||
|
semsg(_(e_missingparen), "lambda");
|
||||||
|
}
|
||||||
|
clear_tv(rettv);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return call_func_rettv(arg, rettv, evaluate, NULL, &base);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluate "->method()".
|
* Evaluate "->method()".
|
||||||
* "*arg" points to the '-'.
|
* "*arg" points to the '-'.
|
||||||
@ -4865,15 +4954,15 @@ eval_method(
|
|||||||
else if (VIM_ISWHITE((*arg)[-1]))
|
else if (VIM_ISWHITE((*arg)[-1]))
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
semsg(_("E274: No white space allowed before parenthesis"));
|
semsg(_(e_nowhitespace));
|
||||||
ret = FAIL;
|
ret = FAIL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ret = eval_func(arg, name, len, rettv, evaluate, &base);
|
ret = eval_func(arg, name, len, rettv, evaluate, &base);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the funcref afterwards, so that deleting it while
|
// Clear the funcref afterwards, so that deleting it while
|
||||||
* evaluating the arguments is possible (see test55). */
|
// evaluating the arguments is possible (see test55).
|
||||||
if (evaluate)
|
if (evaluate)
|
||||||
clear_tv(&base);
|
clear_tv(&base);
|
||||||
|
|
||||||
@ -7455,8 +7544,6 @@ handle_subscript(
|
|||||||
{
|
{
|
||||||
int ret = OK;
|
int ret = OK;
|
||||||
dict_T *selfdict = NULL;
|
dict_T *selfdict = NULL;
|
||||||
char_u *s;
|
|
||||||
typval_T functv;
|
|
||||||
|
|
||||||
// "." is ".name" lookup when we found a dict or when evaluating and
|
// "." is ".name" lookup when we found a dict or when evaluating and
|
||||||
// scriptversion is at least 2, where string concatenation is "..".
|
// scriptversion is at least 2, where string concatenation is "..".
|
||||||
@ -7473,43 +7560,11 @@ handle_subscript(
|
|||||||
{
|
{
|
||||||
if (**arg == '(')
|
if (**arg == '(')
|
||||||
{
|
{
|
||||||
partial_T *pt = NULL;
|
ret = call_func_rettv(arg, rettv, evaluate, selfdict, NULL);
|
||||||
funcexe_T funcexe;
|
|
||||||
|
|
||||||
/* need to copy the funcref so that we can clear rettv */
|
// Stop the expression evaluation when immediately aborting on
|
||||||
if (evaluate)
|
// error, or when an interrupt occurred or an exception was thrown
|
||||||
{
|
// but not caught.
|
||||||
functv = *rettv;
|
|
||||||
rettv->v_type = VAR_UNKNOWN;
|
|
||||||
|
|
||||||
/* Invoke the function. Recursive! */
|
|
||||||
if (functv.v_type == VAR_PARTIAL)
|
|
||||||
{
|
|
||||||
pt = functv.vval.v_partial;
|
|
||||||
s = partial_name(pt);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
s = functv.vval.v_string;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
s = (char_u *)"";
|
|
||||||
|
|
||||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
|
||||||
funcexe.firstline = curwin->w_cursor.lnum;
|
|
||||||
funcexe.lastline = curwin->w_cursor.lnum;
|
|
||||||
funcexe.evaluate = evaluate;
|
|
||||||
funcexe.partial = pt;
|
|
||||||
funcexe.selfdict = selfdict;
|
|
||||||
ret = get_func_tv(s, -1, rettv, arg, &funcexe);
|
|
||||||
|
|
||||||
/* Clear the funcref afterwards, so that deleting it while
|
|
||||||
* evaluating the arguments is possible (see test55). */
|
|
||||||
if (evaluate)
|
|
||||||
clear_tv(&functv);
|
|
||||||
|
|
||||||
/* Stop the expression evaluation when immediately aborting on
|
|
||||||
* error, or when an interrupt occurred or an exception was thrown
|
|
||||||
* but not caught. */
|
|
||||||
if (aborting())
|
if (aborting())
|
||||||
{
|
{
|
||||||
if (ret == OK)
|
if (ret == OK)
|
||||||
@ -7521,11 +7576,12 @@ handle_subscript(
|
|||||||
}
|
}
|
||||||
else if (**arg == '-')
|
else if (**arg == '-')
|
||||||
{
|
{
|
||||||
if (eval_method(arg, rettv, evaluate, verbose) == FAIL)
|
if ((*arg)[2] == '{')
|
||||||
{
|
// expr->{lambda}()
|
||||||
clear_tv(rettv);
|
ret = eval_lambda(arg, rettv, evaluate, verbose);
|
||||||
ret = FAIL;
|
else
|
||||||
}
|
// expr->name()
|
||||||
|
ret = eval_method(arg, rettv, evaluate, verbose);
|
||||||
}
|
}
|
||||||
else /* **arg == '[' || **arg == '.' */
|
else /* **arg == '[' || **arg == '.' */
|
||||||
{
|
{
|
||||||
|
@ -122,3 +122,13 @@ func Test_method_syntax()
|
|||||||
call assert_fails('eval [1, 2, 3]->sort ()', 'E274:')
|
call assert_fails('eval [1, 2, 3]->sort ()', 'E274:')
|
||||||
call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:')
|
call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_method_lambda()
|
||||||
|
eval "text"->{x -> x .. " extended"}()->assert_equal('text extended')
|
||||||
|
eval "text"->{x, y -> x .. " extended " .. y}('more')->assert_equal('text extended more')
|
||||||
|
|
||||||
|
call assert_fails('eval "text"->{x -> x .. " extended"} ()', 'E274:')
|
||||||
|
|
||||||
|
" todo: lambda accepts more arguments than it consumes
|
||||||
|
" call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:')
|
||||||
|
endfunc
|
||||||
|
@ -769,6 +769,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1834,
|
||||||
/**/
|
/**/
|
||||||
1833,
|
1833,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user