diff options
author | Bram Moolenaar <Bram@vim.org> | 2005-01-15 22:18:47 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2005-01-15 22:18:47 +0000 |
commit | e9a41264986c009b366b42ba310074b0c17654c1 (patch) | |
tree | 7fd8e4312b7ba80630869387c889bc8cd99d1f27 /src | |
parent | f1ab380df5e001d79499ea27634a57cd1b980cc6 (diff) |
updated for version 7.0039v7.0039
Diffstat (limited to 'src')
-rw-r--r-- | src/eval.c | 965 |
1 files changed, 675 insertions, 290 deletions
diff --git a/src/eval.c b/src/eval.c index 100bc80a99..c25cb124ea 100644 --- a/src/eval.c +++ b/src/eval.c @@ -105,7 +105,7 @@ struct listvar_S typedef struct listvar_S listvar; -#define VAR_LIST_MAXNEST 100 /* maximum nesting of lists */ +#define VAR_MAXNEST 100 /* maximum nesting of lists and dicts */ /* * Structure to hold an item of a Dictionary. @@ -113,7 +113,7 @@ typedef struct listvar_S listvar; struct dictitem_S { struct dictitem_S *di_next; /* next item in list */ - char_u *di_key; /* key string */ + char_u *di_key; /* key (never NULL!) */ typeval di_tv; /* type and value of the variable */ }; @@ -137,7 +137,7 @@ 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_listdictarg = N_("E999: Argument of %s must be a List or Dictionaary"); +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"); @@ -157,7 +157,7 @@ static garray_T ga_scripts = {0, 0, sizeof(garray_T), 4, NULL}; #define VAR_ENTRY(idx) (((VAR)(variables.ga_data))[idx]) -#define VAR_GAP_ENTRY(idx, gap) (((VAR)(gap->ga_data))[idx]) +#define VAR_GAP_ENTRY(idx, gap) (((VAR)((gap)->ga_data))[idx]) #define BVAR_ENTRY(idx) (((VAR)(curbuf->b_vars.ga_data))[idx]) #define WVAR_ENTRY(idx) (((VAR)(curwin->w_vars.ga_data))[idx]) @@ -185,6 +185,7 @@ struct ufunc /* function flags */ #define FC_ABORT 1 /* abort function on error */ #define FC_RANGE 2 /* function accepts range */ +#define FC_DICT 4 /* Dict function, uses "self" */ /* * All user-defined functions are found in the forward-linked function list. @@ -224,9 +225,6 @@ typedef struct forinfo_S listvar *fi_list; /* list being used */ } forinfo; -/* used for map() */ -static typeval amp_tv; - /* * Return the name of the executed function. */ @@ -290,53 +288,61 @@ current_func_returned() #define VV_RO 2 /* read-only */ #define VV_RO_SBX 4 /* read-only in the sandbox*/ +#define VV_NAME(s) s, sizeof(s) - 1 + struct vimvar { char *name; /* name of variable, without v: */ int len; /* length of name */ - char_u *val; /* current value (can also be a number!) */ - char type; /* VAR_NUMBER or VAR_STRING */ + typeval tv; /* type and value */ char flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */ } vimvars[VV_LEN] = -{ /* The order here must match the VV_ defines in vim.h! */ - {"count", sizeof("count") - 1, NULL, VAR_NUMBER, VV_COMPAT+VV_RO}, - {"count1", sizeof("count1") - 1, NULL, VAR_NUMBER, VV_RO}, - {"prevcount", sizeof("prevcount") - 1, NULL, VAR_NUMBER, VV_RO}, - {"errmsg", sizeof("errmsg") - 1, NULL, VAR_STRING, VV_COMPAT}, - {"warningmsg", sizeof("warningmsg") - 1, NULL, VAR_STRING, 0}, - {"statusmsg", sizeof("statusmsg") - 1, NULL, VAR_STRING, 0}, - {"shell_error", sizeof("shell_error") - 1, NULL, VAR_NUMBER, - VV_COMPAT+VV_RO}, - {"this_session", sizeof("this_session") - 1, NULL, VAR_STRING, VV_COMPAT}, - {"version", sizeof("version") - 1, (char_u *)VIM_VERSION_100, - VAR_NUMBER, VV_COMPAT+VV_RO}, - {"lnum", sizeof("lnum") - 1, NULL, VAR_NUMBER, VV_RO_SBX}, - {"termresponse", sizeof("termresponse") - 1, NULL, VAR_STRING, VV_RO}, - {"fname", sizeof("fname") - 1, NULL, VAR_STRING, VV_RO}, - {"lang", sizeof("lang") - 1, NULL, VAR_STRING, VV_RO}, - {"lc_time", sizeof("lc_time") - 1, NULL, VAR_STRING, VV_RO}, - {"ctype", sizeof("ctype") - 1, NULL, VAR_STRING, VV_RO}, - {"charconvert_from", sizeof("charconvert_from") - 1, NULL, VAR_STRING, VV_RO}, - {"charconvert_to", sizeof("charconvert_to") - 1, NULL, VAR_STRING, VV_RO}, - {"fname_in", sizeof("fname_in") - 1, NULL, VAR_STRING, VV_RO}, - {"fname_out", sizeof("fname_out") - 1, NULL, VAR_STRING, VV_RO}, - {"fname_new", sizeof("fname_new") - 1, NULL, VAR_STRING, VV_RO}, - {"fname_diff", sizeof("fname_diff") - 1, NULL, VAR_STRING, VV_RO}, - {"cmdarg", sizeof("cmdarg") - 1, NULL, VAR_STRING, VV_RO}, - {"foldstart", sizeof("foldstart") - 1, NULL, VAR_NUMBER, VV_RO_SBX}, - {"foldend", sizeof("foldend") - 1, NULL, VAR_NUMBER, VV_RO_SBX}, - {"folddashes", sizeof("folddashes") - 1, NULL, VAR_STRING, VV_RO_SBX}, - {"foldlevel", sizeof("foldlevel") - 1, NULL, VAR_NUMBER, VV_RO_SBX}, - {"progname", sizeof("progname") - 1, NULL, VAR_STRING, VV_RO}, - {"servername", sizeof("servername") - 1, NULL, VAR_STRING, VV_RO}, - {"dying", sizeof("dying") - 1, NULL, VAR_NUMBER, VV_RO}, - {"exception", sizeof("exception") - 1, NULL, VAR_STRING, VV_RO}, - {"throwpoint", sizeof("throwpoint") - 1, NULL, VAR_STRING, VV_RO}, - {"register", sizeof("register") - 1, NULL, VAR_STRING, VV_RO}, - {"cmdbang", sizeof("cmdbang") - 1, NULL, VAR_NUMBER, VV_RO}, - {"insertmode", sizeof("insertmode") - 1, NULL, VAR_STRING, VV_RO}, +{ + /* + * The order here must match the VV_ defines in vim.h! + */ + {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}, }; +/* shorthand */ +#define vv_nr tv.vval.v_number +#define vv_str tv.vval.v_string + static int eval0 __ARGS((char_u *arg, typeval *rettv, char_u **nextcmd, int evaluate)); static int eval1 __ARGS((char_u **arg, typeval *rettv, int evaluate)); static int eval2 __ARGS((char_u **arg, typeval *rettv, int evaluate)); @@ -376,6 +382,7 @@ 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 dictitem *dictitem_copy __ARGS((dictitem *org)); 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)); @@ -388,8 +395,8 @@ static char_u *string_quote __ARGS((char_u *str, int function)); static int get_env_tv __ARGS((char_u **arg, typeval *rettv, int evaluate)); static int find_internal_func __ARGS((char_u *name)); static char_u *deref_func_name __ARGS((char_u *name, int *lenp)); -static int get_func_tv __ARGS((char_u *name, int len, typeval *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate)); -static int call_func __ARGS((char_u *name, int len, typeval *rettv, int argcount, typeval *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate)); +static int get_func_tv __ARGS((char_u *name, int len, typeval *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dictvar *selfdict)); +static int call_func __ARGS((char_u *name, int len, typeval *rettv, int argcount, typeval *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dictvar *selfdict)); static void f_add __ARGS((typeval *argvars, typeval *rettv)); static void f_append __ARGS((typeval *argvars, typeval *rettv)); @@ -462,6 +469,7 @@ static void f_getwinvar __ARGS((typeval *argvars, typeval *rettv)); static void f_glob __ARGS((typeval *argvars, typeval *rettv)); static void f_globpath __ARGS((typeval *argvars, typeval *rettv)); static void f_has __ARGS((typeval *argvars, typeval *rettv)); +static void f_has_key __ARGS((typeval *argvars, typeval *rettv)); static void f_hasmapto __ARGS((typeval *argvars, typeval *rettv)); static void f_histadd __ARGS((typeval *argvars, typeval *rettv)); static void f_histdel __ARGS((typeval *argvars, typeval *rettv)); @@ -575,7 +583,6 @@ static long get_tv_number __ARGS((typeval *varp)); static linenr_T get_tv_lnum __ARGS((typeval *argvars)); static char_u *get_tv_string __ARGS((typeval *varp)); static char_u *get_tv_string_buf __ARGS((typeval *varp, char_u *buf)); -static int get_amp_tv __ARGS((typeval *rettv)); static VAR find_var __ARGS((char_u *name, int writing)); static VAR find_var_in_ga __ARGS((garray_T *gap, char_u *varname)); static garray_T *find_var_ga __ARGS((char_u *name, char_u **varname)); @@ -585,6 +592,7 @@ static void list_vim_var __ARGS((int i)); static void list_one_var_a __ARGS((char_u *prefix, char_u *name, int type, char_u *string)); 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 internal)); static int eval_fname_script __ARGS((char_u *p)); @@ -593,7 +601,7 @@ 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 call_user_func __ARGS((ufunc_T *fp, int argcount, typeval *argvars, typeval *rettv, linenr_T firstline, linenr_T lastline)); +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) #define get_var_string_buf(p, b) get_tv_string_buf(&(p)->tv, (b)) @@ -961,7 +969,7 @@ call_vim_function(func, argc, argv, safe) rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ if (call_func(func, (int)STRLEN(func), &rettv, argc, argvars, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, TRUE) == OK) + &doesrange, TRUE, NULL) == OK) retval = vim_strsave(get_tv_string(&rettv)); clear_tv(&rettv); @@ -1341,7 +1349,7 @@ list_all_vars() if (WVAR_ENTRY(i).v_name != NULL) list_one_var(&WVAR_ENTRY(i), (char_u *)"w:"); for (i = 0; i < VV_LEN && !got_int; ++i) - if (vimvars[i].type == VAR_NUMBER || vimvars[i].val != NULL) + if (vimvars[i].tv.v_type == VAR_NUMBER || vimvars[i].vv_str != NULL) list_vim_var(i); } @@ -1558,7 +1566,7 @@ ex_let_one(arg, tv, copy, endchars) /* * ":let var = expr": Set internal variable. */ - else if (eval_isnamec(*arg) && !VIM_ISDIGIT(*arg)) + else if ((eval_isnamec(*arg) && !VIM_ISDIGIT(*arg)) || *arg == '{') { char_u *exp_name = NULL; char_u *expr_start, *expr_end; @@ -2249,8 +2257,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) == FAIL) + if (get_func_tv(name, len, &rettv, &arg, eap->line1, eap->line2, + &doesrange, !eap->skip, NULL) == FAIL) { failed = TRUE; break; @@ -3091,7 +3099,8 @@ eval5(arg, rettv, evaluate) rettv->v_type = VAR_STRING; rettv->vval.v_string = p; } - else if (rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) + else if (op == '+' && rettv->v_type == VAR_LIST + && var2.v_type == VAR_LIST) { /* concatenate Lists */ if (list_concat(rettv->vval.v_list, var2.vval.v_list, @@ -3242,6 +3251,7 @@ eval7(arg, rettv, evaluate) char_u *start_leader, *end_leader; int ret = OK; char_u *alias; + dictvar *selfdict; /* * Initialise variable so that clear_tv() can't mistake this for a @@ -3306,16 +3316,9 @@ eval7(arg, rettv, evaluate) break; /* - * Option value: &name or map() item "&". + * Option value: &name */ - case '&': if (!ASCII_ISALPHA(*(*arg + 1))) - { - *arg = skipwhite(*arg + 1); - if (evaluate) - ret = get_amp_tv(rettv); - } - else - ret = get_option_tv(arg, rettv, evaluate); + case '&': ret = get_option_tv(arg, rettv, evaluate); break; /* @@ -3380,7 +3383,7 @@ eval7(arg, rettv, evaluate) /* Invoke the function. */ ret = get_func_tv(s, len, rettv, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate); + &len, evaluate, NULL); /* Stop the expression evaluation when immediately * aborting on error, or when an interrupt occurred or * an exception was thrown but not caught. */ @@ -3403,14 +3406,47 @@ eval7(arg, rettv, evaluate) /* * Handle expr[expr], expr[expr:expr] subscript and .name lookup. + * Also handle function call with Funcref variable: func(expr) + * Can all be combined: dict.func(expr)[idx].func(expr) */ - while ((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT)) - && !vim_iswhite(*(*arg - 1)) && ret == OK) + selfdict = NULL; + while (ret == OK + && (**arg == '[' + || (**arg == '.' && rettv->v_type == VAR_DICT) + || (**arg == '(' && rettv->v_type == VAR_FUNC)) + && !vim_iswhite(*(*arg - 1))) { - if (eval_index(arg, rettv, evaluate) == FAIL) + if (**arg == '(') { - clear_tv(rettv); - return FAIL; + s = rettv->vval.v_string; + + /* Invoke the function. Recursive! */ + ret = get_func_tv(s, STRLEN(s), rettv, arg, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &len, evaluate, selfdict); + + /* 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; + } + selfdict = NULL; + } + else + { + if (rettv->v_type == VAR_DICT) + selfdict = rettv->vval.v_dict; + else + selfdict = NULL; + if (eval_index(arg, rettv, evaluate) == FAIL) + { + clear_tv(rettv); + ret = FAIL; + } } } @@ -4375,7 +4411,7 @@ list_concat(l1, l2, tv) } /* - * Make a copy of list "l". Shallow if "deep" is FALSE. + * Make a copy of list "orig". Shallow if "deep" is FALSE. * The refcount of the new list is set to 1. * Returns NULL when out of memory. */ @@ -4387,16 +4423,9 @@ list_copy(orig, deep) listvar *copy; listitem *item; listitem *ni; - static int recurse = 0; if (orig == NULL) return NULL; - if (recurse >= VAR_LIST_MAXNEST) - { - EMSG(_("E698: List nested too deep for making a copy")); - return NULL; - } - ++recurse; copy = list_alloc(); if (copy != NULL) @@ -4406,17 +4435,8 @@ list_copy(orig, deep) ni = listitem_alloc(); if (ni == NULL) break; - if (deep && item->li_tv.v_type == VAR_LIST) - { - ni->li_tv.v_type = VAR_LIST; - ni->li_tv.vval.v_list = list_copy(item->li_tv.vval.v_list, - TRUE); - if (ni->li_tv.vval.v_list == NULL) - { - vim_free(ni); - break; - } - } + if (deep) + item_copy(&item->li_tv, &ni->li_tv, deep); else copy_tv(&item->li_tv, &ni->li_tv); list_append(copy, ni); @@ -4424,7 +4444,6 @@ list_copy(orig, deep) ++copy->lv_refcount; } - --recurse; return copy; } @@ -4561,6 +4580,29 @@ dictitem_alloc() } /* + * Make a copy of a Dictionary item. + */ + static dictitem * +dictitem_copy(org) + dictitem *org; +{ + dictitem *di; + + di = (dictitem *)alloc(sizeof(dictitem)); + if (di != NULL) + { + di->di_key = vim_strsave(org->di_key); + if (di->di_key == NULL) + { + vim_free(di); + return NULL; + } + copy_tv(&org->di_tv, &di->di_tv); + } + return di; +} + +/* * Free a dict item. Also clears the value. */ static void @@ -4573,6 +4615,49 @@ dictitem_free(item) } /* + * Make a copy of dict "d". Shallow if "deep" is FALSE. + * The refcount of the new dict is set to 1. + * Returns NULL when out of memory. + */ + static dictvar * +dict_copy(orig, deep) + dictvar *orig; + int deep; +{ + dictvar *copy; + dictitem *item; + dictitem *di; + + if (orig == NULL) + return NULL; + + copy = dict_alloc(); + if (copy != NULL) + { + for (item = orig->dv_first; item != NULL; item = item->di_next) + { + di = dictitem_alloc(); + if (di == NULL) + break; + di->di_key = vim_strsave(item->di_key); + if (di->di_key == NULL) + { + vim_free(di); + break; + } + if (deep) + item_copy(&item->di_tv, &di->di_tv, deep); + else + copy_tv(&item->di_tv, &di->di_tv); + dict_add(copy, di); + } + ++copy->dv_refcount; + } + + return copy; +} + +/* * Add item "item" to Dictionary "d". */ static void @@ -4639,6 +4724,23 @@ dict_set_item(d, type, key, val) #endif /* + * Get the number of items in a Dictionary. + */ + static long +dict_len(d) + dictvar *d; +{ + dictitem *item; + long len = 0; + + if (d == NULL) + return 0L; + for (item = d->dv_first; item != NULL; item = item->di_next) + ++len; + return len; +} + +/* * Find item "key[len]" in Dictionary "d". * If "len" is negative use strlen(key). * Returns NULL when not found. @@ -4725,11 +4827,15 @@ get_dict_tv(arg, rettv, evaluate) * 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. + * But {} is an empty Dictionary. */ - if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */ - return FAIL; - if (*start == '}') - return NOTDONE; + if (*start != '}') + { + if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */ + return FAIL; + if (*start == '}') + return NOTDONE; + } if (evaluate) { @@ -4832,25 +4938,43 @@ echo_string(tv, tofree, numbuf) char_u **tofree; char_u *numbuf; { + static int recurse = 0; + char_u *r = NULL; + + if (recurse >= VAR_MAXNEST) + { + EMSG(_("E999: variable nested too deep for displaying")); + *tofree = NULL; + return NULL; + } + ++recurse; + switch (tv->v_type) { case VAR_FUNC: *tofree = NULL; - return tv->vval.v_string; + r = tv->vval.v_string; + break; case VAR_LIST: *tofree = list2string(tv); - return *tofree; + r = *tofree; + break; case VAR_DICT: *tofree = dict2string(tv); - return *tofree; + r = *tofree; + break; case VAR_STRING: case VAR_NUMBER: + *tofree = NULL; + r = get_tv_string_buf(tv, numbuf); break; default: EMSG2(_(e_intern2), "echo_string()"); + *tofree = NULL; } - *tofree = NULL; - return get_tv_string_buf(tv, numbuf); + + --recurse; + return r; } /* @@ -4868,25 +4992,20 @@ tv2string(tv, tofree, numbuf) { switch (tv->v_type) { - case VAR_NUMBER: - break; case VAR_FUNC: *tofree = string_quote(tv->vval.v_string, TRUE); return *tofree; case VAR_STRING: *tofree = string_quote(tv->vval.v_string, FALSE); return *tofree; + case VAR_NUMBER: case VAR_LIST: - *tofree = list2string(tv); - return *tofree; case VAR_DICT: - *tofree = dict2string(tv); - return *tofree; + break; default: EMSG2(_(e_intern2), "tv2string()"); } - *tofree = NULL; - return get_tv_string_buf(tv, numbuf); + return echo_string(tv, tofree, numbuf); } /* @@ -5008,7 +5127,7 @@ static struct fst {"bufwinnr", 1, 1, f_bufwinnr}, {"byte2line", 1, 1, f_byte2line}, {"byteidx", 2, 2, f_byteidx}, - {"call", 2, 2, f_call}, + {"call", 2, 3, f_call}, {"char2nr", 1, 1, f_char2nr}, {"cindent", 1, 1, f_cindent}, {"col", 1, 1, f_col}, @@ -5065,6 +5184,7 @@ static struct fst {"glob", 1, 1, f_glob}, {"globpath", 2, 2, f_globpath}, {"has", 1, 1, f_has}, + {"has_key", 2, 2, f_has_key}, {"hasmapto", 1, 2, f_hasmapto}, {"highlightID", 1, 1, f_hlID}, /* obsolete */ {"highlight_exists",1, 1, f_hlexists}, /* obsolete */ @@ -5288,7 +5408,8 @@ deref_func_name(name, lenp) * Return OK or FAIL. */ static int -get_func_tv(name, len, rettv, arg, firstline, lastline, doesrange, evaluate) +get_func_tv(name, len, rettv, arg, firstline, lastline, doesrange, + evaluate, selfdict) char_u *name; /* name of the function */ int len; /* length of "name" */ typeval *rettv; @@ -5297,6 +5418,7 @@ get_func_tv(name, len, rettv, arg, firstline, lastline, doesrange, evaluate) linenr_T lastline; /* last line of range */ int *doesrange; /* return: function handled range */ int evaluate; + dictvar *selfdict; /* Dictionary for "self" */ { char_u *argp; int ret = OK; @@ -5329,7 +5451,7 @@ get_func_tv(name, len, rettv, arg, firstline, lastline, doesrange, evaluate) if (ret == OK) ret = call_func(name, len, rettv, argcount, argvars, - firstline, lastline, doesrange, evaluate); + firstline, lastline, doesrange, evaluate, selfdict); else if (!aborting()) EMSG2(_("E116: Invalid arguments for function %s"), name); @@ -5347,7 +5469,7 @@ get_func_tv(name, len, rettv, arg, firstline, lastline, doesrange, evaluate) */ static int call_func(name, len, rettv, argcount, argvars, firstline, lastline, - doesrange, evaluate) + doesrange, evaluate, selfdict) char_u *name; /* name of the function */ int len; /* length of "name" */ typeval *rettv; /* return value goes here */ @@ -5357,14 +5479,16 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline, linenr_T lastline; /* last line of range */ int *doesrange; /* return: function handled range */ int evaluate; + dictvar *selfdict; /* Dictionary for "self" */ { int ret = FAIL; #define ERROR_UNKNOWN 0 #define ERROR_TOOMANY 1 #define ERROR_TOOFEW 2 #define ERROR_SCRIPT 3 -#define ERROR_NONE 4 -#define ERROR_OTHER 5 +#define ERROR_DICT 4 +#define ERROR_NONE 5 +#define ERROR_OTHER 6 int error = ERROR_NONE; int i; int llen; @@ -5453,6 +5577,8 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline, error = ERROR_TOOFEW; else if (!fp->varargs && argcount > fp->args.ga_len) error = ERROR_TOOMANY; + else if ((fp->flags & FC_DICT) && selfdict == NULL) + error = ERROR_DICT; else { /* @@ -5464,7 +5590,8 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline, saveRedobuff(); ++fp->calls; call_user_func(fp, argcount, argvars, rettv, - firstline, lastline); + firstline, lastline, + (fp->flags & FC_DICT) ? selfdict : NULL); --fp->calls; restoreRedobuff(); restore_search_patterns(); @@ -5529,6 +5656,10 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline, EMSG2(_("E120: Using <SID> not in a script context: %s"), name); break; + case ERROR_DICT: + EMSG2(_("E999: Calling dict function without Dictionary: %s"), + name); + break; } } @@ -5965,6 +6096,7 @@ f_call(argvars, rettv) int argc = 0; listitem *item; int dummy; + dictvar *selfdict = NULL; rettv->vval.v_number = 0; if (argvars[1].v_type != VAR_LIST) @@ -5980,6 +6112,16 @@ f_call(argvars, rettv) else func = get_tv_string(&argvars[0]); + if (argvars[2].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + selfdict = argvars[2].vval.v_dict; + } + for (item = argvars[1].vval.v_list->lv_first; item != NULL; item = item->li_next) { @@ -5994,7 +6136,8 @@ f_call(argvars, rettv) if (item == NULL) (void)call_func(func, STRLEN(func), rettv, argc, argv, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, TRUE); + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &dummy, TRUE, selfdict); /* Free the arguments. */ while (argc > 0) @@ -6154,13 +6297,7 @@ f_copy(argvars, rettv) typeval *argvars; typeval *rettv; { - if (argvars[0].v_type == VAR_LIST) - { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = list_copy(argvars[0].vval.v_list, FALSE); - } - else - copy_tv(&argvars[0], rettv); + item_copy(&argvars[0], rettv, FALSE); } /* @@ -6171,29 +6308,56 @@ f_count(argvars, rettv) typeval *argvars; typeval *rettv; { - listitem *li; long n = 0; int ic = FALSE; - if (argvars[0].v_type != VAR_LIST) - EMSG(_(e_listreq)); - else if (argvars[0].vval.v_list != NULL) + if (argvars[0].v_type == VAR_LIST) { - li = argvars[0].vval.v_list->lv_first; - if (argvars[2].v_type != VAR_UNKNOWN) + listitem *li; + listvar *l; + long idx; + + if ((l = argvars[0].vval.v_list) != NULL) { - for (n = get_tv_number(&argvars[2]); n > 0 && li != NULL; - li = li->li_next) - --n; - if (argvars[3].v_type != VAR_UNKNOWN) - ic = get_tv_number(&argvars[3]); + li = l->lv_first; + if (argvars[2].v_type != VAR_UNKNOWN) + { + ic = get_tv_number(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) + { + idx = get_tv_number(&argvars[3]); + li = list_find(l, idx); + if (li == NULL) + EMSGN(_(e_listidx), idx); + } + } + + for ( ; li != NULL; li = li->li_next) + if (tv_equal(&li->li_tv, &argvars[1], ic)) + ++n; } + } + else if (argvars[0].v_type == VAR_DICT) + { + if (argvars[0].vval.v_dict != NULL) + { + dictitem *di; - n = 0; - for ( ; li != NULL; li = li->li_next) - if (tv_equal(&li->li_tv, &argvars[1], ic)) - ++n; + di = argvars[0].vval.v_dict->dv_first; + if (argvars[2].v_type != VAR_UNKNOWN) + { + ic = get_tv_number(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) + EMSG(_(e_invarg)); + } + + for ( ; di != NULL; di = di->di_next) + if (tv_equal(&di->di_tv, &argvars[1], ic)) + ++n; + } } + else + EMSG2(_(e_listdictarg), "count()"); rettv->vval.v_number = n; } @@ -6271,13 +6435,7 @@ f_deepcopy(argvars, rettv) typeval *argvars; typeval *rettv; { - if (argvars[0].v_type == VAR_LIST) - { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = list_copy(argvars[0].vval.v_list, TRUE); - } - else - copy_tv(&argvars[0], rettv); + item_copy(&argvars[0], rettv, TRUE); } /* @@ -6407,6 +6565,10 @@ f_empty(argvars, rettv) n = argvars[0].vval.v_list == NULL || argvars[0].vval.v_list->lv_first == NULL; break; + case VAR_DICT: + n = argvars[0].vval.v_dict == NULL + || argvars[0].vval.v_dict->dv_first == NULL; + break; default: EMSG2(_(e_intern2), "f_empty()"); n = 0; @@ -6590,44 +6752,101 @@ f_expand(argvars, rettv) /* * "extend(list, list [, idx])" function + * "extend(dict, dict [, action])" function */ static void f_extend(argvars, rettv) typeval *argvars; typeval *rettv; { - long before; - long n; - listitem *item; - listvar *l1, *l2; - rettv->vval.v_number = 0; - if (argvars[0].v_type != VAR_LIST || argvars[1].v_type != VAR_LIST) + if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { - EMSG(_(e_listreq)); - return; + listvar *l1, *l2; + listitem *item; + long before; + long n; + + l1 = argvars[0].vval.v_list; + l2 = argvars[1].vval.v_list; + if (l1 != NULL && l2 != NULL) + { + if (argvars[2].v_type != VAR_UNKNOWN) + { + n = before = get_tv_number(&argvars[2]); + item = list_find_ext(l1, &n); + if (n != 0) + { + EMSGN(_(e_listidx), before); + return; + } + } + else + item = NULL; + list_extend(l1, l2, item); + + ++l1->lv_refcount; + copy_tv(&argvars[0], rettv); + } } - l1 = argvars[0].vval.v_list; - l2 = argvars[1].vval.v_list; - if (l1 != NULL && l2 != NULL) + else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - if (argvars[2].v_type != VAR_UNKNOWN) + dictvar *d1, *d2; + dictitem *d1i, *d2i; + char_u *action; + int i; + + d1 = argvars[0].vval.v_dict; + d2 = argvars[1].vval.v_dict; + if (d1 != NULL && d2 != NULL) { - n = before = get_tv_number(&argvars[2]); - item = list_find_ext(l1, &n); - if (n != 0) + /* Check the third argument. */ + if (argvars[2].v_type != VAR_UNKNOWN) { - EMSGN(_(e_listidx), before); - return; + static char *(av[]) = {"keep", "force", "error"}; + + action = get_tv_string(&argvars[2]); + for (i = 0; i < 3; ++i) + if (STRCMP(action, av[i]) == 0) + break; + if (i == 3) + { + EMSGN(_(e_invarg2), action); + return; + } + } + else + action = (char_u *)"force"; + + /* Go over all entries in the second dict and add them to the + * first dict. */ + for (d2i = d2->dv_first; d2i != NULL; d2i = d2i->di_next) + { + d1i = dict_find(d1, d2i->di_key, -1); + if (d1i == NULL) + { + d1i = dictitem_copy(d2i); + if (d1i != NULL) + dict_add(d1, d1i); + } + else if (*action == 'e') + { + EMSG2(_("Key already exists: %s"), d2i->di_key); + break; + } + else if (*action == 'f') + { + clear_tv(&d1i->di_tv); + copy_tv(&d2i->di_tv, &d1i->di_tv); + } } - } - else - item = NULL; - list_extend(l1, l2, item); - ++l1->lv_refcount; - copy_tv(&argvars[0], rettv); + ++d1->dv_refcount; + copy_tv(&argvars[0], rettv); + } } + else + EMSG2(_(e_listdictarg), "extend()"); } /* @@ -6739,6 +6958,7 @@ findfilendir(argvars, rettv, dir) } static void filter_map __ARGS((typeval *argvars, typeval *rettv, int map)); +static int filter_map_one __ARGS((typeval *tv, char_u *expr, int map, int *remp)); /* * Implementation of map() and filter(). @@ -6750,52 +6970,111 @@ filter_map(argvars, rettv, map) int map; { char_u buf[NUMBUFLEN]; - char_u *expr, *s; + char_u *expr; listitem *li, *nli; - listvar *l; + listvar *l = NULL; + dictitem *di, **pdi; + dictvar *d = NULL; + typeval save_val; + typeval save_key; + int rem; rettv->vval.v_number = 0; - if (argvars[0].v_type != VAR_LIST) - EMSG(_(e_listreq)); - else if ((l = argvars[0].vval.v_list) != NULL) + if (argvars[0].v_type == VAR_LIST) { - expr = skipwhite(get_tv_string_buf(&argvars[1], buf)); - for (li = l->lv_first; li != NULL; li = nli) + if ((l = argvars[0].vval.v_list) == NULL) + return; + } + else if (argvars[0].v_type == VAR_DICT) + { + if ((d = argvars[0].vval.v_dict) == NULL) + return; + } + else + { + EMSG2(_(e_listdictarg), map ? "map()" : "filter()"); + return; + } + + expr = skipwhite(get_tv_string_buf(&argvars[1], buf)); + save_val = vimvars[VV_VAL].tv; + + if (argvars[0].v_type == VAR_DICT) + { + save_key = vimvars[VV_KEY].tv; + vimvars[VV_KEY].tv.v_type = VAR_STRING; + pdi = &d->dv_first; + for (di = d->dv_first; di != NULL; di = *pdi) { - copy_tv(&li->li_tv, &_tv); - s = expr; - if (eval1(&s, rettv, TRUE) == FAIL) + vimvars[VV_KEY].tv.vval.v_string = vim_strsave(di->di_key); + if (filter_map_one(&di->di_tv, expr, map, &rem) == FAIL) break; - if (*s != NUL) /* check for trailing chars after expr */ + if (!map && rem) { - EMSG2(_(e_invexpr2), s); - break; + *pdi = di->di_next; + dictitem_free(di); } + else + pdi = &di->di_next; + clear_tv(&vimvars[VV_KEY].tv); + } + clear_tv(&vimvars[VV_KEY].tv); + vimvars[VV_KEY].tv = save_key; + } + else + { + for (li = l->lv_first; li != NULL; li = nli) + { nli = li->li_next; - if (map) + if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL) + break; + if (!map && rem) { - /* map(): replace the list item value */ clear_tv(&li->li_tv); - li->li_tv = *rettv; - } - else - { - /* filter(): when expr is zero remove the item */ - if (get_tv_number(rettv) == 0) - { - list_getrem(l, li, li); - clear_tv(&li->li_tv); - } - clear_tv(rettv); + list_getrem(l, li, li); } - clear_tv(&_tv); } + } + + clear_tv(&vimvars[VV_VAL].tv); + vimvars[VV_VAL].tv = save_val; - clear_tv(&_tv); - amp_tv.v_type = VAR_UNKNOWN; + copy_tv(&a |