]> granicus.if.org Git - vim/commitdiff
patch 8.2.3765: Vim9: cannot use a lambda for 'opfunc' and others v8.2.3765
authorBram Moolenaar <Bram@vim.org>
Thu, 9 Dec 2021 14:23:43 +0000 (14:23 +0000)
committerBram Moolenaar <Bram@vim.org>
Thu, 9 Dec 2021 14:23:43 +0000 (14:23 +0000)
Problem:    Vim9: cannot use a lambda for 'opfunc' and others.
Solution:   Convert the lambda to a string.

src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_func.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 184f5575866b226ae13d6627316a6e7b6e65514d..4cf37b76c0af597e2e5dbd91d425378c200392b0 100644 (file)
@@ -316,6 +316,7 @@ def s:ScriptFuncStore()
   w:windowvar = 'wv'
   t:tabpagevar = 'tv'
   &tabstop = 8
+  &opfunc = (t) => len(t)
   $ENVVAR = 'ev'
   @z = 'rv'
 enddef
@@ -343,12 +344,17 @@ def Test_disassemble_store()
         ' STOREW w:windowvar.*' ..
         't:tabpagevar = ''tv''.*' ..
         ' STORET t:tabpagevar.*' ..
-        '&tabstop = 8.*' ..
-        ' STOREOPT &tabstop.*' ..
-        '$ENVVAR = ''ev''.*' ..
-        ' STOREENV $ENVVAR.*' ..
+        '&tabstop = 8\_s*' ..
+        '\d\+ PUSHNR 8\_s*' ..
+        '\d\+ STOREOPT &tabstop\_s*' ..
+        '&opfunc = (t) => len(t)\_s*' ..
+        '\d\+ FUNCREF <lambda>\d\+\_s*' ..
+        '\d\+ STOREFUNCOPT &opfunc\_s*' ..
+        '$ENVVAR = ''ev''\_s*' ..
+        '\d\+ PUSHS "ev"\_s*' ..
+        '\d\+ STOREENV $ENVVAR\_s*' ..
         '@z = ''rv''.*' ..
-        ' STOREREG @z.*',
+        '\d\+ STOREREG @z.*',
         res)
 enddef
 
index 651da050994fdb53405d70f8348228e0aea583d4..40affb39cb30a83e6f729a32be9848e70fd4cdba 100644 (file)
@@ -1202,6 +1202,28 @@ def Test_lambda_in_reduce_line_break()
   CheckScriptSuccess(lines)
 enddef
 
+def Test_set_opfunc_to_lambda()
+  var lines =<< trim END
+    vim9script
+    nnoremap <expr> <F4> <SID>CountSpaces() .. '_'
+    def CountSpaces(type = ''): string
+      if type == ''
+        &operatorfunc = (t) => CountSpaces(t)
+        return 'g@'
+      endif
+      normal! '[V']y
+      g:result = getreg('"')->count(' ')
+      return ''
+    enddef
+    new
+    'a b c d e'->setline(1)
+    feedkeys("\<F4>", 'x')
+    assert_equal(4, g:result)
+    bwipe!
+  END
+  CheckScriptSuccess(lines)
+enddef
+
 " Default arg and varargs
 def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
   var res = one .. ',' .. two
index 06b91839e8434b5fa7e832f06a5d6bd3f2b33bfe..083c111f0e0625117b261c6e5842cab8861f03f7 100644 (file)
@@ -753,6 +753,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3765,
 /**/
     3764,
 /**/
index c82ba81d022ff298743c12b2dda2b4e1f79be3ce..99b952fe483a233d80a7b0cbf54c0a6674ecef37 100644 (file)
@@ -55,7 +55,8 @@ typedef enum {
     ISN_STORES,            // pop into script variable isn_arg.loadstore
     ISN_STOREOUTER,  // pop variable into outer scope isn_arg.outer
     ISN_STORESCRIPT, // pop into script variable isn_arg.script
-    ISN_STOREOPT,    // pop into option isn_arg.string
+    ISN_STOREOPT,    // pop into option isn_arg.storeopt
+    ISN_STOREFUNCOPT, // pop into option isn_arg.storeopt
     ISN_STOREENV,    // pop into environment variable isn_arg.string
     ISN_STOREREG,    // pop into register isn_arg.number
     // ISN_STOREOTHER, // pop into other script variable isn_arg.other.
@@ -291,7 +292,7 @@ typedef struct {
     varnumber_T        stnr_val;
 } storenr_T;
 
-// arguments to ISN_STOREOPT
+// arguments to ISN_STOREOPT and ISN_STOREFUNCOPT
 typedef struct {
     char_u     *so_name;
     int                so_flags;
index 31d06b27c2e0876482192088db2f624bcdc9ed3f..cde56ac208a47d0f14e62950e08f030627e093f7 100644 (file)
@@ -118,6 +118,7 @@ typedef struct {
 typedef enum {
     dest_local,
     dest_option,
+    dest_func_option,
     dest_env,
     dest_global,
     dest_buffer,
@@ -1412,15 +1413,19 @@ generate_STORENR(cctx_T *cctx, int idx, varnumber_T value)
 }
 
 /*
- * Generate an ISN_STOREOPT instruction
+ * Generate an ISN_STOREOPT or ISN_STOREFUNCOPT instruction
  */
     static int
-generate_STOREOPT(cctx_T *cctx, char_u *name, int opt_flags)
+generate_STOREOPT(
+       cctx_T      *cctx,
+       isntype_T   isn_type,
+       char_u      *name,
+       int         opt_flags)
 {
     isn_T      *isn;
 
     RETURN_OK_IF_SKIP(cctx);
-    if ((isn = generate_instr_drop(cctx, ISN_STOREOPT, 1)) == NULL)
+    if ((isn = generate_instr_drop(cctx, isn_type, 1)) == NULL)
        return FAIL;
     isn->isn_arg.storeopt.so_name = vim_strsave(name);
     isn->isn_arg.storeopt.so_flags = opt_flags;
@@ -5980,6 +5985,7 @@ generate_loadvar(
     switch (dest)
     {
        case dest_option:
+       case dest_func_option:
            generate_LOAD(cctx, ISN_LOADOPT, 0, name, type);
            break;
        case dest_global:
@@ -6094,6 +6100,7 @@ get_var_dest(
        int             cc;
        long            numval;
        getoption_T     opt_type;
+       int             opt_p_flags;
 
        *dest = dest_option;
        if (cmdidx == CMD_final || cmdidx == CMD_const)
@@ -6112,7 +6119,7 @@ get_var_dest(
        cc = *p;
        *p = NUL;
        opt_type = get_option_value(skip_option_env_lead(name),
-                                          &numval, NULL, NULL, *option_scope);
+                                  &numval, NULL, &opt_p_flags, *option_scope);
        *p = cc;
        switch (opt_type)
        {
@@ -6121,7 +6128,16 @@ get_var_dest(
                    return FAIL;
            case gov_string:
            case gov_hidden_string:
-                   *type = &t_string;
+                   if (opt_p_flags & P_FUNC)
+                   {
+                       // might be a Funcref, check the type later
+                       *type = &t_any;
+                       *dest = dest_func_option;
+                   }
+                   else
+                   {
+                       *type = &t_string;
+                   }
                    break;
            case gov_bool:
            case gov_hidden_bool:
@@ -6204,8 +6220,11 @@ generate_store_var(
     switch (dest)
     {
        case dest_option:
-           return generate_STOREOPT(cctx, skip_option_env_lead(name),
-                                                                   opt_flags);
+           return generate_STOREOPT(cctx, ISN_STOREOPT,
+                                       skip_option_env_lead(name), opt_flags);
+       case dest_func_option:
+           return generate_STOREOPT(cctx, ISN_STOREFUNCOPT,
+                                       skip_option_env_lead(name), opt_flags);
        case dest_global:
            // include g: with the name, easier to execute that way
            return generate_STORE(cctx, vim_strchr(name, AUTOLOAD_CHAR) == NULL
@@ -6468,7 +6487,7 @@ compile_lhs(
     if (lhs->lhs_varlen > 1 || var_start[lhs->lhs_varlen] != ':')
        var_end = lhs->lhs_dest_end;
 
-    if (lhs->lhs_dest != dest_option)
+    if (lhs->lhs_dest != dest_option && lhs->lhs_dest != dest_func_option)
     {
        if (is_decl && *var_end == ':')
        {
@@ -7223,7 +7242,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                emsg(_(e_const_requires_a_value));
                goto theend;
            }
-           else if (!lhs.lhs_has_type || lhs.lhs_dest == dest_option)
+           else if (!lhs.lhs_has_type || lhs.lhs_dest == dest_option
+                                          || lhs.lhs_dest == dest_func_option)
            {
                emsg(_(e_type_or_initialization_required));
                goto theend;
@@ -10454,6 +10474,7 @@ delete_instr(isn_T *isn)
            break;
 
        case ISN_STOREOPT:
+       case ISN_STOREFUNCOPT:
            vim_free(isn->isn_arg.storeopt.so_name);
            break;
 
index f805dc0f14911653d9275ff7e338f545de3f31c5..874e57c58c06f56dc9fe632352ae99ec763cd813 100644 (file)
@@ -2336,10 +2336,14 @@ exec_instructions(ectx_T *ectx)
 
            // store option
            case ISN_STOREOPT:
+           case ISN_STOREFUNCOPT:
                {
+                   char_u      *opt_name = iptr->isn_arg.storeopt.so_name;
+                   int         opt_flags = iptr->isn_arg.storeopt.so_flags;
                    long        n = 0;
                    char_u      *s = NULL;
                    char        *msg;
+                   callback_T  cb = {NULL, NULL, 0};
 
                    --ectx->ec_stack.ga_len;
                    tv = STACK_TV_BOT(0);
@@ -2349,11 +2353,22 @@ exec_instructions(ectx_T *ectx)
                        if (s == NULL)
                            s = (char_u *)"";
                    }
+                   else if (iptr->isn_type == ISN_STOREFUNCOPT)
+                   {
+                       SOURCING_LNUM = iptr->isn_lnum;
+                       cb = get_callback(tv);
+                       if (cb.cb_name == NULL || *cb.cb_name == NUL)
+                       {
+                           clear_tv(tv);
+                           free_callback(&cb);
+                           goto on_error;
+                       }
+                       s = cb.cb_name;
+                   }
                    else
                        // must be VAR_NUMBER, CHECKTYPE makes sure
                        n = tv->vval.v_number;
-                   msg = set_option_value(iptr->isn_arg.storeopt.so_name,
-                                       n, s, iptr->isn_arg.storeopt.so_flags);
+                   msg = set_option_value(opt_name, n, s, opt_flags);
                    clear_tv(tv);
                    if (msg != NULL)
                    {
@@ -2361,6 +2376,8 @@ exec_instructions(ectx_T *ectx)
                        emsg(_(msg));
                        goto on_error;
                    }
+                   if (cb.cb_name != NULL)
+                       free_callback(&cb);
                }
                break;
 
@@ -5335,7 +5352,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                }
                break;
            case ISN_STOREOPT:
-               smsg("%s%4d STOREOPT &%s", pfx, current,
+           case ISN_STOREFUNCOPT:
+               smsg("%s%4d %s &%s", pfx, current,
+                 iptr->isn_type == ISN_STOREOPT ? "STOREOPT" : "STOREFUNCOPT",
                                               iptr->isn_arg.storeopt.so_name);
                break;
            case ISN_STOREENV: