Patch 8.2.1462
Problem: Vim9: string slice not supported yet.
Solution: Add support for string slicing.
Files: src/errors.h, src/vim9compile.c, src/vim9.h, src/vim9execute.c,
src/eval.c, src/proto/
eval.pro, src/testdir/test_vim9_expr.vim,
src/testdir/test_vim9_disassemble.vim
*** ../vim-8.2.1461/src/errors.h 2020-08-15 16:33:24.493747355 +0200
--- src/errors.h 2020-08-15 19:47:45.062730517 +0200
***************
*** 69,75 ****
INIT(= N_("E1021: const requires a value"));
EXTERN char e_type_or_initialization_required[]
INIT(= N_("E1022: type or initialization required"));
! // E1023 unused
// E1024 unused
EXTERN char e_using_rcurly_outside_if_block_scope[]
INIT(= N_("E1025: using } outside of a block scope"));
--- 69,76 ----
INIT(= N_("E1021: const requires a value"));
EXTERN char e_type_or_initialization_required[]
INIT(= N_("E1022: type or initialization required"));
! EXTERN char e_cannot_slice_dictionary[]
! INIT(= N_("E1023: cannot slice a dictionary"));
// E1024 unused
EXTERN char e_using_rcurly_outside_if_block_scope[]
INIT(= N_("E1025: using } outside of a block scope"));
*** ../vim-8.2.1461/src/vim9compile.c 2020-08-15 16:33:24.497747330 +0200
--- src/vim9compile.c 2020-08-15 21:01:01.288819028 +0200
***************
*** 3068,3073 ****
--- 3068,3074 ----
garray_T *stack = &cctx->ctx_type_stack;
type_T **typep;
vartype_T vtype;
+ int is_slice = FALSE;
// list index: list[123]
// dict member: dict[key]
***************
*** 3082,3092 ****
*arg = skipwhite(p);
if (may_get_next_line_error(p, arg, cctx) == FAIL)
return FAIL;
! if (compile_expr0(arg, cctx) == FAIL)
! return FAIL;
- if (may_get_next_line_error(p, arg, cctx) == FAIL)
- return FAIL;
if (**arg != ']')
{
emsg(_(e_missbrac));
--- 3083,3118 ----
*arg = skipwhite(p);
if (may_get_next_line_error(p, arg, cctx) == FAIL)
return FAIL;
! if (**arg == ':')
! // missing first index is equal to zero
! generate_PUSHNR(cctx, 0);
! else
! {
! if (compile_expr0(arg, cctx) == FAIL)
! return FAIL;
! if (may_get_next_line_error(p, arg, cctx) == FAIL)
! return FAIL;
! *arg = skipwhite(*arg);
! }
! if (**arg == ':')
! {
! *arg = skipwhite(*arg + 1);
! if (may_get_next_line_error(p, arg, cctx) == FAIL)
! return FAIL;
! if (**arg == ']')
! // missing second index is equal to end of string
! generate_PUSHNR(cctx, -1);
! else
! {
! if (compile_expr0(arg, cctx) == FAIL)
! return FAIL;
! if (may_get_next_line_error(p, arg, cctx) == FAIL)
! return FAIL;
! *arg = skipwhite(*arg);
! }
! is_slice = TRUE;
! }
if (**arg != ']')
{
emsg(_(e_missbrac));
***************
*** 3098,3104 ****
// we can use the index value type.
// TODO: If we don't know use an instruction to figure it out at
// runtime.
! typep = ((type_T **)stack->ga_data) + stack->ga_len - 2;
vtype = (*typep)->tt_type;
if (*typep == &t_any)
{
--- 3124,3131 ----
// we can use the index value type.
// TODO: If we don't know use an instruction to figure it out at
// runtime.
! typep = ((type_T **)stack->ga_data) + stack->ga_len
! - (is_slice ? 3 : 2);
vtype = (*typep)->tt_type;
if (*typep == &t_any)
{
***************
*** 3109,3114 ****
--- 3136,3146 ----
}
if (vtype == VAR_DICT)
{
+ if (is_slice)
+ {
+ emsg(_(e_cannot_slice_dictionary));
+ return FAIL;
+ }
if ((*typep)->tt_type == VAR_DICT)
*typep = (*typep)->tt_member;
else
***************
*** 3124,3135 ****
}
else if (vtype == VAR_STRING)
{
! *typep = &t_number;
! if (generate_instr_drop(cctx, ISN_STRINDEX, 1) == FAIL)
return FAIL;
}
else if (vtype == VAR_LIST || *typep == &t_any)
{
if ((*typep)->tt_type == VAR_LIST)
*typep = (*typep)->tt_member;
if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
--- 3156,3179 ----
}
else if (vtype == VAR_STRING)
{
! *typep = &t_string;
! if ((is_slice
! ? generate_instr_drop(cctx, ISN_STRSLICE, 2)
! : generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL)
return FAIL;
}
+ else if (vtype == VAR_BLOB)
+ {
+ emsg("Sorry, blob index and slice not implemented yet");
+ return FAIL;
+ }
else if (vtype == VAR_LIST || *typep == &t_any)
{
+ if (is_slice)
+ {
+ emsg("Sorry, list slice not implemented yet");
+ return FAIL;
+ }
if ((*typep)->tt_type == VAR_LIST)
*typep = (*typep)->tt_member;
if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
***************
*** 7052,7057 ****
--- 7096,7102 ----
case ISN_FOR:
case ISN_LISTINDEX:
case ISN_STRINDEX:
+ case ISN_STRSLICE:
case ISN_GETITEM:
case ISN_SLICE:
case ISN_MEMBER:
*** ../vim-8.2.1461/src/vim9.h 2020-08-12 21:34:43.266489468 +0200
--- src/vim9.h 2020-08-15 19:50:39.509355291 +0200
***************
*** 117,122 ****
--- 117,123 ----
// expression operations
ISN_CONCAT,
ISN_STRINDEX, // [expr] string index
+ ISN_STRSLICE, // [expr:expr] string slice
ISN_LISTINDEX, // [expr] list index
ISN_SLICE, // drop isn_arg.number items from start of list
ISN_GETITEM, // push list item, isn_arg.number is the index
*** ../vim-8.2.1461/src/vim9execute.c 2020-08-15 18:38:32.072814243 +0200
--- src/vim9execute.c 2020-08-15 20:26:33.377361155 +0200
***************
*** 70,76 ****
} ectx_T;
// Get pointer to item relative to the bottom of the stack, -1 is the last one.
! #define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + idx)
void
to_string_error(vartype_T vartype)
--- 70,76 ----
} ectx_T;
// Get pointer to item relative to the bottom of the stack, -1 is the last one.
! #define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + (idx))
void
to_string_error(vartype_T vartype)
***************
*** 2232,2243 ****
break;
case ISN_STRINDEX:
{
! varnumber_T n;
char_u *res;
// string index: string is at stack-2, index at stack-1
! tv = STACK_TV_BOT(-2);
if (tv->v_type != VAR_STRING)
{
SOURCING_LNUM = iptr->isn_lnum;
--- 2232,2247 ----
break;
case ISN_STRINDEX:
+ case ISN_STRSLICE:
{
! int is_slice = iptr->isn_type == ISN_STRSLICE;
! varnumber_T n1 = 0, n2;
char_u *res;
// string index: string is at stack-2, index at stack-1
! // string slice: string is at stack-3, first index at
! // stack-2, second index at stack-1
! tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2);
if (tv->v_type != VAR_STRING)
{
SOURCING_LNUM = iptr->isn_lnum;
***************
*** 2245,2250 ****
--- 2249,2266 ----
goto on_error;
}
+ if (is_slice)
+ {
+ tv = STACK_TV_BOT(-2);
+ if (tv->v_type != VAR_NUMBER)
+ {
+ SOURCING_LNUM = iptr->isn_lnum;
+ emsg(_(e_number_exp));
+ goto on_error;
+ }
+ n1 = tv->vval.v_number;
+ }
+
tv = STACK_TV_BOT(-1);
if (tv->v_type != VAR_NUMBER)
{
***************
*** 2252,2265 ****
emsg(_(e_number_exp));
goto on_error;
}
! n = tv->vval.v_number;
! // The resulting variable is a string of a single
! // character. If the index is too big or negative the
! // result is empty.
! --ectx.ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
! res = char_from_string(tv->vval.v_string, n);
vim_free(tv->vval.v_string);
tv->vval.v_string = res;
}
--- 2268,2285 ----
emsg(_(e_number_exp));
goto on_error;
}
! n2 = tv->vval.v_number;
! ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
tv = STACK_TV_BOT(-1);
! if (is_slice)
! // Slice: Select the characters from the string
! res = string_slice(tv->vval.v_string, n1, n2);
! else
! // Index: The resulting variable is a string of a
! // single character. If the index is too big or
! // negative the result is empty.
! res = char_from_string(tv->vval.v_string, n2);
vim_free(tv->vval.v_string);
tv->vval.v_string = res;
}
***************
*** 3140,3145 ****
--- 3160,3166 ----
// expression operations
case ISN_CONCAT: smsg("%4d CONCAT", current); break;
case ISN_STRINDEX: smsg("%4d STRINDEX", current); break;
+ case ISN_STRSLICE: smsg("%4d STRSLICE", current); break;
case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break;
case ISN_SLICE: smsg("%4d SLICE %lld",
current, iptr->isn_arg.number); break;
*** ../vim-8.2.1461/src/eval.c 2020-08-15 18:38:32.072814243 +0200
--- src/eval.c 2020-08-15 21:06:22.074597948 +0200
***************
*** 3699,3705 ****
case VAR_STRING:
s = tv_get_string(rettv);
len = (long)STRLEN(s);
! if (range)
{
// The resulting variable is a substring. If the indexes
// are out of range the result is empty.
--- 3699,3712 ----
case VAR_STRING:
s = tv_get_string(rettv);
len = (long)STRLEN(s);
! if (in_vim9script())
! {
! if (range)
! s = string_slice(s, n1, n2);
! else
! s = char_from_string(s, n1);
! }
! else if (range)
{
// The resulting variable is a substring. If the indexes
// are out of range the result is empty.
***************
*** 3718,3727 ****
else
s = vim_strnsave(s + n1, n2 - n1 + 1);
}
- else if (in_vim9script())
- {
- s = char_from_string(s, n1);
- }
else
{
// The resulting variable is a string of a single
--- 3725,3730 ----
***************
*** 5313,5318 ****
--- 5316,5384 ----
}
/*
+ * Get the byte index for character index "idx" in string "str" with length
+ * "str_len".
+ * If going over the end return "str_len".
+ * If "idx" is negative count from the end, -1 is the last character.
+ * When going over the start return zero.
+ */
+ static size_t
+ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
+ {
+ varnumber_T nchar = idx;
+ size_t nbyte = 0;
+
+ if (nchar >= 0)
+ {
+ while (nchar > 0 && nbyte < str_len)
+ {
+ nbyte += MB_CPTR2LEN(str + nbyte);
+ --nchar;
+ }
+ }
+ else
+ {
+ nbyte = str_len;
+ while (nchar < 0 && nbyte > 0)
+ {
+ --nbyte;
+ nbyte -= mb_head_off(str, str + nbyte);
+ ++nchar;
+ }
+ }
+ return nbyte;
+ }
+
+ /*
+ * Return the slice "str[first:last]" using character indexes.
+ * Return NULL when the result is empty.
+ */
+ char_u *
+ string_slice(char_u *str, varnumber_T first, varnumber_T last)
+ {
+ size_t start_byte, end_byte;
+ size_t slen;
+
+ if (str == NULL)
+ return NULL;
+ slen = STRLEN(str);
+ start_byte = char_idx2byte(str, slen, first);
+ if (last == -1)
+ end_byte = slen;
+ else
+ {
+ end_byte = char_idx2byte(str, slen, last);
+ if (end_byte < slen)
+ // end index is inclusive
+ end_byte += MB_CPTR2LEN(str + end_byte);
+ }
+
+ if (start_byte >= slen || end_byte <= start_byte)
+ return NULL;
+ return vim_strnsave(str + start_byte, end_byte - start_byte);
+ }
+
+ /*
* Handle:
* - expr[expr], expr[expr:expr] subscript
* - ".name" lookup
*** ../vim-8.2.1461/src/proto/
eval.pro 2020-08-15 18:38:32.072814243 +0200
--- src/proto/
eval.pro 2020-08-15 20:16:49.965762970 +0200
***************
*** 60,65 ****
--- 60,66 ----
int eval_isnamec1(int c);
int eval_isdictc(int c);
char_u *char_from_string(char_u *str, varnumber_T index);
+ char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last);
int handle_subscript(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);
*** ../vim-8.2.1461/src/testdir/test_vim9_expr.vim 2020-08-15 18:38:32.076814213 +0200
--- src/testdir/test_vim9_expr.vim 2020-08-15 21:02:24.500245402 +0200
***************
*** 2074,2080 ****
assert_equal(123, d.key)
enddef
! def Test_expr7_subscript()
let lines =<< trim END
let text = 'abcdef'
assert_equal('', text[-1])
--- 2074,2080 ----
assert_equal(123, d.key)
enddef
! def Test_expr7_string_subscript()
let lines =<< trim END
let text = 'abcdef'
assert_equal('', text[-1])
***************
*** 2094,2099 ****
--- 2094,2121 ----
assert_equal('f', text[5])
assert_equal('', text[6])
assert_equal('', text[999])
+
+ assert_equal('ábçdëf', text[0:-1])
+ assert_equal('ábçdëf', text[0 :-1])
+ assert_equal('ábçdëf', text[0: -1])
+ assert_equal('ábçdëf', text[0 : -1])
+ assert_equal('ábçdëf', text[0
+ :-1])
+ assert_equal('ábçdëf', text[0:
+ -1])
+ assert_equal('ábçdëf', text[0 : -1
+ ])
+ assert_equal('bçdëf', text[1:-1])
+ assert_equal('çdëf', text[2:-1])
+ assert_equal('dëf', text[3:-1])
+ assert_equal('ëf', text[4:-1])
+ assert_equal('f', text[5:-1])
+ assert_equal('', text[6:-1])
+ assert_equal('', text[999:-1])
+
+ assert_equal('ábçd', text[:3])
+ assert_equal('bçdëf', text[1:])
+ assert_equal('ábçdëf', text[:])
END
CheckDefSuccess(lines)
CheckScriptSuccess(['vim9script'] + lines)
*** ../vim-8.2.1461/src/testdir/test_vim9_disassemble.vim 2020-08-15 16:33:24.501747305 +0200
--- src/testdir/test_vim9_disassemble.vim 2020-08-15 20:42:10.652074157 +0200
***************
*** 971,977 ****
assert_equal('aabb', ConcatString())
enddef
! def StringIndex(): number
let s = "abcd"
let res = s[1]
return res
--- 971,977 ----
assert_equal('aabb', ConcatString())
enddef
! def StringIndex(): string
let s = "abcd"
let res = s[1]
return res
*** ../vim-8.2.1461/src/version.c 2020-08-15 18:38:32.076814213 +0200
--- src/version.c 2020-08-15 19:48:40.182295712 +0200
***************
*** 756,757 ****
--- 756,759 ----
{ /* Add new patch number below this line */
+ /**/
+ 1462,
/**/
--
hundred-and-one symptoms of being an internet addict:
211. Your husband leaves you...taking the computer with him and you
call him crying, and beg him to bring the computer back.
/// Bram Moolenaar -- Br...@Moolenaar.net --
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 ///