]> granicus.if.org Git - vim/commitdiff
patch 8.2.2719: Vim9: appending to dict item doesn't work in a :def function v8.2.2719
authorBram Moolenaar <Bram@vim.org>
Mon, 5 Apr 2021 15:11:17 +0000 (17:11 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 5 Apr 2021 15:11:17 +0000 (17:11 +0200)
Problem:    Vim9: appending to dict item doesn't work in a :def function.
Solution:   Implement assignment with operator on indexed item.

src/testdir/test_vim9_assign.vim
src/version.c
src/vim9compile.c

index db8f551e8effe4ad03a5a2771ba6c0c95b2f2778..4e37efb28209e87775d418d6fcd7cf4bbd3e92b2 100644 (file)
@@ -1116,7 +1116,6 @@ enddef
 
 def Test_assign_dict_with_op()
   var lines =<< trim END
-    vim9script
     var ds: dict<string> = {a: 'x'}
     ds['a'] ..= 'y'
     ds.a ..= 'z'
@@ -1148,8 +1147,46 @@ def Test_assign_dict_with_op()
     dn.a %= 6
     assert_equal(2, dn.a)
   END
-  # TODO: this should also work with a :def function
-  CheckScriptSuccess(lines)
+  CheckDefAndScriptSuccess(lines)
+enddef
+
+def Test_assign_list_with_op()
+  var lines =<< trim END
+    var ls: list<string> = ['x']
+    ls[0] ..= 'y'
+    assert_equal('xy', ls[0])
+
+    var ln: list<number> = [9]
+    ln[0] += 2
+    assert_equal(11, ln[0])
+
+    ln[0] -= 3
+    assert_equal(8, ln[0])
+
+    ln[0] *= 2
+    assert_equal(16, ln[0])
+
+    ln[0] /= 3
+    assert_equal(5, ln[0])
+
+    ln[0] %= 3
+    assert_equal(2, ln[0])
+  END
+  CheckDefAndScriptSuccess(lines)
+enddef
+
+def Test_assign_with_op_fails()
+  var lines =<< trim END
+      var s = 'abc'
+      s[1] += 'x'
+  END
+  CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2)
+
+  lines =<< trim END
+      var s = 'abc'
+      s[1] ..= 'x'
+  END
+  CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2)
 enddef
 
 def Test_assign_lambda()
index 961ddea1d643259c5a9d2604267089acfc870f7c..76da319622eea310640c05d5c3ce2c4aeb3c87d8 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2719,
 /**/
     2718,
 /**/
index ce033c1b3d1a42db6fdadd1276f083b709f203e5..455aa77fa2fbe2fe48547519e115c4febd94d26c 100644 (file)
@@ -2649,6 +2649,112 @@ clear_ppconst(ppconst_T *ppconst)
     ppconst->pp_used = 0;
 }
 
+/*
+ * Compile getting a member from a list/dict/string/blob.  Stack has the
+ * indexable value and the index.
+ */
+    static int
+compile_member(int is_slice, cctx_T *cctx)
+{
+    type_T     **typep;
+    garray_T   *stack = &cctx->ctx_type_stack;
+    vartype_T  vtype;
+    type_T     *valtype;
+
+    // We can index a list and a dict.  If we don't know the type
+    // 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;
+    valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+    // If the index is a string, the variable must be a Dict.
+    if (*typep == &t_any && valtype == &t_string)
+       vtype = VAR_DICT;
+    if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
+    {
+       if (need_type(valtype, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
+           return FAIL;
+       if (is_slice)
+       {
+           valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
+           if (need_type(valtype, &t_number, -2, 0, cctx,
+                                                        FALSE, FALSE) == FAIL)
+               return FAIL;
+       }
+    }
+
+    if (vtype == VAR_DICT)
+    {
+       if (is_slice)
+       {
+           emsg(_(e_cannot_slice_dictionary));
+           return FAIL;
+       }
+       if ((*typep)->tt_type == VAR_DICT)
+       {
+           *typep = (*typep)->tt_member;
+           if (*typep == &t_unknown)
+               // empty dict was used
+               *typep = &t_any;
+       }
+       else
+       {
+           if (need_type(*typep, &t_dict_any, -2, 0, cctx,
+                                                        FALSE, FALSE) == FAIL)
+               return FAIL;
+           *typep = &t_any;
+       }
+       if (may_generate_2STRING(-1, cctx) == FAIL)
+           return FAIL;
+       if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
+           return FAIL;
+    }
+    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)
+       {
+           if (generate_instr_drop(cctx,
+                    vtype == VAR_LIST ?  ISN_LISTSLICE : ISN_ANYSLICE,
+                                                           2) == FAIL)
+               return FAIL;
+       }
+       else
+       {
+           if ((*typep)->tt_type == VAR_LIST)
+           {
+               *typep = (*typep)->tt_member;
+               if (*typep == &t_unknown)
+                   // empty list was used
+                   *typep = &t_any;
+           }
+           if (generate_instr_drop(cctx,
+                vtype == VAR_LIST ?  ISN_LISTINDEX : ISN_ANYINDEX, 1) == FAIL)
+               return FAIL;
+       }
+    }
+    else
+    {
+       emsg(_(e_string_list_dict_or_blob_required));
+       return FAIL;
+    }
+    return OK;
+}
+
 /*
  * Generate an instruction to load script-local variable "name", without the
  * leading "s:".
@@ -3934,10 +4040,6 @@ compile_subscript(
        }
        else if (**arg == '[')
        {
-           garray_T    *stack = &cctx->ctx_type_stack;
-           type_T      **typep;
-           type_T      *valtype;
-           vartype_T   vtype;
            int         is_slice = FALSE;
 
            // list index: list[123]
@@ -4004,99 +4106,8 @@ compile_subscript(
            }
            *arg = *arg + 1;
 
-           // We can index a list and a dict.  If we don't know the type
-           // 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;
-           valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
-           // If the index is a string, the variable must be a Dict.
-           if (*typep == &t_any && valtype == &t_string)
-               vtype = VAR_DICT;
-           if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
-           {
-               if (need_type(valtype, &t_number, -1, 0, cctx,
-                                                        FALSE, FALSE) == FAIL)
-                   return FAIL;
-               if (is_slice)
-               {
-                   valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
-                   if (need_type(valtype, &t_number, -2, 0, cctx,
-                                                        FALSE, FALSE) == FAIL)
-                       return FAIL;
-               }
-           }
-
-           if (vtype == VAR_DICT)
-           {
-               if (is_slice)
-               {
-                   emsg(_(e_cannot_slice_dictionary));
-                   return FAIL;
-               }
-               if ((*typep)->tt_type == VAR_DICT)
-               {
-                   *typep = (*typep)->tt_member;
-                   if (*typep == &t_unknown)
-                       // empty dict was used
-                       *typep = &t_any;
-               }
-               else
-               {
-                   if (need_type(*typep, &t_dict_any, -2, 0, cctx,
-                                                        FALSE, FALSE) == FAIL)
-                       return FAIL;
-                   *typep = &t_any;
-               }
-               if (may_generate_2STRING(-1, cctx) == FAIL)
-                   return FAIL;
-               if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
-                   return FAIL;
-           }
-           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");
+           if (compile_member(is_slice, cctx) == FAIL)
                return FAIL;
-           }
-           else if (vtype == VAR_LIST || *typep == &t_any)
-           {
-               if (is_slice)
-               {
-                   if (generate_instr_drop(cctx,
-                            vtype == VAR_LIST ?  ISN_LISTSLICE : ISN_ANYSLICE,
-                                                                   2) == FAIL)
-                       return FAIL;
-               }
-               else
-               {
-                   if ((*typep)->tt_type == VAR_LIST)
-                   {
-                       *typep = (*typep)->tt_member;
-                       if (*typep == &t_unknown)
-                           // empty list was used
-                           *typep = &t_any;
-                   }
-                   if (generate_instr_drop(cctx,
-                            vtype == VAR_LIST ?  ISN_LISTINDEX : ISN_ANYINDEX,
-                                                                   1) == FAIL)
-                       return FAIL;
-               }
-           }
-           else
-           {
-               emsg(_(e_string_list_dict_or_blob_required));
-               return FAIL;
-           }
        }
        else if (*p == '.' && p[1] != '.')
        {
@@ -5905,9 +5916,11 @@ compile_lhs(
            lhs->lhs_type = lhs->lhs_lvar->lv_type;
     }
 
-    if (oplen == 3 && !heredoc && lhs->lhs_dest != dest_global
-                   && lhs->lhs_type->tt_type != VAR_STRING
-                   && lhs->lhs_type->tt_type != VAR_ANY)
+    if (oplen == 3 && !heredoc
+                  && lhs->lhs_dest != dest_global
+                  && !lhs->lhs_has_index
+                  && lhs->lhs_type->tt_type != VAR_STRING
+                  && lhs->lhs_type->tt_type != VAR_ANY)
     {
        emsg(_(e_can_only_concatenate_to_string));
        return FAIL;
@@ -5993,26 +6006,21 @@ compile_lhs(
 }
 
 /*
- * Assignment to a list or dict member, or ":unlet" for the item, using the
- * information in "lhs".
- * Returns OK or FAIL.
+ * For an assignment with an index, compile the "idx" in "var[idx]" or "key" in
+ * "var.key".
  */
     static int
-compile_assign_unlet(
+compile_assign_index(
        char_u  *var_start,
        lhs_T   *lhs,
        int     is_assign,
-       type_T  *rhs_type,
+       int     *range,
        cctx_T  *cctx)
 {
-    char_u     *p;
-    int                r;
-    vartype_T  dest_type;
     size_t     varlen = lhs->lhs_varlen;
-    garray_T    *stack = &cctx->ctx_type_stack;
-    int                range = FALSE;
+    char_u     *p;
+    int                r = OK;
 
-    // Compile the "idx" in "var[idx]" or "key" in "var.key".
     p = var_start + varlen;
     if (*p == '[')
     {
@@ -6027,7 +6035,7 @@ compile_assign_unlet(
                semsg(_(e_cannot_use_range_with_assignment_str), p);
                return FAIL;
            }
-           range = TRUE;
+           *range = TRUE;
            p = skipwhite(p);
            if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
            {
@@ -6053,7 +6061,29 @@ compile_assign_unlet(
 
        r = generate_PUSHS(cctx, key);
     }
-    if (r == FAIL)
+    return r;
+}
+
+/*
+ * Assignment to a list or dict member, or ":unlet" for the item, using the
+ * information in "lhs".
+ * Returns OK or FAIL.
+ */
+    static int
+compile_assign_unlet(
+       char_u  *var_start,
+       lhs_T   *lhs,
+       int     is_assign,
+       type_T  *rhs_type,
+       cctx_T  *cctx)
+{
+    char_u     *p;
+    vartype_T  dest_type;
+    size_t     varlen = lhs->lhs_varlen;
+    garray_T    *stack = &cctx->ctx_type_stack;
+    int                range = FALSE;
+
+    if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL)
        return FAIL;
 
     if (lhs->lhs_type == &t_any)
@@ -6330,9 +6360,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
 
                        if (lhs.lhs_has_index)
                        {
-                           // TODO: get member from list or dict
-                           emsg("Index with operation not supported yet");
-                           goto theend;
+                           int range = FALSE;
+
+                           // Get member from list or dict.  First compile the
+                           // index value.
+                           if (compile_assign_index(var_start, &lhs,
+                                                  TRUE, &range, cctx) == FAIL)
+                               goto theend;
+
+                           // Get the member.
+                           if (compile_member(FALSE, cctx) == FAIL)
+                               goto theend;
                        }
                    }