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 -- [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/202008151910.07FJAke8372762%40masaka.moolenaar.net.

Raspunde prin e-mail lui