Patch 8.2.1155
Problem: Vim9: cannot handle line break inside lambda.
Solution: Pass the compilation context through. (closes #6407, closes #6409)
Files: src/structs.h, src/vim9compile.c, src/proto/vim9compile.pro,
src/eval.c, src/testdir/test_vim9_func.vim
*** ../vim-8.2.1154/src/structs.h 2020-07-08 15:16:15.534128895 +0200
--- src/structs.h 2020-07-08 15:25:39.568322556 +0200
***************
*** 1765,1770 ****
--- 1765,1773 ----
char_u *(*eval_getline)(int, void *, int, int);
void *eval_cookie; // argument for eval_getline()
+ // used when compiling a :def function, NULL otherwise
+ cctx_T *eval_cctx;
+
// Used to collect lines while parsing them, so that they can be
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
// "eval_ga.ga_data" is a list of pointers to lines.
*** ../vim-8.2.1154/src/vim9compile.c 2020-07-07 22:50:08.831459969 +0200
--- src/vim9compile.c 2020-07-08 17:13:23.770249096 +0200
***************
*** 2397,2404 ****
* comment. Skips over white space.
* Returns NULL if there is none.
*/
! static char_u *
! peek_next_line(cctx_T *cctx)
{
int lnum = cctx->ctx_lnum;
--- 2397,2404 ----
* comment. Skips over white space.
* Returns NULL if there is none.
*/
! char_u *
! peek_next_line_from_context(cctx_T *cctx)
{
int lnum = cctx->ctx_lnum;
***************
*** 2430,2436 ****
*nextp = NULL;
if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
{
! *nextp = peek_next_line(cctx);
if (*nextp != NULL)
return *nextp;
}
--- 2430,2436 ----
*nextp = NULL;
if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
{
! *nextp = peek_next_line_from_context(cctx);
if (*nextp != NULL)
return *nextp;
}
***************
*** 2442,2448 ****
* Skips over empty lines. Skips over comment lines if "skip_comment" is
TRUE.
* Returns NULL when at the end.
*/
! static char_u *
next_line_from_context(cctx_T *cctx, int skip_comment)
{
char_u *line;
--- 2442,2448 ----
* Skips over empty lines. Skips over comment lines if "skip_comment" is
TRUE.
* Returns NULL when at the end.
*/
! char_u *
next_line_from_context(cctx_T *cctx, int skip_comment)
{
char_u *line;
***************
*** 3079,3087 ****
{
typval_T rettv;
ufunc_T *ufunc;
// Get the funcref in "rettv".
! if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) != OK)
return FAIL;
ufunc = rettv.vval.v_partial->pt_func;
--- 3079,3092 ----
{
typval_T rettv;
ufunc_T *ufunc;
+ evalarg_T evalarg;
+
+ CLEAR_FIELD(evalarg);
+ evalarg.eval_flags = EVAL_EVALUATE;
+ evalarg.eval_cctx = cctx;
// Get the funcref in "rettv".
! if (get_lambda_tv(arg, &rettv, &evalarg) != OK)
return FAIL;
ufunc = rettv.vval.v_partial->pt_func;
***************
*** 3535,3540 ****
--- 3540,3546 ----
/*
* Compile whatever comes after "name" or "name()".
+ * Advances "*arg" only when something was recognized.
*/
static int
compile_subscript(
***************
*** 3550,3556 ****
if (*p == NUL || (VIM_ISWHITE(**arg) && comment_start(p)))
{
! char_u *next = peek_next_line(cctx);
// If a following line starts with "->{" or "->X" advance to that
// line, so that a line break before "->" is allowed.
--- 3556,3562 ----
if (*p == NUL || (VIM_ISWHITE(**arg) && comment_start(p)))
{
! char_u *next = peek_next_line_from_context(cctx);
// If a following line starts with "->{" or "->X" advance to that
// line, so that a line break before "->" is allowed.
***************
*** 3560,3570 ****
next = next_line_from_context(cctx, TRUE);
if (next == NULL)
return FAIL;
! *arg = skipwhite(next);
}
}
! if (**arg == '(')
{
garray_T *stack = &cctx->ctx_type_stack;
type_T *type;
--- 3566,3577 ----
next = next_line_from_context(cctx, TRUE);
if (next == NULL)
return FAIL;
! *arg = next;
! p = skipwhite(*arg);
}
}
! if (*p == '(')
{
garray_T *stack = &cctx->ctx_type_stack;
type_T *type;
***************
*** 3576,3588 ****
// funcref(arg)
type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
! *arg = skipwhite(*arg + 1);
if (compile_arguments(arg, cctx, &argcount) == FAIL)
return FAIL;
if (generate_PCALL(cctx, argcount, end_leader, type, TRUE) == FAIL)
return FAIL;
}
! else if (**arg == '-' && (*arg)[1] == '>')
{
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
--- 3583,3595 ----
// funcref(arg)
type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
! *arg = skipwhite(p + 1);
if (compile_arguments(arg, cctx, &argcount) == FAIL)
return FAIL;
if (generate_PCALL(cctx, argcount, end_leader, type, TRUE) == FAIL)
return FAIL;
}
! else if (*p == '-' && p[1] == '>')
{
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
***************
*** 3594,3600 ****
return FAIL;
*start_leader = end_leader; // don't apply again later
! p = *arg + 2;
*arg = skipwhite(p);
if (may_get_next_line(p, arg, cctx) == FAIL)
return FAIL;
--- 3601,3607 ----
return FAIL;
*start_leader = end_leader; // don't apply again later
! p += 2;
*arg = skipwhite(p);
if (may_get_next_line(p, arg, cctx) == FAIL)
return FAIL;
***************
*** 3622,3628 ****
return FAIL;
}
}
! else if (**arg == '[')
{
garray_T *stack = &cctx->ctx_type_stack;
type_T **typep;
--- 3629,3635 ----
return FAIL;
}
}
! else if (*p == '[')
{
garray_T *stack = &cctx->ctx_type_stack;
type_T **typep;
***************
*** 3635,3641 ****
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
! p = *arg + 1;
*arg = skipwhite(p);
if (may_get_next_line(p, arg, cctx) == FAIL)
return FAIL;
--- 3642,3648 ----
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
! ++p;
*arg = skipwhite(p);
if (may_get_next_line(p, arg, cctx) == FAIL)
return FAIL;
***************
*** 3671,3682 ****
return FAIL;
}
}
! else if (**arg == '.' && (*arg)[1] != '.')
{
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
! ++*arg;
if (may_get_next_line(*arg, arg, cctx) == FAIL)
return FAIL;
// dictionary member: dict.name
--- 3678,3689 ----
return FAIL;
}
}
! else if (*p == '.' && p[1] != '.')
{
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
! *arg = p + 1;
if (may_get_next_line(*arg, arg, cctx) == FAIL)
return FAIL;
// dictionary member: dict.name
*** ../vim-8.2.1154/src/proto/vim9compile.pro 2020-07-06 21:53:14.472719363
+0200
--- src/proto/vim9compile.pro 2020-07-08 13:38:49.964316011 +0200
***************
*** 8,13 ****
--- 8,15 ----
char *type_name(type_T *type, char **tofree);
int get_script_item_idx(int sid, char_u *name, int check_writable);
imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
+ char_u *peek_next_line_from_context(cctx_T *cctx);
+ char_u *next_line_from_context(cctx_T *cctx, int skip_comment);
char_u *to_name_const_end(char_u *arg);
int assignment_len(char_u *p, int *heredoc);
void vim9_declare_error(char_u *name);
*** ../vim-8.2.1154/src/eval.c 2020-07-04 14:14:55.633073475 +0200
--- src/eval.c 2020-07-08 17:31:11.235141345 +0200
***************
*** 390,400 ****
garray_T *gap = &evalarg->eval_ga;
int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
! if (vim9script && evalarg->eval_cookie != NULL)
{
ga_init2(gap, sizeof(char_u *), 10);
if (ga_grow(gap, 1) == OK)
- // leave room for "start"
++gap->ga_len;
}
--- 390,401 ----
garray_T *gap = &evalarg->eval_ga;
int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
! if (vim9script
! && (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL))
{
ga_init2(gap, sizeof(char_u *), 10);
+ // leave room for "start"
if (ga_grow(gap, 1) == OK)
++gap->ga_len;
}
***************
*** 406,437 ****
if (evalarg != NULL)
evalarg->eval_flags = save_flags;
! if (vim9script && evalarg->eval_cookie != NULL
! && evalarg->eval_ga.ga_len > 1)
{
! char_u *p;
! size_t endoff = STRLEN(*end);
! // Line breaks encountered, concatenate all the lines.
! *((char_u **)gap->ga_data) = *start;
! p = ga_concat_strings(gap, "");
! *((char_u **)gap->ga_data) = NULL;
! ga_clear_strings(gap);
! gap->ga_itemsize = 0;
! if (p == NULL)
! return FAIL;
! *start = p;
! vim_free(evalarg->eval_tofree);
! evalarg->eval_tofree = p;
! // Compute "end" relative to the end.
! *end = *start + STRLEN(*start) - endoff;
}
return res;
}
/*
! * Top level evaluation function, returning a string.
* When "convert" is TRUE convert a List into a sequence of lines and convert
* a Float to a String.
* Return pointer to allocated memory, or NULL for failure.
--- 407,455 ----
if (evalarg != NULL)
evalarg->eval_flags = save_flags;
! if (vim9script
! && (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL))
{
! if (evalarg->eval_ga.ga_len == 1)
! {
! // just one line, no need to concatenate
! ga_clear(gap);
! gap->ga_itemsize = 0;
! }
! else
! {
! char_u *p;
! size_t endoff = STRLEN(*end);
! // Line breaks encountered, concatenate all the lines.
! *((char_u **)gap->ga_data) = *start;
! p = ga_concat_strings(gap, "");
!
! // free the lines only when using getsourceline()
! if (evalarg->eval_cookie != NULL)
! {
! *((char_u **)gap->ga_data) = NULL;
! ga_clear_strings(gap);
! }
! else
! ga_clear(gap);
! gap->ga_itemsize = 0;
! if (p == NULL)
! return FAIL;
! *start = p;
! vim_free(evalarg->eval_tofree);
! evalarg->eval_tofree = p;
! // Compute "end" relative to the end.
! *end = *start + STRLEN(*start) - endoff;
! }
}
return res;
}
/*
! * Top level evaluation function, returning a string. Does not handle line
! * breaks.
* When "convert" is TRUE convert a List into a sequence of lines and convert
* a Float to a String.
* Return pointer to allocated memory, or NULL for failure.
***************
*** 1878,1888 ****
*getnext = FALSE;
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
&& evalarg != NULL
! && evalarg->eval_cookie != NULL
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
&& *arg == '#' && arg[1] != '{')))
{
! char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
if (p != NULL)
{
--- 1896,1911 ----
*getnext = FALSE;
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
&& evalarg != NULL
! && (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL)
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
&& *arg == '#' && arg[1] != '{')))
{
! char_u *p;
!
! if (evalarg->eval_cookie != NULL)
! p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
! else
! p = peek_next_line_from_context(evalarg->eval_cctx);
if (p != NULL)
{
***************
*** 1902,1908 ****
garray_T *gap = &evalarg->eval_ga;
char_u *line;
! line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
++evalarg->eval_break_count;
if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
{
--- 1925,1934 ----
garray_T *gap = &evalarg->eval_ga;
char_u *line;
! if (evalarg->eval_cookie != NULL)
! line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
! else
! line = next_line_from_context(evalarg->eval_cctx, TRUE);
++evalarg->eval_break_count;
if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
{
***************
*** 5034,5068 ****
int ret = OK;
dict_T *selfdict = NULL;
int check_white = TRUE;
! // When at the end of the line and ".name" follows in the next line then
! // consume the line break. Only when rettv is a dict.
! if (rettv->v_type == VAR_DICT)
{
! int getnext;
! char_u *p = eval_next_non_blank(*arg, evalarg, &getnext);
!
! if (getnext && *p == '.' && ASCII_ISALPHA(p[1]))
{
*arg = eval_next_line(evalarg);
check_white = FALSE;
}
- }
! // "." is ".name" lookup when we found a dict or when evaluating and
! // scriptversion is at least 2, where string concatenation is "..".
! while (ret == OK
! && (((**arg == '['
! || (**arg == '.' && (rettv->v_type == VAR_DICT
! || (!evaluate
! && (*arg)[1] != '.'
! && current_sctx.sc_version >= 2)))
! || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
! || rettv->v_type == VAR_PARTIAL)))
! && (!check_white || !VIM_ISWHITE(*(*arg - 1))))
! || (**arg == '-' && (*arg)[1] == '>')))
! {
! if (**arg == '(')
{
ret = call_func_rettv(arg, evalarg, rettv, evaluate,
selfdict, NULL);
--- 5060,5086 ----
int ret = OK;
dict_T *selfdict = NULL;
int check_white = TRUE;
+ int getnext;
+ char_u *p;
! while (ret == OK)
{
! // When at the end of the line and ".name" or "->{" or "->X" follows in
! // the next line then consume the line break.
! p = eval_next_non_blank(*arg, evalarg, &getnext);
! if (getnext
! && ((rettv->v_type == VAR_DICT && *p == '.'
! && ASCII_ISALPHA(p[1]))
! || (*p == '-' && p[1] == '>'
! && (p[2] == '{' || ASCII_ISALPHA(p[2])))))
{
*arg = eval_next_line(evalarg);
check_white = FALSE;
}
! if ((**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
! || rettv->v_type == VAR_PARTIAL))
! && (!check_white || !VIM_ISWHITE(*(*arg - 1))))
{
ret = call_func_rettv(arg, evalarg, rettv, evaluate,
selfdict, NULL);
***************
*** 5079,5085 ****
dict_unref(selfdict);
selfdict = NULL;
}
! else if (**arg == '-')
{
if (ret == OK)
{
--- 5097,5103 ----
dict_unref(selfdict);
selfdict = NULL;
}
! else if (**arg == '-' && (*arg)[1] == '>')
{
if (ret == OK)
{
***************
*** 5091,5097 ****
ret = eval_method(arg, rettv, evalarg, verbose);
}
}
! else // **arg == '[' || **arg == '.'
{
dict_unref(selfdict);
if (rettv->v_type == VAR_DICT)
--- 5109,5121 ----
ret = eval_method(arg, rettv, evalarg, verbose);
}
}
! // "." is ".name" lookup when we found a dict or when evaluating and
! // scriptversion is at least 2, where string concatenation is "..".
! else if (**arg == '['
! || (**arg == '.' && (rettv->v_type == VAR_DICT
! || (!evaluate
! && (*arg)[1] != '.'
! && current_sctx.sc_version >= 2))))
{
dict_unref(selfdict);
if (rettv->v_type == VAR_DICT)
***************
*** 5108,5113 ****
--- 5132,5139 ----
ret = FAIL;
}
}
+ else
+ break;
}
// Turn "dict.Func" into a partial for "Func" bound to "dict".
*** ../vim-8.2.1154/src/testdir/test_vim9_func.vim 2020-07-05
21:10:20.869634742 +0200
--- src/testdir/test_vim9_func.vim 2020-07-08 16:51:18.386260987 +0200
***************
*** 965,970 ****
--- 965,982 ----
assert_equal('full', Line_continuation_in_def('.'))
enddef
+ def Line_continuation_in_lambda(): list<number>
+ let x = range(97, 100)
+ ->map({_,v -> nr2char(v)
+ ->toupper()})
+ ->reverse()
+ return x
+ enddef
+
+ def Test_line_continuation_in_lambda()
+ assert_equal(['D', 'C', 'B', 'A'], Line_continuation_in_lambda())
+ enddef
+
func Test_silent_echo()
CheckScreendump
*** ../vim-8.2.1154/src/version.c 2020-07-08 15:16:15.534128895 +0200
--- src/version.c 2020-07-08 17:35:35.026243838 +0200
***************
*** 756,757 ****
--- 756,759 ----
{ /* Add new patch number below this line */
+ /**/
+ 1155,
/**/
--
For society, it's probably a good thing that engineers value function over
appearance. For example, you wouldn't want engineers to build nuclear power
plants that only _look_ like they would keep all the radiation inside.
(Scott Adams - The Dilbert principle)
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/202007081536.068FarLo1375015%40masaka.moolenaar.net.