summaryrefslogtreecommitdiff
path: root/src/eval.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2005-01-14 21:53:12 +0000
committerBram Moolenaar <Bram@vim.org>2005-01-14 21:53:12 +0000
commit8c711458a6affdad1cbae635694ff0a014850a27 (patch)
tree1645457d30b263a5eb31ea79bd62230a1cb4caa0 /src/eval.c
parentd8b0273231d69a2a6c24844c1acc938330acf035 (diff)
downloadvim-8c711458a6affdad1cbae635694ff0a014850a27.zip
updated for version 7.0038
Diffstat (limited to 'src/eval.c')
-rw-r--r--src/eval.c1369
1 files changed, 1023 insertions, 346 deletions
diff --git a/src/eval.c b/src/eval.c
index 54215198d..100bc80a9 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -47,6 +47,7 @@ typedef struct
varnumber_T v_number; /* number value */
char_u *v_string; /* string value (can be NULL!) */
struct listvar_S *v_list; /* list value (can be NULL!) */
+ struct dictvar_S *v_dict; /* dict value (can be NULL!) */
} vval;
} typeval;
@@ -56,6 +57,7 @@ typedef struct
#define VAR_STRING 2 /* "v_string" is used */
#define VAR_FUNC 3 /* "v_string" is function name */
#define VAR_LIST 4 /* "v_list" is used */
+#define VAR_DICT 5 /* "v_dict" is used */
/*
* Structure to hold an internal variable with a name.
@@ -104,12 +106,43 @@ struct listvar_S
typedef struct listvar_S listvar;
#define VAR_LIST_MAXNEST 100 /* maximum nesting of lists */
+
+/*
+ * Structure to hold an item of a Dictionary.
+ */
+struct dictitem_S
+{
+ struct dictitem_S *di_next; /* next item in list */
+ char_u *di_key; /* key string */
+ typeval di_tv; /* type and value of the variable */
+};
+
+typedef struct dictitem_S dictitem;
+
+/*
+ * Structure to hold info about a Dictionary.
+ */
+struct dictvar_S
+{
+ int dv_refcount; /* reference count */
+ dictitem *dv_first; /* first item, NULL if none */
+};
+
+typedef struct dictvar_S dictvar;
+
+
static char *e_letunexp = N_("E18: Unexpected characters in :let");
static char *e_listidx = N_("E684: list index out of range: %ld");
static char *e_undefvar = N_("E121: Undefined variable: %s");
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_listarg = N_("E686: Argument of %s must be a List");
+static char *e_listdictarg = N_("E999: 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_toomanyarg = N_("E118: Too many arguments for function: %s");
+static char *e_dictkey = N_("E999: key not found in Dictionary: %s");
/*
* All user-defined global variables are stored in "variables".
@@ -316,7 +349,6 @@ static int eval_index __ARGS((char_u **arg, typeval *rettv, int evaluate));
static int get_option_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
static int get_string_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
static int get_lit_string_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
-static int get_sharp_string_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
static int get_list_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
static listvar *list_alloc __ARGS((void));
static void list_unref __ARGS((listvar *l));
@@ -340,6 +372,16 @@ static void list_getrem __ARGS((listvar *l, listitem *item, listitem *item2));
static char_u *list2string __ARGS((typeval *tv));
static void list_join __ARGS((garray_T *gap, listvar *l, char_u *sep, int echo));
+static dictvar *dict_alloc __ARGS((void));
+static void dict_unref __ARGS((dictvar *d));
+static void dict_free __ARGS((dictvar *d));
+static dictitem *dictitem_alloc __ARGS((void));
+static void dictitem_free __ARGS((dictitem *item));
+static void dict_add __ARGS((dictvar *d, dictitem *item));
+static dictitem *dict_find __ARGS((dictvar *d, char_u *key, int len));
+static char_u *dict2string __ARGS((typeval *tv));
+static int get_dict_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
+
static char_u *echo_string __ARGS((typeval *tv, char_u **tofree, char_u *numbuf));
static char_u *tv2string __ARGS((typeval *tv, char_u **tofree, char_u *numbuf));
static char_u *string_quote __ARGS((char_u *str, int function));
@@ -438,7 +480,9 @@ static void f_inputsave __ARGS((typeval *argvars, typeval *rettv));
static void f_inputsecret __ARGS((typeval *argvars, typeval *rettv));
static void f_insert __ARGS((typeval *argvars, typeval *rettv));
static void f_isdirectory __ARGS((typeval *argvars, typeval *rettv));
+static void f_items __ARGS((typeval *argvars, typeval *rettv));
static void f_join __ARGS((typeval *argvars, typeval *rettv));
+static void f_keys __ARGS((typeval *argvars, typeval *rettv));
static void f_last_buffer_nr __ARGS((typeval *argvars, typeval *rettv));
static void f_len __ARGS((typeval *argvars, typeval *rettv));
static void f_libcall __ARGS((typeval *argvars, typeval *rettv));
@@ -459,6 +503,7 @@ static void f_mode __ARGS((typeval *argvars, typeval *rettv));
static void f_nextnonblank __ARGS((typeval *argvars, typeval *rettv));
static void f_nr2char __ARGS((typeval *argvars, typeval *rettv));
static void f_prevnonblank __ARGS((typeval *argvars, typeval *rettv));
+static void f_range __ARGS((typeval *argvars, typeval *rettv));
static void f_remote_expr __ARGS((typeval *argvars, typeval *rettv));
static void f_remote_foreground __ARGS((typeval *argvars, typeval *rettv));
static void f_remote_peek __ARGS((typeval *argvars, typeval *rettv));
@@ -501,6 +546,7 @@ static void f_tolower __ARGS((typeval *argvars, typeval *rettv));
static void f_toupper __ARGS((typeval *argvars, typeval *rettv));
static void f_tr __ARGS((typeval *argvars, typeval *rettv));
static void f_type __ARGS((typeval *argvars, typeval *rettv));
+static void f_values __ARGS((typeval *argvars, typeval *rettv));
static void f_virtcol __ARGS((typeval *argvars, typeval *rettv));
static void f_visualmode __ARGS((typeval *argvars, typeval *rettv));
static void f_winbufnr __ARGS((typeval *argvars, typeval *rettv));
@@ -561,8 +607,12 @@ 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 int check_changedtick __ARGS((char_u *arg));
static char_u *set_var_idx __ARGS((char_u *name, char_u *ip, typeval *rettv, int copy, char_u *endchars));
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));
+static int do_unlet_var __ARGS((char_u *name, int forceit));
/*
* Set an internal variable to a string value. Creates the variable if it does
@@ -1529,15 +1579,12 @@ ex_let_one(arg, tv, copy, endchars)
if (!aborting())
EMSG2(_(e_invarg2), arg);
}
- else if (*p == '[')
+ else if (*p == '[' || *p == '.')
arg_end = set_var_idx(arg, p, tv, copy, endchars);
else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(p)) == NULL)
EMSG(_(e_letunexp));
- else if (STRNCMP(arg, "b:changedtick", 13) == 0
- && !eval_isnamec(arg[13]))
- EMSG2(_(e_readonlyvar), arg);
- else
+ else if (!check_changedtick(arg))
{
c1 = *p;
*p = NUL;
@@ -1556,8 +1603,24 @@ ex_let_one(arg, tv, copy, endchars)
}
/*
+ * If "arg" is equal to "b:changedtick" give an error and return TRUE.
+ */
+ static int
+check_changedtick(arg)
+ char_u *arg;
+{
+ if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13]))
+ {
+ EMSG2(_(e_readonlyvar), arg);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
* Set a variable with an index: "name[expr]", "name[expr:expr]",
- * "name[expr][expr]", etc. Only works if "name" is an existing List.
+ * "name[expr][expr]", "name.key", "name.key[expr]" etc.
+ * Only works if "name" is an existing List or Dictionary.
* "ip" points to the first '['.
* Returns a pointer to just after the last used ']'; NULL for error.
*/
@@ -1577,11 +1640,15 @@ set_var_idx(name, ip, rettv, copy, endchars)
int range = FALSE;
typeval *tv;
long n1 = 0, n2 = 0;
- int empty1, empty2 = FALSE;
- listitem *item = NULL;
+ int empty1 = FALSE, empty2 = FALSE;
+ listitem *li = NULL;
listitem *ni;
listitem *ri;
listvar *l = NULL;
+ dictitem *di;
+ char_u *key = NULL;
+ char_u *newkey = NULL;
+ int len;
c1 = *ip;
*ip = NUL;
@@ -1593,11 +1660,12 @@ set_var_idx(name, ip, rettv, copy, endchars)
return NULL;
tv = &v->tv;
- for (p = ip; *p == '['; p = skipwhite(p + 1))
+ for (p = ip; *p == '[' || (*p == '.' && tv->v_type == VAR_DICT); )
{
- if (tv->v_type != VAR_LIST || tv->vval.v_list == NULL)
+ if (!(tv->v_type == VAR_LIST && tv->vval.v_list != NULL)
+ && !(tv->v_type == VAR_DICT && tv->vval.v_dict != NULL))
{
- EMSG(_("E689: Can only index a List"));
+ EMSG(_("E689: Can only index a List or Dictionary"));
p = NULL;
break;
}
@@ -1608,120 +1676,191 @@ set_var_idx(name, ip, rettv, copy, endchars)
break;
}
- /* Get the index [expr] or the first index [expr: ]. */
- p = skipwhite(p + 1);
- if (*p == ':')
- empty1 = TRUE;
- else
+ len = -1;
+ if (*p == '.')
{
- empty1 = FALSE;
- if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */
+ key = p + 1;
+ for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
+ ;
+ if (len == 0)
{
+ EMSG(_(e_emptykey));
p = NULL;
break;
}
+ p = key + len;
}
-
- /* Optionally get the second index [ :expr]. */
- if (*p == ':')
+ else
{
- if (rettv->v_type != VAR_LIST || rettv->vval.v_list == NULL)
- {
- EMSG(_("E709: [:] requires a List value"));
- p = NULL;
- if (!empty1)
- clear_tv(&var1);
- break;
- }
+ /* Get the index [expr] or the first index [expr: ]. */
p = skipwhite(p + 1);
- if (*p == ']')
- empty2 = TRUE;
+ if (*p == ':')
+ empty1 = TRUE;
else
{
- empty2 = FALSE;
- if (eval1(&p, &var2, TRUE) == FAIL) /* recursive! */
+ empty1 = FALSE;
+ if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */
{
p = NULL;
+ break;
+ }
+ }
+
+ /* Optionally get the second index [ :expr]. */
+ if (*p == ':')
+ {
+ if (tv->v_type == VAR_DICT)
+ {
+ EMSG(_("E999: Cannot use [:] with a Dictionary"));
+ p = NULL;
if (!empty1)
clear_tv(&var1);
break;
}
+ if (rettv->v_type != VAR_LIST || rettv->vval.v_list == NULL)
+ {
+ EMSG(_("E709: [:] requires a List value"));
+ p = NULL;
+ if (!empty1)
+ clear_tv(&var1);
+ break;
+ }
+ p = skipwhite(p + 1);
+ if (*p == ']')
+ empty2 = TRUE;
+ else
+ {
+ empty2 = FALSE;
+ if (eval1(&p, &var2, TRUE) == FAIL) /* recursive! */
+ {
+ p = NULL;
+ if (!empty1)
+ clear_tv(&var1);
+ break;
+ }
+ }
+ range = TRUE;
}
- range = TRUE;
+ else
+ range = FALSE;
+
+ if (*p != ']')
+ {
+ EMSG(_(e_missbrac));
+ if (!empty1)
+ clear_tv(&var1);
+ if (range && !empty2)
+ clear_tv(&var2);
+ p = NULL;
+ break;
+ }
+
+ /* Skip to past ']'. */
+ ++p;
}
- else
- range = FALSE;
- if (*p != ']')
+ if (tv->v_type == VAR_DICT)
{
- EMSG(_(e_missbrac));
- if (!empty1)
+ if (len == -1)
+ {
+ key = get_tv_string(&var1);
+ if (*key == NUL)
+ {
+ EMSG(_(e_emptykey));
+ clear_tv(&var1);
+ p = NULL;
+ break;
+ }
+ }
+ di = dict_find(tv->vval.v_dict, key, len);
+ if (di == NULL)
+ {
+ /* Key does not exist in dict: may need toadd it. */
+ if (*p == '[' || *p == '.')
+ {
+ EMSG2(_("E999: Key does not exist in Dictionary: %s"), key);
+ p = NULL;
+ if (len == -1)
+ clear_tv(&var1);
+ break;
+ }
+ if (len == -1)
+ newkey = vim_strsave(key);
+ else
+ newkey = vim_strnsave(key, len);
+ if (len == -1)
+ clear_tv(&var1);
+ if (newkey == NULL)
+ p = NULL;
+ break;
+ }
+ if (len == -1)
clear_tv(&var1);
- if (range && !empty2)
- clear_tv(&var2);
- p = NULL;
- break;
+ tv = &di->di_tv;
}
-
- /*
- * Get the number and item for the only or first index.
- */
- if (empty1)
- n1 = 0;
else
{
- n1 = get_tv_number(&var1);
- clear_tv(&var1);
- }
- l = tv->vval.v_list;
- item = list_find(l, n1);
- if (item == NULL)
- {
- EMSGN(_(e_listidx), n1);
- p = NULL;
+ /*
+ * Get the number and item for the only or first index of the List.
+ */
+ if (empty1)
+ n1 = 0;
+ else
+ {
+ n1 = get_tv_number(&var1);
+ clear_tv(&var1);
+ }
+ l = tv->vval.v_list;
+ li = list_find(l, n1);
+ if (li == NULL)
+ {
+ EMSGN(_(e_listidx), n1);
+ p = NULL;
+ if (range && !empty2)
+ clear_tv(&var2);
+ break;
+ }
+
+ /*
+ * May need to find the item or absolute index for the second
+ * index of a range.
+ * When no index given: "empty2" is TRUE.
+ * Otherwise "n2" is set to the second index.
+ */
if (range && !empty2)
+ {
+ n2 = get_tv_number(&var2);
clear_tv(&var2);
- break;
- }
+ if (n2 < 0)
+ {
+ ni = list_find(l, n2);
+ if (ni == NULL)
+ {
+ EMSGN(_(e_listidx), n2);
+ p = NULL;
+ break;
+ }
+ n2 = list_idx_of_item(l, ni);
+ }
- /*
- * May need to find the item or absolute index for the second index of
- * a range.
- * When no index given: "empty2" is TRUE.
- * Otherwise "n2" is set to the second index.
- */
- if (range && !empty2)
- {
- n2 = get_tv_number(&var2);
- clear_tv(&var2);
- if (n2 < 0)
- {
- ni = list_find(l, n2);
- if (ni == NULL)
+ /* Check that n2 isn't before n1. */
+ if (n1 < 0)
+ n1 = list_idx_of_item(l, li);
+ if (n2 < n1)
{
EMSGN(_(e_listidx), n2);
p = NULL;
break;
}
- n2 = list_idx_of_item(l, ni);
}
- /* Check that n2 isn't before n1. */
- if (n1 < 0)
- n1 = list_idx_of_item(l, item);
- if (n2 < n1)
- {
- EMSGN(_(e_listidx), n2);
- p = NULL;
- break;
- }
+ tv = &li->li_tv;
}
-
- tv = &item->li_tv;
}
if (p != NULL)
{
+ p = skipwhite(p);
if (endchars != NULL && vim_strchr(endchars, *p) == NULL)
{
EMSG(_(e_letunexp));
@@ -1734,12 +1873,12 @@ set_var_idx(name, ip, rettv, copy, endchars)
*/
for (ri = rettv->vval.v_list->lv_first; ri != NULL; )
{
- clear_tv(&item->li_tv);
- copy_tv(&ri->li_tv, &item->li_tv);
+ clear_tv(&li->li_tv);
+ copy_tv(&ri->li_tv, &li->li_tv);
ri = ri->li_next;
if (ri == NULL || (!empty2 && n2 == n1))
break;
- if (item->li_next == NULL)
+ if (li->li_next == NULL)
{
/* Need to add an empty item. */
ni = listitem_alloc();
@@ -1752,29 +1891,49 @@ set_var_idx(name, ip, rettv, copy, endchars)
ni->li_tv.vval.v_number = 0;
list_append(l, ni);
}
- item = item->li_next;
+ li = li->li_next;
++n1;
}
if (ri != NULL)
EMSG(_("E710: List value has more items than target"));
- else if (empty2 ? item != NULL && item->li_next != NULL : n1 != n2)
+ else if (empty2 ? li != NULL && li->li_next != NULL : n1 != n2)
EMSG(_("E711: List value has not enough items"));
}
else
{
+ if (newkey != NULL)
+ {
+ /* Need to add the item to the dictionary. */
+ di = dictitem_alloc();
+ if (di == NULL)
+ p = NULL;
+ else
+ {
+ di->di_key = newkey;
+ newkey = NULL;
+ dict_add(tv->vval.v_dict, di);
+ tv = &di->di_tv;
+ }
+ }
+ else
+ clear_tv(tv);
+
/*
* Assign the value to the variable or list item.
*/
- clear_tv(tv);
- if (copy)
- copy_tv(rettv, tv);
- else
+ if (p != NULL)
{
- *tv = *rettv;
- init_tv(rettv);
+ if (copy)
+ copy_tv(rettv, tv);
+ else
+ {
+ *tv = *rettv;
+ init_tv(rettv);
+ }
}
}
}
+ vim_free(newkey);
return p;
}
@@ -2004,17 +2163,11 @@ set_context_for_expression(xp, arg, cmdidx)
}
else if (c == '\'') /* literal string */
{
+ /* Trick: '' is like stopping and starting a literal string. */
while ((c = *++xp->xp_pattern) != NUL && c != '\'')
/* skip */ ;
xp->xp_context = EXPAND_NOTHING;
}
- else if (c == '#') /* sharp string */
- {
- /* Trick: ## is like stopping and starting a sharp string. */
- while ((c = *++xp->xp_pattern) != NUL && c != '#')
- /* skip */ ;
- xp->xp_context = EXPAND_NOTHING;
- }
else if (c == '|')
{
if (xp->xp_pattern[1] == '|')
@@ -2149,7 +2302,7 @@ ex_unlet(eap)
do
{
/* Find the end of the name. */
- name_end = find_name_end(arg, &expr_start, &expr_end, FALSE);
+ name_end = find_name_end(arg, &expr_start, &expr_end, TRUE);
if (!vim_iswhite(*name_end) && !ends_excmd(*name_end))
{
@@ -2183,11 +2336,8 @@ ex_unlet(eap)
}
else
{
- if (do_unlet(temp_string) == FAIL && !eap->forceit)
- {
- EMSG2(_("E108: No such variable: \"%s\""), temp_string);
+ if (do_unlet_var(temp_string, eap->forceit) == FAIL)
error = TRUE;
- }
vim_free(temp_string);
}
}
@@ -2195,13 +2345,8 @@ ex_unlet(eap)
{
cc = *name_end;
*name_end = NUL;
-
- if (do_unlet(arg) == FAIL && !eap->forceit)
- {
- EMSG2(_("E108: No such variable: \"%s\""), arg);
+ if (do_unlet_var(arg, eap->forceit) == FAIL)
error = TRUE;
- }
-
*name_end = cc;
}
}
@@ -2211,6 +2356,21 @@ ex_unlet(eap)
eap->nextcmd = check_nextcmd(arg);
}
+ static int
+do_unlet_var(name, forceit)
+ char_u *name;
+ int forceit;
+{
+ if (check_changedtick(name))
+ return FAIL;
+ if (do_unlet(name) == FAIL && !forceit)
+ {
+ EMSG2(_("E108: No such variable: \"%s\""), name);
+ return FAIL;
+ }
+ return OK;
+}
+
/*
* "unlet" a variable. Return OK if it existed, FAIL if not.
*/
@@ -3061,7 +3221,8 @@ eval6(arg, rettv, evaluate)
* ! in front logical NOT
* - in front unary minus
* + in front unary plus (ignored)
- * trailing [] subscript in String
+ * trailing [] subscript in String or List
+ * trailing .name entry in Dictionary
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
@@ -3127,21 +3288,21 @@ eval7(arg, rettv, evaluate)
break;
/*
- * Literal string constant: 'string'.
+ * Literal string constant: 'str''ing'.
*/
case '\'': ret = get_lit_string_tv(arg, rettv, evaluate);
break;
/*
- * Sharp string constant: #str##ing#.
+ * List: [expr, expr]
*/
- case '#': ret = get_sharp_string_tv(arg, rettv, evaluate);
+ case '[': ret = get_list_tv(arg, rettv, evaluate);
break;
/*
- * List: [expr, expr]
+ * Dictionary: {key: val, key: val}
*/
- case '[': ret = get_list_tv(arg, rettv, evaluate);
+ case '{': ret = get_dict_tv(arg, rettv, evaluate);
break;
/*
@@ -3191,53 +3352,60 @@ eval7(arg, rettv, evaluate)
}
break;
- /*
- * Must be a variable or function name then.
- */
- default: s = *arg;
- len = get_func_len(arg, &alias, evaluate);
- if (alias != NULL)
- s = alias;
+ default: ret = NOTDONE;
+ break;
+ }
- if (len == 0)
- ret = FAIL;
- else
+ if (ret == NOTDONE)
+ {
+ /*
+ * Must be a variable or function name.
+ * Can also be a curly-braces kind of name: {expr}.
+ */
+ s = *arg;
+ len = get_func_len(arg, &alias, evaluate);
+ if (alias != NULL)
+ s = alias;
+
+ if (len == 0)
+ ret = FAIL;
+ else
+ {
+ if (**arg == '(') /* recursive! */
+ {
+ /* If "s" is the name of a variable of type VAR_FUNC
+ * use its contents. */
+ s = deref_func_name(s, &len);
+
+ /* Invoke the function. */
+ ret = get_func_tv(s, len, rettv, arg,
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &len, evaluate);
+ /* 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 (**arg == '(') /* recursive! */
- {
- /* If "s" is the name of a variable of type VAR_FUNC
- * use its contents. */
- s = deref_func_name(s, &len);
-
- /* Invoke the function. */
- ret = get_func_tv(s, len, rettv, arg,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum,
- &len, evaluate);
- /* 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;
- }
- }
- else if (evaluate)
- ret = get_var_tv(s, len, rettv);
+ if (ret == OK)
+ clear_tv(rettv);
+ ret = FAIL;
}
+ }
+ else if (evaluate)
+ ret = get_var_tv(s, len, rettv);
+ }
- if (alias != NULL)
- vim_free(alias);
-
- break;
+ if (alias != NULL)
+ vim_free(alias);
}
+
*arg = skipwhite(*arg);
/*
- * Handle expr[expr] and expr[expr:expr] subscript.
+ * Handle expr[expr], expr[expr:expr] subscript and .name lookup.
*/
- while (**arg == '[' && ret == OK)
+ while ((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT))
+ && !vim_iswhite(*(*arg - 1)) && ret == OK)
{
if (eval_index(arg, rettv, evaluate) == FAIL)
{
@@ -3282,9 +3450,10 @@ eval_index(arg, rettv, evaluate)
int empty1 = FALSE, empty2 = FALSE;
typeval var1, var2;
long n1, n2 = 0;
- long len;
- int range;
+ long len = -1;
+ int range = FALSE;
char_u *s;
+ char_u *key = NULL;
if (rettv->v_type == VAR_FUNC)
{
@@ -3292,48 +3461,63 @@ eval_index(arg, rettv, evaluate)
return FAIL;
}
- /*
- * Get the (first) variable from inside the [].
- */
- *arg = skipwhite(*arg + 1);
- if (**arg == ':')
- empty1 = TRUE;
- else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */
- return FAIL;
-
- /*
- * Get the second variable from inside the [:].
- */
- if (**arg == ':')
+ if (**arg == '.')
{
- range = TRUE;
+ /*
+ * dict.name
+ */
+ key = *arg + 1;
+ for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
+ ;
+ if (len == 0)
+ return FAIL;
+ *arg = skipwhite(key + len);
+ }
+ else
+ {
+ /*
+ * something[idx]
+ *
+ * Get the (first) variable from inside the [].
+ */
*arg = skipwhite(*arg + 1);
- if (**arg == ']')
- empty2 = TRUE;
- else if (eval1(arg, &var2, evaluate) == FAIL) /* recursive! */
+ if (**arg == ':')
+ empty1 = TRUE;
+ else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */
+ return FAIL;
+
+ /*
+ * Get the second variable from inside the [:].
+ */
+ if (**arg == ':')
{
+ range = TRUE;
+ *arg = skipwhite(*arg + 1);
+ if (**arg == ']')
+ empty2 = TRUE;
+ else if (eval1(arg, &var2, evaluate) == FAIL) /* recursive! */
+ {
+ clear_tv(&var1);
+ return FAIL;
+ }
+ }
+
+ /* Check for the ']'. */
+ if (**arg != ']')
+ {
+ EMSG(_(e_missbrac));
clear_tv(&var1);
+ if (range)
+ clear_tv(&var2);
return FAIL;
}
- }
- else
- range = FALSE;
-
- /* Check for the ']'. */
- if (**arg != ']')
- {
- EMSG(_(e_missbrac));
- clear_tv(&var1);
- if (range)
- clear_tv(&var2);
- return FAIL;
+ *arg = skipwhite(*arg + 1); /* skip the ']' */
}
if (evaluate)
{
- if (empty1)
- n1 = 0;
- else
+ n1 = 0;
+ if (!empty1 && rettv->v_type != VAR_DICT)
{
n1 = get_tv_number(&var1);
clear_tv(&var1);
@@ -3405,7 +3589,7 @@ eval_index(arg, rettv, evaluate)
if (n2 < 0)
n2 = len + n2;
- if (!empty2 && (n2 < 0 || n2 >= len || n2 < n1))
+ if (!empty2 && (n2 < 0 || n2 >= len || n2 + 1 < n1))
{
EMSGN(_(e_listidx), n2);
return FAIL;
@@ -3436,10 +3620,46 @@ eval_index(arg, rettv, evaluate)
*rettv = var1;
}
break;
+
+ case VAR_DICT:
+ if (range)
+ {
+ EMSG(_("E999: Using range with Dictionary"));
+ if (len == -1)
+ clear_tv(&var1);
+ return FAIL;
+ }
+ {
+ dictitem *item;
+
+ if (len == -1)
+ {
+ key = get_tv_string(&var1);
+ if (*key == NUL)
+ {
+ EMSG(_("E999: Empty key for Dictionary"));
+ clear_tv(&var1);
+ return FAIL;
+ }
+ }
+
+ item = dict_find(rettv->vval.v_dict, key, (int)len);
+
+ if (item == NULL)
+ EMSG2(_("E999: Key not found in Dictionary: %s"), key);
+ if (len == -1)
+ clear_tv(&var1);
+ if (item == NULL)
+ return FAIL;
+
+ copy_tv(&item->di_tv, &var1);
+ clear_tv(rettv);
+ *rettv = var1;
+ }
+ break;
}
}
- *arg = skipwhite(*arg + 1); /* skip the ']' */
return OK;
}
@@ -3536,7 +3756,6 @@ get_string_tv(arg, rettv, evaluate)
{
char_u *p;
char_u *name;
- int i;
int extra = 0;
/*
@@ -3574,20 +3793,21 @@ get_string_tv(arg, rettv, evaluate)
name = alloc((unsigned)(p - *arg + extra));
if (name == NULL)
return FAIL;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = name;
- i = 0;
- for (p = *arg + 1; *p != NUL && *p != '"'; ++p)
+ for (p = *arg + 1; *p != NUL && *p != '"'; )
{
if (*p == '\\')
{
switch (*++p)
{
- case 'b': name[i++] = BS; break;
- case 'e': name[i++] = ESC; break;
- case 'f': name[i++] = FF; break;
- case 'n': name[i++] = NL; break;
- case 'r': name[i++] = CAR; break;
- case 't': name[i++] = TAB; break;
+ case 'b': *name++ = BS; ++p; break;
+ case 'e': *name++ = ESC; ++p; break;
+ case 'f': *name++ = FF; ++p; break;
+ case 'n': *name++ = NL; ++p; break;
+ case 'r': *name++ = CAR; ++p; break;
+ case 't': *name++ = TAB; ++p; break;
case 'X': /* hex: "\x1", "\x12" */
case 'x':
@@ -3608,17 +3828,16 @@ get_string_tv(arg, rettv, evaluate)
++p;
nr = (nr << 4) + hex2nr(*p);
}
+ ++p;
#ifdef FEAT_MBYTE
/* For "\u" store the number according to
* 'encoding'. */
if (c != 'X')
- i += (*mb_char2bytes)(nr, name + i);
+ name += (*mb_char2bytes)(nr, name);
else
#endif
- name[i++] = nr;
+ *name++ = nr;
}
- else
- name[i++] = *p;
break;
/* octal: "\1", "\12", "\123" */
@@ -3629,59 +3848,41 @@ get_string_tv(arg, rettv, evaluate)
case '4':
case '5':
case '6':
- case '7': name[i] = *p - '0';
- if (p[1] >= '0' && p[1] <= '7')
+ case '7': *name = *p++ - '0';
+ if (*p >= '0' && *p <= '7')
{
- ++p;
- name[i] = (name[i] << 3) + *p - '0';
- if (p[1] >= '0' && p[1] <= '7')
- {
- ++p;
- name[i] = (name[i] << 3) + *p - '0';
- }
+ *name = (*name << 3) + *p++ - '0';
+ if (*p >= '0' && *p <= '7')
+ *name = (*name << 3) + *p++ - '0';
}
- ++i;
+ ++name;
break;
/* Special key, e.g.: "\<C-W>" */
- case '<': extra = trans_special(&p, name + i, TRUE);
+ case '<': extra = trans_special(&p, name, TRUE);
if (extra != 0)
{
- i += extra;
- --p;
+ name += extra;
break;
}
/* FALLTHROUGH */
- default: name[i++] = *p;
+ default: MB_COPY_CHAR(p, name);
break;
}
}
else
- name[i++] = *p;
-
-#ifdef FEAT_MBYTE
- /* For a multi-byte character copy the bytes after the first one. */
- if (has_mbyte)
- {
- int l = (*mb_ptr2len_check)(p);
+ MB_COPY_CHAR(p, name);
- while (--l > 0)
- name[i++] = *++p;
- }
-#endif
}
- name[i] = NUL;
+ *name = NUL;
*arg = p + 1;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = name;
-
return OK;
}
/*
- * Allocate a variable for an backtick-string constant.
+ * Allocate a variable for a 'str''ing' constant.
* Return OK or FAIL.
*/
static int
@@ -3691,72 +3892,30 @@ get_lit_string_tv(arg, rettv, evaluate)
int evaluate;
{
char_u *p;
- char_u *name;
-
- /*
- * Find the end of the string.
- */
- p = vim_strchr(*arg + 1, '\'');
- if (p == NULL)
- {
- EMSG2(_("E115: Missing quote: %s"), *arg);
- return FAIL;
- }
-
- if (evaluate)
- {
- /*
- * Copy the string into allocated memory.
- */
- name = vim_strnsave(*arg + 1, (int)(p - (*arg + 1)));
- if (name == NULL)
- return FAIL;
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = name;
- }
-
- *arg = p + 1;
-
- return OK;
-}
-
-/*
- * Allocate a variable for a #string# constant.
- * Return OK or FAIL.
- */
- static int
-get_sharp_string_tv(arg, rettv, evaluate)
- char_u **arg;
- typeval *rettv;
- int evaluate;
-{
- char_u *p;
char_u *str;
- int i;
int reduce = 0;
/*
- * Find the end of the string, skipping ##.
+ * Find the end of the string, skipping ''.
*/
for (p = *arg + 1; *p != NUL; mb_ptr_adv(p))
{
- if (*p == '#')
+ if (*p == '\'')
{
- if (p[1] != '#')
+ if (p[1] != '\'')
break;
++reduce;
++p;
}
}
- if (*p != '#')
+ if (*p != '\'')
{
- EMSG2(_("E999: Missing #: %s"), *arg);
+ EMSG2(_("E115: Missing quote: %s"), *arg);
return FAIL;
}
- /* If only parsing, set *arg and return here */
+ /* If only parsing return after setting "*arg" */
if (!evaluate)
{
*arg = p + 1;
@@ -3764,40 +3923,27 @@ get_sharp_string_tv(arg, rettv, evaluate)
}
/*
- * Copy the string into allocated memory, handling ## to # reduction.
+ * Copy the string into allocated memory, handling '' to ' reduction.
*/
str = alloc((unsigned)((p - *arg) - reduce));
if (str == NULL)
return FAIL;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = str;
- i = 0;
- for (p = *arg + 1; *p != NUL; ++p)
+ for (p = *arg + 1; *p != NUL; )
{
- if (*p == '#')
+ if (*p == '\'')
{
- if (p[1] != '#')
+ if (p[1] != '\'')
break;
++p;
}
- str[i++] = *p;
-
-#ifdef FEAT_MBYTE
- /* For a multi-byte character copy the bytes after the first one. */
- if (has_mbyte)
- {
- int l = (*mb_ptr2len_check)(p);
-
- while (--l > 0)
- str[i++] = *++p;
- }
-#endif
+ MB_COPY_CHAR(p, str);
}
- str[i] = NUL;
+ *str = NUL;
*arg = p + 1;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = str;
-
return OK;
}
@@ -3841,7 +3987,7 @@ get_list_tv(arg, rettv, evaluate)
break;
if (**arg != ',')
{
- EMSG2(_("E696: Missing comma in list: %s"), *arg);
+ EMSG2(_("E696: Missing comma in List: %s"), *arg);
goto failret;
}
*arg = skipwhite(*arg + 1);
@@ -3849,7 +3995,7 @@ get_list_tv(arg, rettv, evaluate)
if (**arg != ']')
{
- EMSG2(_("E697: Missing end of list ']': %s"), *arg);
+ EMSG2(_("E697: Missing end of List ']': %s"), *arg);
failret:
if (evaluate)
list_free(l);
@@ -4366,6 +4512,314 @@ list_join(gap, l, sep, echo)
}
/*
+ * Allocate an empty header for a dictionary.
+ */
+ static dictvar *
+dict_alloc()
+{
+ return (dictvar *)alloc_clear(sizeof(dictvar));
+}
+
+/*
+ * Unreference a Dictionary: decrement the reference count and free it when it
+ * becomes zero.
+ */
+ static void
+dict_unref(d)
+ dictvar *d;
+{
+ if (d != NULL && --d->dv_refcount <= 0)
+ dict_free(d);
+}
+
+/*
+ * Free a Dictionary, including all items it contains.
+ * Ignores the reference count.
+ */
+ static void
+dict_free(d)
+ dictvar *d;
+{
+ dictitem *item;
+ dictitem *next;
+
+ for (item = d->dv_first; item != NULL; item = next)
+ {
+ next = item->di_next;
+ dictitem_free(item);
+ }
+ vim_free(d);
+}
+
+/*
+ * Allocate a Dictionary item.
+ */
+ static dictitem *
+dictitem_alloc()
+{
+ return (dictitem *)alloc(sizeof(dictitem));
+}
+
+/*
+ * Free a dict item. Also clears the value.
+ */
+ static void
+dictitem_free(item)
+ dictitem *item;
+{
+ vim_free(item->di_key);
+ clear_tv(&item->di_tv);
+ vim_free(item);
+}
+
+/*
+ * Add item "item" to Dictionary "d".
+ */
+ static void
+dict_add(d, item)
+ dictvar *d;
+ dictitem *item;
+{
+ item->di_next = d->dv_first;
+ d->dv_first = item;
+}
+
+#if 0 /* not currently used */
+static void dict_set_item __ARGS((dictvar *d, int type, char *key, void *val));
+
+/*
+ * Add an item to Dictionary "d" with type "type", key "key" and value "val".
+ * If it already exists it is overwritten.
+ * The key and value are copied to allocated memory.
+ */
+ static void
+dict_set_item(d, type, key, val)
+ dictvar *d;
+ int type;
+ char *key;
+ void *val;
+{
+ dictitem *di;
+ char_u *dkey;
+
+ di = dict_find(d, (char_u *)key, -1);
+ if (di == NULL)
+ {
+ dkey = vim_strsave((char_u *)key);
+ if (dkey != NULL)
+ {
+ di = dictitem_alloc();
+ if (di == NULL)
+ vim_free(dkey);
+ else
+ di->di_key = dkey;
+ }
+ }
+ else
+ clear_tv(&di->di_tv);
+
+ if (di != NULL)
+ {
+ di->di_tv.v_type = type;
+ switch (type)
+ {
+ case VAR_NUMBER:
+ di->di_tv.vval.v_number = (varnumber_T)val;
+ break;
+ case VAR_FUNC:
+ case VAR_STRING:
+ di->di_tv.vval.v_string = vim_strsave((char_u *)val);
+ break;
+ default:
+ EMSG2(_(e_intern2), "dict_set_item()");
+ }
+ dict_add(d, di);
+ }
+}
+#endif
+
+/*
+ * Find item "key[len]" in Dictionary "d".
+ * If "len" is negative use strlen(key).
+ * Returns NULL when not found.
+ */
+ static dictitem *
+dict_find(d, key, len)
+ dictvar *d;
+ char_u *key;
+ int len;
+{
+ static dictitem *di;
+
+ for (di = d->dv_first; di != NULL; di = di->di_next)
+ if (len < 0
+ ? STRCMP(di->di_key, key) == 0
+ : STRNCMP(di->di_key, key, len) == 0 && di->di_key[len] == NUL)
+ return di;
+ return NULL;
+}
+
+/*
+ * Return an allocated string with the string representation of a Dictionary.
+ * May return NULL.
+ */
+ static char_u *
+dict2string(tv)
+ typeval *tv;
+{
+ garray_T ga;
+ int first = TRUE;
+ char_u *tofree;
+ char_u numbuf[NUMBUFLEN];
+ dictitem *item;
+ char_u *s;
+
+ if (tv->vval.v_dict == NULL)
+ return NULL;
+ ga_init2(&ga, (int)sizeof(char), 80);
+ ga_append(&ga, '{');
+
+ for (item = tv->vval.v_dict->dv_first; item != NULL; item = item->di_next)
+ {
+ if (first)
+ first = FALSE;
+ else
+ ga_concat(&ga, (char_u *)", ");
+
+ tofree = string_quote(item->di_key, FALSE);
+ if (tofree != NULL)
+ {
+ ga_concat(&ga, tofree);
+ vim_free(tofree);
+ }
+ ga_concat(&ga, (char_u *)": ");
+ s = tv2string(&item->di_tv, &tofree, numbuf);
+ if (s != NULL)
+ ga_concat(&ga, s);
+ vim_free(tofree);
+ }
+
+ ga_append(&ga, '}');
+ ga_append(&ga, NUL);
+ return (char_u *)ga.ga_data;
+}
+
+/*
+ * Allocate a variable for a Dictionary and fill it from "*arg".
+ * Return OK or FAIL. Returns NOTDONE for {expr}.
+ */
+ static int
+get_dict_tv(arg, rettv, evaluate)
+ char_u **arg;
+ typeval *rettv;
+ int evaluate;
+{
+ dictvar *d = NULL;
+ typeval tv;
+ char_u *key;
+ dictitem *item;
+ char_u *start = skipwhite(*arg + 1);
+
+ /*
+ * First check if it's not a curly-braces thing: {expr}.
+ * Must do this without evaluating, otherwise a function may be called
+ * twice. Unfortunately this means we need to call eval1() twice for the
+ * first item.
+ */
+ if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */
+ return FAIL;
+ if (*start == '}')
+ return NOTDONE;
+
+ if (evaluate)
+ {
+ d = dict_alloc();
+ if (d == NULL)
+ return FAIL;
+ }
+
+ *arg = skipwhite(*arg + 1);
+ while (**arg != '}' && **arg != NUL)
+ {
+ if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */
+ goto failret;
+ if (**arg != ':')
+ {
+ EMSG2(_("E999: Missing colon in Dictionary: %s"), *arg);
+ clear_tv(&tv);
+ goto failret;
+ }
+ key = get_tv_string(&tv);
+ if (*key == NUL)
+ {
+ EMSG(_(e_emptykey));
+ clear_tv(&tv);
+ goto failret;
+ }
+ key = vim_strsave(key);
+ clear_tv(&tv);
+ if (key == NULL)
+ goto failret;
+
+ *arg = skipwhite(*arg + 1);
+ if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */
+ {
+ vim_free(key);
+ goto failret;
+ }
+ if (evaluate)
+ {
+ item = dict_find(d, key, -1);
+ if (item != NULL)
+ {
+ EMSG(_("E999: Duplicate key in Dictionary"));
+ vim_free(key);
+ clear_tv(&tv);
+ goto failret;
+ }
+ item = dictitem_alloc();
+ if (item == NULL)
+ vim_free(key);
+ else
+ {
+ item->di_key = key;
+ item->di_tv = tv;
+ dict_add(d, item);
+ }
+ }
+
+ if (**arg == '}')
+ break;
+ if (**arg != ',')
+ {
+ EMSG2(_("E999: Missing comma in Dictionary: %s"), *arg);
+ goto failret;
+ }
+ *arg = skipwhite(*arg + 1);
+ }
+
+ if (**arg != '}')
+ {
+ EMSG2(_("E999: Missing end of Dictionary '}': %s"), *arg);
+failret:
+ if (evaluate)
+ dict_free(d);
+ return FAIL;
+ }
+
+ *arg = skipwhite(*arg + 1);
+ if (evaluate)
+ {
+ rettv->v_type = VAR_DICT;
+ rettv->vval.v_dict = d;
+ ++d->dv_refcount;
+ }
+
+ return OK;
+}
+
+
+/*
* Return a string with the string representation of a variable.
* If the memory is allocated "tofree" is set to it, otherwise NULL.
* "numbuf" is used for a number.
@@ -4386,6 +4840,9 @@ echo_string(tv, tofree, numbuf)
case VAR_LIST:
*tofree = list2string(tv);
return *tofree;
+ case VAR_DICT:
+ *tofree = dict2string(tv);
+ return *tofree;
case VAR_STRING:
case VAR_NUMBER:
break;
@@ -4422,6 +4879,9 @@ tv2string(tv, tofree, numbuf)
case VAR_LIST:
*tofree = list2string(tv);
return *tofree;
+ case VAR_DICT:
+ *tofree = dict2string(tv);
+ return *tofree;
default:
EMSG2(_(e_intern2), "tv2string()");
}
@@ -4430,8 +4890,8 @@ tv2string(tv, tofree, numbuf)
}
/*
- * Return a string in # quotes, doubling # characters.
- * If "function" is TRUE make it function(#string#).
+ * Return a string in ' quotes, doubling ' characters.
+ * If "function" is TRUE make it function('string').
*/
static char_u *
string_quote(str, function)
@@ -4442,35 +4902,25 @@ string_quote(str, function)
char_u *p, *r, *s;
for (p = str; *p != NUL; mb_ptr_adv(p))
- if (*p == '#')
+ if (*p == '\'')
++len;
s = r = alloc(len);
if (r != NULL)
{
if (function)
{
- STRCPY(r, "function(#");
+ STRCPY(r, "function('");
r += 10;
}
else
- *r++ = '#';
- for (p = str; *p != NUL; ++p)
+ *r++ = '\'';
+ for (p = str; *p != NUL; )
{
- if (*p == '#')
- *r++ = '#';
- *r++ = *p;
-#ifdef FEAT_MBYTE
- /* For a multi-byte character copy the bytes after the first one. */
- if (has_mbyte)
- {
- int l = (*mb_ptr2len_check)(p);
-
- while (--l > 0)
- *r++ = *++p;
- }
-#endif
+ if (*p == '\'')
+ *r++ = '\'';
+ MB_COPY_CHAR(p, r);
}
- *r++ = '#';
+ *r++ = '\'';
if (function)
*r++ = ')';
*r++ = NUL;
@@ -4635,7 +5085,9 @@ static struct fst
{"inputsecret", 1, 2, f_inputsecret},
{"insert", 2, 3, f_insert},
{"isdirectory", 1, 1, f_isdirectory},
+ {"items", 1, 1, f_items},
{"join", 1, 2, f_join},
+ {"keys", 1, 1, f_keys},
{"last_buffer_nr", 0, 0, f_last_buffer_nr},/* obsolete */
{"len", 1, 1, f_len},
{"libcall", 3, 3, f_libcall},
@@ -4656,6 +5108,7 @@ static struct fst
{"nextnonblank", 1, 1, f_nextnonblank},
{"nr2char", 1, 1, f_nr2char},
{"prevnonblank", 1, 1, f_prevnonblank},
+ {"range", 1, 3, f_range},
{"remote_expr", 2, 3, f_remote_expr},
{"remote_foreground", 1, 1, f_remote_foreground},
{"remote_peek", 1, 2, f_remote_peek},
@@ -4698,6 +5151,7 @@ static struct fst
{"toupper", 1, 1, f_toupper},
{"tr", 3, 3, f_tr},
{"type", 1, 1, f_type},
+ {"values", 1, 1, f_values},
{"virtcol", 1, 1, f_virtcol},
{"visualmode", 0, 1, f_visualmode},
{"winbufnr", 1, 1, f_winbufnr},
@@ -4905,12 +5359,6 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline,
int evaluate;
{
int ret = FAIL;
- static char *errors[] =
- {N_("E117: Unknown function: %s"),
- N_("E118: Too many arguments for function: %s"),
- N_("E119: Not enough arguments for function: %s"),
- N_("E120: Using <SID> not in a script context: %s"),
- };
#define ERROR_UNKNOWN 0
#define ERROR_TOOMANY 1
#define ERROR_TOOFEW 2
@@ -5063,8 +5511,26 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline,
* Report an error unless the argument evaluation or function call has been
* cancelled due to an aborting error, an interrupt, or an exception.
*/
- if (error < ERROR_NONE && !aborting())
- EMSG2((char_u *)_(errors[error]), name);
+ if (!aborting())
+ {
+ switch (error)
+ {
+ case ERROR_UNKNOWN:
+ EMSG2(_("E117: Unknown function: %s"), name);
+ break;
+ case ERROR_TOOMANY:
+ EMSG2(_(e_toomanyarg), name);
+ break;
+ case ERROR_TOOFEW:
+ EMSG2(_("E119: Not enough arguments for function: %s"),
+ name);
+ break;
+ case ERROR_SCRIPT:
+ EMSG2(_("E120: Using <SID> not in a script context: %s"),
+ name);
+ break;
+ }
+ }
name[len] = cc;
if (fname != name && fname != fname_buf)
@@ -8208,6 +8674,97 @@ f_isdirectory(argvars, rettv)
rettv->vval.v_number = mch_isdir(get_tv_string(&argvars[0]));
}
+static void dict_list __ARGS((typeval *argvars, typeval *rettv, int what));
+
+/*
+ * Turn a dict into a list:
+ * "what" == 0: list of keys
+ * "what" == 1: list of values
+ * "what" == 2: list of items
+ */
+ static void
+dict_list(argvars, rettv, what)
+ typeval *argvars;
+ typeval *rettv;
+ int what;
+{
+ listvar *l;
+ listvar *l2;
+ dictitem *di;
+ listitem *li;
+ listitem *li2;
+
+ rettv->vval.v_number = 0;
+ if (argvars[0].v_type != VAR_DICT)
+ {
+ EMSG(_(e_dictreq));
+ return;
+ }
+ if (argvars[0].vval.v_dict == NULL)
+ return;
+
+ l = list_alloc();
+ if (l == NULL)
+ return;
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = l;
+ ++l->lv_refcount;
+
+ for (di = argvars[0].vval.v_dict->dv_first; di != NULL; di = di->di_next)
+ {
+ li = listitem_alloc();
+ if (li == NULL)
+ break;
+ list_append(l, li);
+
+ if (what == 0)
+ {
+ /* keys() */
+ li->li_tv.v_type = VAR_STRING;
+ li->li_tv.vval.v_string = vim_strsave(di->di_key);
+ }
+ else if (what == 1)
+ {
+ /* values() */
+ copy_tv(&di->di_tv, &li->li_tv);
+ }
+ else
+ {
+ /* items() */
+ l2 = list_alloc();
+ li->li_tv.v_type = VAR_LIST;
+ li->li_tv.vval.v_list = l2;
+ if (l2 == NULL)
+ break;
+ ++l2->lv_refcount;
+
+ li2 = listitem_alloc();
+ if (li2 == NULL)
+ break;
+ list_append(l2, li2);
+ li2->li_tv.v_type = VAR_STRING;
+ li2->li_tv.vval.v_string = vim_strsave(di->di_key);
+
+ li2 = listitem_alloc();
+ if (li2 == NULL)
+ break;
+ list_append(l2, li2);
+ copy_tv(&di->di_tv, &li2->li_tv);
+ }
+ }
+}
+
+/*
+ * "items(dict)" function
+ */
+ static void
+f_items(argvars, rettv)
+ typeval *argvars;
+ typeval *rettv;
+{
+ dict_list(argvars, rettv, 2);
+}
+
/*
* "join()" function
*/
@@ -8241,6 +8798,17 @@ f_join(argvars, rettv)
}
/*
+ * "keys()" function
+ */
+ static void
+f_keys(argvars, rettv)
+ typeval *argvars;
+ typeval *rettv;
+{
+ dict_list(argvars, rettv, 0);
+}
+
+/*
* "last_buffer_nr()" function.
*/
/*ARGSUSED*/
@@ -8859,6 +9427,61 @@ f_prevnonblank(argvars, rettv)
rettv->vval.v_number = lnum;
}
+/*
+ * "range()" function
+ */
+ static void
+f_range(argvars, rettv)
+ typeval *argvars;
+ typeval *rettv;
+{
+ long start;
+ long end;
+ long stride = 1;
+ long i;
+ listvar *l;
+ listitem *li;
+
+ start = get_tv_number(&argvars[0]);
+ if (argvars[1].v_type == VAR_UNKNOWN)
+ {
+ end = start - 1;
+ start = 0;
+ }
+ else
+ {
+ end = get_tv_number(&argvars[1]);
+ if (argvars[2].v_type != VAR_UNKNOWN)
+ stride = get_tv_number(&argvars[2]);
+ }
+
+ rettv->vval.v_number = 0;
+ if (stride == 0)
+ EMSG(_("E999: Stride is zero"));
+ else if (stride > 0 ? end < start : end > start)
+ EMSG(_("E999: Start past end"));
+ else
+ {
+ l = list_alloc();
+ if (l != NULL)
+ {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = l;
+ ++l->lv_refcount;
+
+ for (i = start; stride > 0 ? i <= end : i >= end; i += stride)
+ {
+ li = listitem_alloc();
+ if (li == NULL)
+ break;
+ li->li_tv.v_type = VAR_NUMBER;
+ li->li_tv.vval.v_number = i;
+ list_append(l, li);
+ }
+ }
+ }
+}
+
#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)
static void make_connection __ARGS((void));
static int check_connection __ARGS((void));
@@ -9086,7 +9709,7 @@ f_remote_send(argvars, rettv)
}
/*
- * "remove({list}, {idx} [, {end}])" function
+ * "remove()" function
*/
static void
f_remove(argvars, rettv)
@@ -9098,10 +9721,32 @@ f_remove(argvars, rettv)
listitem *li;
long idx;
long end;
+ char_u *key;
+ dictvar *d;
+ dictitem *di, **pdi;
rettv->vval.v_number = 0;
- if (argvars[0].v_type != VAR_LIST)
- EMSG2(_(e_listarg), "remove()");
+ if (argvars[0].v_type == VAR_DICT)
+ {
+ if (argvars[2].v_type != VAR_UNKNOWN)
+ EMSG2(_(e_toomanyarg), "remove()");
+ else if ((d = argvars[0].vval.v_dict) != NULL)
+ {
+ key = get_tv_string(&argvars[1]);
+ pdi = &d->dv_first;
+ for (di = d->dv_first; di != NULL; pdi = &di->di_next, di = *pdi)
+ if (STRCMP(di->di_key, key) == 0)
+ {
+ *pdi = di->di_next;
+ dictitem_free(di);
+ break;
+ }
+ if (di == NULL)
+ EMSG2(_(e_dictkey), key);
+ }
+ }
+ else if (argvars[0].v_type != VAR_LIST)
+ EMSG2(_(e_listdictarg), "remove()");
else if ((l = argvars[0].vval.v_list) != NULL)
{
idx = get_tv_number(&argvars[1]);
@@ -10930,6 +11575,17 @@ f_type(argvars, rettv)
}
/*
+ * "values(dict)" function
+ */
+ static void
+f_values(argvars, rettv)
+ typeval *argvars;
+ typeval *rettv;
+{
+ dict_list(argvars, rettv, 1);
+}
+
+/*
* "virtcol(string)" function
*/
static void
@@ -11325,7 +11981,7 @@ find_name_end(arg, expr_start, expr_end, incl_br)
char_u *arg;
char_u **expr_start;
char_u **expr_end;
- int incl_br; /* Include [] indexes */
+ int incl_br; /* Include [] indexes and .name */
{
int mb_nest = 0;
int br_nest = 0;
@@ -11339,7 +11995,7 @@ find_name_end(arg, expr_start, expr_end, incl_br)
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
- || (*p == '[' && incl_br)
+ || (incl_br && (*p == '[' || *p == '.'))
|| mb_nest != 0
|| br_nest != 0); ++p)
{
@@ -11720,6 +12376,9 @@ clear_tv(varp)
case VAR_LIST:
list_unref(varp->vval.v_list);
break;
+ case VAR_DICT:
+ dict_unref(varp->vval.v_dict);
+ break;
case VAR_NUMBER:
varp->vval.v_number = 0;
break;
@@ -11823,10 +12482,13 @@ get_tv_string_buf(varp, buf)
sprintf((char *)buf, "%ld", (long)varp->vval.v_number);
return buf;
case VAR_FUNC:
- EMSG(_("E99: using Funcref as a String"));
+ EMSG(_("E999: using Funcref as a String"));
break;
case VAR_LIST:
- EMSG(_("E99: using List as a String"));
+ EMSG(_("E999: using List as a String"));
+ break;
+ case VAR_DICT:
+ EMSG(_("E999: using Dictionary as a String"));
break;
case VAR_STRING:
if (varp->vval.v_string != NULL)
@@ -12088,6 +12750,12 @@ list_one_var_a(prefix, name, type, string)
if (*string == '[')
++string;
}
+ else if (type == VAR_DICT)
+ {
+ msg_putchar('{');
+ if (*string == '{')
+ ++string;
+ }
else
msg_putchar(' ');
@@ -12239,6 +12907,15 @@ copy_tv(from, to)
++to->vval.v_list->lv_refcount;
}
break;
+ case VAR_DICT:
+ if (from->vval.v_dict == NULL)
+ to->vval.v_dict = NULL;
+ else
+ {
+ to->vval.v_dict = from->vval.v_dict;
+ ++to->vval.v_dict->dv_refcount;
+ }
+ break;
default:
EMSG2(_(e_intern2), "copy_tv()");
break;