summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2005-01-17 22:23:00 +0000
committerBram Moolenaar <Bram@vim.org>2005-01-17 22:23:00 +0000
commit9ef486dbf367500876987f54d5b80ddb3fc3bd41 (patch)
treefe7efecdf75642bb7fd862376e558398dd33a2c3
parentbac234ead63cb6f3274c97faf81eacab27626a2b (diff)
updated for version 7.0041v7.0041
-rw-r--r--src/eval.c866
1 files changed, 611 insertions, 255 deletions
diff --git a/src/eval.c b/src/eval.c
index 1b21a110dc..f2ce3f3e63 100644
--- 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: Missing ']'");
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, sizeof(garray_T), 4, NULL};
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((listitem *item));
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((void));
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, typeval *varp, int copy));
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((char_u *arg, int *var_count, int *semicolon
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, expr_end, in_end)
* ":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, semicolon, var_count, nextchars)
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, semicolon, var_count, nextchars)
/*
* ":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, semicolon, var_count, nextchars)
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, semicolon, var_count, nextchars)
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, quiet)
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, quiet)
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, quiet)
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.
+ * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=".
*/
static void
-set_var_lval(lp, endp, rettv, copy)
+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;
- typeval rettv;
+ char_u *tofree;
int len;
+ typeval rettv;
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, argvars, firstline, lastline,
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, argvars, firstline, lastline,
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_STRING:
case VAR_FUNC:
+ func_unref(varp->vval.v_string);
+ /*FALLTHROUGH*/
+ case VAR_STRING:
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_STRING:
case VAR_FUNC:
+ func_unref(varp->vval.v_string);
+ /*FALLTHROUGH*/
+ case VAR_STRING:
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