diff src/eval.c @ 17612:e259d11e2900 v8.1.1803

patch 8.1.1803: all builtin functions are global commit https://github.com/vim/vim/commit/ac92e25a33c37ec5becbfffeccda136c73b761ac Author: Bram Moolenaar <Bram@vim.org> Date: Sat Aug 3 21:58:38 2019 +0200 patch 8.1.1803: all builtin functions are global Problem: All builtin functions are global. Solution: Add the method call operator ->. Implemented for a limited number of functions.
author Bram Moolenaar <Bram@vim.org>
date Sat, 03 Aug 2019 22:00:07 +0200
parents ff097edaae89
children 9ffec4eb8d33
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -4412,6 +4412,7 @@ eval6(
  *  + in front		unary plus (ignored)
  *  trailing []		subscript in String or List
  *  trailing .name	entry in Dictionary
+ *  trailing ->name()	method call
  *
  * "arg" must point to the first non-white of the expression.
  * "arg" is advanced to the next non-white after the recognized expression.
@@ -4690,13 +4691,12 @@ eval7(
 		    funcexe_T funcexe;
 
 		    // Invoke the function.
-		    funcexe.argv_func = NULL;
+		    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.selfdict = NULL;
 		    ret = get_func_tv(s, len, rettv, arg, &funcexe);
 		}
 		vim_free(s);
@@ -4802,6 +4802,70 @@ eval7(
 }
 
 /*
+ * Evaluate "->method()".
+ * "*arg" points to the '-'.
+ * Returns FAIL or OK. "*arg" is advanced to after the ')'.
+ */
+    static int
+eval_method(
+    char_u	**arg,
+    typval_T	*rettv,
+    int		evaluate,
+    int		verbose)	/* give error messages */
+{
+    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; ASCII_ISALNUM(name[len]) || 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;
+
+    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;
+}
+
+/*
  * Evaluate an "[expr]" or "[expr:expr]" index.  Also "dict.key".
  * "*arg" points to the '[' or '.'.
  * Returns FAIL or OK. "*arg" is advanced to after the ']'.
@@ -7359,9 +7423,13 @@ check_vars(char_u *name, int len)
 }
 
 /*
- * Handle expr[expr], expr[expr:expr] subscript and .name lookup.
- * Also handle function call with Funcref variable: func(expr)
- * Can all be combined: dict.func(expr)[idx]['func'](expr)
+ * Handle:
+ * - expr[expr], expr[expr:expr] subscript
+ * - ".name" lookup
+ * - function call with Funcref variable: func(expr)
+ * - method call: var->method()
+ *
+ * Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len()
  */
     int
 handle_subscript(
@@ -7378,14 +7446,15 @@ handle_subscript(
     // "." is ".name" lookup when we found a dict or when evaluating and
     // scriptversion is at least 2, where string concatenation is "..".
     while (ret == OK
-	    && (**arg == '['
-		|| (**arg == '.' && (rettv->v_type == VAR_DICT
+	    && (((**arg == '['
+		    || (**arg == '.' && (rettv->v_type == VAR_DICT
 			|| (!evaluate
 			    && (*arg)[1] != '.'
 			    && current_sctx.sc_version >= 2)))
-		|| (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
+		    || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
 					    || rettv->v_type == VAR_PARTIAL)))
-	    && !VIM_ISWHITE(*(*arg - 1)))
+		&& !VIM_ISWHITE(*(*arg - 1)))
+	    || (**arg == '-' && (*arg)[1] == '>')))
     {
 	if (**arg == '(')
 	{
@@ -7410,10 +7479,9 @@ handle_subscript(
 	    else
 		s = (char_u *)"";
 
-	    funcexe.argv_func = NULL;
+	    vim_memset(&funcexe, 0, sizeof(funcexe));
 	    funcexe.firstline = curwin->w_cursor.lnum;
 	    funcexe.lastline = curwin->w_cursor.lnum;
-	    funcexe.doesrange = NULL;
 	    funcexe.evaluate = evaluate;
 	    funcexe.partial = pt;
 	    funcexe.selfdict = selfdict;
@@ -7436,6 +7504,14 @@ handle_subscript(
 	    dict_unref(selfdict);
 	    selfdict = NULL;
 	}
+	else if (**arg == '-')
+	{
+	    if (eval_method(arg, rettv, evaluate, verbose) == FAIL)
+	    {
+		clear_tv(rettv);
+		ret = FAIL;
+	    }
+	}
 	else /* **arg == '[' || **arg == '.' */
 	{
 	    dict_unref(selfdict);