changeset 21393:320581a133d9 v8.2.1247

patch 8.2.1247: Vim9: cannot index a character in a string Commit: https://github.com/vim/vim/commit/bf9d8c3765a5255c0a0b577ca2e25d70a8bcb688 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jul 19 17:55:44 2020 +0200 patch 8.2.1247: Vim9: cannot index a character in a string Problem: Vim9: cannot index a character in a string. Solution: Add ISN_STRINDEX instruction. (closes https://github.com/vim/vim/issues/6478)
author Bram Moolenaar <Bram@vim.org>
date Sun, 19 Jul 2020 18:00:04 +0200
parents 6b34289ace89
children 8b85d3da76d6
files src/testdir/test_vim9_expr.vim src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c
diffstat 5 files changed, 63 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -1509,6 +1509,15 @@ def Test_expr7_trailing()
   assert_equal(123, d.key)
 enddef
 
+def Test_expr7_subscript()
+  let text = 'abcdef'
+  assert_equal('', text[-1])
+  assert_equal('a', text[0])
+  assert_equal('e', text[4])
+  assert_equal('f', text[5])
+  assert_equal('', text[6])
+enddef
+
 def Test_expr7_subscript_linebreak()
   let range = range(
   		3)
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1247,
+/**/
     1246,
 /**/
     1245,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -111,7 +111,8 @@ typedef enum {
 
     // expression operations
     ISN_CONCAT,
-    ISN_INDEX,	    // [expr] list index
+    ISN_STRINDEX,   // [expr] string index
+    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
     ISN_MEMBER,	    // dict[member]
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3752,6 +3752,7 @@ compile_subscript(
 
 	    // list index: list[123]
 	    // dict member: dict[key]
+	    // string index: text[123]
 	    // TODO: blob index
 	    // TODO: more arguments
 	    // TODO: recognize list or dict at runtime
@@ -3799,11 +3800,17 @@ compile_subscript(
 		if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
 		    return FAIL;
 	    }
+	    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_INDEX, 1) == FAIL)
+		if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
 		    return FAIL;
 	    }
 	    else
@@ -7542,7 +7549,8 @@ delete_instr(isn_T *isn)
 	case ISN_EXECCONCAT:
 	case ISN_EXECUTE:
 	case ISN_FOR:
-	case ISN_INDEX:
+	case ISN_LISTINDEX:
+	case ISN_STRINDEX:
 	case ISN_GETITEM:
 	case ISN_SLICE:
 	case ISN_MEMBER:
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2122,7 +2122,44 @@ call_def_function(
 		}
 		break;
 
-	    case ISN_INDEX:
+	    case ISN_STRINDEX:
+		{
+		    char_u	*s;
+		    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)
+		    {
+			emsg(_(e_stringreq));
+			goto on_error;
+		    }
+		    s = tv->vval.v_string;
+
+		    tv = STACK_TV_BOT(-1);
+		    if (tv->v_type != VAR_NUMBER)
+		    {
+			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.
+		    if (n < 0 || n >= (varnumber_T)STRLEN(s))
+			res = NULL;
+		    else
+			res = vim_strnsave(s + n, 1);
+		    --ectx.ec_stack.ga_len;
+		    tv = STACK_TV_BOT(-1);
+		    vim_free(tv->vval.v_string);
+		    tv->vval.v_string = res;
+		}
+		break;
+
+	    case ISN_LISTINDEX:
 		{
 		    list_T	*list;
 		    varnumber_T	n;
@@ -2947,7 +2984,8 @@ ex_disassemble(exarg_T *eap)
 
 	    // expression operations
 	    case ISN_CONCAT: smsg("%4d CONCAT", current); break;
-	    case ISN_INDEX: smsg("%4d INDEX", current); break;
+	    case ISN_STRINDEX: smsg("%4d STRINDEX", current); break;
+	    case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break;
 	    case ISN_SLICE: smsg("%4d SLICE %lld",
 					 current, iptr->isn_arg.number); break;
 	    case ISN_GETITEM: smsg("%4d ITEM %lld",