Patch 8.2.4930
Problem: Interpolated string expression requires escaping.
Solution: Do not require escaping in the expression.
Files: runtime/doc/eval.txt, src/typval.c, src/proto/
typval.pro,
src/dict.c, src/eval.c, src/evalvars.c, src/proto/
evalvars.pro,
src/vim9compile.c, src/proto/
vim9compile.pro, src/vim9expr.c,
src/vim9instr.c, src/alloc.c, src/proto/
alloc.pro,
src/testdir/test_expr.vim, src/testdir/test_let.vim
*** ../vim-8.2.4929/runtime/doc/eval.txt 2022-05-06 13:14:43.789076617 +0100
--- runtime/doc/eval.txt 2022-05-10 11:11:12.243900263 +0100
***************
*** 3215,3234 ****
{endmarker}.
If "eval" is not specified, then each line of text is
! used as a |literal-string|. If "eval" is specified,
! then any Vim expression in the form ``={expr}`` is
! evaluated and the result replaces the expression.
Example where $HOME is expanded: >
let lines =<< trim eval END
some text
! See the file `=$HOME`/.vimrc
more text
END
< There can be multiple Vim expressions in a single line
but an expression cannot span multiple lines. If any
expression evaluation fails, then the assignment fails.
- once the "`=" has been found {expr} and a backtick
- must follow. {expr} cannot be empty.
{endmarker} must not contain white space.
{endmarker} cannot start with a lower case character.
--- 3261,3280 ----
{endmarker}.
If "eval" is not specified, then each line of text is
! used as a |literal-string|, except that single quotes
! doe not need to be doubled.
! If "eval" is specified, then any Vim expression in the
! form {expr} is evaluated and the result replaces the
! expression, like with |interp-string|.
Example where $HOME is expanded: >
let lines =<< trim eval END
some text
! See the file {$HOME}/.vimrc
more text
END
< There can be multiple Vim expressions in a single line
but an expression cannot span multiple lines. If any
expression evaluation fails, then the assignment fails.
{endmarker} must not contain white space.
{endmarker} cannot start with a lower case character.
*** ../vim-8.2.4929/src/typval.c 2022-05-09 20:09:19.294641425 +0100
--- src/typval.c 2022-05-10 12:47:12.209820875 +0100
***************
*** 2065,2083 ****
}
/*
! * Allocate a variable for a string constant.
* Return OK or FAIL.
*/
int
! eval_string(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *p;
char_u *end;
! int extra = 0;
int len;
// Find the end of the string, skipping backslashed characters.
! for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p))
{
if (*p == '\\' && p[1] != NUL)
{
--- 2065,2087 ----
}
/*
! * Evaluate a string constant and put the result in "rettv".
! * "*arg" points to the double quote or to after it when "interpolate" is TRUE.
! * When "interpolate" is TRUE reduce "{{" to "{", reduce "}}" to "}" and stop
! * at a single "{".
* Return OK or FAIL.
*/
int
! eval_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate)
{
char_u *p;
char_u *end;
! int extra = interpolate ? 1 : 0;
! int off = interpolate ? 0 : 1;
int len;
// Find the end of the string, skipping backslashed characters.
! for (p = *arg + off; *p != NUL && *p != '"'; MB_PTR_ADV(p))
{
if (*p == '\\' && p[1] != NUL)
{
***************
*** 2088,2096 ****
if (*p == '<')
extra += 5;
}
}
! if (*p != '"')
{
semsg(_(e_missing_double_quote_str), *arg);
return FAIL;
--- 2092,2112 ----
if (*p == '<')
extra += 5;
}
+ else if (interpolate && (*p == '{' || *p == '}'))
+ {
+ if (*p == '{' && p[1] != '{') // start of expression
+ break;
+ ++p;
+ if (p[-1] == '}' && *p != '}') // single '}' is an error
+ {
+ semsg(_(e_stray_closing_curly_str), *arg);
+ return FAIL;
+ }
+ --extra; // "{{" becomes "{", "}}" becomes "}"
+ }
}
! if (*p != '"' && !(interpolate && *p == '{'))
{
semsg(_(e_missing_double_quote_str), *arg);
return FAIL;
***************
*** 2099,2105 ****
// If only parsing, set *arg and return here
if (!evaluate)
{
! *arg = p + 1;
return OK;
}
--- 2115,2121 ----
// If only parsing, set *arg and return here
if (!evaluate)
{
! *arg = p + off;
return OK;
}
***************
*** 2112,2118 ****
return FAIL;
end = rettv->vval.v_string;
! for (p = *arg + 1; *p != NUL && *p != '"'; )
{
if (*p == '\\')
{
--- 2128,2134 ----
return FAIL;
end = rettv->vval.v_string;
! for (p = *arg + off; *p != NUL && *p != '"'; )
{
if (*p == '\\')
{
***************
*** 2192,2206 ****
}
// FALLTHROUGH
! default: MB_COPY_CHAR(p, end);
break;
}
}
else
MB_COPY_CHAR(p, end);
}
*end = NUL;
! if (*p != NUL) // just in case
++p;
*arg = p;
--- 2208,2230 ----
}
// FALLTHROUGH
! default: MB_COPY_CHAR(p, end);
break;
}
}
else
+ {
+ if (interpolate && (*p == '{' || *p == '}'))
+ {
+ if (*p == '{' && p[1] != '{') // start of expression
+ break;
+ ++p; // reduce "{{" to "{" and "}}" to "}"
+ }
MB_COPY_CHAR(p, end);
+ }
}
*end = NUL;
! if (*p == '"' && !interpolate)
++p;
*arg = p;
***************
*** 2209,2225 ****
/*
* Allocate a variable for a 'str''ing' constant.
! * Return OK or FAIL.
*/
int
! eval_lit_string(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *p;
char_u *str;
! int reduce = 0;
// Find the end of the string, skipping ''.
! for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p))
{
if (*p == '\'')
{
--- 2233,2252 ----
/*
* Allocate a variable for a 'str''ing' constant.
! * When "interpolate" is TRUE reduce "{{" to "{" and stop at a single "{".
! * Return OK when a "rettv" was set to the string.
! * Return FAIL on error, "rettv" is not set.
*/
int
! eval_lit_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate)
{
char_u *p;
char_u *str;
! int reduce = interpolate ? -1 : 0;
! int off = interpolate ? 0 : 1;
// Find the end of the string, skipping ''.
! for (p = *arg + off; *p != NUL; MB_PTR_ADV(p))
{
if (*p == '\'')
{
***************
*** 2228,2236 ****
++reduce;
++p;
}
}
! if (*p != '\'')
{
semsg(_(e_missing_single_quote_str), *arg);
return FAIL;
--- 2255,2283 ----
++reduce;
++p;
}
+ else if (interpolate)
+ {
+ if (*p == '{')
+ {
+ if (p[1] != '{')
+ break;
+ ++p;
+ ++reduce;
+ }
+ else if (*p == '}')
+ {
+ ++p;
+ if (*p != '}')
+ {
+ semsg(_(e_stray_closing_curly_str), *arg);
+ return FAIL;
+ }
+ ++reduce;
+ }
+ }
}
! if (*p != '\'' && !(interpolate && *p == '{'))
{
semsg(_(e_missing_single_quote_str), *arg);
return FAIL;
***************
*** 2239,2256 ****
// If only parsing return after setting "*arg"
if (!evaluate)
{
! *arg = p + 1;
return OK;
}
! // Copy the string into allocated memory, handling '' to ' reduction.
str = alloc((p - *arg) - reduce);
if (str == NULL)
return FAIL;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = str;
! for (p = *arg + 1; *p != NUL; )
{
if (*p == '\'')
{
--- 2286,2304 ----
// If only parsing return after setting "*arg"
if (!evaluate)
{
! *arg = p + off;
return OK;
}
! // Copy the string into allocated memory, handling '' to ' reduction and
! // any expressions.
str = alloc((p - *arg) - reduce);
if (str == NULL)
return FAIL;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = str;
! for (p = *arg + off; *p != NUL; )
{
if (*p == '\'')
{
***************
*** 2258,2295 ****
break;
++p;
}
MB_COPY_CHAR(p, str);
}
*str = NUL;
! *arg = p + 1;
return OK;
}
int
eval_interp_string(char_u **arg, typval_T *rettv, int evaluate)
{
typval_T tv;
! int ret;
!
! // *arg is on the '$' character.
! (*arg)++;
! rettv->v_type = VAR_STRING;
! if (**arg == '"')
! ret = eval_string(arg, &tv, evaluate);
! else
! ret = eval_lit_string(arg, &tv, evaluate);
! if (ret == FAIL || !evaluate)
! return ret;
! rettv->vval.v_string = eval_all_expr_in_str(tv.vval.v_string);
! clear_tv(&tv);
! return rettv->vval.v_string != NULL ? OK : FAIL;
}
/*
--- 2306,2387 ----
break;
++p;
}
+ else if (interpolate && (*p == '{' || *p == '}'))
+ {
+ if (*p == '{' && p[1] != '{')
+ break;
+ ++p;
+ }
MB_COPY_CHAR(p, str);
}
*str = NUL;
! *arg = p + off;
return OK;
}
+ /*
+ * Evaluate a single or double quoted string possibly containing expressions.
+ * "arg" points to the '$'. The result is put in "rettv".
+ * Returns OK or FAIL.
+ */
int
eval_interp_string(char_u **arg, typval_T *rettv, int evaluate)
{
typval_T tv;
! int ret = OK;
! int quote;
! garray_T ga;
! char_u *p;
! ga_init2(&ga, 1, 80);
! // *arg is on the '$' character, move it to the first string character.
! ++*arg;
! quote = **arg;
! ++*arg;
! for (;;)
! {
! // Get the string up to the matching quote or to a single '{'.
! // "arg" is advanced to either the quote or the '{'.
! if (quote == '"')
! ret = eval_string(arg, &tv, evaluate, TRUE);
! else
! ret = eval_lit_string(arg, &tv, evaluate, TRUE);
! if (ret == FAIL)
! break;
! if (evaluate)
! {
! ga_concat(&ga, tv.vval.v_string);
! clear_tv(&tv);
! }
! if (**arg != '{')
! {
! // found terminating quote
! ++*arg;
! break;
! }
! p = eval_one_expr_in_str(*arg, &ga);
! if (p == NULL)
! {
! ret = FAIL;
! break;
! }
! *arg = p;
! }
! rettv->v_type = VAR_STRING;
! if (ret == FAIL || !evaluate || ga_append(&ga, NUL) == FAIL)
! {
! ga_clear(&ga);
! rettv->vval.v_string = NULL;
! return ret;
! }
! rettv->vval.v_string = ga.ga_data;
! return OK;
}
/*
*** ../vim-8.2.4929/src/proto/
typval.pro 2022-05-06 13:14:43.793076613 +0100
--- src/proto/
typval.pro 2022-05-09 21:54:39.619665829 +0100
***************
*** 68,78 ****
int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive);
int eval_option(char_u **arg, typval_T *rettv, int evaluate);
int eval_number(char_u **arg, typval_T *rettv, int evaluate, int want_string);
! int eval_string(char_u **arg, typval_T *rettv, int evaluate);
! int eval_lit_string(char_u **arg, typval_T *rettv, int evaluate);
char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
int eval_env_var(char_u **arg, typval_T *rettv, int evaluate);
- int eval_interp_string(char_u **arg, typval_T *rettv, int evaluate);
linenr_T tv_get_lnum(typval_T *argvars);
linenr_T tv_get_lnum_buf(typval_T *argvars, buf_T *buf);
buf_T *tv_get_buf(typval_T *tv, int curtab_only);
--- 68,78 ----
int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive);
int eval_option(char_u **arg, typval_T *rettv, int evaluate);
int eval_number(char_u **arg, typval_T *rettv, int evaluate, int want_string);
! int eval_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate);
! int eval_lit_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate);
! int eval_interp_string(char_u **arg, typval_T *rettv, int evaluate);
char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
int eval_env_var(char_u **arg, typval_T *rettv, int evaluate);
linenr_T tv_get_lnum(typval_T *argvars);
linenr_T tv_get_lnum_buf(typval_T *argvars, buf_T *buf);
buf_T *tv_get_buf(typval_T *tv, int curtab_only);
*** ../vim-8.2.4929/src/dict.c 2022-04-04 15:16:50.738014123 +0100
--- src/dict.c 2022-05-09 21:50:55.507907210 +0100
***************
*** 866,878 ****
if (**arg == '\'')
{
! if (eval_lit_string(arg, &rettv, TRUE) == FAIL)
return NULL;
key = rettv.vval.v_string;
}
else if (**arg == '"')
{
! if (eval_string(arg, &rettv, TRUE) == FAIL)
return NULL;
key = rettv.vval.v_string;
}
--- 866,878 ----
if (**arg == '\'')
{
! if (eval_lit_string(arg, &rettv, TRUE, FALSE) == FAIL)
return NULL;
key = rettv.vval.v_string;
}
else if (**arg == '"')
{
! if (eval_string(arg, &rettv, TRUE, FALSE) == FAIL)
return NULL;
key = rettv.vval.v_string;
}
*** ../vim-8.2.4929/src/eval.c 2022-05-07 21:14:01.642973330 +0100
--- src/eval.c 2022-05-09 21:51:03.947897209 +0100
***************
*** 3726,3738 ****
/*
* String constant: "string".
*/
! case '"': ret = eval_string(arg, rettv, evaluate);
break;
/*
* Literal string constant: 'str''ing'.
*/
! case '\'': ret = eval_lit_string(arg, rettv, evaluate);
break;
/*
--- 3726,3738 ----
/*
* String constant: "string".
*/
! case '"': ret = eval_string(arg, rettv, evaluate, FALSE);
break;
/*
* Literal string constant: 'str''ing'.
*/
! case '\'': ret = eval_lit_string(arg, rettv, evaluate, FALSE);
break;
/*
*** ../vim-8.2.4929/src/evalvars.c 2022-05-06 13:14:43.789076617 +0100
--- src/evalvars.c 2022-05-10 13:06:18.053795337 +0100
***************
*** 603,618 ****
}
/*
! * Evaluate all the Vim expressions ({expr}) in string "str" and return the
! * resulting string. The caller must free the returned string.
*/
char_u *
eval_all_expr_in_str(char_u *str)
{
garray_T ga;
char_u *p;
- char_u save_c;
- char_u *expr_val;
ga_init2(&ga, 1, 80);
p = str;
--- 603,654 ----
}
/*
! * Evaluate one Vim expression {expr} in string "p" and append the
! * resulting string to "gap". "p" points to the opening "{".
! * Return a pointer to the character after "}", NULL for an error.
! */
! char_u *
! eval_one_expr_in_str(char_u *p, garray_T *gap)
! {
! char_u *block_start = skipwhite(p + 1); // skip the opening {
! char_u *block_end = block_start;
! char_u *expr_val;
!
! if (*block_start == NUL)
! {
! semsg(_(e_missing_close_curly_str), p);
! return NULL;
! }
! if (skip_expr(&block_end, NULL) == FAIL)
! return NULL;
! block_end = skipwhite(block_end);
! if (*block_end != '}')
! {
! semsg(_(e_missing_close_curly_str), p);
! return NULL;
! }
! *block_end = NUL;
! expr_val = eval_to_string(block_start, TRUE);
! *block_end = '}';
! if (expr_val == NULL)
! return NULL;
! ga_concat(gap, expr_val);
! vim_free(expr_val);
!
! return block_end + 1;
! }
!
! /*
! * Evaluate all the Vim expressions {expr} in "str" and return the resulting
! * string in allocated memory. "{{" is reduced to "{" and "}}" to "}".
! * Used for a heredoc assignment.
! * Returns NULL for an error.
*/
char_u *
eval_all_expr_in_str(char_u *str)
{
garray_T ga;
char_u *p;
ga_init2(&ga, 1, 80);
p = str;
***************
*** 620,627 ****
while (*p != NUL)
{
char_u *lit_start;
- char_u *block_start;
- char_u *block_end;
int escaped_brace = FALSE;
// Look for a block start.
--- 656,661 ----
***************
*** 656,690 ****
continue;
}
! // Skip the opening {.
! block_start = ++p;
! block_end = block_start;
! if (*block_start != NUL && skip_expr(&block_end, NULL) == FAIL)
! {
! ga_clear(&ga);
! return NULL;
! }
! block_end = skipwhite(block_end);
! // The block must be closed by a }.
! if (*block_end != '}')
{
- semsg(_(e_missing_close_curly_str), str);
ga_clear(&ga);
return NULL;
}
- save_c = *block_end;
- *block_end = NUL;
- expr_val = eval_to_string(block_start, TRUE);
- *block_end = save_c;
- if (expr_val == NULL)
- {
- ga_clear(&ga);
- return NULL;
- }
- ga_concat(&ga, expr_val);
- vim_free(expr_val);
-
- p = block_end + 1;
}
ga_append(&ga, NUL);
--- 690,702 ----
continue;
}
! // Evaluate the expression and append the result.
! p = eval_one_expr_in_str(p, &ga);
! if (p == NULL)
{
ga_clear(&ga);
return NULL;
}
}
ga_append(&ga, NUL);
*** ../vim-8.2.4929/src/proto/
evalvars.pro 2022-05-06 13:14:43.793076613 +0100
--- src/proto/
evalvars.pro 2022-05-09 21:55:08.719637657 +0100
***************
*** 13,18 ****
--- 13,20 ----
int get_spellword(list_T *list, char_u **pp);
void prepare_vimvar(int idx, typval_T *save_tv);
void restore_vimvar(int idx, typval_T *save_tv);
+ char_u *eval_one_expr_in_str(char_u *p, garray_T *gap);
+ char_u *eval_all_expr_in_str(char_u *str);
list_T *heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile);
void ex_var(exarg_T *eap);
void ex_let(exarg_T *eap);
***************
*** 105,110 ****
void copy_callback(callback_T *dest, callback_T *src);
void expand_autload_callback(callback_T *cb);
void free_callback(callback_T *callback);
- char_u *eval_all_expr_in_str(char_u *str);
-
/* vim: set ft=c : */
--- 107,110 ----
*** ../vim-8.2.4929/src/vim9compile.c 2022-05-09 20:09:19.294641425 +0100
--- src/vim9compile.c 2022-05-10 12:32:36.730408912 +0100
***************
*** 969,974 ****
--- 969,1004 ----
}
/*
+ * Compile one Vim expression {expr} in string "p".
+ * "p" points to the opening "{".
+ * Return a pointer to the character after "}", NULL for an error.
+ */
+ char_u *
+ compile_one_expr_in_str(char_u *p, cctx_T *cctx)
+ {
+ char_u *block_start;
+ char_u *block_end;
+
+ // Skip the opening {.
+ block_start = skipwhite(p + 1);
+ block_end = block_start;
+ if (*block_start != NUL && skip_expr(&block_end, NULL) == FAIL)
+ return NULL;
+ block_end = skipwhite(block_end);
+ // The block must be closed by a }.
+ if (*block_end != '}')
+ {
+ semsg(_(e_missing_close_curly_str), p);
+ return NULL;
+ }
+ if (compile_expr0(&block_start, cctx) == FAIL)
+ return NULL;
+ may_generate_2STRING(-1, TRUE, cctx);
+
+ return block_end + 1;
+ }
+
+ /*
* Compile a string "str" (either containing a literal string or a mix of
* literal strings and Vim expressions of the form `{expr}`). This is used
* when compiling a heredoc assignment to a variable or an interpolated string
***************
*** 997,1004 ****
while (*p != NUL)
{
char_u *lit_start;
- char_u *block_start;
- char_u *block_end;
int escaped_brace = FALSE;
// Look for a block start.
--- 1027,1032 ----
***************
*** 1038,1065 ****
continue;
}
! // Skip the opening {.
! block_start = skipwhite(p + 1);
! block_end = block_start;
! if (*block_start != NUL && skip_expr(&block_end, NULL) == FAIL)
! return FAIL;
! block_end = skipwhite(block_end);
! // The block must be closed by a }.
! if (*block_end != '}')
! {
! semsg(_(e_missing_close_curly_str), str);
return FAIL;
- }
- if (compile_expr0(&block_start, cctx) == FAIL)
- return FAIL;
- may_generate_2STRING(-1, TRUE, cctx);
++count;
-
- p = block_end + 1;
}
// Small optimization, if there's only a single piece skip the ISN_CONCAT.
! if (count != 1)
return generate_CONCAT(cctx, count);
return OK;
--- 1066,1079 ----
continue;
}
! p = compile_one_expr_in_str(p, cctx);
! if (p == NULL)
return FAIL;
++count;
}
// Small optimization, if there's only a single piece skip the ISN_CONCAT.
! if (count > 1)
return generate_CONCAT(cctx, count);
return OK;
*** ../vim-8.2.4929/src/proto/
vim9compile.pro 2022-05-06 13:14:43.793076613 +0100
--- src/proto/
vim9compile.pro 2022-05-10 12:32:07.306419821 +0100
***************
*** 16,21 ****
--- 16,22 ----
int may_get_next_line_error(char_u *whitep, char_u **arg, cctx_T *cctx);
void fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx);
int func_needs_compiling(ufunc_T *ufunc, compiletype_T compile_type);
+ char_u *compile_one_expr_in_str(char_u *p, cctx_T *cctx);
int compile_all_expr_in_str(char_u *str, int evalstr, cctx_T *cctx);
int assignment_len(char_u *p, int *heredoc);
void vim9_declare_error(char_u *name);
*** ../vim-8.2.4929/src/vim9expr.c 2022-05-06 13:14:43.793076613 +0100
--- src/vim9expr.c 2022-05-10 13:21:14.224792916 +0100
***************
*** 762,770 ****
argvars[0].v_type = VAR_UNKNOWN;
if (*s == '"')
! (void)eval_string(&s, &argvars[0], TRUE);
else if (*s == '\'')
! (void)eval_lit_string(&s, &argvars[0], TRUE);
s = skipwhite(s);
if (*s == ')' && argvars[0].v_type == VAR_STRING
&& ((is_has && !dynamic_feature(argvars[0].vval.v_string))
--- 762,770 ----
argvars[0].v_type = VAR_UNKNOWN;
if (*s == '"')
! (void)eval_string(&s, &argvars[0], TRUE, FALSE);
else if (*s == '\'')
! (void)eval_lit_string(&s, &argvars[0], TRUE, FALSE);
s = skipwhite(s);
if (*s == ')' && argvars[0].v_type == VAR_STRING
&& ((is_has && !dynamic_feature(argvars[0].vval.v_string))
***************
*** 1375,1404 ****
}
/*
! * Compile "$"string"" or "$'string'".
*/
static int
compile_interp_string(char_u **arg, cctx_T *cctx)
{
typval_T tv;
int ret;
int evaluate = cctx->ctx_skip != SKIP_YES;
! // *arg is on the '$' character.
! (*arg)++;
! if (**arg == '"')
! ret = eval_string(arg, &tv, evaluate);
! else
! ret = eval_lit_string(arg, &tv, evaluate);
if (ret == FAIL || !evaluate)
return ret;
! ret = compile_all_expr_in_str(tv.vval.v_string, TRUE, cctx);
! clear_tv(&tv);
! return ret;
}
/*
--- 1375,1447 ----
}
/*
! * Compile $"string" or $'string'.
*/
static int
compile_interp_string(char_u **arg, cctx_T *cctx)
{
typval_T tv;
int ret;
+ int quote;
int evaluate = cctx->ctx_skip != SKIP_YES;
+ int count = 0;
+ char_u *p;
! // *arg is on the '$' character, move it to the first string character.
! ++*arg;
! quote = **arg;
! ++*arg;
! for (;;)
! {
! // Get the string up to the matching quote or to a single '{'.
! // "arg" is advanced to either the quote or the '{'.
! if (quote == '"')
! ret = eval_string(arg, &tv, evaluate, TRUE);
! else
! ret = eval_lit_string(arg, &tv, evaluate, TRUE);
! if (ret == FAIL)
! break;
! if (evaluate)
! {
! if ((tv.vval.v_string != NULL && *tv.vval.v_string != NUL)
! || (**arg != '{' && count == 0))
! {
! // generate non-empty string or empty string if it's the only
! // one
! if (generate_PUSHS(cctx, &tv.vval.v_string) == FAIL)
! return FAIL;
! tv.vval.v_string = NULL; // don't free it now
! ++count;
! }
! clear_tv(&tv);
! }
!
! if (**arg != '{')
! {
! // found terminating quote
! ++*arg;
! break;
! }
!
! p = compile_one_expr_in_str(*arg, cctx);
! if (p == NULL)
! {
! ret = FAIL;
! break;
! }
! ++count;
! *arg = p;
! }
if (ret == FAIL || !evaluate)
return ret;
! // Small optimization, if there's only a single piece skip the ISN_CONCAT.
! if (count > 1)
! return generate_CONCAT(cctx, count);
! return OK;
}
/*
***************
*** 2161,2174 ****
/*
* String constant: "string".
*/
! case '"': if (eval_string(arg, rettv, TRUE) == FAIL)
return FAIL;
break;
/*
* Literal string constant: 'str''ing'.
*/
! case '\'': if (eval_lit_string(arg, rettv, TRUE) == FAIL)
return FAIL;
break;
--- 2204,2217 ----
/*
* String constant: "string".
*/
! case '"': if (eval_string(arg, rettv, TRUE, FALSE) == FAIL)
return FAIL;
break;
/*
* Literal string constant: 'str''ing'.
*/
! case '\'': if (eval_lit_string(arg, rettv, TRUE, FALSE) == FAIL)
return FAIL;
break;
*** ../vim-8.2.4929/src/vim9instr.c 2022-05-04 16:46:51.349318219 +0100
--- src/vim9instr.c 2022-05-10 13:20:25.392819519 +0100
***************
*** 726,731 ****
--- 726,733 ----
/*
* Generate an ISN_PUSHS instruction.
* Consumes "*str". When freed *str is set to NULL, unless "str" is NULL.
+ * Note that if "str" is used in the instruction OK is returned and "*str" is
+ * not set to NULL.
*/
int
generate_PUSHS(cctx_T *cctx, char_u **str)
*** ../vim-8.2.4929/src/alloc.c 2022-04-09 11:09:03.526052266 +0100
--- src/alloc.c 2022-05-10 12:08:35.776303190 +0100
***************
*** 832,838 ****
/*
* Concatenate a string to a growarray which contains bytes.
! * When "s" is NULL does not do anything.
* Note: Does NOT copy the NUL at the end!
*/
void
--- 832,838 ----
/*
* Concatenate a string to a growarray which contains bytes.
! * When "s" is NULL memory allocation fails does not do anything.
* Note: Does NOT copy the NUL at the end!
*/
void
***************
*** 869,882 ****
/*
* Append one byte to a growarray which contains bytes.
*/
! void
ga_append(garray_T *gap, int c)
{
! if (ga_grow(gap, 1) == OK)
! {
! *((char *)gap->ga_data + gap->ga_len) = c;
! ++gap->ga_len;
! }
}
#if (defined(UNIX) && !defined(USE_SYSTEM)) || defined(MSWIN) \
--- 869,882 ----
/*
* Append one byte to a growarray which contains bytes.
*/
! int
ga_append(garray_T *gap, int c)
{
! if (ga_grow(gap, 1) == FAIL)
! return FAIL;
! *((char *)gap->ga_data + gap->ga_len) = c;
! ++gap->ga_len;
! return OK;
}
#if (defined(UNIX) && !defined(USE_SYSTEM)) || defined(MSWIN) \
*** ../vim-8.2.4929/src/proto/
alloc.pro 2022-04-09 11:09:03.526052266 +0100
--- src/proto/
alloc.pro 2022-05-10 12:08:39.188316249 +0100
***************
*** 19,31 ****
void ga_init(garray_T *gap);
void ga_init2(garray_T *gap, size_t itemsize, int growsize);
int ga_grow(garray_T *gap, int n);
! int ga_grow_id(garray_T *gap, int n, alloc_id_T id UNUSED);
int ga_grow_inner(garray_T *gap, int n);
char_u *ga_concat_strings(garray_T *gap, char *sep);
int ga_copy_string(garray_T *gap, char_u *p);
int ga_add_string(garray_T *gap, char_u *p);
void ga_concat(garray_T *gap, char_u *s);
void ga_concat_len(garray_T *gap, char_u *s, size_t len);
! void ga_append(garray_T *gap, int c);
void append_ga_line(garray_T *gap);
/* vim: set ft=c : */
--- 19,31 ----
void ga_init(garray_T *gap);
void ga_init2(garray_T *gap, size_t itemsize, int growsize);
int ga_grow(garray_T *gap, int n);
! int ga_grow_id(garray_T *gap, int n, alloc_id_T id);
int ga_grow_inner(garray_T *gap, int n);
char_u *ga_concat_strings(garray_T *gap, char *sep);
int ga_copy_string(garray_T *gap, char_u *p);
int ga_add_string(garray_T *gap, char_u *p);
void ga_concat(garray_T *gap, char_u *s);
void ga_concat_len(garray_T *gap, char_u *s, size_t len);
! int ga_append(garray_T *gap, int c);
void append_ga_line(garray_T *gap);
/* vim: set ft=c : */
*** ../vim-8.2.4929/src/testdir/test_expr.vim 2022-05-06 13:14:43.793076613 +0100
--- src/testdir/test_expr.vim 2022-05-10 12:11:55.768948906 +0100
***************
*** 897,903 ****
#" Escaping rules.
call assert_equal('"foo"{bar}', $"\"foo\"{{bar}}")
call assert_equal('"foo"{bar}', $'"foo"{{bar}}')
! call assert_equal('foobar', $"{\"foo\"}" .. $'{''bar''}')
#" Whitespace before/after the expression.
call assert_equal('3', $"{ 1 + 2 }")
#" String conversion.
--- 897,903 ----
#" Escaping rules.
call assert_equal('"foo"{bar}', $"\"foo\"{{bar}}")
call assert_equal('"foo"{bar}', $'"foo"{{bar}}')
! call assert_equal('foobar', $"{"foo"}" .. $'{'bar'}')
#" Whitespace before/after the expression.
call assert_equal('3', $"{ 1 + 2 }")
#" String conversion.
***************
*** 907,914 ****
call assert_equal(string(v:true), $"{v:true}")
call assert_equal('(1+1=2)', $"(1+1={1 + 1})")
#" Hex-escaped opening brace: char2nr('{') == 0x7b
! call assert_equal('esc123ape', $"esc\x7b123}ape")
! call assert_equal('me{}me', $"me{\x7b}\x7dme")
VAR var1 = "sun"
VAR var2 = "shine"
call assert_equal('sunshine', $"{var1}{var2}")
--- 907,914 ----
call assert_equal(string(v:true), $"{v:true}")
call assert_equal('(1+1=2)', $"(1+1={1 + 1})")
#" Hex-escaped opening brace: char2nr('{') == 0x7b
! call assert_equal('esc123ape', $"esc{123}ape")
! call assert_equal('me{}me', $"me{"\x7b"}\x7dme")
VAR var1 = "sun"
VAR var2 = "shine"
call assert_equal('sunshine', $"{var1}{var2}")
***************
*** 916,922 ****
#" Multibyte strings.
call assert_equal('say ハロー・ワールド', $"say {'ハロー・ワールド'}")
#" Nested.
! call assert_equal('foobarbaz', $"foo{$\"{'bar'}\"}baz")
#" Do not evaluate blocks when the expr is skipped.
VAR tmp = 0
if v:false
--- 916,922 ----
#" Multibyte strings.
call assert_equal('say ハロー・ワールド', $"say {'ハロー・ワールド'}")
#" Nested.
! call assert_equal('foobarbaz', $"foo{$"{'bar'}"}baz")
#" Do not evaluate blocks when the expr is skipped.
VAR tmp = 0
if v:false
*** ../vim-8.2.4929/src/testdir/test_let.vim 2022-05-06 13:14:43.793076613 +0100
--- src/testdir/test_let.vim 2022-05-10 13:12:25.437216707 +0100
***************
*** 387,395 ****
let text = 'text'
call assert_equal('text{{', $'{text .. "{{"}')
call assert_equal('text{{', $"{text .. '{{'}")
! " FIXME: should not need to escape quotes in the expression
! call assert_equal('text{{', $'{text .. ''{{''}')
! call assert_equal('text{{', $"{text .. \"{{\"}")
endfunc
" Test for the setting a variable using the heredoc syntax.
--- 387,394 ----
let text = 'text'
call assert_equal('text{{', $'{text .. "{{"}')
call assert_equal('text{{', $"{text .. '{{'}")
! call assert_equal('text{{', $'{text .. '{{'}')
! call assert_equal('text{{', $"{text .. "{{"}")
endfunc
" Test for the setting a variable using the heredoc syntax.
*** ../vim-8.2.4929/src/version.c 2022-05-09 21:03:30.781853316 +0100
--- src/version.c 2022-05-10 11:11:45.423852626 +0100
***************
*** 748,749 ****
--- 748,751 ----
{ /* Add new patch number below this line */
+ /**/
+ 4930,
/**/
--
Send $25.00 for handy leaflet on how to make money by selling leaflets
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///