Mercurial > vim
comparison src/list.c @ 26676:b856b797c5d1 v8.2.3867
patch 8.2.3867: implementation of some list functions too complicated
Commit: https://github.com/vim/vim/commit/d92813a59877c707e4b64bea6d786aad152acb45
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Tue Dec 21 13:19:42 2021 +0000
patch 8.2.3867: implementation of some list functions too complicated
Problem: Implementation of some list functions too complicated.
Solution: Refactor do_sort_uniq(), f_count() and extend() (Yegappan
Lakshmanan, closes #9378)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Tue, 21 Dec 2021 14:30:03 +0100 |
parents | 7c055fdd6200 |
children | 2126feddeda6 |
comparison
equal
deleted
inserted
replaced
26675:3a473d09904c | 26676:b856b797c5d1 |
---|---|
1985 | 1985 |
1986 return res; | 1986 return res; |
1987 } | 1987 } |
1988 | 1988 |
1989 /* | 1989 /* |
1990 * sort() List "l" | |
1991 */ | |
1992 static void | |
1993 do_sort(list_T *l, sortinfo_T *info) | |
1994 { | |
1995 long len; | |
1996 sortItem_T *ptrs; | |
1997 long i = 0; | |
1998 listitem_T *li; | |
1999 | |
2000 len = list_len(l); | |
2001 | |
2002 // Make an array with each entry pointing to an item in the List. | |
2003 ptrs = ALLOC_MULT(sortItem_T, len); | |
2004 if (ptrs == NULL) | |
2005 return; | |
2006 | |
2007 // sort(): ptrs will be the list to sort | |
2008 FOR_ALL_LIST_ITEMS(l, li) | |
2009 { | |
2010 ptrs[i].item = li; | |
2011 ptrs[i].idx = i; | |
2012 ++i; | |
2013 } | |
2014 | |
2015 info->item_compare_func_err = FALSE; | |
2016 info->item_compare_keep_zero = FALSE; | |
2017 // test the compare function | |
2018 if ((info->item_compare_func != NULL | |
2019 || info->item_compare_partial != NULL) | |
2020 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) | |
2021 == ITEM_COMPARE_FAIL) | |
2022 emsg(_("E702: Sort compare function failed")); | |
2023 else | |
2024 { | |
2025 // Sort the array with item pointers. | |
2026 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), | |
2027 info->item_compare_func == NULL | |
2028 && info->item_compare_partial == NULL | |
2029 ? item_compare : item_compare2); | |
2030 | |
2031 if (!info->item_compare_func_err) | |
2032 { | |
2033 // Clear the List and append the items in sorted order. | |
2034 l->lv_first = l->lv_u.mat.lv_last | |
2035 = l->lv_u.mat.lv_idx_item = NULL; | |
2036 l->lv_len = 0; | |
2037 for (i = 0; i < len; ++i) | |
2038 list_append(l, ptrs[i].item); | |
2039 } | |
2040 } | |
2041 | |
2042 vim_free(ptrs); | |
2043 } | |
2044 | |
2045 /* | |
2046 * uniq() List "l" | |
2047 */ | |
2048 static void | |
2049 do_uniq(list_T *l, sortinfo_T *info) | |
2050 { | |
2051 long len; | |
2052 sortItem_T *ptrs; | |
2053 long i = 0; | |
2054 listitem_T *li; | |
2055 int (*item_compare_func_ptr)(const void *, const void *); | |
2056 | |
2057 len = list_len(l); | |
2058 | |
2059 // Make an array with each entry pointing to an item in the List. | |
2060 ptrs = ALLOC_MULT(sortItem_T, len); | |
2061 if (ptrs == NULL) | |
2062 return; | |
2063 | |
2064 // f_uniq(): ptrs will be a stack of items to remove | |
2065 info->item_compare_func_err = FALSE; | |
2066 info->item_compare_keep_zero = TRUE; | |
2067 item_compare_func_ptr = info->item_compare_func != NULL | |
2068 || info->item_compare_partial != NULL | |
2069 ? item_compare2 : item_compare; | |
2070 | |
2071 for (li = l->lv_first; li != NULL && li->li_next != NULL; | |
2072 li = li->li_next) | |
2073 { | |
2074 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) | |
2075 == 0) | |
2076 ptrs[i++].item = li; | |
2077 if (info->item_compare_func_err) | |
2078 { | |
2079 emsg(_("E882: Uniq compare function failed")); | |
2080 break; | |
2081 } | |
2082 } | |
2083 | |
2084 if (!info->item_compare_func_err) | |
2085 { | |
2086 while (--i >= 0) | |
2087 { | |
2088 li = ptrs[i].item->li_next; | |
2089 ptrs[i].item->li_next = li->li_next; | |
2090 if (li->li_next != NULL) | |
2091 li->li_next->li_prev = ptrs[i].item; | |
2092 else | |
2093 l->lv_u.mat.lv_last = ptrs[i].item; | |
2094 list_fix_watch(l, li); | |
2095 listitem_free(l, li); | |
2096 l->lv_len--; | |
2097 } | |
2098 } | |
2099 | |
2100 vim_free(ptrs); | |
2101 } | |
2102 | |
2103 /* | |
2104 * Parse the optional arguments to sort() and uniq() and return the values in | |
2105 * 'info'. | |
2106 */ | |
2107 static int | |
2108 parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info) | |
2109 { | |
2110 info->item_compare_ic = FALSE; | |
2111 info->item_compare_lc = FALSE; | |
2112 info->item_compare_numeric = FALSE; | |
2113 info->item_compare_numbers = FALSE; | |
2114 #ifdef FEAT_FLOAT | |
2115 info->item_compare_float = FALSE; | |
2116 #endif | |
2117 info->item_compare_func = NULL; | |
2118 info->item_compare_partial = NULL; | |
2119 info->item_compare_selfdict = NULL; | |
2120 | |
2121 if (argvars[1].v_type == VAR_UNKNOWN) | |
2122 return OK; | |
2123 | |
2124 // optional second argument: {func} | |
2125 if (argvars[1].v_type == VAR_FUNC) | |
2126 info->item_compare_func = argvars[1].vval.v_string; | |
2127 else if (argvars[1].v_type == VAR_PARTIAL) | |
2128 info->item_compare_partial = argvars[1].vval.v_partial; | |
2129 else | |
2130 { | |
2131 int error = FALSE; | |
2132 int nr = 0; | |
2133 | |
2134 if (argvars[1].v_type == VAR_NUMBER) | |
2135 { | |
2136 nr = tv_get_number_chk(&argvars[1], &error); | |
2137 if (error) | |
2138 return FAIL; | |
2139 if (nr == 1) | |
2140 info->item_compare_ic = TRUE; | |
2141 } | |
2142 if (nr != 1) | |
2143 { | |
2144 if (argvars[1].v_type != VAR_NUMBER) | |
2145 info->item_compare_func = tv_get_string(&argvars[1]); | |
2146 else if (nr != 0) | |
2147 { | |
2148 emsg(_(e_invarg)); | |
2149 return FAIL; | |
2150 } | |
2151 } | |
2152 if (info->item_compare_func != NULL) | |
2153 { | |
2154 if (*info->item_compare_func == NUL) | |
2155 { | |
2156 // empty string means default sort | |
2157 info->item_compare_func = NULL; | |
2158 } | |
2159 else if (STRCMP(info->item_compare_func, "n") == 0) | |
2160 { | |
2161 info->item_compare_func = NULL; | |
2162 info->item_compare_numeric = TRUE; | |
2163 } | |
2164 else if (STRCMP(info->item_compare_func, "N") == 0) | |
2165 { | |
2166 info->item_compare_func = NULL; | |
2167 info->item_compare_numbers = TRUE; | |
2168 } | |
2169 #ifdef FEAT_FLOAT | |
2170 else if (STRCMP(info->item_compare_func, "f") == 0) | |
2171 { | |
2172 info->item_compare_func = NULL; | |
2173 info->item_compare_float = TRUE; | |
2174 } | |
2175 #endif | |
2176 else if (STRCMP(info->item_compare_func, "i") == 0) | |
2177 { | |
2178 info->item_compare_func = NULL; | |
2179 info->item_compare_ic = TRUE; | |
2180 } | |
2181 else if (STRCMP(info->item_compare_func, "l") == 0) | |
2182 { | |
2183 info->item_compare_func = NULL; | |
2184 info->item_compare_lc = TRUE; | |
2185 } | |
2186 } | |
2187 } | |
2188 | |
2189 if (argvars[2].v_type != VAR_UNKNOWN) | |
2190 { | |
2191 // optional third argument: {dict} | |
2192 if (argvars[2].v_type != VAR_DICT) | |
2193 { | |
2194 emsg(_(e_dictreq)); | |
2195 return FAIL; | |
2196 } | |
2197 info->item_compare_selfdict = argvars[2].vval.v_dict; | |
2198 } | |
2199 | |
2200 return OK; | |
2201 } | |
2202 | |
2203 /* | |
1990 * "sort()" or "uniq()" function | 2204 * "sort()" or "uniq()" function |
1991 */ | 2205 */ |
1992 static void | 2206 static void |
1993 do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) | 2207 do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) |
1994 { | 2208 { |
1995 list_T *l; | 2209 list_T *l; |
1996 listitem_T *li; | |
1997 sortItem_T *ptrs; | |
1998 sortinfo_T *old_sortinfo; | 2210 sortinfo_T *old_sortinfo; |
1999 sortinfo_T info; | 2211 sortinfo_T info; |
2000 long len; | 2212 long len; |
2001 long i; | |
2002 | 2213 |
2003 if (in_vim9script() | 2214 if (in_vim9script() |
2004 && (check_for_list_arg(argvars, 0) == FAIL | 2215 && (check_for_list_arg(argvars, 0) == FAIL |
2005 || (argvars[1].v_type != VAR_UNKNOWN | 2216 || (argvars[1].v_type != VAR_UNKNOWN |
2006 && check_for_opt_dict_arg(argvars, 2) == FAIL))) | 2217 && check_for_opt_dict_arg(argvars, 2) == FAIL))) |
2007 return; | 2218 return; |
2008 | 2219 |
2220 if (argvars[0].v_type != VAR_LIST) | |
2221 { | |
2222 semsg(_(e_listarg), sort ? "sort()" : "uniq()"); | |
2223 return; | |
2224 } | |
2225 | |
2009 // Pointer to current info struct used in compare function. Save and | 2226 // Pointer to current info struct used in compare function. Save and |
2010 // restore the current one for nested calls. | 2227 // restore the current one for nested calls. |
2011 old_sortinfo = sortinfo; | 2228 old_sortinfo = sortinfo; |
2012 sortinfo = &info; | 2229 sortinfo = &info; |
2013 | 2230 |
2014 if (argvars[0].v_type != VAR_LIST) | 2231 l = argvars[0].vval.v_list; |
2015 semsg(_(e_listarg), sort ? "sort()" : "uniq()"); | 2232 if (l != NULL && value_check_lock(l->lv_lock, |
2233 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), | |
2234 TRUE)) | |
2235 goto theend; | |
2236 rettv_list_set(rettv, l); | |
2237 if (l == NULL) | |
2238 goto theend; | |
2239 CHECK_LIST_MATERIALIZE(l); | |
2240 | |
2241 len = list_len(l); | |
2242 if (len <= 1) | |
2243 goto theend; // short list sorts pretty quickly | |
2244 | |
2245 if (parse_sort_uniq_args(argvars, &info) == FAIL) | |
2246 goto theend; | |
2247 | |
2248 if (sort) | |
2249 do_sort(l, &info); | |
2016 else | 2250 else |
2017 { | 2251 do_uniq(l, &info); |
2018 l = argvars[0].vval.v_list; | 2252 |
2019 if (l != NULL && value_check_lock(l->lv_lock, | |
2020 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), | |
2021 TRUE)) | |
2022 goto theend; | |
2023 rettv_list_set(rettv, l); | |
2024 if (l == NULL) | |
2025 goto theend; | |
2026 CHECK_LIST_MATERIALIZE(l); | |
2027 | |
2028 len = list_len(l); | |
2029 if (len <= 1) | |
2030 goto theend; // short list sorts pretty quickly | |
2031 | |
2032 info.item_compare_ic = FALSE; | |
2033 info.item_compare_lc = FALSE; | |
2034 info.item_compare_numeric = FALSE; | |
2035 info.item_compare_numbers = FALSE; | |
2036 #ifdef FEAT_FLOAT | |
2037 info.item_compare_float = FALSE; | |
2038 #endif | |
2039 info.item_compare_func = NULL; | |
2040 info.item_compare_partial = NULL; | |
2041 info.item_compare_selfdict = NULL; | |
2042 if (argvars[1].v_type != VAR_UNKNOWN) | |
2043 { | |
2044 // optional second argument: {func} | |
2045 if (argvars[1].v_type == VAR_FUNC) | |
2046 info.item_compare_func = argvars[1].vval.v_string; | |
2047 else if (argvars[1].v_type == VAR_PARTIAL) | |
2048 info.item_compare_partial = argvars[1].vval.v_partial; | |
2049 else | |
2050 { | |
2051 int error = FALSE; | |
2052 int nr = 0; | |
2053 | |
2054 if (argvars[1].v_type == VAR_NUMBER) | |
2055 { | |
2056 nr = tv_get_number_chk(&argvars[1], &error); | |
2057 if (error) | |
2058 goto theend; // type error; errmsg already given | |
2059 if (nr == 1) | |
2060 info.item_compare_ic = TRUE; | |
2061 } | |
2062 if (nr != 1) | |
2063 { | |
2064 if (argvars[1].v_type != VAR_NUMBER) | |
2065 info.item_compare_func = tv_get_string(&argvars[1]); | |
2066 else if (nr != 0) | |
2067 { | |
2068 emsg(_(e_invarg)); | |
2069 goto theend; | |
2070 } | |
2071 } | |
2072 if (info.item_compare_func != NULL) | |
2073 { | |
2074 if (*info.item_compare_func == NUL) | |
2075 { | |
2076 // empty string means default sort | |
2077 info.item_compare_func = NULL; | |
2078 } | |
2079 else if (STRCMP(info.item_compare_func, "n") == 0) | |
2080 { | |
2081 info.item_compare_func = NULL; | |
2082 info.item_compare_numeric = TRUE; | |
2083 } | |
2084 else if (STRCMP(info.item_compare_func, "N") == 0) | |
2085 { | |
2086 info.item_compare_func = NULL; | |
2087 info.item_compare_numbers = TRUE; | |
2088 } | |
2089 #ifdef FEAT_FLOAT | |
2090 else if (STRCMP(info.item_compare_func, "f") == 0) | |
2091 { | |
2092 info.item_compare_func = NULL; | |
2093 info.item_compare_float = TRUE; | |
2094 } | |
2095 #endif | |
2096 else if (STRCMP(info.item_compare_func, "i") == 0) | |
2097 { | |
2098 info.item_compare_func = NULL; | |
2099 info.item_compare_ic = TRUE; | |
2100 } | |
2101 else if (STRCMP(info.item_compare_func, "l") == 0) | |
2102 { | |
2103 info.item_compare_func = NULL; | |
2104 info.item_compare_lc = TRUE; | |
2105 } | |
2106 } | |
2107 } | |
2108 | |
2109 if (argvars[2].v_type != VAR_UNKNOWN) | |
2110 { | |
2111 // optional third argument: {dict} | |
2112 if (argvars[2].v_type != VAR_DICT) | |
2113 { | |
2114 emsg(_(e_dictreq)); | |
2115 goto theend; | |
2116 } | |
2117 info.item_compare_selfdict = argvars[2].vval.v_dict; | |
2118 } | |
2119 } | |
2120 | |
2121 // Make an array with each entry pointing to an item in the List. | |
2122 ptrs = ALLOC_MULT(sortItem_T, len); | |
2123 if (ptrs == NULL) | |
2124 goto theend; | |
2125 | |
2126 i = 0; | |
2127 if (sort) | |
2128 { | |
2129 // sort(): ptrs will be the list to sort | |
2130 FOR_ALL_LIST_ITEMS(l, li) | |
2131 { | |
2132 ptrs[i].item = li; | |
2133 ptrs[i].idx = i; | |
2134 ++i; | |
2135 } | |
2136 | |
2137 info.item_compare_func_err = FALSE; | |
2138 info.item_compare_keep_zero = FALSE; | |
2139 // test the compare function | |
2140 if ((info.item_compare_func != NULL | |
2141 || info.item_compare_partial != NULL) | |
2142 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) | |
2143 == ITEM_COMPARE_FAIL) | |
2144 emsg(_("E702: Sort compare function failed")); | |
2145 else | |
2146 { | |
2147 // Sort the array with item pointers. | |
2148 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), | |
2149 info.item_compare_func == NULL | |
2150 && info.item_compare_partial == NULL | |
2151 ? item_compare : item_compare2); | |
2152 | |
2153 if (!info.item_compare_func_err) | |
2154 { | |
2155 // Clear the List and append the items in sorted order. | |
2156 l->lv_first = l->lv_u.mat.lv_last | |
2157 = l->lv_u.mat.lv_idx_item = NULL; | |
2158 l->lv_len = 0; | |
2159 for (i = 0; i < len; ++i) | |
2160 list_append(l, ptrs[i].item); | |
2161 } | |
2162 } | |
2163 } | |
2164 else | |
2165 { | |
2166 int (*item_compare_func_ptr)(const void *, const void *); | |
2167 | |
2168 // f_uniq(): ptrs will be a stack of items to remove | |
2169 info.item_compare_func_err = FALSE; | |
2170 info.item_compare_keep_zero = TRUE; | |
2171 item_compare_func_ptr = info.item_compare_func != NULL | |
2172 || info.item_compare_partial != NULL | |
2173 ? item_compare2 : item_compare; | |
2174 | |
2175 for (li = l->lv_first; li != NULL && li->li_next != NULL; | |
2176 li = li->li_next) | |
2177 { | |
2178 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) | |
2179 == 0) | |
2180 ptrs[i++].item = li; | |
2181 if (info.item_compare_func_err) | |
2182 { | |
2183 emsg(_("E882: Uniq compare function failed")); | |
2184 break; | |
2185 } | |
2186 } | |
2187 | |
2188 if (!info.item_compare_func_err) | |
2189 { | |
2190 while (--i >= 0) | |
2191 { | |
2192 li = ptrs[i].item->li_next; | |
2193 ptrs[i].item->li_next = li->li_next; | |
2194 if (li->li_next != NULL) | |
2195 li->li_next->li_prev = ptrs[i].item; | |
2196 else | |
2197 l->lv_u.mat.lv_last = ptrs[i].item; | |
2198 list_fix_watch(l, li); | |
2199 listitem_free(l, li); | |
2200 l->lv_len--; | |
2201 } | |
2202 } | |
2203 } | |
2204 | |
2205 vim_free(ptrs); | |
2206 } | |
2207 theend: | 2253 theend: |
2208 sortinfo = old_sortinfo; | 2254 sortinfo = old_sortinfo; |
2209 } | 2255 } |
2210 | 2256 |
2211 /* | 2257 /* |
2833 else | 2879 else |
2834 emsg(_(e_listblobreq)); | 2880 emsg(_(e_listblobreq)); |
2835 } | 2881 } |
2836 | 2882 |
2837 /* | 2883 /* |
2884 * Count the number of times "needle" occurs in string "haystack". Case is | |
2885 * ignored if "ic" is TRUE. | |
2886 */ | |
2887 static long | |
2888 count_string(char_u *haystack, char_u *needle, int ic) | |
2889 { | |
2890 long n = 0; | |
2891 char_u *p = haystack; | |
2892 char_u *next; | |
2893 | |
2894 if (p == NULL || needle == NULL || *needle == NUL) | |
2895 return 0; | |
2896 | |
2897 if (ic) | |
2898 { | |
2899 size_t len = STRLEN(needle); | |
2900 | |
2901 while (*p != NUL) | |
2902 { | |
2903 if (MB_STRNICMP(p, needle, len) == 0) | |
2904 { | |
2905 ++n; | |
2906 p += len; | |
2907 } | |
2908 else | |
2909 MB_PTR_ADV(p); | |
2910 } | |
2911 } | |
2912 else | |
2913 while ((next = (char_u *)strstr((char *)p, (char *)needle)) != NULL) | |
2914 { | |
2915 ++n; | |
2916 p = next + STRLEN(needle); | |
2917 } | |
2918 | |
2919 return n; | |
2920 } | |
2921 | |
2922 /* | |
2923 * Count the number of times item "needle" occurs in List "l" starting at index | |
2924 * "idx". Case is ignored if "ic" is TRUE. | |
2925 */ | |
2926 static long | |
2927 count_list(list_T *l, typval_T *needle, long idx, int ic) | |
2928 { | |
2929 long n = 0; | |
2930 listitem_T *li; | |
2931 | |
2932 if (l == NULL) | |
2933 return 0; | |
2934 | |
2935 CHECK_LIST_MATERIALIZE(l); | |
2936 | |
2937 if (list_len(l) == 0) | |
2938 return 0; | |
2939 | |
2940 li = list_find(l, idx); | |
2941 if (li == NULL) | |
2942 { | |
2943 semsg(_(e_listidx), idx); | |
2944 return 0; | |
2945 } | |
2946 | |
2947 for ( ; li != NULL; li = li->li_next) | |
2948 if (tv_equal(&li->li_tv, needle, ic, FALSE)) | |
2949 ++n; | |
2950 | |
2951 return n; | |
2952 } | |
2953 | |
2954 /* | |
2955 * Count the number of times item "needle" occurs in Dict "d". Case is ignored | |
2956 * if "ic" is TRUE. | |
2957 */ | |
2958 static long | |
2959 count_dict(dict_T *d, typval_T *needle, int ic) | |
2960 { | |
2961 int todo; | |
2962 hashitem_T *hi; | |
2963 long n = 0; | |
2964 | |
2965 if (d == NULL) | |
2966 return 0; | |
2967 | |
2968 todo = (int)d->dv_hashtab.ht_used; | |
2969 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) | |
2970 { | |
2971 if (!HASHITEM_EMPTY(hi)) | |
2972 { | |
2973 --todo; | |
2974 if (tv_equal(&HI2DI(hi)->di_tv, needle, ic, FALSE)) | |
2975 ++n; | |
2976 } | |
2977 } | |
2978 | |
2979 return n; | |
2980 } | |
2981 | |
2982 /* | |
2838 * "count()" function | 2983 * "count()" function |
2839 */ | 2984 */ |
2840 void | 2985 void |
2841 f_count(typval_T *argvars, typval_T *rettv) | 2986 f_count(typval_T *argvars, typval_T *rettv) |
2842 { | 2987 { |
2852 return; | 2997 return; |
2853 | 2998 |
2854 if (argvars[2].v_type != VAR_UNKNOWN) | 2999 if (argvars[2].v_type != VAR_UNKNOWN) |
2855 ic = (int)tv_get_bool_chk(&argvars[2], &error); | 3000 ic = (int)tv_get_bool_chk(&argvars[2], &error); |
2856 | 3001 |
2857 if (argvars[0].v_type == VAR_STRING) | 3002 if (!error && argvars[0].v_type == VAR_STRING) |
2858 { | 3003 n = count_string(argvars[0].vval.v_string, |
2859 char_u *expr = tv_get_string_chk(&argvars[1]); | 3004 tv_get_string_chk(&argvars[1]), ic); |
2860 char_u *p = argvars[0].vval.v_string; | 3005 else if (!error && argvars[0].v_type == VAR_LIST) |
2861 char_u *next; | 3006 { |
2862 | 3007 long idx = 0; |
2863 if (!error && expr != NULL && *expr != NUL && p != NULL) | 3008 |
2864 { | 3009 if (argvars[2].v_type != VAR_UNKNOWN |
2865 if (ic) | 3010 && argvars[3].v_type != VAR_UNKNOWN) |
2866 { | 3011 idx = (long)tv_get_number_chk(&argvars[3], &error); |
2867 size_t len = STRLEN(expr); | 3012 if (!error) |
2868 | 3013 n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic); |
2869 while (*p != NUL) | 3014 } |
2870 { | 3015 else if (!error && argvars[0].v_type == VAR_DICT) |
2871 if (MB_STRNICMP(p, expr, len) == 0) | 3016 { |
2872 { | 3017 if (argvars[2].v_type != VAR_UNKNOWN |
2873 ++n; | 3018 && argvars[3].v_type != VAR_UNKNOWN) |
2874 p += len; | 3019 emsg(_(e_invarg)); |
2875 } | 3020 else |
2876 else | 3021 n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic); |
2877 MB_PTR_ADV(p); | |
2878 } | |
2879 } | |
2880 else | |
2881 while ((next = (char_u *)strstr((char *)p, (char *)expr)) | |
2882 != NULL) | |
2883 { | |
2884 ++n; | |
2885 p = next + STRLEN(expr); | |
2886 } | |
2887 } | |
2888 | |
2889 } | |
2890 else if (argvars[0].v_type == VAR_LIST) | |
2891 { | |
2892 listitem_T *li; | |
2893 list_T *l; | |
2894 long idx; | |
2895 | |
2896 if ((l = argvars[0].vval.v_list) != NULL) | |
2897 { | |
2898 CHECK_LIST_MATERIALIZE(l); | |
2899 li = l->lv_first; | |
2900 if (argvars[2].v_type != VAR_UNKNOWN) | |
2901 { | |
2902 if (argvars[3].v_type != VAR_UNKNOWN) | |
2903 { | |
2904 idx = (long)tv_get_number_chk(&argvars[3], &error); | |
2905 if (!error) | |
2906 { | |
2907 li = list_find(l, idx); | |
2908 if (li == NULL) | |
2909 semsg(_(e_listidx), idx); | |
2910 } | |
2911 } | |
2912 if (error) | |
2913 li = NULL; | |
2914 } | |
2915 | |
2916 for ( ; li != NULL; li = li->li_next) | |
2917 if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE)) | |
2918 ++n; | |
2919 } | |
2920 } | |
2921 else if (argvars[0].v_type == VAR_DICT) | |
2922 { | |
2923 int todo; | |
2924 dict_T *d; | |
2925 hashitem_T *hi; | |
2926 | |
2927 if ((d = argvars[0].vval.v_dict) != NULL) | |
2928 { | |
2929 if (argvars[2].v_type != VAR_UNKNOWN) | |
2930 { | |
2931 if (argvars[3].v_type != VAR_UNKNOWN) | |
2932 emsg(_(e_invarg)); | |
2933 } | |
2934 | |
2935 todo = error ? 0 : (int)d->dv_hashtab.ht_used; | |
2936 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) | |
2937 { | |
2938 if (!HASHITEM_EMPTY(hi)) | |
2939 { | |
2940 --todo; | |
2941 if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE)) | |
2942 ++n; | |
2943 } | |
2944 } | |
2945 } | |
2946 } | 3022 } |
2947 else | 3023 else |
2948 semsg(_(e_listdictarg), "count()"); | 3024 semsg(_(e_listdictarg), "count()"); |
2949 rettv->vval.v_number = n; | 3025 rettv->vval.v_number = n; |
2950 } | 3026 } |
2951 | 3027 |
2952 /* | 3028 /* |
3029 * extend() a List. Append List argvars[1] to List argvars[0] before index | |
3030 * argvars[3] and return the resulting list in "rettv". "is_new" is TRUE for | |
3031 * extendnew(). | |
3032 */ | |
3033 static void | |
3034 extend_list( | |
3035 typval_T *argvars, | |
3036 type_T *type, | |
3037 char *func_name, | |
3038 char_u *arg_errmsg, | |
3039 int is_new, | |
3040 typval_T *rettv) | |
3041 { | |
3042 list_T *l1, *l2; | |
3043 listitem_T *item; | |
3044 long before; | |
3045 int error = FALSE; | |
3046 | |
3047 l1 = argvars[0].vval.v_list; | |
3048 if (l1 == NULL) | |
3049 { | |
3050 emsg(_(e_cannot_extend_null_list)); | |
3051 return; | |
3052 } | |
3053 l2 = argvars[1].vval.v_list; | |
3054 if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE)) | |
3055 && l2 != NULL) | |
3056 { | |
3057 if (is_new) | |
3058 { | |
3059 l1 = list_copy(l1, FALSE, get_copyID()); | |
3060 if (l1 == NULL) | |
3061 return; | |
3062 } | |
3063 | |
3064 if (argvars[2].v_type != VAR_UNKNOWN) | |
3065 { | |
3066 before = (long)tv_get_number_chk(&argvars[2], &error); | |
3067 if (error) | |
3068 return; // type error; errmsg already given | |
3069 | |
3070 if (before == l1->lv_len) | |
3071 item = NULL; | |
3072 else | |
3073 { | |
3074 item = list_find(l1, before); | |
3075 if (item == NULL) | |
3076 { | |
3077 semsg(_(e_listidx), before); | |
3078 return; | |
3079 } | |
3080 } | |
3081 } | |
3082 else | |
3083 item = NULL; | |
3084 if (type != NULL && check_typval_arg_type( | |
3085 type, &argvars[1], func_name, 2) == FAIL) | |
3086 return; | |
3087 list_extend(l1, l2, item); | |
3088 | |
3089 if (is_new) | |
3090 { | |
3091 rettv->v_type = VAR_LIST; | |
3092 rettv->vval.v_list = l1; | |
3093 rettv->v_lock = FALSE; | |
3094 } | |
3095 else | |
3096 copy_tv(&argvars[0], rettv); | |
3097 } | |
3098 } | |
3099 | |
3100 /* | |
3101 * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the | |
3102 * resulting Dict in "rettv". "is_new" is TRUE for extendnew(). | |
3103 */ | |
3104 static void | |
3105 extend_dict( | |
3106 typval_T *argvars, | |
3107 type_T *type, | |
3108 char *func_name, | |
3109 char_u *arg_errmsg, | |
3110 int is_new, | |
3111 typval_T *rettv) | |
3112 { | |
3113 dict_T *d1, *d2; | |
3114 char_u *action; | |
3115 int i; | |
3116 | |
3117 d1 = argvars[0].vval.v_dict; | |
3118 if (d1 == NULL) | |
3119 { | |
3120 emsg(_(e_cannot_extend_null_dict)); | |
3121 return; | |
3122 } | |
3123 d2 = argvars[1].vval.v_dict; | |
3124 if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE)) | |
3125 && d2 != NULL) | |
3126 { | |
3127 if (is_new) | |
3128 { | |
3129 d1 = dict_copy(d1, FALSE, get_copyID()); | |
3130 if (d1 == NULL) | |
3131 return; | |
3132 } | |
3133 | |
3134 // Check the third argument. | |
3135 if (argvars[2].v_type != VAR_UNKNOWN) | |
3136 { | |
3137 static char *(av[]) = {"keep", "force", "error"}; | |
3138 | |
3139 action = tv_get_string_chk(&argvars[2]); | |
3140 if (action == NULL) | |
3141 return; | |
3142 for (i = 0; i < 3; ++i) | |
3143 if (STRCMP(action, av[i]) == 0) | |
3144 break; | |
3145 if (i == 3) | |
3146 { | |
3147 semsg(_(e_invarg2), action); | |
3148 return; | |
3149 } | |
3150 } | |
3151 else | |
3152 action = (char_u *)"force"; | |
3153 | |
3154 if (type != NULL && check_typval_arg_type(type, &argvars[1], | |
3155 func_name, 2) == FAIL) | |
3156 return; | |
3157 dict_extend(d1, d2, action, func_name); | |
3158 | |
3159 if (is_new) | |
3160 { | |
3161 rettv->v_type = VAR_DICT; | |
3162 rettv->vval.v_dict = d1; | |
3163 rettv->v_lock = FALSE; | |
3164 } | |
3165 else | |
3166 copy_tv(&argvars[0], rettv); | |
3167 } | |
3168 } | |
3169 | |
3170 /* | |
2953 * "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew(). | 3171 * "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew(). |
2954 */ | 3172 */ |
2955 static void | 3173 static void |
2956 extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new) | 3174 extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new) |
2957 { | 3175 { |
2965 ga_init2(&type_list, sizeof(type_T *), 10); | 3183 ga_init2(&type_list, sizeof(type_T *), 10); |
2966 type = typval2type(argvars, get_copyID(), &type_list, TRUE); | 3184 type = typval2type(argvars, get_copyID(), &type_list, TRUE); |
2967 } | 3185 } |
2968 | 3186 |
2969 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) | 3187 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) |
2970 { | 3188 extend_list(argvars, type, func_name, arg_errmsg, is_new, rettv); |
2971 list_T *l1, *l2; | |
2972 listitem_T *item; | |
2973 long before; | |
2974 int error = FALSE; | |
2975 | |
2976 l1 = argvars[0].vval.v_list; | |
2977 if (l1 == NULL) | |
2978 { | |
2979 emsg(_(e_cannot_extend_null_list)); | |
2980 goto theend; | |
2981 } | |
2982 l2 = argvars[1].vval.v_list; | |
2983 if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE)) | |
2984 && l2 != NULL) | |
2985 { | |
2986 if (is_new) | |
2987 { | |
2988 l1 = list_copy(l1, FALSE, get_copyID()); | |
2989 if (l1 == NULL) | |
2990 goto theend; | |
2991 } | |
2992 | |
2993 if (argvars[2].v_type != VAR_UNKNOWN) | |
2994 { | |
2995 before = (long)tv_get_number_chk(&argvars[2], &error); | |
2996 if (error) | |
2997 goto theend; // type error; errmsg already given | |
2998 | |
2999 if (before == l1->lv_len) | |
3000 item = NULL; | |
3001 else | |
3002 { | |
3003 item = list_find(l1, before); | |
3004 if (item == NULL) | |
3005 { | |
3006 semsg(_(e_listidx), before); | |
3007 goto theend; | |
3008 } | |
3009 } | |
3010 } | |
3011 else | |
3012 item = NULL; | |
3013 if (type != NULL && check_typval_arg_type( | |
3014 type, &argvars[1], func_name, 2) == FAIL) | |
3015 goto theend; | |
3016 list_extend(l1, l2, item); | |
3017 | |
3018 if (is_new) | |
3019 { | |
3020 rettv->v_type = VAR_LIST; | |
3021 rettv->vval.v_list = l1; | |
3022 rettv->v_lock = FALSE; | |
3023 } | |
3024 else | |
3025 copy_tv(&argvars[0], rettv); | |
3026 } | |
3027 } | |
3028 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) | 3189 else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) |
3029 { | 3190 extend_dict(argvars, type, func_name, arg_errmsg, is_new, rettv); |
3030 dict_T *d1, *d2; | |
3031 char_u *action; | |
3032 int i; | |
3033 | |
3034 d1 = argvars[0].vval.v_dict; | |
3035 if (d1 == NULL) | |
3036 { | |
3037 emsg(_(e_cannot_extend_null_dict)); | |
3038 goto theend; | |
3039 } | |
3040 d2 = argvars[1].vval.v_dict; | |
3041 if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE)) | |
3042 && d2 != NULL) | |
3043 { | |
3044 if (is_new) | |
3045 { | |
3046 d1 = dict_copy(d1, FALSE, get_copyID()); | |
3047 if (d1 == NULL) | |
3048 goto theend; | |
3049 } | |
3050 | |
3051 // Check the third argument. | |
3052 if (argvars[2].v_type != VAR_UNKNOWN) | |
3053 { | |
3054 static char *(av[]) = {"keep", "force", "error"}; | |
3055 | |
3056 action = tv_get_string_chk(&argvars[2]); | |
3057 if (action == NULL) | |
3058 goto theend; // type error; errmsg already given | |
3059 for (i = 0; i < 3; ++i) | |
3060 if (STRCMP(action, av[i]) == 0) | |
3061 break; | |
3062 if (i == 3) | |
3063 { | |
3064 semsg(_(e_invarg2), action); | |
3065 goto theend; | |
3066 } | |
3067 } | |
3068 else | |
3069 action = (char_u *)"force"; | |
3070 | |
3071 if (type != NULL && check_typval_arg_type(type, &argvars[1], | |
3072 func_name, 2) == FAIL) | |
3073 goto theend; | |
3074 dict_extend(d1, d2, action, func_name); | |
3075 | |
3076 if (is_new) | |
3077 { | |
3078 rettv->v_type = VAR_DICT; | |
3079 rettv->vval.v_dict = d1; | |
3080 rettv->v_lock = FALSE; | |
3081 } | |
3082 else | |
3083 copy_tv(&argvars[0], rettv); | |
3084 } | |
3085 } | |
3086 else | 3191 else |
3087 semsg(_(e_listdictarg), func_name); | 3192 semsg(_(e_listdictarg), func_name); |
3088 | 3193 |
3089 theend: | |
3090 if (type != NULL) | 3194 if (type != NULL) |
3091 clear_type_list(&type_list); | 3195 clear_type_list(&type_list); |
3092 } | 3196 } |
3093 | 3197 |
3094 /* | 3198 /* |
3306 } | 3410 } |
3307 } | 3411 } |
3308 } | 3412 } |
3309 | 3413 |
3310 /* | 3414 /* |
3311 * reduce() on a List | 3415 * reduce() List argvars[0] using the function 'funcname' with arguments in |
3416 * 'funcexe' starting with the initial value argvars[2] and return the result | |
3417 * in 'rettv'. | |
3312 */ | 3418 */ |
3313 static void | 3419 static void |
3314 reduce_list( | 3420 reduce_list( |
3315 typval_T *argvars, | 3421 typval_T *argvars, |
3316 char_u *func_name, | 3422 char_u *func_name, |
3363 } | 3469 } |
3364 l->lv_lock = prev_locked; | 3470 l->lv_lock = prev_locked; |
3365 } | 3471 } |
3366 | 3472 |
3367 /* | 3473 /* |
3368 * reduce() on a String | 3474 * reduce() String argvars[0] using the function 'funcname' with arguments in |
3475 * 'funcexe' starting with the initial value argvars[2] and return the result | |
3476 * in 'rettv'. | |
3369 */ | 3477 */ |
3370 static void | 3478 static void |
3371 reduce_string( | 3479 reduce_string( |
3372 typval_T *argvars, | 3480 typval_T *argvars, |
3373 char_u *func_name, | 3481 char_u *func_name, |
3412 return; | 3520 return; |
3413 } | 3521 } |
3414 } | 3522 } |
3415 | 3523 |
3416 /* | 3524 /* |
3417 * reduce() on a Blob | 3525 * reduce() Blob argvars[0] using the function 'funcname' with arguments in |
3526 * 'funcexe' starting with the initial value argvars[2] and return the result | |
3527 * in 'rettv'. | |
3418 */ | 3528 */ |
3419 static void | 3529 static void |
3420 reduce_blob( | 3530 reduce_blob( |
3421 typval_T *argvars, | 3531 typval_T *argvars, |
3422 char_u *func_name, | 3532 char_u *func_name, |
3468 } | 3578 } |
3469 } | 3579 } |
3470 | 3580 |
3471 /* | 3581 /* |
3472 * "reduce(list, { accumulator, element -> value } [, initial])" function | 3582 * "reduce(list, { accumulator, element -> value } [, initial])" function |
3583 * "reduce(blob, { accumulator, element -> value } [, initial])" | |
3584 * "reduce(string, { accumulator, element -> value } [, initial])" | |
3473 */ | 3585 */ |
3474 void | 3586 void |
3475 f_reduce(typval_T *argvars, typval_T *rettv) | 3587 f_reduce(typval_T *argvars, typval_T *rettv) |
3476 { | 3588 { |
3477 char_u *func_name; | 3589 char_u *func_name; |