To: vim_dev@googlegroups.com Subject: Patch 8.1.1820 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1820 Problem: Using expr->FuncRef() does not work. Solution: Make FuncRef work as a method. Files: src/eval.c, src/userfunc.c, src/testdir/test_method.vim *** ../vim-8.1.1819/src/eval.c 2019-08-04 23:04:35.525945914 +0200 --- src/eval.c 2019-08-05 22:49:59.044167083 +0200 *************** *** 3655,3660 **** --- 3655,3728 ---- } /* + * Handle a name followed by "(". Both for just "name(arg)" and for + * "expr->name(arg)". + * Returns OK or FAIL. + */ + static int + eval_func( + char_u **arg, // points to "(", will be advanced + char_u *name, + int name_len, + typval_T *rettv, + int evaluate, + typval_T *basetv) // "expr" for "expr->name(arg)" + { + char_u *s = name; + int len = name_len; + partial_T *partial; + int ret = OK; + + if (!evaluate) + check_vars(s, len); + + /* If "s" is the name of a variable of type VAR_FUNC + * use its contents. */ + s = deref_func_name(s, &len, &partial, !evaluate); + + /* Need to make a copy, in case evaluating the arguments makes + * the name invalid. */ + s = vim_strsave(s); + if (s == NULL) + ret = FAIL; + else + { + funcexe_T funcexe; + + // Invoke the function. + vim_memset(&funcexe, 0, sizeof(funcexe)); + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.doesrange = &len; + funcexe.evaluate = evaluate; + funcexe.partial = partial; + funcexe.basetv = basetv; + ret = get_func_tv(s, len, rettv, arg, &funcexe); + } + vim_free(s); + + /* If evaluate is FALSE rettv->v_type was not set in + * get_func_tv, but it's needed in handle_subscript() to parse + * what follows. So set it here. */ + if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') + { + rettv->vval.v_string = NULL; + rettv->v_type = VAR_FUNC; + } + + /* Stop the expression evaluation when immediately + * aborting on error, or when an interrupt occurred or + * an exception was thrown but not caught. */ + if (evaluate && aborting()) + { + if (ret == OK) + clear_tv(rettv); + ret = FAIL; + } + return ret; + } + + /* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type * VAR_UNKNOWN. The function still returns FAIL for a syntax error. *************** *** 4671,4725 **** else { if (**arg == '(') /* recursive! */ ! { ! partial_T *partial; ! ! if (!evaluate) ! check_vars(s, len); ! ! /* If "s" is the name of a variable of type VAR_FUNC ! * use its contents. */ ! s = deref_func_name(s, &len, &partial, !evaluate); ! ! /* Need to make a copy, in case evaluating the arguments makes ! * the name invalid. */ ! s = vim_strsave(s); ! if (s == NULL) ! ret = FAIL; ! else ! { ! funcexe_T funcexe; ! ! // Invoke the function. ! vim_memset(&funcexe, 0, sizeof(funcexe)); ! funcexe.firstline = curwin->w_cursor.lnum; ! funcexe.lastline = curwin->w_cursor.lnum; ! funcexe.doesrange = &len; ! funcexe.evaluate = evaluate; ! funcexe.partial = partial; ! ret = get_func_tv(s, len, rettv, arg, &funcexe); ! } ! vim_free(s); ! ! /* If evaluate is FALSE rettv->v_type was not set in ! * get_func_tv, but it's needed in handle_subscript() to parse ! * what follows. So set it here. */ ! if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') ! { ! rettv->vval.v_string = NULL; ! rettv->v_type = VAR_FUNC; ! } ! ! /* Stop the expression evaluation when immediately ! * aborting on error, or when an interrupt occurred or ! * an exception was thrown but not caught. */ ! if (evaluate && aborting()) ! { ! if (ret == OK) ! clear_tv(rettv); ! ret = FAIL; ! } ! } else if (evaluate) ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); else --- 4739,4745 ---- else { if (**arg == '(') /* recursive! */ ! ret = eval_func(arg, s, len, rettv, evaluate, NULL); else if (evaluate) ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); else *************** *** 4815,4869 **** { char_u *name; long len; ! funcexe_T funcexe; ! int ret = OK; typval_T base = *rettv; // Skip over the ->. *arg += 2; - // Locate the method name. name = *arg; ! for (len = 0; eval_isnamec(name[len]); ++len) ! ; ! if (len == 0) { if (verbose) emsg(_("E260: Missing name after ->")); ! return FAIL; } ! ! // Check for the "(". Skip over white space after it. ! if (name[len] != '(') { ! if (verbose) ! semsg(_(e_missingparen), name); ! return FAIL; } - *arg += len; - - // TODO: if "name" is a function reference, resolve it. - - vim_memset(&funcexe, 0, sizeof(funcexe)); - funcexe.evaluate = evaluate; - funcexe.basetv = &base; - rettv->v_type = VAR_UNKNOWN; - ret = get_func_tv(name, len, rettv, arg, &funcexe); /* Clear the funcref afterwards, so that deleting it while * evaluating the arguments is possible (see test55). */ if (evaluate) clear_tv(&base); - /* Stop the expression evaluation when immediately aborting on - * error, or when an interrupt occurred or an exception was thrown - * but not caught. */ - if (aborting()) - { - if (ret == OK) - clear_tv(rettv); - ret = FAIL; - } return ret; } --- 4835,4876 ---- { char_u *name; long len; ! char_u *alias; typval_T base = *rettv; + int ret; // Skip over the ->. *arg += 2; + rettv->v_type = VAR_UNKNOWN; name = *arg; ! len = get_name_len(arg, &alias, evaluate, TRUE); ! if (alias != NULL) ! name = alias; ! ! if (len <= 0) { if (verbose) emsg(_("E260: Missing name after ->")); ! ret = FAIL; } ! else { ! if (**arg != '(') ! { ! if (verbose) ! semsg(_(e_missingparen), name); ! ret = FAIL; ! } ! else ! ret = eval_func(arg, name, len, rettv, evaluate, &base); } /* Clear the funcref afterwards, so that deleting it while * evaluating the arguments is possible (see test55). */ if (evaluate) clear_tv(&base); return ret; } *** ../vim-8.1.1819/src/userfunc.c 2019-08-04 23:04:35.529945897 +0200 --- src/userfunc.c 2019-08-05 22:58:05.585534120 +0200 *************** *** 1498,1503 **** --- 1498,1504 ---- typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or // "funcexe->basetv" is not NULL int argv_clear = 0; + int argv_base = 0; partial_T *partial = funcexe->partial; // Make a copy of the name, if it comes from a funcref variable it could *************** *** 1590,1598 **** mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount); argv[0] = *funcexe->basetv; argcount++; } - else - memcpy(argv, argvars, sizeof(typval_T) * argcount); if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) *funcexe->doesrange = TRUE; --- 1591,1599 ---- mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount); argv[0] = *funcexe->basetv; argcount++; + argvars = argv; + argv_base = 1; } if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) *funcexe->doesrange = TRUE; *************** *** 1621,1627 **** did_save_redo = TRUE; } ++fp->uf_calls; ! call_user_func(fp, argcount, argv, rettv, funcexe->firstline, funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) --- 1622,1628 ---- did_save_redo = TRUE; } ++fp->uf_calls; ! call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) *************** *** 1698,1705 **** } } while (argv_clear > 0) ! clear_tv(&argv[--argv_clear]); vim_free(tofree); vim_free(name); --- 1699,1708 ---- } } + // clear the copies made from the partial while (argv_clear > 0) ! clear_tv(&argv[--argv_clear + argv_base]); ! vim_free(tofree); vim_free(name); *** ../vim-8.1.1819/src/testdir/test_method.vim 2019-08-04 17:35:49.331707782 +0200 --- src/testdir/test_method.vim 2019-08-05 23:05:37.498972087 +0200 *************** *** 1,6 **** " Tests for ->method() ! func Test_list() let l = [1, 2, 3] call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) eval l->assert_equal(l) --- 1,6 ---- " Tests for ->method() ! func Test_list_method() let l = [1, 2, 3] call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) eval l->assert_equal(l) *************** *** 34,40 **** call assert_fails('eval l->values()', 'E715:') endfunc ! func Test_dict() let d = #{one: 1, two: 2, three: 3} call assert_equal(d, d->copy()) --- 34,40 ---- call assert_fails('eval l->values()', 'E715:') endfunc ! func Test_dict_method() let d = #{one: 1, two: 2, three: 3} call assert_equal(d, d->copy()) *************** *** 66,72 **** call assert_equal([1, 2, 3], d->values()) endfunc ! func Test_string() call assert_equal(['1', '2', '3'], '1 2 3'->split()) call assert_equal([1, 2, 3], '1 2 3'->split()->map({i, v -> str2nr(v)})) call assert_equal([65, 66, 67], 'ABC'->str2list()) --- 66,72 ---- call assert_equal([1, 2, 3], d->values()) endfunc ! func Test_string_method() call assert_equal(['1', '2', '3'], '1 2 3'->split()) call assert_equal([1, 2, 3], '1 2 3'->split()->map({i, v -> str2nr(v)})) call assert_equal([65, 66, 67], 'ABC'->str2list()) *************** *** 76,82 **** call assert_equal('axc', 'abc'->substitute('b', 'x', '')) endfunc ! func Test_append() new eval ['one', 'two', 'three']->append(1) call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) --- 76,82 ---- call assert_equal('axc', 'abc'->substitute('b', 'x', '')) endfunc ! func Test_method_append() new eval ['one', 'two', 'three']->append(1) call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) *************** *** 89,91 **** --- 89,104 ---- exe 'bwipe! ' .. bnr endfunc + + func Test_method_funcref() + func Concat(one, two, three) + return a:one .. a:two .. a:three + endfunc + let FuncRef = function('Concat') + eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail') + + let Partial = function('Concat', ['two']) + eval 'one'->Partial('three')->assert_equal('onetwothree') + + delfunc Concat + endfunc *** ../vim-8.1.1819/src/version.c 2019-08-05 21:51:36.801568843 +0200 --- src/version.c 2019-08-05 22:36:18.051902871 +0200 *************** *** 775,776 **** --- 775,778 ---- { /* Add new patch number below this line */ + /**/ + 1820, /**/ -- How To Keep A Healthy Level Of Insanity: 13. Go to a poetry recital and ask why the poems don't rhyme. /// 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 ///