diff src/evalvars.c @ 19181:94eda51ba9ba v8.2.0149

patch 8.2.0149: maintaining a Vim9 branch separately is more work Commit: https://github.com/vim/vim/commit/8a7d6542b33e5d2b352262305c3bfdb2d14e1cf8 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 26 15:56:19 2020 +0100 patch 8.2.0149: maintaining a Vim9 branch separately is more work Problem: Maintaining a Vim9 branch separately is more work. Solution: Merge the Vim9 script changes.
author Bram Moolenaar <Bram@vim.org>
date Sun, 26 Jan 2020 16:00:05 +0100
parents 44c6498535c9
children 133ef7ba4e4e
line wrap: on
line diff
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -15,8 +15,6 @@
 
 #if defined(FEAT_EVAL) || defined(PROTO)
 
-static char *e_letunexp	= N_("E18: Unexpected characters in :let");
-
 static dictitem_T	globvars_var;		// variable used for g:
 static dict_T		globvardict;		// Dictionary with g: variables
 #define globvarht globvardict.dv_hashtab
@@ -41,6 +39,8 @@ static hashtab_T	compat_hashtab;
 
 #define VV_NAME(s, t)	s, {{t, 0, {0}}, 0, {0}}
 
+typedef struct vimvar vimvar_T;
+
 static struct vimvar
 {
     char	*vv_name;	// name of variable, without v:
@@ -163,17 +163,14 @@ static dict_T		vimvardict;		// Dictionar
 // for VIM_VERSION_ defines
 #include "version.h"
 
-#define SCRIPT_SV(id) (SCRIPT_ITEM(id).sn_vars)
-#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
-
 static void ex_let_const(exarg_T *eap, int is_const);
-static char_u *skip_var_one(char_u *arg);
+static char_u *skip_var_one(char_u *arg, int include_type);
 static void list_glob_vars(int *first);
 static void list_buf_vars(int *first);
 static void list_win_vars(int *first);
 static void list_tab_vars(int *first);
 static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
-static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int is_const, char_u *endchars, char_u *op);
+static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op);
 static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep);
 static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit);
 static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock);
@@ -544,7 +541,7 @@ list_script_vars(int *first)
  * indentation in the 'cmd' line) is stripped.
  * Returns a List with {lines} or NULL.
  */
-    static list_T *
+    list_T *
 heredoc_get(exarg_T *eap, char_u *cmd)
 {
     char_u	*theline;
@@ -669,6 +666,7 @@ heredoc_get(exarg_T *eap, char_u *cmd)
  * ":let var .= expr"		assignment command.
  * ":let var ..= expr"		assignment command.
  * ":let [var1, var2] = expr"	unpack list.
+ * ":let var =<< ..."		heredoc
  */
     void
 ex_let(exarg_T *eap)
@@ -701,8 +699,13 @@ ex_let_const(exarg_T *eap, int is_const)
     char_u	*argend;
     int		first = TRUE;
     int		concat;
-
-    argend = skip_var_list(arg, &var_count, &semicolon);
+    int		flags = is_const ? LET_IS_CONST : 0;
+
+    // detect Vim9 assignment without ":let" or ":const"
+    if (eap->arg == eap->cmd)
+	flags |= LET_NO_COMMAND;
+
+    argend = skip_var_list(arg, TRUE, &var_count, &semicolon);
     if (argend == NULL)
 	return;
     if (argend > arg && argend[-1] == '.')  // for var.='str'
@@ -749,7 +752,7 @@ ex_let_const(exarg_T *eap, int is_const)
 		op[0] = '=';
 		op[1] = NUL;
 		(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
-								is_const, op);
+								flags, op);
 	    }
 	    clear_tv(&rettv);
 	}
@@ -783,7 +786,7 @@ ex_let_const(exarg_T *eap, int is_const)
 	else if (i != FAIL)
 	{
 	    (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
-								 is_const, op);
+								 flags, op);
 	    clear_tv(&rettv);
 	}
     }
@@ -804,7 +807,7 @@ ex_let_vars(
     int		copy,		// copy values from "tv", don't move
     int		semicolon,	// from skip_var_list()
     int		var_count,	// from skip_var_list()
-    int		is_const,	// lock variables for const
+    int		flags,		// LET_IS_CONST and/or LET_NO_COMMAND
     char_u	*op)
 {
     char_u	*arg = arg_start;
@@ -816,7 +819,7 @@ ex_let_vars(
     if (*arg != '[')
     {
 	// ":let var = expr" or ":for var in list"
-	if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL)
+	if (ex_let_one(arg, tv, copy, flags, op, op) == NULL)
 	    return FAIL;
 	return OK;
     }
@@ -844,8 +847,7 @@ ex_let_vars(
     while (*arg != ']')
     {
 	arg = skipwhite(arg + 1);
-	arg = ex_let_one(arg, &item->li_tv, TRUE, is_const,
-							  (char_u *)",;]", op);
+	arg = ex_let_one(arg, &item->li_tv, TRUE, flags, (char_u *)",;]", op);
 	item = item->li_next;
 	if (arg == NULL)
 	    return FAIL;
@@ -869,7 +871,7 @@ ex_let_vars(
 	    ltv.vval.v_list = l;
 	    l->lv_refcount = 1;
 
-	    arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, is_const,
+	    arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, flags,
 							    (char_u *)"]", op);
 	    clear_tv(&ltv);
 	    if (arg == NULL)
@@ -896,6 +898,7 @@ ex_let_vars(
     char_u *
 skip_var_list(
     char_u	*arg,
+    int		include_type,
     int		*var_count,
     int		*semicolon)
 {
@@ -908,7 +911,7 @@ skip_var_list(
 	for (;;)
 	{
 	    p = skipwhite(p + 1);	// skip whites after '[', ';' or ','
-	    s = skip_var_one(p);
+	    s = skip_var_one(p, TRUE);
 	    if (s == p)
 	    {
 		semsg(_(e_invarg2), p);
@@ -937,20 +940,29 @@ skip_var_list(
 	return p + 1;
     }
     else
-	return skip_var_one(arg);
+	return skip_var_one(arg, include_type);
 }
 
 /*
  * Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
  * l[idx].
+ * In Vim9 script also skip over ": type" if "include_type" is TRUE.
  */
     static char_u *
-skip_var_one(char_u *arg)
+skip_var_one(char_u *arg, int include_type)
 {
+    char_u *end;
+
     if (*arg == '@' && arg[1] != NUL)
 	return arg + 2;
-    return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
+    end = find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
 				   NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
+    if (include_type && current_sctx.sc_version == SCRIPT_VERSION_VIM9
+								&& *end == ':')
+    {
+	end = skip_type(skipwhite(end + 1));
+    }
+    return end;
 }
 
 /*
@@ -1141,7 +1153,7 @@ ex_let_one(
     char_u	*arg,		// points to variable name
     typval_T	*tv,		// value to assign to variable
     int		copy,		// copy value from "tv"
-    int		is_const,	// lock variable for const
+    int		flags,		// LET_IS_CONST and/or LET_NO_COMMAND
     char_u	*endchars,	// valid chars after variable name  or NULL
     char_u	*op)		// "+", "-", "."  or NULL
 {
@@ -1156,7 +1168,7 @@ ex_let_one(
     // ":let $VAR = expr": Set environment variable.
     if (*arg == '$')
     {
-	if (is_const)
+	if (flags & LET_IS_CONST)
 	{
 	    emsg(_("E996: Cannot lock an environment variable"));
 	    return NULL;
@@ -1214,9 +1226,9 @@ ex_let_one(
     // ":let &g:option = expr": Set global option value.
     else if (*arg == '&')
     {
-	if (is_const)
+	if (flags & LET_IS_CONST)
 	{
-	    emsg(_("E996: Cannot lock an option"));
+	    emsg(_(e_const_option));
 	    return NULL;
 	}
 	// Find the end of the name.
@@ -1281,7 +1293,7 @@ ex_let_one(
     // ":let @r = expr": Set register contents.
     else if (*arg == '@')
     {
-	if (is_const)
+	if (flags & LET_IS_CONST)
 	{
 	    emsg(_("E996: Cannot lock a register"));
 	    return NULL;
@@ -1317,6 +1329,7 @@ ex_let_one(
     }
 
     // ":let var = expr": Set internal variable.
+    // ":let var: type = expr": Set internal variable with type.
     // ":let {expr} = expr": Idem, name made with curly braces
     else if (eval_isnamec1(*arg) || *arg == '{')
     {
@@ -1325,11 +1338,12 @@ ex_let_one(
 	p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START);
 	if (p != NULL && lv.ll_name != NULL)
 	{
-	    if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL)
+	    if (endchars != NULL && vim_strchr(endchars,
+					   *skipwhite(lv.ll_name_end)) == NULL)
 		emsg(_(e_letunexp));
 	    else
 	    {
-		set_var_lval(&lv, p, tv, copy, is_const, op);
+		set_var_lval(&lv, p, tv, copy, flags, op);
 		arg_end = p;
 	    }
 	}
@@ -1657,12 +1671,13 @@ item_lock(typval_T *tv, int deep, int lo
     switch (tv->v_type)
     {
 	case VAR_UNKNOWN:
+	case VAR_VOID:
 	case VAR_NUMBER:
+	case VAR_BOOL:
 	case VAR_STRING:
 	case VAR_FUNC:
 	case VAR_PARTIAL:
 	case VAR_FLOAT:
-	case VAR_BOOL:
 	case VAR_SPECIAL:
 	case VAR_JOB:
 	case VAR_CHANNEL:
@@ -1901,6 +1916,22 @@ get_vimvar_dict(void)
 }
 
 /*
+ * Returns the index of a v:variable.  Negative if not found.
+ */
+    int
+find_vim_var(char_u *name)
+{
+    dictitem_T *di = find_var_in_ht(&vimvarht, 0, name, TRUE);
+    struct vimvar *vv;
+
+    if (di == NULL)
+	return -1;
+    vv = (struct vimvar *)((char *)di - offsetof(vimvar_T, vv_di));
+    return (int)(vv - vimvars);
+}
+
+
+/*
  * Set type of v: variable to "type".
  */
     void
@@ -1919,6 +1950,12 @@ set_vim_var_nr(int idx, varnumber_T val)
     vimvars[idx].vv_nr = val;
 }
 
+    char *
+get_vim_var_name(int idx)
+{
+    return vimvars[idx].vv_name;
+}
+
 /*
  * Get typval_T v: variable value.
  */
@@ -2245,6 +2282,20 @@ get_var_tv(
 	    *dip = v;
     }
 
+    if (tv == NULL && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
+    {
+	imported_T *import = find_imported(name, NULL);
+
+	// imported variable from another script
+	if (import != NULL)
+	{
+	    scriptitem_T    *si = &SCRIPT_ITEM(import->imp_sid);
+	    svar_T	    *sv = ((svar_T *)si->sn_var_vals.ga_data)
+						    + import->imp_var_vals_idx;
+	    tv = sv->sv_tv;
+	}
+    }
+
     if (tv == NULL)
     {
 	if (rettv != NULL && verbose)
@@ -2366,6 +2417,58 @@ find_var_in_ht(
 }
 
 /*
+ * Get the script-local hashtab.  NULL if not in a script context.
+ */
+    hashtab_T *
+get_script_local_ht(void)
+{
+    scid_T sid = current_sctx.sc_sid;
+
+    if (sid > 0 && sid <= script_items.ga_len)
+	return &SCRIPT_VARS(sid);
+    return NULL;
+}
+
+/*
+ * Look for "name[len]" in script-local variables.
+ * Return -1 when not found.
+ */
+    int
+lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
+{
+    hashtab_T	*ht = get_script_local_ht();
+    char_u	buffer[30];
+    char_u	*p;
+    int		res;
+    hashitem_T	*hi;
+
+    if (ht == NULL)
+	return -1;
+    if (len < sizeof(buffer) - 1)
+    {
+	vim_strncpy(buffer, name, len);
+	p = buffer;
+    }
+    else
+    {
+	p = vim_strnsave(name, (int)len);
+	if (p == NULL)
+	    return -1;
+    }
+
+    hi = hash_find(ht, p);
+    res = HASHITEM_EMPTY(hi) ? -1 : 1;
+
+    // if not script-local, then perhaps imported
+    if (res == -1 && find_imported(p, NULL) != NULL)
+	res = 1;
+
+    if (p != buffer)
+	vim_free(p);
+    return res;
+}
+
+/*
  * Find the hashtab used for a variable name.
  * Return NULL if the name is not valid.
  * Set "varname" to the start of name without ':'.
@@ -2395,9 +2498,18 @@ find_var_ht(char_u *name, char_u **varna
 	}
 
 	ht = get_funccal_local_ht();
-	if (ht == NULL)
-	    return &globvarht;			// global variable
-	return ht;				// local variable
+	if (ht != NULL)
+	    return ht;				// local variable
+
+	// in Vim9 script items at the script level are script-local
+	if (current_sctx.sc_version == SCRIPT_VERSION_VIM9)
+	{
+	    ht = get_script_local_ht();
+	    if (ht != NULL)
+		return ht;
+	}
+
+	return &globvarht;			// global variable
     }
     *varname = name + 2;
     if (*name == 'g')				// global variable
@@ -2414,14 +2526,19 @@ find_var_ht(char_u *name, char_u **varna
 	return &curtab->tp_vars->dv_hashtab;
     if (*name == 'v')				// v: variable
 	return &vimvarht;
-    if (*name == 'a')				// a: function argument
-	return get_funccal_args_ht();
-    if (*name == 'l')				// l: local function variable
-	return get_funccal_local_ht();
-    if (*name == 's'				// script variable
-	    && current_sctx.sc_sid > 0
-	    && current_sctx.sc_sid <= script_items.ga_len)
-	return &SCRIPT_VARS(current_sctx.sc_sid);
+    if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
+    {
+	if (*name == 'a')			// a: function argument
+	    return get_funccal_args_ht();
+	if (*name == 'l')			// l: local function variable
+	    return get_funccal_local_ht();
+    }
+    if (*name == 's')				// script variable
+    {
+	ht = get_script_local_ht();
+	if (ht != NULL)
+	    return ht;
+    }
     return NULL;
 }
 
@@ -2617,7 +2734,7 @@ set_var(
     typval_T	*tv,
     int		copy)	    // make copy of value in "tv"
 {
-    set_var_const(name, tv, copy, FALSE);
+    set_var_const(name, NULL, tv, copy, 0);
 }
 
 /*
@@ -2628,13 +2745,15 @@ set_var(
     void
 set_var_const(
     char_u	*name,
+    type_T	*type,
     typval_T	*tv,
     int		copy,	    // make copy of value in "tv"
-    int		is_const)   // disallow to modify existing variable
+    int		flags)	    // LET_IS_CONST and/or LET_NO_COMMAND
 {
-    dictitem_T	*v;
+    dictitem_T	*di;
     char_u	*varname;
     hashtab_T	*ht;
+    int		is_script_local;
 
     ht = find_var_ht(name, &varname);
     if (ht == NULL || *varname == NUL)
@@ -2642,75 +2761,92 @@ set_var_const(
 	semsg(_(e_illvar), name);
 	return;
     }
-    v = find_var_in_ht(ht, 0, varname, TRUE);
+    is_script_local = ht == get_script_local_ht();
+
+    di = find_var_in_ht(ht, 0, varname, TRUE);
 
     // Search in parent scope which is possible to reference from lambda
-    if (v == NULL)
-	v = find_var_in_scoped_ht(name, TRUE);
+    if (di == NULL)
+	di = find_var_in_scoped_ht(name, TRUE);
 
     if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
-				      && var_check_func_name(name, v == NULL))
+				      && var_check_func_name(name, di == NULL))
 	return;
 
-    if (v != NULL)
+    if (di != NULL)
     {
-	if (is_const)
+	if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
 	{
-	    emsg(_(e_cannot_mod));
-	    return;
+	    if (flags & LET_IS_CONST)
+	    {
+		emsg(_(e_cannot_mod));
+		return;
+	    }
+
+	    if (var_check_ro(di->di_flags, name, FALSE)
+			       || var_check_lock(di->di_tv.v_lock, name, FALSE))
+		return;
+
+	    if ((flags & LET_NO_COMMAND) == 0
+		    && is_script_local
+		    && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
+	    {
+		semsg(_("E1041: Redefining script item %s"), name);
+		return;
+	    }
 	}
+	else
+	    // can only redefine once
+	    di->di_flags &= ~DI_FLAGS_RELOAD;
 
 	// existing variable, need to clear the value
-	if (var_check_ro(v->di_flags, name, FALSE)
-			      || var_check_lock(v->di_tv.v_lock, name, FALSE))
-	    return;
-
-	// Handle setting internal v: variables separately where needed to
+
+	// Handle setting internal di: variables separately where needed to
 	// prevent changing the type.
 	if (ht == &vimvarht)
 	{
-	    if (v->di_tv.v_type == VAR_STRING)
+	    if (di->di_tv.v_type == VAR_STRING)
 	    {
-		VIM_CLEAR(v->di_tv.vval.v_string);
+		VIM_CLEAR(di->di_tv.vval.v_string);
 		if (copy || tv->v_type != VAR_STRING)
 		{
 		    char_u *val = tv_get_string(tv);
 
 		    // Careful: when assigning to v:errmsg and tv_get_string()
 		    // causes an error message the variable will alrady be set.
-		    if (v->di_tv.vval.v_string == NULL)
-			v->di_tv.vval.v_string = vim_strsave(val);
+		    if (di->di_tv.vval.v_string == NULL)
+			di->di_tv.vval.v_string = vim_strsave(val);
 		}
 		else
 		{
 		    // Take over the string to avoid an extra alloc/free.
-		    v->di_tv.vval.v_string = tv->vval.v_string;
+		    di->di_tv.vval.v_string = tv->vval.v_string;
 		    tv->vval.v_string = NULL;
 		}
 		return;
 	    }
-	    else if (v->di_tv.v_type == VAR_NUMBER)
+	    else if (di->di_tv.v_type == VAR_NUMBER)
 	    {
-		v->di_tv.vval.v_number = tv_get_number(tv);
+		di->di_tv.vval.v_number = tv_get_number(tv);
 		if (STRCMP(varname, "searchforward") == 0)
-		    set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
+		    set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
 #ifdef FEAT_SEARCH_EXTRA
 		else if (STRCMP(varname, "hlsearch") == 0)
 		{
-		    no_hlsearch = !v->di_tv.vval.v_number;
+		    no_hlsearch = !di->di_tv.vval.v_number;
 		    redraw_all_later(SOME_VALID);
 		}
 #endif
 		return;
 	    }
-	    else if (v->di_tv.v_type != tv->v_type)
+	    else if (di->di_tv.v_type != tv->v_type)
 	    {
 		semsg(_("E963: setting %s to value with wrong type"), name);
 		return;
 	    }
 	}
 
-	clear_tv(&v->di_tv);
+	clear_tv(&di->di_tv);
     }
     else		    // add a new variable
     {
@@ -2725,31 +2861,53 @@ set_var_const(
 	if (!valid_varname(varname))
 	    return;
 
-	v = alloc(sizeof(dictitem_T) + STRLEN(varname));
-	if (v == NULL)
+	di = alloc(sizeof(dictitem_T) + STRLEN(varname));
+	if (di == NULL)
 	    return;
-	STRCPY(v->di_key, varname);
-	if (hash_add(ht, DI2HIKEY(v)) == FAIL)
+	STRCPY(di->di_key, varname);
+	if (hash_add(ht, DI2HIKEY(di)) == FAIL)
 	{
-	    vim_free(v);
+	    vim_free(di);
 	    return;
 	}
-	v->di_flags = DI_FLAGS_ALLOC;
-	if (is_const)
-	    v->di_flags |= DI_FLAGS_LOCK;
+	di->di_flags = DI_FLAGS_ALLOC;
+	if (flags & LET_IS_CONST)
+	    di->di_flags |= DI_FLAGS_LOCK;
+
+	if (is_script_local && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
+	{
+	    scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
+
+	    // Store a pointer to the typval_T, so that it can be found by
+	    // index instead of using a hastab lookup.
+	    if (ga_grow(&si->sn_var_vals, 1) == OK)
+	    {
+		svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
+						      + si->sn_var_vals.ga_len;
+		sv->sv_name = di->di_key;
+		sv->sv_tv = &di->di_tv;
+		sv->sv_type = type == NULL ? &t_any : type;
+		sv->sv_const = (flags & LET_IS_CONST);
+		sv->sv_export = is_export;
+		++si->sn_var_vals.ga_len;
+
+		// let ex_export() know the export worked.
+		is_export = FALSE;
+	    }
+	}
     }
 
     if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
-	copy_tv(tv, &v->di_tv);
+	copy_tv(tv, &di->di_tv);
     else
     {
-	v->di_tv = *tv;
-	v->di_tv.v_lock = 0;
+	di->di_tv = *tv;
+	di->di_tv.v_lock = 0;
 	init_tv(tv);
     }
 
-    if (is_const)
-	v->di_tv.v_lock |= VAR_LOCKED;
+    if (flags & LET_IS_CONST)
+	di->di_tv.v_lock |= VAR_LOCKED;
 }
 
 /*
@@ -3130,9 +3288,9 @@ var_redir_start(char_u *name, int append
     tv.v_type = VAR_STRING;
     tv.vval.v_string = (char_u *)"";
     if (append)
-	set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)".");
+	set_var_lval(redir_lval, redir_endp, &tv, TRUE, 0, (char_u *)".");
     else
-	set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"=");
+	set_var_lval(redir_lval, redir_endp, &tv, TRUE, 0, (char_u *)"=");
     clear_lval(redir_lval);
     err = did_emsg;
     did_emsg |= save_emsg;
@@ -3205,7 +3363,7 @@ var_redir_stop(void)
 	    redir_endp = get_lval(redir_varname, NULL, redir_lval,
 					FALSE, FALSE, 0, FNE_CHECK_START);
 	    if (redir_endp != NULL && redir_lval->ll_name != NULL)
-		set_var_lval(redir_lval, redir_endp, &tv, FALSE, FALSE,
+		set_var_lval(redir_lval, redir_endp, &tv, FALSE, 0,
 								(char_u *)".");
 	    clear_lval(redir_lval);
 	}