comparison src/list.c @ 22844:36fc73078bce v8.2.1969

patch 8.2.1969: Vim9: map() may change the list or dict item type Commit: https://github.com/vim/vim/commit/ea696852e7abcdebaf7f17a7f23dc90df1f5e2ed Author: Bram Moolenaar <Bram@vim.org> Date: Mon Nov 9 18:31:39 2020 +0100 patch 8.2.1969: Vim9: map() may change the list or dict item type Problem: Vim9: map() may change the list or dict item type. Solution: Add mapnew().
author Bram Moolenaar <Bram@vim.org>
date Mon, 09 Nov 2020 18:45:04 +0100
parents 3e0f909ca1f2
children e4fbe8d1bde9
comparison
equal deleted inserted replaced
22843:b80694c9bb40 22844:36fc73078bce
1901 f_uniq(typval_T *argvars, typval_T *rettv) 1901 f_uniq(typval_T *argvars, typval_T *rettv)
1902 { 1902 {
1903 do_sort_uniq(argvars, rettv, FALSE); 1903 do_sort_uniq(argvars, rettv, FALSE);
1904 } 1904 }
1905 1905
1906 typedef enum {
1907 FILTERMAP_FILTER,
1908 FILTERMAP_MAP,
1909 FILTERMAP_MAPNEW
1910 } filtermap_T;
1911
1906 /* 1912 /*
1907 * Handle one item for map() and filter(). 1913 * Handle one item for map() and filter().
1914 * Sets v:val to "tv". Caller must set v:key.
1908 */ 1915 */
1909 static int 1916 static int
1910 filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) 1917 filter_map_one(
1911 { 1918 typval_T *tv, // original value
1912 typval_T rettv; 1919 typval_T *expr, // callback
1920 filtermap_T filtermap,
1921 typval_T *newtv, // for map() and mapnew(): new value
1922 int *remp) // for filter(): remove flag
1923 {
1913 typval_T argv[3]; 1924 typval_T argv[3];
1914 int retval = FAIL; 1925 int retval = FAIL;
1915 1926
1916 copy_tv(tv, get_vim_var_tv(VV_VAL)); 1927 copy_tv(tv, get_vim_var_tv(VV_VAL));
1917 argv[0] = *get_vim_var_tv(VV_KEY); 1928 argv[0] = *get_vim_var_tv(VV_KEY);
1918 argv[1] = *get_vim_var_tv(VV_VAL); 1929 argv[1] = *get_vim_var_tv(VV_VAL);
1919 if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) 1930 if (eval_expr_typval(expr, argv, 2, newtv) == FAIL)
1920 goto theend; 1931 goto theend;
1921 if (map) 1932 if (filtermap == FILTERMAP_FILTER)
1922 {
1923 // map(): replace the list item value
1924 clear_tv(tv);
1925 rettv.v_lock = 0;
1926 *tv = rettv;
1927 }
1928 else
1929 { 1933 {
1930 int error = FALSE; 1934 int error = FALSE;
1931 1935
1932 // filter(): when expr is zero remove the item 1936 // filter(): when expr is zero remove the item
1933 if (in_vim9script()) 1937 if (in_vim9script())
1934 *remp = !tv2bool(&rettv); 1938 *remp = !tv2bool(newtv);
1935 else 1939 else
1936 *remp = (tv_get_number_chk(&rettv, &error) == 0); 1940 *remp = (tv_get_number_chk(newtv, &error) == 0);
1937 clear_tv(&rettv); 1941 clear_tv(newtv);
1938 // On type error, nothing has been removed; return FAIL to stop the 1942 // On type error, nothing has been removed; return FAIL to stop the
1939 // loop. The error message was given by tv_get_number_chk(). 1943 // loop. The error message was given by tv_get_number_chk().
1940 if (error) 1944 if (error)
1941 goto theend; 1945 goto theend;
1942 } 1946 }
1948 1952
1949 /* 1953 /*
1950 * Implementation of map() and filter(). 1954 * Implementation of map() and filter().
1951 */ 1955 */
1952 static void 1956 static void
1953 filter_map(typval_T *argvars, typval_T *rettv, int map) 1957 filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
1954 { 1958 {
1955 typval_T *expr; 1959 typval_T *expr;
1956 listitem_T *li, *nli; 1960 listitem_T *li, *nli;
1957 list_T *l = NULL; 1961 list_T *l = NULL;
1958 dictitem_T *di; 1962 dictitem_T *di;
1960 hashitem_T *hi; 1964 hashitem_T *hi;
1961 dict_T *d = NULL; 1965 dict_T *d = NULL;
1962 blob_T *b = NULL; 1966 blob_T *b = NULL;
1963 int rem; 1967 int rem;
1964 int todo; 1968 int todo;
1965 char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); 1969 char_u *ermsg = (char_u *)(filtermap == FILTERMAP_MAP ? "map()"
1966 char_u *arg_errmsg = (char_u *)(map ? N_("map() argument") 1970 : filtermap == FILTERMAP_MAPNEW ? "mapnew()"
1967 : N_("filter() argument")); 1971 : "filter()");
1972 char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
1973 ? N_("map() argument")
1974 : filtermap == FILTERMAP_MAPNEW
1975 ? N_("mapnew() argument")
1976 : N_("filter() argument"));
1968 int save_did_emsg; 1977 int save_did_emsg;
1969 int idx = 0; 1978 int idx = 0;
1970 1979
1971 // Always return the first argument, also on failure. 1980 // map() and filter() return the first argument, also on failure.
1972 copy_tv(&argvars[0], rettv); 1981 if (filtermap != FILTERMAP_MAPNEW)
1982 copy_tv(&argvars[0], rettv);
1973 1983
1974 if (argvars[0].v_type == VAR_BLOB) 1984 if (argvars[0].v_type == VAR_BLOB)
1975 { 1985 {
1986 if (filtermap == FILTERMAP_MAPNEW)
1987 {
1988 rettv->v_type = VAR_BLOB;
1989 rettv->vval.v_blob = NULL;
1990 }
1976 if ((b = argvars[0].vval.v_blob) == NULL) 1991 if ((b = argvars[0].vval.v_blob) == NULL)
1977 return; 1992 return;
1978 } 1993 }
1979 else if (argvars[0].v_type == VAR_LIST) 1994 else if (argvars[0].v_type == VAR_LIST)
1980 { 1995 {
1996 if (filtermap == FILTERMAP_MAPNEW)
1997 {
1998 rettv->v_type = VAR_LIST;
1999 rettv->vval.v_list = NULL;
2000 }
1981 if ((l = argvars[0].vval.v_list) == NULL 2001 if ((l = argvars[0].vval.v_list) == NULL
1982 || (!map && value_check_lock(l->lv_lock, arg_errmsg, TRUE))) 2002 || (filtermap == FILTERMAP_FILTER
2003 && value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
1983 return; 2004 return;
1984 } 2005 }
1985 else if (argvars[0].v_type == VAR_DICT) 2006 else if (argvars[0].v_type == VAR_DICT)
1986 { 2007 {
2008 if (filtermap == FILTERMAP_MAPNEW)
2009 {
2010 rettv->v_type = VAR_DICT;
2011 rettv->vval.v_dict = NULL;
2012 }
1987 if ((d = argvars[0].vval.v_dict) == NULL 2013 if ((d = argvars[0].vval.v_dict) == NULL
1988 || (!map && value_check_lock(d->dv_lock, arg_errmsg, TRUE))) 2014 || (filtermap == FILTERMAP_FILTER
2015 && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
1989 return; 2016 return;
1990 } 2017 }
1991 else 2018 else
1992 { 2019 {
1993 semsg(_(e_listdictblobarg), ermsg); 2020 semsg(_(e_listdictblobarg), ermsg);
2012 did_emsg = FALSE; 2039 did_emsg = FALSE;
2013 2040
2014 if (argvars[0].v_type == VAR_DICT) 2041 if (argvars[0].v_type == VAR_DICT)
2015 { 2042 {
2016 int prev_lock = d->dv_lock; 2043 int prev_lock = d->dv_lock;
2017 2044 dict_T *d_ret = NULL;
2018 if (map && d->dv_lock == 0) 2045
2046 if (filtermap == FILTERMAP_MAPNEW)
2047 {
2048 if (rettv_dict_alloc(rettv) == FAIL)
2049 return;
2050 d_ret = rettv->vval.v_dict;
2051 }
2052
2053 if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
2019 d->dv_lock = VAR_LOCKED; 2054 d->dv_lock = VAR_LOCKED;
2020 ht = &d->dv_hashtab; 2055 ht = &d->dv_hashtab;
2021 hash_lock(ht); 2056 hash_lock(ht);
2022 todo = (int)ht->ht_used; 2057 todo = (int)ht->ht_used;
2023 for (hi = ht->ht_array; todo > 0; ++hi) 2058 for (hi = ht->ht_array; todo > 0; ++hi)
2024 { 2059 {
2025 if (!HASHITEM_EMPTY(hi)) 2060 if (!HASHITEM_EMPTY(hi))
2026 { 2061 {
2027 int r; 2062 int r;
2063 typval_T newtv;
2028 2064
2029 --todo; 2065 --todo;
2030 di = HI2DI(hi); 2066 di = HI2DI(hi);
2031 if (map && (value_check_lock(di->di_tv.v_lock, 2067 if (filtermap != FILTERMAP_FILTER
2068 && (value_check_lock(di->di_tv.v_lock,
2032 arg_errmsg, TRUE) 2069 arg_errmsg, TRUE)
2033 || var_check_ro(di->di_flags, 2070 || var_check_ro(di->di_flags,
2034 arg_errmsg, TRUE))) 2071 arg_errmsg, TRUE)))
2035 break; 2072 break;
2036 set_vim_var_string(VV_KEY, di->di_key, -1); 2073 set_vim_var_string(VV_KEY, di->di_key, -1);
2037 r = filter_map_one(&di->di_tv, expr, map, &rem); 2074 r = filter_map_one(&di->di_tv, expr, filtermap,
2075 &newtv, &rem);
2038 clear_tv(get_vim_var_tv(VV_KEY)); 2076 clear_tv(get_vim_var_tv(VV_KEY));
2039 if (r == FAIL || did_emsg) 2077 if (r == FAIL || did_emsg)
2078 {
2079 clear_tv(&newtv);
2040 break; 2080 break;
2041 if (!map && rem) 2081 }
2082 if (filtermap == FILTERMAP_MAP)
2042 { 2083 {
2084 // map(): replace the dict item value
2085 clear_tv(&di->di_tv);
2086 newtv.v_lock = 0;
2087 di->di_tv = newtv;
2088 }
2089 else if (filtermap == FILTERMAP_MAPNEW)
2090 {
2091 // mapnew(): add the item value to the new dict
2092 r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
2093 clear_tv(&newtv);
2094 if (r == FAIL)
2095 break;
2096 }
2097 else if (filtermap == FILTERMAP_FILTER && rem)
2098 {
2099 // filter(false): remove the item from the dict
2043 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) 2100 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
2044 || var_check_ro(di->di_flags, arg_errmsg, TRUE)) 2101 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
2045 break; 2102 break;
2046 dictitem_remove(d, di); 2103 dictitem_remove(d, di);
2047 } 2104 }
2053 else if (argvars[0].v_type == VAR_BLOB) 2110 else if (argvars[0].v_type == VAR_BLOB)
2054 { 2111 {
2055 int i; 2112 int i;
2056 typval_T tv; 2113 typval_T tv;
2057 varnumber_T val; 2114 varnumber_T val;
2115 blob_T *b_ret = b;
2116
2117 if (filtermap == FILTERMAP_MAPNEW)
2118 {
2119 if (blob_copy(b, rettv) == FAIL)
2120 return;
2121 b_ret = rettv->vval.v_blob;
2122 }
2058 2123
2059 // set_vim_var_nr() doesn't set the type 2124 // set_vim_var_nr() doesn't set the type
2060 set_vim_var_type(VV_KEY, VAR_NUMBER); 2125 set_vim_var_type(VV_KEY, VAR_NUMBER);
2061 2126
2062 for (i = 0; i < b->bv_ga.ga_len; i++) 2127 for (i = 0; i < b->bv_ga.ga_len; i++)
2063 { 2128 {
2129 typval_T newtv;
2130
2064 tv.v_type = VAR_NUMBER; 2131 tv.v_type = VAR_NUMBER;
2065 val = blob_get(b, i); 2132 val = blob_get(b, i);
2066 tv.vval.v_number = val; 2133 tv.vval.v_number = val;
2067 set_vim_var_nr(VV_KEY, idx); 2134 set_vim_var_nr(VV_KEY, idx);
2068 if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) 2135 if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
2136 || did_emsg)
2069 break; 2137 break;
2070 if (tv.v_type != VAR_NUMBER) 2138 if (newtv.v_type != VAR_NUMBER)
2071 { 2139 {
2140 clear_tv(&newtv);
2072 emsg(_(e_invalblob)); 2141 emsg(_(e_invalblob));
2073 break; 2142 break;
2074 } 2143 }
2075 if (map) 2144 if (filtermap != FILTERMAP_FILTER)
2076 { 2145 {
2077 if (tv.vval.v_number != val) 2146 if (newtv.vval.v_number != val)
2078 blob_set(b, i, tv.vval.v_number); 2147 blob_set(b_ret, i, newtv.vval.v_number);
2079 } 2148 }
2080 else if (rem) 2149 else if (rem)
2081 { 2150 {
2082 char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; 2151 char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
2083 2152
2089 ++idx; 2158 ++idx;
2090 } 2159 }
2091 } 2160 }
2092 else // argvars[0].v_type == VAR_LIST 2161 else // argvars[0].v_type == VAR_LIST
2093 { 2162 {
2094 int prev_lock = l->lv_lock; 2163 int prev_lock = l->lv_lock;
2095 2164 list_T *l_ret = NULL;
2165
2166 if (filtermap == FILTERMAP_MAPNEW)
2167 {
2168 if (rettv_list_alloc(rettv) == FAIL)
2169 return;
2170 l_ret = rettv->vval.v_list;
2171 }
2096 // set_vim_var_nr() doesn't set the type 2172 // set_vim_var_nr() doesn't set the type
2097 set_vim_var_type(VV_KEY, VAR_NUMBER); 2173 set_vim_var_type(VV_KEY, VAR_NUMBER);
2098 2174
2099 CHECK_LIST_MATERIALIZE(l); 2175 CHECK_LIST_MATERIALIZE(l);
2100 if (map && l->lv_lock == 0) 2176 if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0)
2101 l->lv_lock = VAR_LOCKED; 2177 l->lv_lock = VAR_LOCKED;
2102 for (li = l->lv_first; li != NULL; li = nli) 2178 for (li = l->lv_first; li != NULL; li = nli)
2103 { 2179 {
2104 if (map && value_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) 2180 typval_T newtv;
2181
2182 if (filtermap != FILTERMAP_FILTER
2183 && value_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE))
2105 break; 2184 break;
2106 nli = li->li_next; 2185 nli = li->li_next;
2107 set_vim_var_nr(VV_KEY, idx); 2186 set_vim_var_nr(VV_KEY, idx);
2108 if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL 2187 if (filter_map_one(&li->li_tv, expr, filtermap,
2109 || did_emsg) 2188 &newtv, &rem) == FAIL || did_emsg)
2110 break; 2189 break;
2111 if (!map && rem) 2190 if (filtermap == FILTERMAP_MAP)
2191 {
2192 // map(): replace the list item value
2193 clear_tv(&li->li_tv);
2194 newtv.v_lock = 0;
2195 li->li_tv = newtv;
2196 }
2197 else if (filtermap == FILTERMAP_MAPNEW)
2198 {
2199 // mapnew(): append the list item value
2200 if (list_append_tv_move(l_ret, &newtv) == FAIL)
2201 break;
2202 }
2203 else if (filtermap == FILTERMAP_FILTER && rem)
2112 listitem_remove(l, li); 2204 listitem_remove(l, li);
2113 ++idx; 2205 ++idx;
2114 } 2206 }
2115 l->lv_lock = prev_lock; 2207 l->lv_lock = prev_lock;
2116 } 2208 }
2126 * "filter()" function 2218 * "filter()" function
2127 */ 2219 */
2128 void 2220 void
2129 f_filter(typval_T *argvars, typval_T *rettv) 2221 f_filter(typval_T *argvars, typval_T *rettv)
2130 { 2222 {
2131 filter_map(argvars, rettv, FALSE); 2223 filter_map(argvars, rettv, FILTERMAP_FILTER);
2132 } 2224 }
2133 2225
2134 /* 2226 /*
2135 * "map()" function 2227 * "map()" function
2136 */ 2228 */
2137 void 2229 void
2138 f_map(typval_T *argvars, typval_T *rettv) 2230 f_map(typval_T *argvars, typval_T *rettv)
2139 { 2231 {
2140 filter_map(argvars, rettv, TRUE); 2232 filter_map(argvars, rettv, FILTERMAP_MAP);
2233 }
2234
2235 /*
2236 * "mapnew()" function
2237 */
2238 void
2239 f_mapnew(typval_T *argvars, typval_T *rettv)
2240 {
2241 filter_map(argvars, rettv, FILTERMAP_MAPNEW);
2141 } 2242 }
2142 2243
2143 /* 2244 /*
2144 * "add(list, item)" function 2245 * "add(list, item)" function
2145 */ 2246 */