Mercurial > vim
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 */ |