To: vim_dev@googlegroups.com Subject: Patch 8.2.1462 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@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 ///