changeset 117:d4ea645c7748 v7.0041

updated for version 7.0041
author vimboss
date Mon, 17 Jan 2005 22:23:00 +0000
parents 973ccf576e2c
children 45fd0ec37cf3
files src/eval.c
diffstat 1 files changed, 620 insertions(+), 264 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -170,9 +170,10 @@ typedef struct lval_S
     long	ll_n1;		/* First index for list */
     long	ll_n2;		/* Second index for list range */
     int		ll_empty2;	/* Second index is empty: [i:] */
-    char_u	*ll_newkey;	/* New key for Dict in alloc. mem or NULL. */
+    dictvar	*ll_dict;	/* The Dictionary or NULL */
     dictitem	*ll_di;		/* The dictitem or NULL */
-    dictitem	**ll_pdi;	/* field that points to found dictitem */
+    char_u	*ll_newkey;	/* New key for Dict in alloc. mem or NULL. */
+    dictitem	**ll_pdi;	/* di_next field pointing to found dictitem */
 } lval;
 
 
@@ -183,11 +184,16 @@ static char *e_missbrac = N_("E111: Miss
 static char *e_intern2 = N_("E685: Internal error: %s");
 static char *e_listarg = N_("E686: Argument of %s must be a List");
 static char *e_listdictarg = N_("E712: Argument of %s must be a List or Dictionaary");
-static char *e_emptykey = N_("E999: Empty key in Dictionary");
-static char *e_listreq = N_("E999: List required");
-static char *e_dictreq = N_("E999: Dictionary required");
+static char *e_emptykey = N_("E713: Empty key in Dictionary");
+static char *e_listreq = N_("E714: List required");
+static char *e_dictreq = N_("E715: Dictionary required");
 static char *e_toomanyarg = N_("E118: Too many arguments for function: %s");
-static char *e_dictkey = N_("E999: key not found in Dictionary: %s");
+static char *e_dictkey = N_("E716: Key not present in Dictionary: %s");
+static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
+static char *e_funcdict = N_("E717: Dictionary entry already exists");
+static char *e_funcref = N_("E718: Funcref required");
+static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
+static char *e_letwrong = N_("E734: Wrong variable type for %s=");
 
 /*
  * All user-defined global variables are stored in "variables".
@@ -208,6 +214,10 @@ static garray_T	    ga_scripts = {0, 0, 
 
 static int echo_attr = 0;   /* attributes used for ":echo" */
 
+/* Values for trans_function_name() argument: */
+#define TFN_INT		1	/* internal function name OK */
+#define TFN_QUIET	2	/* no error messages */
+
 /*
  * Structure to hold info for a user function.
  */
@@ -225,6 +235,7 @@ struct ufunc
     garray_T	lines;		/* function lines */
     scid_T	script_ID;	/* ID of script where function was defined,
 				   used for s: variables */
+    int		refcount;	/* for numbered function: reference count */
 };
 
 /* function flags */
@@ -271,6 +282,17 @@ typedef struct forinfo_S
 } forinfo;
 
 /*
+ * Struct used by trans_function_name()
+ */
+typedef struct
+{
+    dictvar	*fd_dict;	/* Dictionary used */
+    char_u	*fd_newkey;	/* new key in "dict" */
+    dictitem	*fd_di;		/* Dictionary item used */
+    dictitem	**fd_pdi;	/* field that points to "fd_di" */
+} funcdict;
+
+/*
  * Return the name of the executed function.
  */
     char_u *
@@ -345,43 +367,44 @@ struct vimvar
 {
     /*
      * The order here must match the VV_ defines in vim.h!
+     * Initializing a union does not work, leave tv.vval empty to get zero's.
      */
-    {VV_NAME("count"),		  {VAR_NUMBER, {NULL}}, VV_COMPAT+VV_RO},
-    {VV_NAME("count1"),		  {VAR_NUMBER, {NULL}}, VV_RO},
-    {VV_NAME("prevcount"),	  {VAR_NUMBER, {NULL}}, VV_RO},
-    {VV_NAME("errmsg"),		  {VAR_STRING, {NULL}}, VV_COMPAT},
-    {VV_NAME("warningmsg"),	  {VAR_STRING, {NULL}}, 0},
-    {VV_NAME("statusmsg"),	  {VAR_STRING, {NULL}}, 0},
-    {VV_NAME("shell_error"),	  {VAR_NUMBER, {NULL}}, VV_COMPAT+VV_RO},
-    {VV_NAME("this_session"),	  {VAR_STRING, {NULL}}, VV_COMPAT},
-    {VV_NAME("version"),	  {VAR_NUMBER, {VIM_VERSION_100}}, VV_COMPAT+VV_RO},
-    {VV_NAME("lnum"),		  {VAR_NUMBER, {NULL}}, VV_RO_SBX},
-    {VV_NAME("termresponse"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("fname"),		  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("lang"),		  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("lc_time"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("ctype"),		  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("charconvert_from"), {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("charconvert_to"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("fname_in"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("fname_out"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("fname_new"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("fname_diff"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("cmdarg"),		  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("foldstart"),	  {VAR_NUMBER, {NULL}}, VV_RO_SBX},
-    {VV_NAME("foldend"),	  {VAR_NUMBER, {NULL}}, VV_RO_SBX},
-    {VV_NAME("folddashes"),	  {VAR_STRING, {NULL}}, VV_RO_SBX},
-    {VV_NAME("foldlevel"),	  {VAR_NUMBER, {NULL}}, VV_RO_SBX},
-    {VV_NAME("progname"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("servername"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("dying"),		  {VAR_NUMBER, {NULL}}, VV_RO},
-    {VV_NAME("exception"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("throwpoint"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("register"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("cmdbang"),	  {VAR_NUMBER, {NULL}}, VV_RO},
-    {VV_NAME("insertmode"),	  {VAR_STRING, {NULL}}, VV_RO},
-    {VV_NAME("val"),		  {VAR_UNKNOWN, {NULL}}, VV_RO},
-    {VV_NAME("key"),		  {VAR_UNKNOWN, {NULL}}, VV_RO},
+    {VV_NAME("count"),		  {VAR_NUMBER}, VV_COMPAT+VV_RO},
+    {VV_NAME("count1"),		  {VAR_NUMBER}, VV_RO},
+    {VV_NAME("prevcount"),	  {VAR_NUMBER}, VV_RO},
+    {VV_NAME("errmsg"),		  {VAR_STRING}, VV_COMPAT},
+    {VV_NAME("warningmsg"),	  {VAR_STRING}, 0},
+    {VV_NAME("statusmsg"),	  {VAR_STRING}, 0},
+    {VV_NAME("shell_error"),	  {VAR_NUMBER}, VV_COMPAT+VV_RO},
+    {VV_NAME("this_session"),	  {VAR_STRING}, VV_COMPAT},
+    {VV_NAME("version"),	  {VAR_NUMBER}, VV_COMPAT+VV_RO},
+    {VV_NAME("lnum"),		  {VAR_NUMBER}, VV_RO_SBX},
+    {VV_NAME("termresponse"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("fname"),		  {VAR_STRING}, VV_RO},
+    {VV_NAME("lang"),		  {VAR_STRING}, VV_RO},
+    {VV_NAME("lc_time"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("ctype"),		  {VAR_STRING}, VV_RO},
+    {VV_NAME("charconvert_from"), {VAR_STRING}, VV_RO},
+    {VV_NAME("charconvert_to"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("fname_in"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("fname_out"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("fname_new"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("fname_diff"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("cmdarg"),		  {VAR_STRING}, VV_RO},
+    {VV_NAME("foldstart"),	  {VAR_NUMBER}, VV_RO_SBX},
+    {VV_NAME("foldend"),	  {VAR_NUMBER}, VV_RO_SBX},
+    {VV_NAME("folddashes"),	  {VAR_STRING}, VV_RO_SBX},
+    {VV_NAME("foldlevel"),	  {VAR_NUMBER}, VV_RO_SBX},
+    {VV_NAME("progname"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("servername"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("dying"),		  {VAR_NUMBER}, VV_RO},
+    {VV_NAME("exception"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("throwpoint"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("register"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("cmdbang"),	  {VAR_NUMBER}, VV_RO},
+    {VV_NAME("insertmode"),	  {VAR_STRING}, VV_RO},
+    {VV_NAME("val"),		  {VAR_UNKNOWN}, VV_RO},
+    {VV_NAME("key"),		  {VAR_UNKNOWN}, VV_RO},
 };
 
 /* shorthand */
@@ -409,6 +432,7 @@ static void listitem_free __ARGS((listit
 static void listitem_remove __ARGS((listvar *l, listitem *item));
 static long list_len __ARGS((listvar *l));
 static int list_equal __ARGS((listvar *l1, listvar *l2, int ic));
+static int dict_equal __ARGS((dictvar *d1, dictvar *d2, int ic));
 static int tv_equal __ARGS((typeval *tv1, typeval *tv2, int ic));
 static int string_isa_number __ARGS((char_u *s));
 static listitem *list_find __ARGS((listvar *l, long n));
@@ -431,6 +455,7 @@ static dictitem *dictitem_alloc __ARGS((
 static dictitem *dictitem_copy __ARGS((dictitem *org));
 static void dictitem_free __ARGS((dictitem *item));
 static void dict_add __ARGS((dictvar *d, dictitem *item));
+static long dict_len __ARGS((dictvar *d));
 static dictitem *dict_find __ARGS((dictvar *d, char_u *key, int len, dictitem ***pdi));
 static char_u *dict2string __ARGS((typeval *tv));
 static int get_dict_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
@@ -640,13 +665,16 @@ static void set_var __ARGS((char_u *name
 static void copy_tv __ARGS((typeval *from, typeval *to));
 static void item_copy __ARGS((typeval *from, typeval *to, int deep));
 static char_u *find_option_end __ARGS((char_u **arg, int *opt_flags));
-static char_u *trans_function_name __ARGS((char_u **pp, int skip, int exists));
+static char_u *trans_function_name __ARGS((char_u **pp, int skip, int flags, funcdict *fd));
 static int eval_fname_script __ARGS((char_u *p));
 static int eval_fname_sid __ARGS((char_u *p));
 static void list_func_head __ARGS((ufunc_T *fp, int indent));
 static void cat_func_name __ARGS((char_u *buf, ufunc_T *fp));
 static ufunc_T *find_func __ARGS((char_u *name));
 static int function_exists __ARGS((char_u *name));
+static void func_free __ARGS((ufunc_T *fp));
+static void func_unref __ARGS((char_u *name));
+static void func_ref __ARGS((char_u *name));
 static void call_user_func __ARGS((ufunc_T *fp, int argcount, typeval *argvars, typeval *rettv, linenr_T firstline, linenr_T lastline, dictvar *selfdict));
 
 #define get_var_string(p)	 get_tv_string(&(p)->tv)
@@ -660,11 +688,12 @@ static char_u *skip_var_list __ARGS((cha
 static char_u *skip_var_one __ARGS((char_u *arg));
 static void list_all_vars __ARGS((void));
 static char_u *list_arg_vars __ARGS((exarg_T *eap, char_u *arg));
-static char_u *ex_let_one __ARGS((char_u *arg, typeval *tv, int copy, char_u *endchars));
+static char_u *ex_let_one __ARGS((char_u *arg, typeval *tv, int copy, char_u *endchars, char_u *op));
 static int check_changedtick __ARGS((char_u *arg));
 static char_u *get_lval __ARGS((char_u *name, typeval *rettv, lval *lp, int unlet, int skip, int quiet));
 static void clear_lval __ARGS((lval *lp));
-static void set_var_lval __ARGS((lval *lp, char_u *endp, typeval *rettv, int copy));
+static void set_var_lval __ARGS((lval *lp, char_u *endp, typeval *rettv, int copy, char_u *op));
+static int tv_op __ARGS((typeval *tv1, typeval *tv2, char_u  *op));
 static void list_add_watch __ARGS((listvar *l, listwatch *lw));
 static void list_rem_watch __ARGS((listvar *l, listwatch *lwrem));
 static void list_fix_watch __ARGS((listvar *l, listitem *item));
@@ -1170,6 +1199,9 @@ make_expanded_name(in_start, expr_start,
  * ":let"			list all variable values
  * ":let var1 var2"		list variable values
  * ":let var = expr"		assignment command.
+ * ":let var += expr"		assignment command.
+ * ":let var -= expr"		assignment command.
+ * ":let var .= expr"		assignment command.
  * ":let [var1, var2] = expr"	unpack list.
  */
     void
@@ -1182,6 +1214,7 @@ ex_let(eap)
     int		i;
     int		var_count = 0;
     int		semicolon = 0;
+    char_u	op[2];
 
     expr = skip_var_list(arg, &var_count, &semicolon);
     if (expr == NULL)
@@ -1201,6 +1234,13 @@ ex_let(eap)
     }
     else
     {
+	op[0] = '=';
+	op[1] = NUL;
+	if (expr > arg)
+	{
+	    if (vim_strchr((char_u *)"+-.", expr[-1]) != NULL)
+		op[0] = expr[-1];   /* +=, -= or .= */
+	}
 	expr = skipwhite(expr + 1);
 
 	if (eap->skip)
@@ -1215,7 +1255,7 @@ ex_let(eap)
 	else if (i != FAIL)
 	{
 	    (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
-							       (char_u *)"=");
+									  op);
 	    clear_tv(&rettv);
 	}
     }
@@ -1224,6 +1264,9 @@ ex_let(eap)
 /*
  * Assign the typevalue "tv" to the variable or variables at "arg_start".
  * Handles both "var" with any type and "[var, var; var]" with a list type.
+ * When "nextchars" is not NULL it points to a string with characters that
+ * must appear after the variable(s).  Use "+", "-" or "." for add, subtract
+ * or concatenate.
  * Returns OK or FAIL;
  */
     static int
@@ -1233,7 +1276,7 @@ ex_let_vars(arg_start, tv, copy, semicol
     int		copy;		/* copy values from "tv", don't move */
     int		semicolon;	/* from skip_var_list() */
     int		var_count;	/* from skip_var_list() */
-    char_u	*nextchars;	/* characters that must follow or NULL */
+    char_u	*nextchars;
 {
     char_u	*arg = arg_start;
     listvar	*l;
@@ -1246,7 +1289,7 @@ ex_let_vars(arg_start, tv, copy, semicol
 	/*
 	 * ":let var = expr" or ":for var in list"
 	 */
-	if (ex_let_one(arg, tv, copy, nextchars) == NULL)
+	if (ex_let_one(arg, tv, copy, nextchars, nextchars) == NULL)
 	    return FAIL;
 	return OK;
     }
@@ -1277,7 +1320,7 @@ ex_let_vars(arg_start, tv, copy, semicol
     while (*arg != ']')
     {
 	arg = skipwhite(arg + 1);
-	arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]");
+	arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars);
 	item = item->li_next;
 	if (arg == NULL)
 	    return FAIL;
@@ -1300,7 +1343,8 @@ ex_let_vars(arg_start, tv, copy, semicol
 	    ltv.vval.v_list = l;
 	    l->lv_refcount = 1;
 
-	    arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, (char_u *)"]");
+	    arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE,
+						    (char_u *)"]", nextchars);
 	    clear_tv(&ltv);
 	    if (arg == NULL)
 		return FAIL;
@@ -1524,11 +1568,12 @@ list_arg_vars(eap, arg)
  * Returns NULL if there is an error.
  */
     static char_u *
-ex_let_one(arg, tv, copy, endchars)
+ex_let_one(arg, tv, copy, endchars, op)
     char_u	*arg;		/* points to variable name */
     typeval	*tv;		/* value to assign to variable */
     int		copy;		/* copy value from "tv" */
     char_u	*endchars;	/* valid chars after variable name  or NULL */
+    char_u	*op;		/* "+", "-", "."  or NULL*/
 {
     int		c1;
     char_u	*name;
@@ -1536,6 +1581,7 @@ ex_let_one(arg, tv, copy, endchars)
     char_u	*arg_end = NULL;
     int		len;
     int		opt_flags;
+    char_u	*tofree = NULL;
 
     /*
      * ":let $VAR = expr": Set environment variable.
@@ -1550,7 +1596,9 @@ ex_let_one(arg, tv, copy, endchars)
 	    EMSG2(_(e_invarg2), name - 1);
 	else
 	{
-	    if (endchars != NULL
+	    if (op != NULL && (*op == '+' || *op == '-'))
+		EMSG2(_(e_letwrong), op);
+	    else if (endchars != NULL
 			     && vim_strchr(endchars, *skipwhite(arg)) == NULL)
 		EMSG(_(e_letunexp));
 	    else
@@ -1558,7 +1606,20 @@ ex_let_one(arg, tv, copy, endchars)
 		c1 = name[len];
 		name[len] = NUL;
 		p = get_tv_string(tv);
-		vim_setenv(name, p);
+		if (op != NULL && *op == '.')
+		{
+		    int	    mustfree = FALSE;
+		    char_u  *s = vim_getenv(name, &mustfree);
+
+		    if (s != NULL)
+		    {
+			p = tofree = concat_str(s, p);
+			if (mustfree)
+			    vim_free(s);
+		    }
+		}
+		if (p != NULL)
+		    vim_setenv(name, p);
 		if (STRICMP(name, "HOME") == 0)
 		    init_homedir();
 		else if (didset_vim && STRICMP(name, "VIM") == 0)
@@ -1567,6 +1628,7 @@ ex_let_one(arg, tv, copy, endchars)
 		    didset_vimruntime = FALSE;
 		name[len] = c1;
 		arg_end = arg;
+		vim_free(tofree);
 	    }
 	}
     }
@@ -1585,12 +1647,45 @@ ex_let_one(arg, tv, copy, endchars)
 	    EMSG(_(e_letunexp));
 	else
 	{
+	    long	n;
+	    int		opt_type;
+	    long	numval;
+	    char_u	*stringval = NULL;
+	    char_u	*s;
+
 	    c1 = *p;
 	    *p = NUL;
-	    set_option_value(arg, get_tv_number(tv),
-				  get_tv_string(tv), opt_flags);
+
+	    n = get_tv_number(tv);
+	    s = get_tv_string(tv);
+	    if (op != NULL && *op != '=')
+	    {
+		opt_type = get_option_value(arg, &numval,
+						       &stringval, opt_flags);
+		if ((opt_type == 1 && *op == '.')
+			|| (opt_type == 0 && *op != '.'))
+		    EMSG2(_(e_letwrong), op);
+		else
+		{
+		    if (opt_type == 1)  /* number */
+		    {
+			if (*op == '+')
+			    n = numval + n;
+			else
+			    n = numval - n;
+		    }
+		    else if (opt_type == 0 && stringval != NULL) /* string */
+		    {
+			s = concat_str(stringval, s);
+			vim_free(stringval);
+			stringval = s;
+		    }
+		}
+	    }
+	    set_option_value(arg, n, s, opt_flags);
 	    *p = c1;
 	    arg_end = p;
+	    vim_free(stringval);
 	}
     }
 
@@ -1600,14 +1695,30 @@ ex_let_one(arg, tv, copy, endchars)
     else if (*arg == '@')
     {
 	++arg;
-	if (endchars != NULL
+	if (op != NULL && (*op == '+' || *op == '-'))
+	    EMSG2(_(e_letwrong), op);
+	else if (endchars != NULL
 			 && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL)
 	    EMSG(_(e_letunexp));
 	else
 	{
-	    write_reg_contents(*arg == '@' ? '"' : *arg,
-					     get_tv_string(tv), -1, FALSE);
+	    char_u	*tofree = NULL;
+	    char_u	*s;
+
+	    p = get_tv_string(tv);
+	    if (op != NULL && *op == '.')
+	    {
+		s = get_reg_contents(*arg == '@' ? '"' : *arg, FALSE);
+		if (s != NULL)
+		{
+		    p = tofree = concat_str(s, p);
+		    vim_free(s);
+		}
+	    }
+	    if (p != NULL)
+		write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE);
 	    arg_end = arg + 1;
+	    vim_free(tofree);
 	}
     }
 
@@ -1626,7 +1737,7 @@ ex_let_one(arg, tv, copy, endchars)
 		EMSG(_(e_letunexp));
 	    else
 	    {
-		set_var_lval(&lv, p, tv, copy);
+		set_var_lval(&lv, p, tv, copy, op);
 		arg_end = p;
 	    }
 	}
@@ -1796,7 +1907,7 @@ get_lval(name, rettv, lp, unlet, skip, q
 		if (lp->ll_tv->v_type == VAR_DICT)
 		{
 		    if (!quiet)
-			EMSG(_("E999: Cannot use [:] with a Dictionary"));
+			EMSG(_(e_dictrange));
 		    if (!empty1)
 			clear_tv(&var1);
 		    return NULL;
@@ -1857,16 +1968,16 @@ get_lval(name, rettv, lp, unlet, skip, q
 		    return NULL;
 		}
 	    }
-	    lp->ll_di = dict_find(lp->ll_tv->vval.v_dict, key, len,
-								 &lp->ll_pdi);
+	    lp->ll_list = NULL;
+	    lp->ll_dict = lp->ll_tv->vval.v_dict;
+	    lp->ll_di = dict_find(lp->ll_dict, key, len, &lp->ll_pdi);
 	    if (lp->ll_di == NULL)
 	    {
 		/* Key does not exist in dict: may need toadd it. */
 		if (*p == '[' || *p == '.' || unlet)
 		{
 		    if (!quiet)
-			EMSG2(_("E999: Key does not exist in Dictionary: %s"),
-									 key);
+			EMSG2(_(e_dictkey), key);
 		    if (len == -1)
 			clear_tv(&var1);
 		    return NULL;
@@ -1897,6 +2008,7 @@ get_lval(name, rettv, lp, unlet, skip, q
 		lp->ll_n1 = get_tv_number(&var1);
 		clear_tv(&var1);
 	    }
+	    lp->ll_dict = NULL;
 	    lp->ll_list = lp->ll_tv->vval.v_list;
 	    lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
 	    if (lp->ll_li == NULL)
@@ -1962,13 +2074,15 @@ clear_lval(lp)
 /*
  * Set a variable that was parsed by get_lval().
  * "endp" points to just after the parsed name.
- */
-    static void
-set_var_lval(lp, endp, rettv, copy)
+ * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=".
+ */
+    static void
+set_var_lval(lp, endp, rettv, copy, op)
     lval	*lp;
     char_u	*endp;
     typeval	*rettv;
     int		copy;
+    char_u	*op;
 {
     int		cc;
     listitem	*ni;
@@ -1981,7 +2095,19 @@ set_var_lval(lp, endp, rettv, copy)
 	{
 	    cc = *endp;
 	    *endp = NUL;
-	    set_var(lp->ll_name, rettv, copy);
+	    if (op != NULL && *op != '=')
+	    {
+		typeval tv;
+
+		if (get_var_tv(lp->ll_name, STRLEN(lp->ll_name), &tv) == OK)
+		{
+		    if (tv_op(&tv, rettv, op) == OK)
+			set_var(lp->ll_name, &tv, FALSE);
+		    clear_tv(&tv);
+		}
+	    }
+	    else
+		set_var(lp->ll_name, rettv, copy);
 	    *endp = cc;
 	}
     }
@@ -1992,8 +2118,13 @@ set_var_lval(lp, endp, rettv, copy)
 	 */
 	for (ri = rettv->vval.v_list->lv_first; ri != NULL; )
 	{
-	    clear_tv(&lp->ll_li->li_tv);
-	    copy_tv(&ri->li_tv, &lp->ll_li->li_tv);
+	    if (op != NULL && *op != '=')
+		tv_op(&lp->ll_li->li_tv, &ri->li_tv, op);
+	    else
+	    {
+		clear_tv(&lp->ll_li->li_tv);
+		copy_tv(&ri->li_tv, &lp->ll_li->li_tv);
+	    }
 	    ri = ri->li_next;
 	    if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1))
 		break;
@@ -2015,7 +2146,8 @@ set_var_lval(lp, endp, rettv, copy)
 	}
 	if (ri != NULL)
 	    EMSG(_("E710: List value has more items than target"));
-	else if (lp->ll_empty2 ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL)
+	else if (lp->ll_empty2
+		? (lp->ll_li != NULL && lp->ll_li->li_next != NULL)
 		: lp->ll_n1 != lp->ll_n2)
 	    EMSG(_("E711: List value has not enough items"));
     }
@@ -2026,6 +2158,12 @@ set_var_lval(lp, endp, rettv, copy)
 	 */
 	if (lp->ll_newkey != NULL)
 	{
+	    if (op != NULL && *op != '=')
+	    {
+		EMSG2(_(e_letwrong), op);
+		return;
+	    }
+
 	    /* Need to add an item to the Dictionary. */
 	    di = dictitem_alloc();
 	    if (di == NULL)
@@ -2035,6 +2173,11 @@ set_var_lval(lp, endp, rettv, copy)
 	    dict_add(lp->ll_tv->vval.v_dict, di);
 	    lp->ll_tv = &di->di_tv;
 	}
+	else if (op != NULL && *op != '=')
+	{
+	    tv_op(lp->ll_tv, rettv, op);
+	    return;
+	}
 	else
 	    clear_tv(lp->ll_tv);
 
@@ -2052,6 +2195,70 @@ set_var_lval(lp, endp, rettv, copy)
 }
 
 /*
+ * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2"
+ * Returns OK or FAIL.
+ */
+    static int
+tv_op(tv1, tv2, op)
+    typeval *tv1;
+    typeval *tv2;
+    char_u  *op;
+{
+    long	n;
+    char_u	numbuf[NUMBUFLEN];
+    char_u	*s;
+
+    /* Can't do anything with a Funcref or a Dict on the right. */
+    if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT)
+    {
+	switch (tv1->v_type)
+	{
+	    case VAR_DICT:
+	    case VAR_FUNC:
+		break;
+
+	    case VAR_LIST:
+		if (*op != '+' || tv2->v_type != VAR_LIST)
+		    break;
+		/* List += List */
+		if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL)
+		    list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
+		return OK;
+
+	    case VAR_NUMBER:
+	    case VAR_STRING:
+		if (tv2->v_type == VAR_LIST)
+		    break;
+		if (*op == '+' || *op == '-')
+		{
+		    /* nr += nr  or  nr -= nr*/
+		    n = get_tv_number(tv1);
+		    if (*op == '+')
+			n += get_tv_number(tv2);
+		    else
+			n -= get_tv_number(tv2);
+		    clear_tv(tv1);
+		    tv1->v_type = VAR_NUMBER;
+		    tv1->vval.v_number = n;
+		}
+		else
+		{
+		    /* str .= str */
+		    s = get_tv_string(tv1);
+		    s = concat_str(s, get_tv_string_buf(tv2, numbuf));
+		    clear_tv(tv1);
+		    tv1->v_type = VAR_STRING;
+		    tv1->vval.v_string = s;
+		}
+		return OK;
+	}
+    }
+
+    EMSG2(_(e_letwrong), op);
+    return FAIL;
+}
+
+/*
  * Add a watcher to a list.
  */
     static void
@@ -2318,20 +2525,21 @@ ex_call(eap)
 {
     char_u	*arg = eap->arg;
     char_u	*startarg;
-    char_u	*alias;
     char_u	*name;
+    char_u	*tofree;
+    int		len;
     typeval	rettv;
-    int		len;
     linenr_T	lnum;
     int		doesrange;
     int		failed = FALSE;
 
-    name = arg;
-    len = get_func_len(&arg, &alias, !eap->skip);
-    if (len == 0)
-	goto end;
-    if (alias != NULL)
-	name = alias;
+    tofree = trans_function_name(&arg, eap->skip, TFN_INT, NULL);
+    if (tofree == NULL)
+	return;
+
+    /* If it is the name of a variable of type VAR_FUNC use its contents. */
+    len = STRLEN(tofree);
+    name = deref_func_name(tofree, &len);
 
     startarg = arg;
     rettv.v_type = VAR_UNKNOWN;	/* clear_tv() uses this */
@@ -2363,8 +2571,8 @@ ex_call(eap)
 	    curwin->w_cursor.col = 0;
 	}
 	arg = startarg;
-	if (get_func_tv(name, len, &rettv, &arg, eap->line1, eap->line2,
-					&doesrange, !eap->skip, NULL) == FAIL)
+	if (get_func_tv(name, STRLEN(name), &rettv, &arg,
+		eap->line1, eap->line2, &doesrange, !eap->skip, NULL) == FAIL)
 	{
 	    failed = TRUE;
 	    break;
@@ -2395,8 +2603,7 @@ ex_call(eap)
     }
 
 end:
-    if (alias != NULL)
-	vim_free(alias);
+    vim_free(tofree);
 }
 
 /*
@@ -3053,6 +3260,35 @@ eval4(arg, rettv, evaluate)
 		}
 	    }
 
+	    else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT)
+	    {
+		if (type_is)
+		{
+		    n1 = (rettv->v_type == var2.v_type
+				   && rettv->vval.v_dict == var2.vval.v_dict);
+		    if (type == TYPE_NEQUAL)
+			n1 = !n1;
+		}
+		else if (rettv->v_type != var2.v_type
+			|| (type != TYPE_EQUAL && type != TYPE_NEQUAL))
+		{
+		    if (rettv->v_type != var2.v_type)
+			EMSG(_("E735: Can only compare Dictionary with Dictionary"));
+		    else
+			EMSG(_("E736: Invalid operation for Dictionary"));
+		    clear_tv(rettv);
+		    clear_tv(&var2);
+		    return FAIL;
+		}
+		else
+		{
+		    /* Compare two Dictionaries for being equal or unequal. */
+		    n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, ic);
+		    if (type == TYPE_NEQUAL)
+			n1 = !n1;
+		}
+	    }
+
 	    else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC)
 	    {
 		if (rettv->v_type != var2.v_type
@@ -3210,13 +3446,7 @@ eval5(arg, rettv, evaluate)
 	    {
 		s1 = get_tv_string_buf(rettv, buf1);
 		s2 = get_tv_string_buf(&var2, buf2);
-		op = (int)STRLEN(s1);
-		p = alloc((unsigned)(op + STRLEN(s2) + 1));
-		if (p != NULL)
-		{
-		    STRCPY(p, s1);
-		    STRCPY(p + op, s2);
-		}
+		p = concat_str(s1, s2);
 		clear_tv(rettv);
 		rettv->v_type = VAR_STRING;
 		rettv->vval.v_string = p;
@@ -3518,6 +3748,8 @@ eval7(arg, rettv, evaluate)
 	    }
 	    else if (evaluate)
 		ret = get_var_tv(s, len, rettv);
+	    else
+		ret = OK;
 	}
 
 	if (alias != NULL)
@@ -3782,7 +4014,7 @@ eval_index(arg, rettv, evaluate)
 	    case VAR_DICT:
 		if (range)
 		{
-		    EMSG(_("E999: Using range with Dictionary"));
+		    EMSG(_(e_dictrange));
 		    if (len == -1)
 			clear_tv(&var1);
 		    return FAIL;
@@ -3795,7 +4027,7 @@ eval_index(arg, rettv, evaluate)
 			key = get_tv_string(&var1);
 			if (*key == NUL)
 			{
-			    EMSG(_("E999: Empty key for Dictionary"));
+			    EMSG(_(e_emptykey));
 			    clear_tv(&var1);
 			    return FAIL;
 			}
@@ -3804,7 +4036,7 @@ eval_index(arg, rettv, evaluate)
 		    item = dict_find(rettv->vval.v_dict, key, (int)len, NULL);
 
 		    if (item == NULL)
-			EMSG2(_("E999: Key not found in Dictionary: %s"), key);
+			EMSG2(_(e_dictkey), key);
 		    if (len == -1)
 			clear_tv(&var1);
 		    if (item == NULL)
@@ -4271,6 +4503,9 @@ list_equal(l1, l2, ic)
 {
     listitem	*item1, *item2;
 
+    if (list_len(l1) != list_len(l2))
+	return FALSE;
+
     for (item1 = l1->lv_first, item2 = l2->lv_first;
 	    item1 != NULL && item2 != NULL;
 			       item1 = item1->li_next, item2 = item2->li_next)
@@ -4280,6 +4515,31 @@ list_equal(l1, l2, ic)
 }
 
 /*
+ * Return TRUE when two dictionaries have exactly the same key/values.
+ */
+    static int
+dict_equal(d1, d2, ic)
+    dictvar	*d1;
+    dictvar	*d2;
+    int		ic;	/* ignore case for strings */
+{
+    dictitem	*item1, *item2;
+
+    if (dict_len(d1) != dict_len(d2))
+	return FALSE;
+
+    for (item1 = d1->dv_first; item1 != NULL; item1 = item1->di_next)
+    {
+	item2 = dict_find(d2, item1->di_key, -1, NULL);
+	if (item2 == NULL)
+	    return FALSE;
+	if (!tv_equal(&item1->di_tv, &item2->di_tv, ic))
+	    return FALSE;
+    }
+    return TRUE;
+}
+
+/*
  * Return TRUE if "tv1" and "tv2" have the same value.
  * Compares the items just like "==" would compare them.
  */
@@ -4994,7 +5254,7 @@ get_dict_tv(arg, rettv, evaluate)
 	    goto failret;
 	if (**arg != ':')
 	{
-	    EMSG2(_("E999: Missing colon in Dictionary: %s"), *arg);
+	    EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg);
 	    clear_tv(&tv);
 	    goto failret;
 	}
@@ -5021,7 +5281,7 @@ get_dict_tv(arg, rettv, evaluate)
 	    item = dict_find(d, key, -1, NULL);
 	    if (item != NULL)
 	    {
-		EMSG(_("E999: Duplicate key in Dictionary"));
+		EMSG(_("E721: Duplicate key in Dictionary"));
 		vim_free(key);
 		clear_tv(&tv);
 		goto failret;
@@ -5041,7 +5301,7 @@ get_dict_tv(arg, rettv, evaluate)
 	    break;
 	if (**arg != ',')
 	{
-	    EMSG2(_("E999: Missing comma in Dictionary: %s"), *arg);
+	    EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg);
 	    goto failret;
 	}
 	*arg = skipwhite(*arg + 1);
@@ -5049,7 +5309,7 @@ get_dict_tv(arg, rettv, evaluate)
 
     if (**arg != '}')
     {
-	EMSG2(_("E999: Missing end of Dictionary '}': %s"), *arg);
+	EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
 failret:
 	if (evaluate)
 	    dict_free(d);
@@ -5067,7 +5327,6 @@ failret:
     return OK;
 }
 
-
 /*
  * Return a string with the string representation of a variable.
  * If the memory is allocated "tofree" is set to it, otherwise NULL.
@@ -5086,7 +5345,7 @@ echo_string(tv, tofree, numbuf)
 
     if (recurse >= VAR_MAXNEST)
     {
-	EMSG(_("E999: variable nested too deep for displaying"));
+	EMSG(_("E724: variable nested too deep for displaying"));
 	*tofree = NULL;
 	return NULL;
     }
@@ -5735,7 +5994,10 @@ call_func(name, len, rettv, argcount, ar
 		    call_user_func(fp, argcount, argvars, rettv,
 					       firstline, lastline,
 				     (fp->flags & FC_DICT) ? selfdict : NULL);
-		    --fp->calls;
+		    if (--fp->calls <= 0 && isdigit(*fp->name))
+			/* Function was unreferenced while being used, free it
+			 * now. */
+			func_free(fp);
 		    restoreRedobuff();
 		    restore_search_patterns();
 		    error = ERROR_NONE;
@@ -5800,7 +6062,7 @@ call_func(name, len, rettv, argcount, ar
 									name);
 		    break;
 	    case ERROR_DICT:
-		    EMSG2(_("E999: Calling dict function without Dictionary: %s"),
+		    EMSG2(_("E725: Calling dict function without Dictionary: %s"),
 									name);
 		    break;
 	}
@@ -9944,9 +10206,9 @@ f_range(argvars, rettv)
 
     rettv->vval.v_number = 0;
     if (stride == 0)
-	EMSG(_("E999: Stride is zero"));
+	EMSG(_("E726: Stride is zero"));
     else if (stride > 0 ? end < start : end > start)
-	EMSG(_("E999: Start past end"));
+	EMSG(_("E727: Start past end"));
     else
     {
 	l = list_alloc();
@@ -10440,7 +10702,7 @@ f_resolve(argvars, rettv)
 			remain = vim_strsave(q - 1);
 		    else
 		    {
-			cpy = vim_strnsave(q-1, STRLEN(q-1)+STRLEN(remain));
+			cpy = vim_strnsave(q-1, STRLEN(q-1) + STRLEN(remain));
 			if (cpy != NULL)
 			{
 			    STRCAT(cpy, remain);
@@ -10514,10 +10776,9 @@ f_resolve(argvars, rettv)
 				    || vim_ispathsep(p[2]))))))
 	    {
 		/* Prepend "./". */
-		cpy = vim_strnsave((char_u *)"./", 2 + STRLEN(p));
+		cpy = concat_str((char_u *)"./", p);
 		if (cpy != NULL)
 		{
-		    STRCAT(cpy, p);
 		    vim_free(p);
 		    p = cpy;
 		}
@@ -12603,6 +12864,10 @@ set_vim_var_string(idx, val, len)
     char_u	*val;
     int		len;	    /* length of "val" to use or -1 (whole string) */
 {
+    /* Need to do this (at least) once, since we can't initialize a union.
+     * Will always be invoked when "v:progname" is set. */
+    vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
+
     vim_free(vimvars[idx].vv_str);
     if (val == NULL)
 	vimvars[idx].vv_str = NULL;
@@ -12830,13 +13095,18 @@ free_tv(varp)
     {
 	switch (varp->v_type)
 	{
+	    case VAR_FUNC:
+		func_unref(varp->vval.v_string);
+		/*FALLTHROUGH*/
 	    case VAR_STRING:
-	    case VAR_FUNC:
 		vim_free(varp->vval.v_string);
 		break;
 	    case VAR_LIST:
 		list_unref(varp->vval.v_list);
 		break;
+	    case VAR_DICT:
+		dict_unref(varp->vval.v_dict);
+		break;
 	    default:
 		break;
 	}
@@ -12855,8 +13125,10 @@ clear_tv(varp)
     {
 	switch (varp->v_type)
 	{
+	    case VAR_FUNC:
+		func_unref(varp->vval.v_string);
+		/*FALLTHROUGH*/
 	    case VAR_STRING:
-	    case VAR_FUNC:
 		vim_free(varp->vval.v_string);
 		varp->vval.v_string = NULL;
 		break;
@@ -12914,7 +13186,11 @@ get_tv_number(varp)
 	case VAR_LIST:
 	    EMSG(_("E703: Using a List as a number"));
 	    break;
+	case VAR_DICT:
+	    EMSG(_("E728: Using a Dictionary as a number"));
+	    break;
 	default:
+	    EMSG2(_(e_intern2), "get_tv_number()");
 	    break;
     }
     return n;
@@ -12969,13 +13245,13 @@ get_tv_string_buf(varp, buf)
 	    sprintf((char *)buf, "%ld", (long)varp->vval.v_number);
 	    return buf;
 	case VAR_FUNC:
-	    EMSG(_("E999: using Funcref as a String"));
+	    EMSG(_("E729: using Funcref as a String"));
 	    break;
 	case VAR_LIST:
-	    EMSG(_("E999: using List as a String"));
+	    EMSG(_("E730: using List as a String"));
 	    break;
 	case VAR_DICT:
-	    EMSG(_("E999: using Dictionary as a String"));
+	    EMSG(_("E731: using Dictionary as a String"));
 	    break;
 	case VAR_STRING:
 	    if (varp->vval.v_string != NULL)
@@ -13361,7 +13637,11 @@ copy_tv(from, to)
 	    if (from->vval.v_string == NULL)
 		to->vval.v_string = NULL;
 	    else
+	    {
 		to->vval.v_string = vim_strsave(from->vval.v_string);
+		if (from->v_type == VAR_FUNC)
+		    func_ref(to->vval.v_string);
+	    }
 	    break;
 	case VAR_LIST:
 	    if (from->vval.v_list == NULL)
@@ -13679,8 +13959,10 @@ ex_function(eap)
     int		indent;
     int		nesting;
     char_u	*skip_until = NULL;
-    static char_u e_funcexts[] = N_("E122: Function %s already exists, add ! to replace it");
     VAR		v;
+    funcdict	fudi;
+    static int	func_nr = 0;	    /* number for nameless function */
+    int		paren;
 
     /*
      * ":function" without argument: list functions.
@@ -13689,14 +13971,30 @@ ex_function(eap)
     {
 	if (!eap->skip)
 	    for (fp = firstfunc; fp != NULL && !got_int; fp = fp->next)
-		list_func_head(fp, FALSE);
+		if (!isdigit(*fp->name))
+		    list_func_head(fp, FALSE);
 	eap->nextcmd = check_nextcmd(eap->arg);
 	return;
     }
 
+    /*
+     * Get the function name.  There are these situations:
+     * func	    normal function name
+     *		    "name" == func, "fudi.fd_dict" == NULL
+     * dict.func    new dictionary entry
+     *		    "name" == NULL, "fudi.fd_dict" set,
+     *		    "fudi.fd_di" == NULL, "fudi.fd_newkey" == func
+     * dict.func    existing dict entry with a Funcref
+     *		    "name" == fname, "fudi.fd_dict" set,
+     *		    "fudi.fd_di" set, "fudi.fd_newkey" == NULL
+     * dict.func    existing dict entry that's not a Funcref
+     *		    "name" == NULL, "fudi.fd_dict" set,
+     *		    "fudi.fd_di" set, "fudi.fd_newkey" == NULL
+     */
     p = eap->arg;
-    name = trans_function_name(&p, eap->skip, FALSE);
-    if (name == NULL && !eap->skip)
+    name = trans_function_name(&p, eap->skip, 0, &fudi);
+    paren = (vim_strchr(p, '(') != NULL);
+    if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
     {
 	/*
 	 * Return on an invalid expression in braces, unless the expression
@@ -13704,7 +14002,10 @@ ex_function(eap)
 	 * interrupt, or an exception.
 	 */
 	if (!aborting())
+	{
+	    vim_free(fudi.fd_newkey);
 	    return;
+	}
 	else
 	    eap->skip = TRUE;
     }
@@ -13716,12 +14017,12 @@ ex_function(eap)
     /*
      * ":function func" with only function name: list function.
      */
-    if (vim_strchr(p, '(') == NULL)
+    if (!paren)
     {
 	if (!ends_excmd(*skipwhite(p)))
 	{
 	    EMSG(_(e_trailing));
-	    goto erret_name;
+	    goto ret_free;
 	}
 	eap->nextcmd = check_nextcmd(p);
 	if (eap->nextcmd != NULL)
@@ -13753,7 +14054,7 @@ ex_function(eap)
 	    else
 		EMSG2(_("E123: Undefined function: %s"), name);
 	}
-	goto erret_name;
+	goto ret_free;
     }
 
     /*
@@ -13765,7 +14066,7 @@ ex_function(eap)
 	if (!eap->skip)
 	{
 	    EMSG2(_("E124: Missing '(': %s"), eap->arg);
-	    goto erret_name;
+	    goto ret_free;
 	}
 	/* attempt to continue by skipping some text */
 	if (vim_strchr(p, '(') != NULL)
@@ -13859,8 +14160,13 @@ ex_function(eap)
 	/* Check if the function already exists, don't let the user type the
 	 * whole function before telling him it doesn't work!  For a script we
 	 * need to skip the body to be able to find what follows. */
-	if (!eap->skip && !eap->forceit && find_func(name) != NULL)
-	    EMSG2(_(e_funcexts), name);
+	if (!eap->skip && !eap->forceit)
+	{
+	    if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL)
+		EMSG(_(e_funcdict));
+	    else if (name != NULL && find_func(name) != NULL)
+		EMSG2(_(e_funcexts), name);
+	}
 
 	msg_putchar('\n');	    /* don't overwrite the function name */
 	cmdline_row = msg_row;
@@ -13925,7 +14231,7 @@ ex_function(eap)
 		p += eval_fname_script(p);
 		if (ASCII_ISALPHA(*p))
 		{
-		    vim_free(trans_function_name(&p, TRUE, FALSE));
+		    vim_free(trans_function_name(&p, TRUE, 0, NULL));
 		    if (*skipwhite(p) == '(')
 		    {
 			++nesting;
@@ -13981,40 +14287,90 @@ ex_function(eap)
     /*
      * If there are no errors, add the function
      */
-    v = find_var(name, FALSE);
-    if (v != NULL && v->tv.v_type == VAR_FUNC)
-    {
-	EMSG2(_("E707: Function name conflicts with variable: %s"), name);
-	goto erret;
-    }
-
-    fp = find_func(name);
-    if (fp != NULL)
-    {
-	if (!eap->forceit)
-	{
-	    EMSG2(_(e_funcexts), name);
+    if (fudi.fd_dict == NULL)
+    {
+	v = find_var(name, FALSE);
+	if (v != NULL && v->tv.v_type == VAR_FUNC)
+	{
+	    EMSG2(_("E707: Function name conflicts with variable: %s"), name);
 	    goto erret;
 	}
-	if (fp->calls)
-	{
-	    EMSG2(_("E127: Cannot redefine function %s: It is in use"), name);
+
+	fp = find_func(name);
+	if (fp != NULL)
+	{
+	    if (!eap->forceit)
+	    {
+		EMSG2(_(e_funcexts), name);
+		goto erret;
+	    }
+	    if (fp->calls > 0)
+	    {
+		EMSG2(_("E127: Cannot redefine function %s: It is in use"),
+									name);
+		goto erret;
+	    }
+	    /* redefine existing function */
+	    ga_clear_strings(&(fp->args));
+	    ga_clear_strings(&(fp->lines));
+	    vim_free(name);
+	    name = NULL;
+	}
+    }
+    else
+    {
+	char	numbuf[20];
+
+	fp = NULL;
+	if (fudi.fd_newkey == NULL && !eap->forceit)
+	{
+	    EMSG(_(e_funcdict));
 	    goto erret;
 	}
-	/* redefine existing function */
-	ga_clear_strings(&(fp->args));
-	ga_clear_strings(&(fp->lines));
+
+	/* Give the function a sequential number.  Can only be used with a
+	 * Funcref! */
 	vim_free(name);
-    }
-    else
+	sprintf(numbuf, "%d", ++func_nr);
+	name = vim_strsave((char_u *)numbuf);
+	if (name == NULL)
+	    goto erret;
+    }
+
+    if (fp == NULL)
     {
 	fp = (ufunc_T *)alloc((unsigned)sizeof(ufunc_T));
 	if (fp == NULL)
 	    goto erret;
+
+	if (fudi.fd_dict != NULL)
+	{
+	    if (fudi.fd_di == NULL)
+	    {
+		/* add new dict entry */
+		fudi.fd_di = dictitem_alloc();
+		if (fudi.fd_di == NULL)
+		{
+		    vim_free(fp);
+		    goto erret;
+		}
+		fudi.fd_di->di_key = fudi.fd_newkey;
+		fudi.fd_newkey = NULL;
+		dict_add(fudi.fd_dict, fudi.fd_di);
+	    }
+	    else
+		/* overwrite existing dict entry */
+		clear_tv(&fudi.fd_di->di_tv);
+	    fudi.fd_di->di_tv.v_type = VAR_FUNC;
+	    fudi.fd_di->di_tv.vval.v_string = vim_strsave(name);
+	    fp->refcount = 1;
+	}
+
 	/* insert the new function in the function list */
+	fp->name = name;
+	name = NULL;
 	fp->next = firstfunc;
 	firstfunc = fp;
-	fp->name = name;
     }
     fp->args = newargs;
     fp->lines = newlines;
@@ -14022,15 +14378,14 @@ ex_function(eap)
     fp->flags = flags;
     fp->calls = 0;
     fp->script_ID = current_SID;
-    did_emsg |= saved_did_emsg;
-    vim_free(skip_until);
-    return;
+    goto ret_free;
 
 erret:
-    vim_free(skip_until);
     ga_clear_strings(&newargs);
     ga_clear_strings(&newlines);
-erret_name:
+ret_free:
+    vim_free(skip_until);
+    vim_free(fudi.fd_newkey);
     vim_free(name);
     did_emsg |= saved_did_emsg;
 }
@@ -14038,14 +14393,17 @@ erret_name:
 /*
  * Get a function name, translating "<SID>" and "<SNR>".
  * Returns the function name in allocated memory, or NULL for failure.
+ * flags:
+ * TFN_INT:   internal function name OK
+ * TFN_QUIET: be quiet
  * Advances "pp" to just after the function name (if no error).
  */
     static char_u *
-trans_function_name(pp, skip, exists)
+trans_function_name(pp, skip, flags, fdp)
     char_u	**pp;
     int		skip;		/* only find the end, don't evaluate */
-    int		exists;		/* TRUE for exists(): internal function name
-				   OK and be quiet. */
+    int		flags;
+    funcdict	*fdp;		/* return: info about dictionary used */
 {
     char_u	*name = NULL;
     char_u	*start;
@@ -14053,12 +14411,10 @@ trans_function_name(pp, skip, exists)
     int		lead;
     char_u	sid_buf[20];
     int		len;
-#if 0
-    char_u	*expr_start, *expr_end;
-    char_u	*temp_string = NULL;
-#else
     lval	lv;
-#endif
+
+    if (fdp != NULL)
+	vim_memset(fdp, 0, sizeof(funcdict));
 
     /* A name starting with "<SID>" or "<SNR>" is local to a script. */
     start = *pp;
@@ -14066,8 +14422,7 @@ trans_function_name(pp, skip, exists)
     if (lead > 0)
 	start += lead;
 
-#if 1
-    end = get_lval(start, NULL, &lv, FALSE, skip, exists);
+    end = get_lval(start, NULL, &lv, FALSE, skip, flags & TFN_QUIET);
     if (end == start)
     {
 	if (!skip)
@@ -14093,7 +14448,14 @@ trans_function_name(pp, skip, exists)
 
     if (lv.ll_tv != NULL)
     {
-	/* TODO: When defining a function accept a Dict here. */
+	if (fdp != NULL)
+	{
+	    fdp->fd_dict = lv.ll_dict;
+	    fdp->fd_newkey = lv.ll_newkey;
+	    lv.ll_newkey = NULL;
+	    fdp->fd_di = lv.ll_di;
+	    fdp->fd_pdi = lv.ll_pdi;
+	}
 	if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL)
 	{
 	    name = vim_strsave(lv.ll_tv->vval.v_string);
@@ -14101,8 +14463,11 @@ trans_function_name(pp, skip, exists)
 	}
 	else
 	{
-	    if (!skip && !exists)
-		EMSG(_("E999: Funcref required"));
+	    if (!skip && !(flags & TFN_QUIET)
+				       && (fdp == NULL || lv.ll_dict == NULL))
+		EMSG(_(e_funcref));
+	    else
+		*pp = end;
 	    name = NULL;
 	}
 	goto theend;
@@ -14141,7 +14506,7 @@ trans_function_name(pp, skip, exists)
 	    lead += (int)STRLEN(sid_buf);
 	}
     }
-    else if (!exists && !ASCII_ISUPPER(*lv.ll_name))
+    else if (!(flags & TFN_INT) && !ASCII_ISUPPER(*lv.ll_name))
     {
 	EMSG2(_("E128: Function name must start with a capital: %s"),
 								  lv.ll_name);
@@ -14166,85 +14531,6 @@ trans_function_name(pp, skip, exists)
 theend:
     clear_lval(&lv);
     return name;
-#endif
-
-#if 0
-
-    end = find_name_end(start, &expr_start, &expr_end, FALSE);
-    if (end == start)
-    {
-	if (!skip)
-	    EMSG(_("E129: Function name required"));
-	return NULL;
-    }
-    if (expr_start != NULL && !skip)
-    {
-	/* expand magic curlies */
-	temp_string = make_expanded_name(start, expr_start, expr_end, end);
-	if (temp_string == NULL)
-	{
-	    /*
-	     * Report an invalid expression in braces, unless the expression
-	     * evaluation has been cancelled due to an aborting error, an
-	     * interrupt, or an exception.
-	     */
-	    if (!aborting())
-		EMSG2(_(e_invarg2), start);
-	    else
-		*pp = end;
-	    return NULL;
-	}
-	start = temp_string;
-	len = (int)STRLEN(temp_string);
-    }
-    else
-	len = (int)(end - start);
-
-    /*
-     * Copy the function name to allocated memory.
-     * Accept <SID>name() inside a script, translate into <SNR>123_name().
-     * Accept <SNR>123_name() outside a script.
-     */
-    if (skip)
-	lead = 0;	/* do nothing */
-    else if (lead > 0)
-    {
-	lead = 3;
-	if (eval_fname_sid(*pp))	/* If it's "<SID>" */
-	{
-	    if (current_SID <= 0)
-	    {
-		EMSG(_(e_usingsid));
-		return NULL;
-	    }
-	    sprintf((char *)sid_buf, "%ld_", (long)current_SID);
-	    lead += (int)STRLEN(sid_buf);
-	}
-    }
-    else if (!exists && !ASCII_ISUPPER(*start))
-    {
-	EMSG2(_("E128: Function name must start with a capital: %s"), start);
-	return NULL;
-    }
-    name = alloc((unsigned)(len + lead + 1));
-    if (name != NULL)
-    {
-	if (lead > 0)
-	{
-	    name[0] = K_SPECIAL;
-	    name[1] = KS_EXTRA;
-	    name[2] = (int)KE_SNR;
-	    if (eval_fname_sid(*pp))	/* If it's "<SID>" */
-		STRCPY(name + 3, sid_buf);
-	}
-	mch_memmove(name + lead, start, (size_t)len);
-	name[len + lead] = NUL;
-    }
-    *pp = end;
-
-    vim_free(temp_string);
-    return name;
-#endif
 }
 
 /*
@@ -14338,7 +14624,7 @@ function_exists(name)
     char_u  *p = name;
     int	    n = FALSE;
 
-    p = trans_function_name(&p, FALSE, TRUE);
+    p = trans_function_name(&p, FALSE, TFN_INT|TFN_QUIET, NULL);
     if (p != NULL)
     {
 	if (ASCII_ISUPPER(*p) || p[0] == K_SPECIAL)
@@ -14412,14 +14698,20 @@ cat_func_name(buf, fp)
 ex_delfunction(eap)
     exarg_T	*eap;
 {
-    ufunc_T	*fp = NULL, *pfp;
+    ufunc_T	*fp = NULL;
     char_u	*p;
     char_u	*name;
+    funcdict	fudi;
 
     p = eap->arg;
-    name = trans_function_name(&p, eap->skip, FALSE);
+    name = trans_function_name(&p, eap->skip, 0, &fudi);
+    vim_free(fudi.fd_newkey);
     if (name == NULL)
+    {
+	if (fudi.fd_dict != NULL && !eap->skip)
+	    EMSG(_(e_funcref));
 	return;
+    }
     if (!ends_excmd(*skipwhite(p)))
     {
 	vim_free(name);
@@ -14441,30 +14733,94 @@ ex_delfunction(eap)
 	    EMSG2(_("E130: Undefined function: %s"), eap->arg);
 	    return;
 	}
-	if (fp->calls)
+	if (fp->calls > 0)
 	{
 	    EMSG2(_("E131: Cannot delete function %s: It is in use"), eap->arg);
 	    return;
 	}
 
-	/* clear this function */
-	vim_free(fp->name);
-	ga_clear_strings(&(fp->args));
-	ga_clear_strings(&(fp->lines));
-
-	/* remove the function from the function list */
-	if (firstfunc == fp)
-	    firstfunc = fp->next;
-	else
-	{
-	    for (pfp = firstfunc; pfp != NULL; pfp = pfp->next)
-		if (pfp->next == fp)
-		{
-		    pfp->next = fp->next;
-		    break;
-		}
-	}
-	vim_free(fp);
+	if (fudi.fd_dict != NULL)
+	{
+	    /* Delete the dict item that refers to the function, it will
+	     * invoke func_unref() and possibly delete the function. */
+	    *fudi.fd_pdi = fudi.fd_di->di_next;
+	    dictitem_free(fudi.fd_di);
+	}
+	else
+	    func_free(fp);
+    }
+}
+
+/*
+ * Free a function and remove it from the list of functions.
+ */
+    static void
+func_free(fp)
+    ufunc_T *fp;
+{
+    ufunc_T	*pfp;
+
+    /* clear this function */
+    vim_free(fp->name);
+    ga_clear_strings(&(fp->args));
+    ga_clear_strings(&(fp->lines));
+
+    /* remove the function from the function list */
+    if (firstfunc == fp)
+	firstfunc = fp->next;
+    else
+    {
+	for (pfp = firstfunc; pfp != NULL; pfp = pfp->next)
+	    if (pfp->next == fp)
+	    {
+		pfp->next = fp->next;
+		break;
+	    }
+    }
+    vim_free(fp);
+}
+
+/*
+ * Unreference a Function: decrement the reference count and free it when it
+ * becomes zero.  Only for numbered functions.
+ */
+    static void
+func_unref(name)
+    char_u	*name;
+{
+    ufunc_T *fp;
+
+    if (name != NULL && isdigit(*name))
+    {
+	fp = find_func(name);
+	if (fp == NULL)
+	    EMSG2(_(e_intern2), "func_unref()");
+	else if (--fp->refcount <= 0)
+	{
+	    /* Only delete it when it's not being used.  Otherwise it's done
+	     * when "calls" becomes zero. */
+	    if (fp->calls == 0)
+		func_free(fp);
+	}
+    }
+}
+
+/*
+ * Count a reference to a Function.
+ */
+    static void
+func_ref(name)
+    char_u	*name;
+{
+    ufunc_T *fp;
+
+    if (name != NULL && isdigit(*name))
+    {
+	fp = find_func(name);
+	if (fp == NULL)
+	    EMSG2(_(e_intern2), "func_ref()");
+	else
+	    ++fp->refcount;
     }
 }