Mercurial > vim
comparison src/list.c @ 17530:ef23ec1eee54 v8.1.1763
patch 8.1.1763: evalfunc.c is still too big
commit https://github.com/vim/vim/commit/9f9fe37f6750f342255a51f158a7bb372c827f7f
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Jul 27 23:12:12 2019 +0200
patch 8.1.1763: evalfunc.c is still too big
Problem: Evalfunc.c is still too big.
Solution: Move dict and list functions to a better place.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 27 Jul 2019 23:15:05 +0200 |
parents | 041156ce1d22 |
children | ff097edaae89 |
comparison
equal
deleted
inserted
replaced
17529:4bd2e3339ba3 | 17530:ef23ec1eee54 |
---|---|
873 | 873 |
874 return retval; | 874 return retval; |
875 } | 875 } |
876 | 876 |
877 /* | 877 /* |
878 * "join()" function | |
879 */ | |
880 void | |
881 f_join(typval_T *argvars, typval_T *rettv) | |
882 { | |
883 garray_T ga; | |
884 char_u *sep; | |
885 | |
886 if (argvars[0].v_type != VAR_LIST) | |
887 { | |
888 emsg(_(e_listreq)); | |
889 return; | |
890 } | |
891 if (argvars[0].vval.v_list == NULL) | |
892 return; | |
893 if (argvars[1].v_type == VAR_UNKNOWN) | |
894 sep = (char_u *)" "; | |
895 else | |
896 sep = tv_get_string_chk(&argvars[1]); | |
897 | |
898 rettv->v_type = VAR_STRING; | |
899 | |
900 if (sep != NULL) | |
901 { | |
902 ga_init2(&ga, (int)sizeof(char), 80); | |
903 list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); | |
904 ga_append(&ga, NUL); | |
905 rettv->vval.v_string = (char_u *)ga.ga_data; | |
906 } | |
907 else | |
908 rettv->vval.v_string = NULL; | |
909 } | |
910 | |
911 /* | |
878 * Allocate a variable for a List and fill it from "*arg". | 912 * Allocate a variable for a List and fill it from "*arg". |
879 * Return OK or FAIL. | 913 * Return OK or FAIL. |
880 */ | 914 */ |
881 int | 915 int |
882 get_list_tv(char_u **arg, typval_T *rettv, int evaluate) | 916 get_list_tv(char_u **arg, typval_T *rettv, int evaluate) |
1005 else | 1039 else |
1006 li->li_next = li + 1; | 1040 li->li_next = li + 1; |
1007 } | 1041 } |
1008 } | 1042 } |
1009 | 1043 |
1044 /* | |
1045 * "list2str()" function | |
1046 */ | |
1047 void | |
1048 f_list2str(typval_T *argvars, typval_T *rettv) | |
1049 { | |
1050 list_T *l; | |
1051 listitem_T *li; | |
1052 garray_T ga; | |
1053 int utf8 = FALSE; | |
1054 | |
1055 rettv->v_type = VAR_STRING; | |
1056 rettv->vval.v_string = NULL; | |
1057 if (argvars[0].v_type != VAR_LIST) | |
1058 { | |
1059 emsg(_(e_invarg)); | |
1060 return; | |
1061 } | |
1062 | |
1063 l = argvars[0].vval.v_list; | |
1064 if (l == NULL) | |
1065 return; // empty list results in empty string | |
1066 | |
1067 if (argvars[1].v_type != VAR_UNKNOWN) | |
1068 utf8 = (int)tv_get_number_chk(&argvars[1], NULL); | |
1069 | |
1070 ga_init2(&ga, 1, 80); | |
1071 if (has_mbyte || utf8) | |
1072 { | |
1073 char_u buf[MB_MAXBYTES + 1]; | |
1074 int (*char2bytes)(int, char_u *); | |
1075 | |
1076 if (utf8 || enc_utf8) | |
1077 char2bytes = utf_char2bytes; | |
1078 else | |
1079 char2bytes = mb_char2bytes; | |
1080 | |
1081 for (li = l->lv_first; li != NULL; li = li->li_next) | |
1082 { | |
1083 buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL; | |
1084 ga_concat(&ga, buf); | |
1085 } | |
1086 ga_append(&ga, NUL); | |
1087 } | |
1088 else if (ga_grow(&ga, list_len(l) + 1) == OK) | |
1089 { | |
1090 for (li = l->lv_first; li != NULL; li = li->li_next) | |
1091 ga_append(&ga, tv_get_number(&li->li_tv)); | |
1092 ga_append(&ga, NUL); | |
1093 } | |
1094 | |
1095 rettv->v_type = VAR_STRING; | |
1096 rettv->vval.v_string = ga.ga_data; | |
1097 } | |
1098 | |
1099 void | |
1100 list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) | |
1101 { | |
1102 list_T *l; | |
1103 listitem_T *item, *item2; | |
1104 listitem_T *li; | |
1105 int error = FALSE; | |
1106 int idx; | |
1107 | |
1108 if ((l = argvars[0].vval.v_list) == NULL | |
1109 || var_check_lock(l->lv_lock, arg_errmsg, TRUE)) | |
1110 return; | |
1111 | |
1112 idx = (long)tv_get_number_chk(&argvars[1], &error); | |
1113 if (error) | |
1114 ; // type error: do nothing, errmsg already given | |
1115 else if ((item = list_find(l, idx)) == NULL) | |
1116 semsg(_(e_listidx), idx); | |
1117 else | |
1118 { | |
1119 if (argvars[2].v_type == VAR_UNKNOWN) | |
1120 { | |
1121 /* Remove one item, return its value. */ | |
1122 vimlist_remove(l, item, item); | |
1123 *rettv = item->li_tv; | |
1124 vim_free(item); | |
1125 } | |
1126 else | |
1127 { | |
1128 // Remove range of items, return list with values. | |
1129 int end = (long)tv_get_number_chk(&argvars[2], &error); | |
1130 | |
1131 if (error) | |
1132 ; // type error: do nothing | |
1133 else if ((item2 = list_find(l, end)) == NULL) | |
1134 semsg(_(e_listidx), end); | |
1135 else | |
1136 { | |
1137 int cnt = 0; | |
1138 | |
1139 for (li = item; li != NULL; li = li->li_next) | |
1140 { | |
1141 ++cnt; | |
1142 if (li == item2) | |
1143 break; | |
1144 } | |
1145 if (li == NULL) /* didn't find "item2" after "item" */ | |
1146 emsg(_(e_invrange)); | |
1147 else | |
1148 { | |
1149 vimlist_remove(l, item, item2); | |
1150 if (rettv_list_alloc(rettv) == OK) | |
1151 { | |
1152 l = rettv->vval.v_list; | |
1153 l->lv_first = item; | |
1154 l->lv_last = item2; | |
1155 item->li_prev = NULL; | |
1156 item2->li_next = NULL; | |
1157 l->lv_len = cnt; | |
1158 } | |
1159 } | |
1160 } | |
1161 } | |
1162 } | |
1163 } | |
1164 | |
1165 static int item_compare(const void *s1, const void *s2); | |
1166 static int item_compare2(const void *s1, const void *s2); | |
1167 | |
1168 /* struct used in the array that's given to qsort() */ | |
1169 typedef struct | |
1170 { | |
1171 listitem_T *item; | |
1172 int idx; | |
1173 } sortItem_T; | |
1174 | |
1175 /* struct storing information about current sort */ | |
1176 typedef struct | |
1177 { | |
1178 int item_compare_ic; | |
1179 int item_compare_numeric; | |
1180 int item_compare_numbers; | |
1181 #ifdef FEAT_FLOAT | |
1182 int item_compare_float; | |
1183 #endif | |
1184 char_u *item_compare_func; | |
1185 partial_T *item_compare_partial; | |
1186 dict_T *item_compare_selfdict; | |
1187 int item_compare_func_err; | |
1188 int item_compare_keep_zero; | |
1189 } sortinfo_T; | |
1190 static sortinfo_T *sortinfo = NULL; | |
1191 #define ITEM_COMPARE_FAIL 999 | |
1192 | |
1193 /* | |
1194 * Compare functions for f_sort() and f_uniq() below. | |
1195 */ | |
1196 static int | |
1197 item_compare(const void *s1, const void *s2) | |
1198 { | |
1199 sortItem_T *si1, *si2; | |
1200 typval_T *tv1, *tv2; | |
1201 char_u *p1, *p2; | |
1202 char_u *tofree1 = NULL, *tofree2 = NULL; | |
1203 int res; | |
1204 char_u numbuf1[NUMBUFLEN]; | |
1205 char_u numbuf2[NUMBUFLEN]; | |
1206 | |
1207 si1 = (sortItem_T *)s1; | |
1208 si2 = (sortItem_T *)s2; | |
1209 tv1 = &si1->item->li_tv; | |
1210 tv2 = &si2->item->li_tv; | |
1211 | |
1212 if (sortinfo->item_compare_numbers) | |
1213 { | |
1214 varnumber_T v1 = tv_get_number(tv1); | |
1215 varnumber_T v2 = tv_get_number(tv2); | |
1216 | |
1217 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; | |
1218 } | |
1219 | |
1220 #ifdef FEAT_FLOAT | |
1221 if (sortinfo->item_compare_float) | |
1222 { | |
1223 float_T v1 = tv_get_float(tv1); | |
1224 float_T v2 = tv_get_float(tv2); | |
1225 | |
1226 return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; | |
1227 } | |
1228 #endif | |
1229 | |
1230 /* tv2string() puts quotes around a string and allocates memory. Don't do | |
1231 * that for string variables. Use a single quote when comparing with a | |
1232 * non-string to do what the docs promise. */ | |
1233 if (tv1->v_type == VAR_STRING) | |
1234 { | |
1235 if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) | |
1236 p1 = (char_u *)"'"; | |
1237 else | |
1238 p1 = tv1->vval.v_string; | |
1239 } | |
1240 else | |
1241 p1 = tv2string(tv1, &tofree1, numbuf1, 0); | |
1242 if (tv2->v_type == VAR_STRING) | |
1243 { | |
1244 if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) | |
1245 p2 = (char_u *)"'"; | |
1246 else | |
1247 p2 = tv2->vval.v_string; | |
1248 } | |
1249 else | |
1250 p2 = tv2string(tv2, &tofree2, numbuf2, 0); | |
1251 if (p1 == NULL) | |
1252 p1 = (char_u *)""; | |
1253 if (p2 == NULL) | |
1254 p2 = (char_u *)""; | |
1255 if (!sortinfo->item_compare_numeric) | |
1256 { | |
1257 if (sortinfo->item_compare_ic) | |
1258 res = STRICMP(p1, p2); | |
1259 else | |
1260 res = STRCMP(p1, p2); | |
1261 } | |
1262 else | |
1263 { | |
1264 double n1, n2; | |
1265 n1 = strtod((char *)p1, (char **)&p1); | |
1266 n2 = strtod((char *)p2, (char **)&p2); | |
1267 res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; | |
1268 } | |
1269 | |
1270 /* When the result would be zero, compare the item indexes. Makes the | |
1271 * sort stable. */ | |
1272 if (res == 0 && !sortinfo->item_compare_keep_zero) | |
1273 res = si1->idx > si2->idx ? 1 : -1; | |
1274 | |
1275 vim_free(tofree1); | |
1276 vim_free(tofree2); | |
1277 return res; | |
1278 } | |
1279 | |
1280 static int | |
1281 item_compare2(const void *s1, const void *s2) | |
1282 { | |
1283 sortItem_T *si1, *si2; | |
1284 int res; | |
1285 typval_T rettv; | |
1286 typval_T argv[3]; | |
1287 int dummy; | |
1288 char_u *func_name; | |
1289 partial_T *partial = sortinfo->item_compare_partial; | |
1290 | |
1291 /* shortcut after failure in previous call; compare all items equal */ | |
1292 if (sortinfo->item_compare_func_err) | |
1293 return 0; | |
1294 | |
1295 si1 = (sortItem_T *)s1; | |
1296 si2 = (sortItem_T *)s2; | |
1297 | |
1298 if (partial == NULL) | |
1299 func_name = sortinfo->item_compare_func; | |
1300 else | |
1301 func_name = partial_name(partial); | |
1302 | |
1303 /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED | |
1304 * in the copy without changing the original list items. */ | |
1305 copy_tv(&si1->item->li_tv, &argv[0]); | |
1306 copy_tv(&si2->item->li_tv, &argv[1]); | |
1307 | |
1308 rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ | |
1309 res = call_func(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, | |
1310 partial, sortinfo->item_compare_selfdict); | |
1311 clear_tv(&argv[0]); | |
1312 clear_tv(&argv[1]); | |
1313 | |
1314 if (res == FAIL) | |
1315 res = ITEM_COMPARE_FAIL; | |
1316 else | |
1317 res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); | |
1318 if (sortinfo->item_compare_func_err) | |
1319 res = ITEM_COMPARE_FAIL; /* return value has wrong type */ | |
1320 clear_tv(&rettv); | |
1321 | |
1322 /* When the result would be zero, compare the pointers themselves. Makes | |
1323 * the sort stable. */ | |
1324 if (res == 0 && !sortinfo->item_compare_keep_zero) | |
1325 res = si1->idx > si2->idx ? 1 : -1; | |
1326 | |
1327 return res; | |
1328 } | |
1329 | |
1330 /* | |
1331 * "sort()" or "uniq()" function | |
1332 */ | |
1333 static void | |
1334 do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) | |
1335 { | |
1336 list_T *l; | |
1337 listitem_T *li; | |
1338 sortItem_T *ptrs; | |
1339 sortinfo_T *old_sortinfo; | |
1340 sortinfo_T info; | |
1341 long len; | |
1342 long i; | |
1343 | |
1344 /* Pointer to current info struct used in compare function. Save and | |
1345 * restore the current one for nested calls. */ | |
1346 old_sortinfo = sortinfo; | |
1347 sortinfo = &info; | |
1348 | |
1349 if (argvars[0].v_type != VAR_LIST) | |
1350 semsg(_(e_listarg), sort ? "sort()" : "uniq()"); | |
1351 else | |
1352 { | |
1353 l = argvars[0].vval.v_list; | |
1354 if (l == NULL || var_check_lock(l->lv_lock, | |
1355 (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), | |
1356 TRUE)) | |
1357 goto theend; | |
1358 rettv_list_set(rettv, l); | |
1359 | |
1360 len = list_len(l); | |
1361 if (len <= 1) | |
1362 goto theend; /* short list sorts pretty quickly */ | |
1363 | |
1364 info.item_compare_ic = FALSE; | |
1365 info.item_compare_numeric = FALSE; | |
1366 info.item_compare_numbers = FALSE; | |
1367 #ifdef FEAT_FLOAT | |
1368 info.item_compare_float = FALSE; | |
1369 #endif | |
1370 info.item_compare_func = NULL; | |
1371 info.item_compare_partial = NULL; | |
1372 info.item_compare_selfdict = NULL; | |
1373 if (argvars[1].v_type != VAR_UNKNOWN) | |
1374 { | |
1375 /* optional second argument: {func} */ | |
1376 if (argvars[1].v_type == VAR_FUNC) | |
1377 info.item_compare_func = argvars[1].vval.v_string; | |
1378 else if (argvars[1].v_type == VAR_PARTIAL) | |
1379 info.item_compare_partial = argvars[1].vval.v_partial; | |
1380 else | |
1381 { | |
1382 int error = FALSE; | |
1383 | |
1384 i = (long)tv_get_number_chk(&argvars[1], &error); | |
1385 if (error) | |
1386 goto theend; /* type error; errmsg already given */ | |
1387 if (i == 1) | |
1388 info.item_compare_ic = TRUE; | |
1389 else if (argvars[1].v_type != VAR_NUMBER) | |
1390 info.item_compare_func = tv_get_string(&argvars[1]); | |
1391 else if (i != 0) | |
1392 { | |
1393 emsg(_(e_invarg)); | |
1394 goto theend; | |
1395 } | |
1396 if (info.item_compare_func != NULL) | |
1397 { | |
1398 if (*info.item_compare_func == NUL) | |
1399 { | |
1400 /* empty string means default sort */ | |
1401 info.item_compare_func = NULL; | |
1402 } | |
1403 else if (STRCMP(info.item_compare_func, "n") == 0) | |
1404 { | |
1405 info.item_compare_func = NULL; | |
1406 info.item_compare_numeric = TRUE; | |
1407 } | |
1408 else if (STRCMP(info.item_compare_func, "N") == 0) | |
1409 { | |
1410 info.item_compare_func = NULL; | |
1411 info.item_compare_numbers = TRUE; | |
1412 } | |
1413 #ifdef FEAT_FLOAT | |
1414 else if (STRCMP(info.item_compare_func, "f") == 0) | |
1415 { | |
1416 info.item_compare_func = NULL; | |
1417 info.item_compare_float = TRUE; | |
1418 } | |
1419 #endif | |
1420 else if (STRCMP(info.item_compare_func, "i") == 0) | |
1421 { | |
1422 info.item_compare_func = NULL; | |
1423 info.item_compare_ic = TRUE; | |
1424 } | |
1425 } | |
1426 } | |
1427 | |
1428 if (argvars[2].v_type != VAR_UNKNOWN) | |
1429 { | |
1430 /* optional third argument: {dict} */ | |
1431 if (argvars[2].v_type != VAR_DICT) | |
1432 { | |
1433 emsg(_(e_dictreq)); | |
1434 goto theend; | |
1435 } | |
1436 info.item_compare_selfdict = argvars[2].vval.v_dict; | |
1437 } | |
1438 } | |
1439 | |
1440 /* Make an array with each entry pointing to an item in the List. */ | |
1441 ptrs = ALLOC_MULT(sortItem_T, len); | |
1442 if (ptrs == NULL) | |
1443 goto theend; | |
1444 | |
1445 i = 0; | |
1446 if (sort) | |
1447 { | |
1448 /* sort(): ptrs will be the list to sort */ | |
1449 for (li = l->lv_first; li != NULL; li = li->li_next) | |
1450 { | |
1451 ptrs[i].item = li; | |
1452 ptrs[i].idx = i; | |
1453 ++i; | |
1454 } | |
1455 | |
1456 info.item_compare_func_err = FALSE; | |
1457 info.item_compare_keep_zero = FALSE; | |
1458 /* test the compare function */ | |
1459 if ((info.item_compare_func != NULL | |
1460 || info.item_compare_partial != NULL) | |
1461 && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) | |
1462 == ITEM_COMPARE_FAIL) | |
1463 emsg(_("E702: Sort compare function failed")); | |
1464 else | |
1465 { | |
1466 /* Sort the array with item pointers. */ | |
1467 qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), | |
1468 info.item_compare_func == NULL | |
1469 && info.item_compare_partial == NULL | |
1470 ? item_compare : item_compare2); | |
1471 | |
1472 if (!info.item_compare_func_err) | |
1473 { | |
1474 /* Clear the List and append the items in sorted order. */ | |
1475 l->lv_first = l->lv_last = l->lv_idx_item = NULL; | |
1476 l->lv_len = 0; | |
1477 for (i = 0; i < len; ++i) | |
1478 list_append(l, ptrs[i].item); | |
1479 } | |
1480 } | |
1481 } | |
1482 else | |
1483 { | |
1484 int (*item_compare_func_ptr)(const void *, const void *); | |
1485 | |
1486 /* f_uniq(): ptrs will be a stack of items to remove */ | |
1487 info.item_compare_func_err = FALSE; | |
1488 info.item_compare_keep_zero = TRUE; | |
1489 item_compare_func_ptr = info.item_compare_func != NULL | |
1490 || info.item_compare_partial != NULL | |
1491 ? item_compare2 : item_compare; | |
1492 | |
1493 for (li = l->lv_first; li != NULL && li->li_next != NULL; | |
1494 li = li->li_next) | |
1495 { | |
1496 if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) | |
1497 == 0) | |
1498 ptrs[i++].item = li; | |
1499 if (info.item_compare_func_err) | |
1500 { | |
1501 emsg(_("E882: Uniq compare function failed")); | |
1502 break; | |
1503 } | |
1504 } | |
1505 | |
1506 if (!info.item_compare_func_err) | |
1507 { | |
1508 while (--i >= 0) | |
1509 { | |
1510 li = ptrs[i].item->li_next; | |
1511 ptrs[i].item->li_next = li->li_next; | |
1512 if (li->li_next != NULL) | |
1513 li->li_next->li_prev = ptrs[i].item; | |
1514 else | |
1515 l->lv_last = ptrs[i].item; | |
1516 list_fix_watch(l, li); | |
1517 listitem_free(li); | |
1518 l->lv_len--; | |
1519 } | |
1520 } | |
1521 } | |
1522 | |
1523 vim_free(ptrs); | |
1524 } | |
1525 theend: | |
1526 sortinfo = old_sortinfo; | |
1527 } | |
1528 | |
1529 /* | |
1530 * "sort({list})" function | |
1531 */ | |
1532 void | |
1533 f_sort(typval_T *argvars, typval_T *rettv) | |
1534 { | |
1535 do_sort_uniq(argvars, rettv, TRUE); | |
1536 } | |
1537 | |
1538 /* | |
1539 * "uniq({list})" function | |
1540 */ | |
1541 void | |
1542 f_uniq(typval_T *argvars, typval_T *rettv) | |
1543 { | |
1544 do_sort_uniq(argvars, rettv, FALSE); | |
1545 } | |
1546 | |
1010 #endif /* defined(FEAT_EVAL) */ | 1547 #endif /* defined(FEAT_EVAL) */ |