mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.2.1462: Vim9: string slice not supported yet
Problem: Vim9: string slice not supported yet. Solution: Add support for string slicing.
This commit is contained in:
parent
3d1cde8a2f
commit
11107bab7e
@ -69,7 +69,8 @@ EXTERN char e_const_requires_a_value[]
|
|||||||
INIT(= N_("E1021: const requires a value"));
|
INIT(= N_("E1021: const requires a value"));
|
||||||
EXTERN char e_type_or_initialization_required[]
|
EXTERN char e_type_or_initialization_required[]
|
||||||
INIT(= N_("E1022: type or initialization required"));
|
INIT(= N_("E1022: type or initialization required"));
|
||||||
// E1023 unused
|
EXTERN char e_cannot_slice_dictionary[]
|
||||||
|
INIT(= N_("E1023: cannot slice a dictionary"));
|
||||||
// E1024 unused
|
// E1024 unused
|
||||||
EXTERN char e_using_rcurly_outside_if_block_scope[]
|
EXTERN char e_using_rcurly_outside_if_block_scope[]
|
||||||
INIT(= N_("E1025: using } outside of a block scope"));
|
INIT(= N_("E1025: using } outside of a block scope"));
|
||||||
|
74
src/eval.c
74
src/eval.c
@ -3699,7 +3699,14 @@ eval_index(
|
|||||||
case VAR_STRING:
|
case VAR_STRING:
|
||||||
s = tv_get_string(rettv);
|
s = tv_get_string(rettv);
|
||||||
len = (long)STRLEN(s);
|
len = (long)STRLEN(s);
|
||||||
|
if (in_vim9script())
|
||||||
|
{
|
||||||
if (range)
|
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
|
// The resulting variable is a substring. If the indexes
|
||||||
// are out of range the result is empty.
|
// are out of range the result is empty.
|
||||||
@ -3718,10 +3725,6 @@ eval_index(
|
|||||||
else
|
else
|
||||||
s = vim_strnsave(s + n1, n2 - n1 + 1);
|
s = vim_strnsave(s + n1, n2 - n1 + 1);
|
||||||
}
|
}
|
||||||
else if (in_vim9script())
|
|
||||||
{
|
|
||||||
s = char_from_string(s, n1);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// The resulting variable is a string of a single
|
// The resulting variable is a string of a single
|
||||||
@ -5312,6 +5315,69 @@ char_from_string(char_u *str, varnumber_T index)
|
|||||||
return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
|
return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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:
|
* Handle:
|
||||||
* - expr[expr], expr[expr:expr] subscript
|
* - expr[expr], expr[expr:expr] subscript
|
||||||
|
@ -60,6 +60,7 @@ int eval_isnamec(int c);
|
|||||||
int eval_isnamec1(int c);
|
int eval_isnamec1(int c);
|
||||||
int eval_isdictc(int c);
|
int eval_isdictc(int c);
|
||||||
char_u *char_from_string(char_u *str, varnumber_T index);
|
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 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);
|
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);
|
void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);
|
||||||
|
@ -971,7 +971,7 @@ def Test_disassemble_concat()
|
|||||||
assert_equal('aabb', ConcatString())
|
assert_equal('aabb', ConcatString())
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def StringIndex(): number
|
def StringIndex(): string
|
||||||
let s = "abcd"
|
let s = "abcd"
|
||||||
let res = s[1]
|
let res = s[1]
|
||||||
return res
|
return res
|
||||||
|
@ -2074,7 +2074,7 @@ def Test_expr7_trailing()
|
|||||||
assert_equal(123, d.key)
|
assert_equal(123, d.key)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_expr7_subscript()
|
def Test_expr7_string_subscript()
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
let text = 'abcdef'
|
let text = 'abcdef'
|
||||||
assert_equal('', text[-1])
|
assert_equal('', text[-1])
|
||||||
@ -2094,6 +2094,28 @@ def Test_expr7_subscript()
|
|||||||
assert_equal('f', text[5])
|
assert_equal('f', text[5])
|
||||||
assert_equal('', text[6])
|
assert_equal('', text[6])
|
||||||
assert_equal('', text[999])
|
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
|
END
|
||||||
CheckDefSuccess(lines)
|
CheckDefSuccess(lines)
|
||||||
CheckScriptSuccess(['vim9script'] + lines)
|
CheckScriptSuccess(['vim9script'] + lines)
|
||||||
|
@ -754,6 +754,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 */
|
||||||
|
/**/
|
||||||
|
1462,
|
||||||
/**/
|
/**/
|
||||||
1461,
|
1461,
|
||||||
/**/
|
/**/
|
||||||
|
@ -117,6 +117,7 @@ typedef enum {
|
|||||||
// expression operations
|
// expression operations
|
||||||
ISN_CONCAT,
|
ISN_CONCAT,
|
||||||
ISN_STRINDEX, // [expr] string index
|
ISN_STRINDEX, // [expr] string index
|
||||||
|
ISN_STRSLICE, // [expr:expr] string slice
|
||||||
ISN_LISTINDEX, // [expr] list index
|
ISN_LISTINDEX, // [expr] list index
|
||||||
ISN_SLICE, // drop isn_arg.number items from start of list
|
ISN_SLICE, // drop isn_arg.number items from start of list
|
||||||
ISN_GETITEM, // push list item, isn_arg.number is the index
|
ISN_GETITEM, // push list item, isn_arg.number is the index
|
||||||
|
@ -3068,6 +3068,7 @@ compile_subscript(
|
|||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
type_T **typep;
|
type_T **typep;
|
||||||
vartype_T vtype;
|
vartype_T vtype;
|
||||||
|
int is_slice = FALSE;
|
||||||
|
|
||||||
// list index: list[123]
|
// list index: list[123]
|
||||||
// dict member: dict[key]
|
// dict member: dict[key]
|
||||||
@ -3082,11 +3083,36 @@ compile_subscript(
|
|||||||
*arg = skipwhite(p);
|
*arg = skipwhite(p);
|
||||||
if (may_get_next_line_error(p, arg, cctx) == FAIL)
|
if (may_get_next_line_error(p, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
if (**arg == ':')
|
||||||
|
// missing first index is equal to zero
|
||||||
|
generate_PUSHNR(cctx, 0);
|
||||||
|
else
|
||||||
|
{
|
||||||
if (compile_expr0(arg, cctx) == FAIL)
|
if (compile_expr0(arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (may_get_next_line_error(p, arg, cctx) == FAIL)
|
if (may_get_next_line_error(p, arg, cctx) == FAIL)
|
||||||
return 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 != ']')
|
if (**arg != ']')
|
||||||
{
|
{
|
||||||
emsg(_(e_missbrac));
|
emsg(_(e_missbrac));
|
||||||
@ -3098,7 +3124,8 @@ compile_subscript(
|
|||||||
// we can use the index value type.
|
// we can use the index value type.
|
||||||
// TODO: If we don't know use an instruction to figure it out at
|
// TODO: If we don't know use an instruction to figure it out at
|
||||||
// runtime.
|
// runtime.
|
||||||
typep = ((type_T **)stack->ga_data) + stack->ga_len - 2;
|
typep = ((type_T **)stack->ga_data) + stack->ga_len
|
||||||
|
- (is_slice ? 3 : 2);
|
||||||
vtype = (*typep)->tt_type;
|
vtype = (*typep)->tt_type;
|
||||||
if (*typep == &t_any)
|
if (*typep == &t_any)
|
||||||
{
|
{
|
||||||
@ -3109,6 +3136,11 @@ compile_subscript(
|
|||||||
}
|
}
|
||||||
if (vtype == VAR_DICT)
|
if (vtype == VAR_DICT)
|
||||||
{
|
{
|
||||||
|
if (is_slice)
|
||||||
|
{
|
||||||
|
emsg(_(e_cannot_slice_dictionary));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
if ((*typep)->tt_type == VAR_DICT)
|
if ((*typep)->tt_type == VAR_DICT)
|
||||||
*typep = (*typep)->tt_member;
|
*typep = (*typep)->tt_member;
|
||||||
else
|
else
|
||||||
@ -3124,12 +3156,24 @@ compile_subscript(
|
|||||||
}
|
}
|
||||||
else if (vtype == VAR_STRING)
|
else if (vtype == VAR_STRING)
|
||||||
{
|
{
|
||||||
*typep = &t_number;
|
*typep = &t_string;
|
||||||
if (generate_instr_drop(cctx, ISN_STRINDEX, 1) == FAIL)
|
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;
|
return FAIL;
|
||||||
}
|
}
|
||||||
else if (vtype == VAR_LIST || *typep == &t_any)
|
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)
|
if ((*typep)->tt_type == VAR_LIST)
|
||||||
*typep = (*typep)->tt_member;
|
*typep = (*typep)->tt_member;
|
||||||
if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
|
if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
|
||||||
@ -7052,6 +7096,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_FOR:
|
case ISN_FOR:
|
||||||
case ISN_LISTINDEX:
|
case ISN_LISTINDEX:
|
||||||
case ISN_STRINDEX:
|
case ISN_STRINDEX:
|
||||||
|
case ISN_STRSLICE:
|
||||||
case ISN_GETITEM:
|
case ISN_GETITEM:
|
||||||
case ISN_SLICE:
|
case ISN_SLICE:
|
||||||
case ISN_MEMBER:
|
case ISN_MEMBER:
|
||||||
|
@ -70,7 +70,7 @@ typedef struct {
|
|||||||
} ectx_T;
|
} ectx_T;
|
||||||
|
|
||||||
// Get pointer to item relative to the bottom of the stack, -1 is the last one.
|
// 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)
|
#define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + (idx))
|
||||||
|
|
||||||
void
|
void
|
||||||
to_string_error(vartype_T vartype)
|
to_string_error(vartype_T vartype)
|
||||||
@ -2232,12 +2232,16 @@ call_def_function(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ISN_STRINDEX:
|
case ISN_STRINDEX:
|
||||||
|
case ISN_STRSLICE:
|
||||||
{
|
{
|
||||||
varnumber_T n;
|
int is_slice = iptr->isn_type == ISN_STRSLICE;
|
||||||
|
varnumber_T n1 = 0, n2;
|
||||||
char_u *res;
|
char_u *res;
|
||||||
|
|
||||||
// string index: string is at stack-2, index at stack-1
|
// string index: string is at stack-2, index at stack-1
|
||||||
tv = STACK_TV_BOT(-2);
|
// 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)
|
if (tv->v_type != VAR_STRING)
|
||||||
{
|
{
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
@ -2245,6 +2249,18 @@ call_def_function(
|
|||||||
goto on_error;
|
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);
|
tv = STACK_TV_BOT(-1);
|
||||||
if (tv->v_type != VAR_NUMBER)
|
if (tv->v_type != VAR_NUMBER)
|
||||||
{
|
{
|
||||||
@ -2252,14 +2268,18 @@ call_def_function(
|
|||||||
emsg(_(e_number_exp));
|
emsg(_(e_number_exp));
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
n = tv->vval.v_number;
|
n2 = tv->vval.v_number;
|
||||||
|
|
||||||
// The resulting variable is a string of a single
|
ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
|
||||||
// character. If the index is too big or negative the
|
|
||||||
// result is empty.
|
|
||||||
--ectx.ec_stack.ga_len;
|
|
||||||
tv = STACK_TV_BOT(-1);
|
tv = STACK_TV_BOT(-1);
|
||||||
res = char_from_string(tv->vval.v_string, n);
|
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);
|
vim_free(tv->vval.v_string);
|
||||||
tv->vval.v_string = res;
|
tv->vval.v_string = res;
|
||||||
}
|
}
|
||||||
@ -3140,6 +3160,7 @@ ex_disassemble(exarg_T *eap)
|
|||||||
// expression operations
|
// expression operations
|
||||||
case ISN_CONCAT: smsg("%4d CONCAT", current); break;
|
case ISN_CONCAT: smsg("%4d CONCAT", current); break;
|
||||||
case ISN_STRINDEX: smsg("%4d STRINDEX", 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_LISTINDEX: smsg("%4d LISTINDEX", current); break;
|
||||||
case ISN_SLICE: smsg("%4d SLICE %lld",
|
case ISN_SLICE: smsg("%4d SLICE %lld",
|
||||||
current, iptr->isn_arg.number); break;
|
current, iptr->isn_arg.number); break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user