Yegappan Lakshmanan | 9cb865e | 2025-03-23 16:42:16 +0100 | [diff] [blame] | 1 | /* vi:set ts=8 sts=4 sw=4 noet: |
| 2 | * |
| 3 | * VIM - Vi IMproved by Bram Moolenaar |
| 4 | * |
| 5 | * Do ":help uganda" in Vim to read copying and usage conditions. |
| 6 | * Do ":help credits" in Vim to see a list of people who contributed. |
| 7 | * See README.txt for an overview of the Vim source code. |
| 8 | */ |
| 9 | |
| 10 | /* |
| 11 | * tuple.c: Tuple support functions. |
| 12 | */ |
| 13 | |
| 14 | #include "vim.h" |
| 15 | |
| 16 | #if defined(FEAT_EVAL) || defined(PROTO) |
| 17 | |
| 18 | // Tuple heads for garbage collection. |
| 19 | static tuple_T *first_tuple = NULL; // list of all tuples |
| 20 | |
| 21 | static void |
| 22 | tuple_init(tuple_T *tuple) |
| 23 | { |
| 24 | // Prepend the tuple to the list of tuples for garbage collection. |
| 25 | if (first_tuple != NULL) |
| 26 | first_tuple->tv_used_prev = tuple; |
| 27 | tuple->tv_used_prev = NULL; |
| 28 | tuple->tv_used_next = first_tuple; |
| 29 | first_tuple = tuple; |
| 30 | |
| 31 | ga_init2(&tuple->tv_items, sizeof(typval_T), 20); |
| 32 | } |
| 33 | |
| 34 | /* |
| 35 | * Allocate an empty header for a tuple. |
| 36 | * Caller should take care of the reference count. |
| 37 | */ |
| 38 | tuple_T * |
| 39 | tuple_alloc(void) |
| 40 | { |
| 41 | tuple_T *tuple; |
| 42 | |
| 43 | tuple = ALLOC_CLEAR_ONE(tuple_T); |
| 44 | if (tuple != NULL) |
| 45 | tuple_init(tuple); |
| 46 | return tuple; |
| 47 | } |
| 48 | |
| 49 | /* |
| 50 | * Allocate space for a tuple with "count" items. |
| 51 | * This uses one allocation for efficiency. |
| 52 | * The reference count is not set. |
| 53 | * Next tuple_set_item() must be called for each item. |
| 54 | */ |
| 55 | tuple_T * |
| 56 | tuple_alloc_with_items(int count) |
| 57 | { |
| 58 | tuple_T *tuple; |
| 59 | |
| 60 | tuple = tuple_alloc(); |
| 61 | if (tuple == NULL) |
| 62 | return NULL; |
| 63 | |
| 64 | if (count <= 0) |
| 65 | return tuple; |
| 66 | |
| 67 | if (ga_grow(&tuple->tv_items, count) == FAIL) |
| 68 | { |
| 69 | tuple_free(tuple); |
| 70 | return NULL; |
| 71 | } |
| 72 | |
| 73 | return tuple; |
| 74 | } |
| 75 | |
| 76 | /* |
| 77 | * Set item "idx" for a tuple previously allocated with |
| 78 | * tuple_alloc_with_items(). |
| 79 | * The contents of "tv" is copied into the tuple item. |
| 80 | * Each item must be set exactly once. |
| 81 | */ |
| 82 | void |
| 83 | tuple_set_item(tuple_T *tuple, int idx, typval_T *tv) |
| 84 | { |
| 85 | *TUPLE_ITEM(tuple, idx) = *tv; |
| 86 | tuple->tv_items.ga_len++; |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | * Allocate an empty tuple for a return value, with reference count set. |
| 91 | * Returns OK or FAIL. |
| 92 | */ |
| 93 | int |
| 94 | rettv_tuple_alloc(typval_T *rettv) |
| 95 | { |
| 96 | tuple_T *tuple = tuple_alloc(); |
| 97 | |
| 98 | if (tuple == NULL) |
| 99 | return FAIL; |
| 100 | |
| 101 | rettv->v_lock = 0; |
| 102 | rettv_tuple_set(rettv, tuple); |
| 103 | return OK; |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | * Set a tuple as the return value. Increments the reference count. |
| 108 | */ |
| 109 | void |
| 110 | rettv_tuple_set(typval_T *rettv, tuple_T *tuple) |
| 111 | { |
| 112 | rettv->v_type = VAR_TUPLE; |
| 113 | rettv->vval.v_tuple = tuple; |
| 114 | if (tuple != NULL) |
| 115 | ++tuple->tv_refcount; |
| 116 | } |
| 117 | |
| 118 | /* |
| 119 | * Set a new tuple with "count" items as the return value. |
| 120 | * Returns OK on success and FAIL on allocation failure. |
| 121 | */ |
| 122 | int |
| 123 | rettv_tuple_set_with_items(typval_T *rettv, int count) |
| 124 | { |
| 125 | tuple_T *new_tuple; |
| 126 | |
| 127 | new_tuple = tuple_alloc_with_items(count); |
| 128 | if (new_tuple == NULL) |
| 129 | return FAIL; |
| 130 | |
| 131 | rettv_tuple_set(rettv, new_tuple); |
| 132 | |
| 133 | return OK; |
| 134 | } |
| 135 | |
| 136 | /* |
| 137 | * Unreference a tuple: decrement the reference count and free it when it |
| 138 | * becomes zero. |
| 139 | */ |
| 140 | void |
| 141 | tuple_unref(tuple_T *tuple) |
| 142 | { |
| 143 | if (tuple != NULL && --tuple->tv_refcount <= 0) |
| 144 | tuple_free(tuple); |
| 145 | } |
| 146 | |
| 147 | /* |
| 148 | * Free a tuple, including all non-container items it points to. |
| 149 | * Ignores the reference count. |
| 150 | */ |
| 151 | static void |
| 152 | tuple_free_contents(tuple_T *tuple) |
| 153 | { |
| 154 | for (int i = 0; i < TUPLE_LEN(tuple); i++) |
| 155 | clear_tv(TUPLE_ITEM(tuple, i)); |
| 156 | |
| 157 | ga_clear(&tuple->tv_items); |
| 158 | } |
| 159 | |
| 160 | /* |
| 161 | * Go through the list of tuples and free items without the copyID. |
| 162 | * But don't free a tuple that has a watcher (used in a for loop), these |
| 163 | * are not referenced anywhere. |
| 164 | */ |
| 165 | int |
| 166 | tuple_free_nonref(int copyID) |
| 167 | { |
| 168 | tuple_T *tt; |
| 169 | int did_free = FALSE; |
| 170 | |
| 171 | for (tt = first_tuple; tt != NULL; tt = tt->tv_used_next) |
| 172 | if ((tt->tv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) |
| 173 | { |
| 174 | // Free the Tuple and ordinary items it contains, but don't recurse |
| 175 | // into Lists and Dictionaries, they will be in the list of dicts |
| 176 | // or list of lists. |
| 177 | tuple_free_contents(tt); |
| 178 | did_free = TRUE; |
| 179 | } |
| 180 | return did_free; |
| 181 | } |
| 182 | |
| 183 | static void |
| 184 | tuple_free_list(tuple_T *tuple) |
| 185 | { |
| 186 | // Remove the tuple from the list of tuples for garbage collection. |
| 187 | if (tuple->tv_used_prev == NULL) |
| 188 | first_tuple = tuple->tv_used_next; |
| 189 | else |
| 190 | tuple->tv_used_prev->tv_used_next = tuple->tv_used_next; |
| 191 | if (tuple->tv_used_next != NULL) |
| 192 | tuple->tv_used_next->tv_used_prev = tuple->tv_used_prev; |
| 193 | |
| 194 | free_type(tuple->tv_type); |
| 195 | vim_free(tuple); |
| 196 | } |
| 197 | |
| 198 | void |
| 199 | tuple_free_items(int copyID) |
| 200 | { |
| 201 | tuple_T *tt, *tt_next; |
| 202 | |
| 203 | for (tt = first_tuple; tt != NULL; tt = tt_next) |
| 204 | { |
| 205 | tt_next = tt->tv_used_next; |
| 206 | if ((tt->tv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) |
| 207 | { |
| 208 | // Free the tuple and ordinary items it contains, but don't recurse |
| 209 | // into Lists and Dictionaries, they will be in the list of dicts |
| 210 | // or list of lists. |
| 211 | tuple_free_list(tt); |
| 212 | } |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | void |
| 217 | tuple_free(tuple_T *tuple) |
| 218 | { |
| 219 | if (in_free_unref_items) |
| 220 | return; |
| 221 | |
| 222 | tuple_free_contents(tuple); |
| 223 | tuple_free_list(tuple); |
| 224 | } |
| 225 | |
| 226 | /* |
| 227 | * Get the number of items in a tuple. |
| 228 | */ |
| 229 | long |
| 230 | tuple_len(tuple_T *tuple) |
| 231 | { |
| 232 | if (tuple == NULL) |
| 233 | return 0L; |
| 234 | return tuple->tv_items.ga_len; |
| 235 | } |
| 236 | |
| 237 | /* |
| 238 | * Return TRUE when two tuples have exactly the same values. |
| 239 | */ |
| 240 | int |
| 241 | tuple_equal( |
| 242 | tuple_T *t1, |
| 243 | tuple_T *t2, |
| 244 | int ic) // ignore case for strings |
| 245 | { |
| 246 | if (t1 == t2) |
| 247 | return TRUE; |
| 248 | |
| 249 | int t1_len = tuple_len(t1); |
| 250 | int t2_len = tuple_len(t2); |
| 251 | |
| 252 | if (t1_len != t2_len) |
| 253 | return FALSE; |
| 254 | |
| 255 | if (t1_len == 0) |
| 256 | // empty and NULL tuples are considered equal |
| 257 | return TRUE; |
| 258 | |
| 259 | // If the tuples "t1" or "t2" is NULL, then it is handled by the length |
| 260 | // checks above. |
| 261 | |
| 262 | for (int i = 0, j = 0; i < t1_len && j < t2_len; i++, j++) |
| 263 | if (!tv_equal(TUPLE_ITEM(t1, i), TUPLE_ITEM(t2, j), ic)) |
| 264 | return FALSE; |
| 265 | |
| 266 | return TRUE; |
| 267 | } |
| 268 | |
| 269 | /* |
| 270 | * Locate item with index "n" in tuple "tuple" and return it. |
| 271 | * A negative index is counted from the end; -1 is the last item. |
| 272 | * Returns NULL when "n" is out of range. |
| 273 | */ |
| 274 | typval_T * |
| 275 | tuple_find(tuple_T *tuple, long n) |
| 276 | { |
| 277 | if (tuple == NULL) |
| 278 | return NULL; |
| 279 | |
| 280 | // Negative index is relative to the end. |
| 281 | if (n < 0) |
| 282 | n = TUPLE_LEN(tuple) + n; |
| 283 | |
| 284 | // Check for index out of range. |
| 285 | if (n < 0 || n >= TUPLE_LEN(tuple)) |
| 286 | return NULL; |
| 287 | |
| 288 | return TUPLE_ITEM(tuple, n); |
| 289 | } |
| 290 | |
| 291 | int |
| 292 | tuple_append_tv(tuple_T *tuple, typval_T *tv) |
| 293 | { |
| 294 | if (ga_grow(&tuple->tv_items, 1) == FAIL) |
| 295 | return FAIL; |
| 296 | |
| 297 | tuple_set_item(tuple, TUPLE_LEN(tuple), tv); |
| 298 | |
| 299 | return OK; |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | * Concatenate tuples "t1" and "t2" into a new tuple, stored in "tv". |
| 304 | * Return FAIL when out of memory. |
| 305 | */ |
| 306 | int |
| 307 | tuple_concat(tuple_T *t1, tuple_T *t2, typval_T *tv) |
| 308 | { |
| 309 | tuple_T *tuple; |
| 310 | |
| 311 | // make a copy of the first tuple. |
| 312 | if (t1 == NULL) |
| 313 | tuple = tuple_alloc(); |
| 314 | else |
| 315 | tuple = tuple_copy(t1, FALSE, TRUE, 0); |
| 316 | if (tuple == NULL) |
| 317 | return FAIL; |
| 318 | |
| 319 | tv->v_type = VAR_TUPLE; |
| 320 | tv->v_lock = 0; |
| 321 | tv->vval.v_tuple = tuple; |
| 322 | if (t1 == NULL) |
| 323 | ++tuple->tv_refcount; |
| 324 | |
| 325 | // append all the items from the second tuple |
| 326 | for (int i = 0; i < tuple_len(t2); i++) |
| 327 | { |
| 328 | typval_T new_tv; |
| 329 | |
| 330 | copy_tv(TUPLE_ITEM(t2, i), &new_tv); |
| 331 | |
| 332 | if (tuple_append_tv(tuple, &new_tv) == FAIL) |
| 333 | { |
| 334 | tuple_free(tuple); |
| 335 | return FAIL; |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | return OK; |
| 340 | } |
| 341 | |
| 342 | /* |
| 343 | * Return a slice of tuple starting at index n1 and ending at index n2, |
| 344 | * inclusive (tuple[n1 : n2]) |
| 345 | */ |
| 346 | tuple_T * |
| 347 | tuple_slice(tuple_T *tuple, long n1, long n2) |
| 348 | { |
| 349 | tuple_T *new_tuple; |
| 350 | |
| 351 | new_tuple = tuple_alloc_with_items(n2 - n1 + 1); |
| 352 | if (new_tuple == NULL) |
| 353 | return NULL; |
| 354 | |
| 355 | for (int i = n1; i <= n2; i++) |
| 356 | { |
| 357 | typval_T new_tv; |
| 358 | |
| 359 | copy_tv(TUPLE_ITEM(tuple, i), &new_tv); |
| 360 | |
| 361 | if (tuple_append_tv(new_tuple, &new_tv) == FAIL) |
| 362 | { |
| 363 | tuple_free(new_tuple); |
| 364 | return NULL; |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | return new_tuple; |
| 369 | } |
| 370 | |
| 371 | int |
| 372 | tuple_slice_or_index( |
| 373 | tuple_T *tuple, |
| 374 | int range, |
| 375 | varnumber_T n1_arg, |
| 376 | varnumber_T n2_arg, |
| 377 | int exclusive, |
| 378 | typval_T *rettv, |
| 379 | int verbose) |
| 380 | { |
| 381 | long len = tuple_len(tuple); |
| 382 | varnumber_T n1 = n1_arg; |
| 383 | varnumber_T n2 = n2_arg; |
| 384 | typval_T var1; |
| 385 | |
| 386 | if (n1 < 0) |
| 387 | n1 = len + n1; |
| 388 | if (n1 < 0 || n1 >= len) |
| 389 | { |
| 390 | // For a range we allow invalid values and for legacy script return an |
| 391 | // empty tuple, for Vim9 script start at the first item. |
| 392 | // A tuple index out of range is an error. |
| 393 | if (!range) |
| 394 | { |
| 395 | if (verbose) |
| 396 | semsg(_(e_tuple_index_out_of_range_nr), (long)n1_arg); |
| 397 | return FAIL; |
| 398 | } |
| 399 | if (in_vim9script()) |
| 400 | n1 = n1 < 0 ? 0 : len; |
| 401 | else |
| 402 | n1 = len; |
| 403 | } |
| 404 | if (range) |
| 405 | { |
| 406 | tuple_T *new_tuple; |
| 407 | |
| 408 | if (n2 < 0) |
| 409 | n2 = len + n2; |
| 410 | else if (n2 >= len) |
| 411 | n2 = len - (exclusive ? 0 : 1); |
| 412 | if (exclusive) |
| 413 | --n2; |
| 414 | if (n2 < 0 || n2 + 1 < n1) |
| 415 | n2 = -1; |
| 416 | new_tuple = tuple_slice(tuple, n1, n2); |
| 417 | if (new_tuple == NULL) |
| 418 | return FAIL; |
| 419 | clear_tv(rettv); |
| 420 | rettv_tuple_set(rettv, new_tuple); |
| 421 | } |
| 422 | else |
| 423 | { |
| 424 | // copy the item to "var1" to avoid that freeing the tuple makes it |
| 425 | // invalid. |
| 426 | copy_tv(tuple_find(tuple, n1), &var1); |
| 427 | clear_tv(rettv); |
| 428 | *rettv = var1; |
| 429 | } |
| 430 | return OK; |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | * Make a copy of tuple "orig". Shallow if "deep" is FALSE. |
| 435 | * The refcount of the new tuple is set to 1. |
| 436 | * See item_copy() for "top" and "copyID". |
| 437 | * Returns NULL when out of memory. |
| 438 | */ |
| 439 | tuple_T * |
| 440 | tuple_copy(tuple_T *orig, int deep, int top, int copyID) |
| 441 | { |
| 442 | tuple_T *copy; |
| 443 | int idx; |
| 444 | |
| 445 | if (orig == NULL) |
| 446 | return NULL; |
| 447 | |
| 448 | copy = tuple_alloc_with_items(TUPLE_LEN(orig)); |
| 449 | if (copy == NULL) |
| 450 | return NULL; |
| 451 | |
| 452 | if (orig->tv_type == NULL || top || deep) |
| 453 | copy->tv_type = NULL; |
| 454 | else |
| 455 | copy->tv_type = alloc_type(orig->tv_type); |
| 456 | if (copyID != 0) |
| 457 | { |
| 458 | // Do this before adding the items, because one of the items may |
| 459 | // refer back to this tuple. |
| 460 | orig->tv_copyID = copyID; |
| 461 | orig->tv_copytuple = copy; |
| 462 | } |
| 463 | |
| 464 | for (idx = 0; idx < TUPLE_LEN(orig) && !got_int; idx++) |
| 465 | { |
| 466 | copy->tv_items.ga_len++; |
| 467 | if (deep) |
| 468 | { |
| 469 | if (item_copy(TUPLE_ITEM(orig, idx), TUPLE_ITEM(copy, idx), |
| 470 | deep, FALSE, copyID) == FAIL) |
| 471 | break; |
| 472 | } |
| 473 | else |
| 474 | copy_tv(TUPLE_ITEM(orig, idx), TUPLE_ITEM(copy, idx)); |
| 475 | } |
| 476 | |
| 477 | ++copy->tv_refcount; |
| 478 | if (idx != TUPLE_LEN(orig)) |
| 479 | { |
| 480 | tuple_unref(copy); |
| 481 | copy = NULL; |
| 482 | } |
| 483 | |
| 484 | return copy; |
| 485 | } |
| 486 | |
| 487 | /* |
| 488 | * Allocate a variable for a tuple and fill it from "*arg". |
| 489 | * "*arg" points to the "," after the first element. |
| 490 | * "rettv" contains the first element. |
| 491 | * Returns OK or FAIL. |
| 492 | */ |
| 493 | int |
| 494 | eval_tuple(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int do_error) |
| 495 | { |
| 496 | int evaluate = evalarg == NULL ? FALSE |
| 497 | : evalarg->eval_flags & EVAL_EVALUATE; |
| 498 | tuple_T *tuple = NULL; |
| 499 | typval_T tv; |
| 500 | int vim9script = in_vim9script(); |
| 501 | int had_comma; |
| 502 | |
| 503 | if (check_typval_is_value(rettv) == FAIL) |
| 504 | { |
| 505 | // the first item is not a valid value type |
| 506 | clear_tv(rettv); |
| 507 | return FAIL; |
| 508 | } |
| 509 | |
| 510 | if (evaluate) |
| 511 | { |
| 512 | tuple = tuple_alloc(); |
| 513 | if (tuple == NULL) |
| 514 | return FAIL; |
| 515 | |
| 516 | if (rettv->v_type != VAR_UNKNOWN) |
| 517 | { |
| 518 | // Add the first item to the tuple from "rettv" |
| 519 | if (tuple_append_tv(tuple, rettv) == FAIL) |
| 520 | return FAIL; |
Yegappan Lakshmanan | 9772025 | 2025-05-23 17:39:44 +0200 | [diff] [blame] | 521 | // The first item in "rettv" is added to the tuple. Set the rettv |
| 522 | // type to unknown, so that the caller doesn't free it. |
| 523 | rettv->v_type = VAR_UNKNOWN; |
Yegappan Lakshmanan | 9cb865e | 2025-03-23 16:42:16 +0100 | [diff] [blame] | 524 | } |
| 525 | } |
| 526 | |
| 527 | if (**arg == ')') |
| 528 | // empty tuple |
| 529 | goto done; |
| 530 | |
| 531 | if (vim9script && !IS_WHITE_NL_OR_NUL((*arg)[1]) && (*arg)[1] != ')') |
| 532 | { |
| 533 | semsg(_(e_white_space_required_after_str_str), ",", *arg); |
| 534 | goto failret; |
| 535 | } |
| 536 | |
| 537 | *arg = skipwhite_and_linebreak(*arg + 1, evalarg); |
| 538 | while (**arg != ')' && **arg != NUL) |
| 539 | { |
| 540 | if (eval1(arg, &tv, evalarg) == FAIL) // recursive! |
| 541 | goto failret; |
| 542 | if (check_typval_is_value(&tv) == FAIL) |
| 543 | { |
| 544 | if (evaluate) |
| 545 | clear_tv(&tv); |
| 546 | goto failret; |
| 547 | } |
| 548 | |
| 549 | if (evaluate) |
| 550 | { |
| 551 | if (tuple_append_tv(tuple, &tv) == FAIL) |
| 552 | { |
| 553 | clear_tv(&tv); |
| 554 | goto failret; |
| 555 | } |
| 556 | } |
| 557 | |
| 558 | if (!vim9script) |
| 559 | *arg = skipwhite(*arg); |
| 560 | |
| 561 | // the comma must come after the value |
| 562 | had_comma = **arg == ','; |
| 563 | if (had_comma) |
| 564 | { |
| 565 | if (vim9script && !IS_WHITE_NL_OR_NUL((*arg)[1]) && (*arg)[1] != ')') |
| 566 | { |
| 567 | semsg(_(e_white_space_required_after_str_str), ",", *arg); |
| 568 | goto failret; |
| 569 | } |
| 570 | *arg = skipwhite(*arg + 1); |
| 571 | } |
| 572 | |
| 573 | // The ")" can be on the next line. But a double quoted string may |
| 574 | // follow, not a comment. |
| 575 | *arg = skipwhite_and_linebreak(*arg, evalarg); |
| 576 | if (**arg == ')') |
| 577 | break; |
| 578 | |
| 579 | if (!had_comma) |
| 580 | { |
| 581 | if (do_error) |
| 582 | { |
| 583 | if (**arg == ',') |
| 584 | semsg(_(e_no_white_space_allowed_before_str_str), |
| 585 | ",", *arg); |
| 586 | else |
| 587 | semsg(_(e_missing_comma_in_tuple_str), *arg); |
| 588 | } |
| 589 | goto failret; |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | if (**arg != ')') |
| 594 | { |
| 595 | if (do_error) |
| 596 | semsg(_(e_missing_end_of_tuple_rsp_str), *arg); |
| 597 | failret: |
| 598 | if (evaluate) |
| 599 | tuple_free(tuple); |
| 600 | return FAIL; |
| 601 | } |
| 602 | |
| 603 | done: |
| 604 | *arg += 1; |
| 605 | if (evaluate) |
| 606 | rettv_tuple_set(rettv, tuple); |
| 607 | |
| 608 | return OK; |
| 609 | } |
| 610 | |
| 611 | /* |
| 612 | * Lock or unlock a tuple. "deep" is number of levels to go. |
| 613 | * When "check_refcount" is TRUE do not lock a tuple with a reference |
| 614 | * count larger than 1. |
| 615 | */ |
| 616 | void |
| 617 | tuple_lock(tuple_T *tuple, int deep, int lock, int check_refcount) |
| 618 | { |
| 619 | if (tuple == NULL || (check_refcount && tuple->tv_refcount > 1)) |
| 620 | return; |
| 621 | |
| 622 | if (lock) |
| 623 | tuple->tv_lock |= VAR_LOCKED; |
| 624 | else |
| 625 | tuple->tv_lock &= ~VAR_LOCKED; |
| 626 | |
| 627 | if (deep < 0 || deep > 1) |
| 628 | { |
| 629 | // recursive: lock/unlock the items the Tuple contains |
| 630 | for (int i = 0; i < TUPLE_LEN(tuple); i++) |
| 631 | item_lock(TUPLE_ITEM(tuple, i), deep - 1, lock, check_refcount); |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | typedef struct join_S { |
| 636 | char_u *s; |
| 637 | char_u *tofree; |
| 638 | } join_T; |
| 639 | |
| 640 | static int |
| 641 | tuple_join_inner( |
| 642 | garray_T *gap, // to store the result in |
| 643 | tuple_T *tuple, |
| 644 | char_u *sep, |
| 645 | int echo_style, |
| 646 | int restore_copyID, |
| 647 | int copyID, |
| 648 | garray_T *join_gap) // to keep each tuple item string |
| 649 | { |
| 650 | int i; |
| 651 | join_T *p; |
| 652 | int len; |
| 653 | int sumlen = 0; |
| 654 | int first = TRUE; |
| 655 | char_u *tofree; |
| 656 | char_u numbuf[NUMBUFLEN]; |
| 657 | char_u *s; |
| 658 | typval_T *tv; |
| 659 | |
| 660 | // Stringify each item in the tuple. |
| 661 | for (i = 0; i < TUPLE_LEN(tuple) && !got_int; i++) |
| 662 | { |
| 663 | tv = TUPLE_ITEM(tuple, i); |
| 664 | s = echo_string_core(tv, &tofree, numbuf, copyID, |
| 665 | echo_style, restore_copyID, !echo_style); |
| 666 | if (s == NULL) |
| 667 | return FAIL; |
| 668 | |
| 669 | len = (int)STRLEN(s); |
| 670 | sumlen += len; |
| 671 | |
| 672 | (void)ga_grow(join_gap, 1); |
| 673 | p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++); |
| 674 | if (tofree != NULL || s != numbuf) |
| 675 | { |
| 676 | p->s = s; |
| 677 | p->tofree = tofree; |
| 678 | } |
| 679 | else |
| 680 | { |
| 681 | p->s = vim_strnsave(s, len); |
| 682 | p->tofree = p->s; |
| 683 | } |
| 684 | |
| 685 | line_breakcheck(); |
| 686 | if (did_echo_string_emsg) // recursion error, bail out |
| 687 | break; |
| 688 | } |
| 689 | |
| 690 | // Allocate result buffer with its total size, avoid re-allocation and |
| 691 | // multiple copy operations. Add 2 for a tailing ')' and NUL. |
| 692 | if (join_gap->ga_len >= 2) |
| 693 | sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1); |
| 694 | if (ga_grow(gap, sumlen + 2) == FAIL) |
| 695 | return FAIL; |
| 696 | |
| 697 | for (i = 0; i < join_gap->ga_len && !got_int; ++i) |
| 698 | { |
| 699 | if (first) |
| 700 | first = FALSE; |
| 701 | else |
| 702 | ga_concat(gap, sep); |
| 703 | p = ((join_T *)join_gap->ga_data) + i; |
| 704 | |
| 705 | if (p->s != NULL) |
| 706 | ga_concat(gap, p->s); |
| 707 | line_breakcheck(); |
| 708 | } |
| 709 | |
| 710 | // If there is only one item in the tuple, then add the separator after |
| 711 | // that. |
| 712 | if (join_gap->ga_len == 1) |
| 713 | ga_concat(gap, sep); |
| 714 | |
| 715 | return OK; |
| 716 | } |
| 717 | |
| 718 | /* |
| 719 | * Join tuple "tuple" into a string in "*gap", using separator "sep". |
| 720 | * When "echo_style" is TRUE use String as echoed, otherwise as inside a Tuple. |
| 721 | * Return FAIL or OK. |
| 722 | */ |
| 723 | int |
| 724 | tuple_join( |
| 725 | garray_T *gap, |
| 726 | tuple_T *tuple, |
| 727 | char_u *sep, |
| 728 | int echo_style, |
| 729 | int restore_copyID, |
| 730 | int copyID) |
| 731 | { |
| 732 | garray_T join_ga; |
| 733 | int retval; |
| 734 | join_T *p; |
| 735 | int i; |
| 736 | |
| 737 | if (TUPLE_LEN(tuple) < 1) |
| 738 | return OK; // nothing to do |
| 739 | ga_init2(&join_ga, sizeof(join_T), TUPLE_LEN(tuple)); |
| 740 | retval = tuple_join_inner(gap, tuple, sep, echo_style, restore_copyID, |
| 741 | copyID, &join_ga); |
| 742 | |
| 743 | if (join_ga.ga_data == NULL) |
| 744 | return retval; |
| 745 | |
| 746 | // Dispose each item in join_ga. |
| 747 | p = (join_T *)join_ga.ga_data; |
| 748 | for (i = 0; i < join_ga.ga_len; ++i) |
| 749 | { |
| 750 | vim_free(p->tofree); |
| 751 | ++p; |
| 752 | } |
| 753 | ga_clear(&join_ga); |
| 754 | |
| 755 | return retval; |
| 756 | } |
| 757 | |
| 758 | /* |
| 759 | * Return an allocated string with the string representation of a tuple. |
| 760 | * May return NULL. |
| 761 | */ |
| 762 | char_u * |
| 763 | tuple2string(typval_T *tv, int copyID, int restore_copyID) |
| 764 | { |
| 765 | garray_T ga; |
| 766 | |
| 767 | if (tv->vval.v_tuple == NULL) |
| 768 | return NULL; |
| 769 | ga_init2(&ga, sizeof(char), 80); |
| 770 | ga_append(&ga, '('); |
| 771 | if (tuple_join(&ga, tv->vval.v_tuple, (char_u *)", ", |
| 772 | FALSE, restore_copyID, copyID) == FAIL) |
| 773 | { |
| 774 | vim_free(ga.ga_data); |
| 775 | return NULL; |
| 776 | } |
| 777 | ga_append(&ga, ')'); |
| 778 | ga_append(&ga, NUL); |
| 779 | return (char_u *)ga.ga_data; |
| 780 | } |
| 781 | |
| 782 | /* |
| 783 | * Implementation of foreach() for a Tuple. Apply "expr" to |
| 784 | * every item in Tuple "tuple" and return the result in "rettv". |
| 785 | */ |
| 786 | void |
| 787 | tuple_foreach( |
| 788 | tuple_T *tuple, |
| 789 | filtermap_T filtermap, |
| 790 | typval_T *expr) |
| 791 | { |
| 792 | int len = tuple_len(tuple); |
| 793 | int rem; |
| 794 | typval_T newtv; |
| 795 | funccall_T *fc; |
| 796 | |
| 797 | // set_vim_var_nr() doesn't set the type |
| 798 | set_vim_var_type(VV_KEY, VAR_NUMBER); |
| 799 | |
| 800 | // Create one funccall_T for all eval_expr_typval() calls. |
| 801 | fc = eval_expr_get_funccal(expr, &newtv); |
| 802 | |
| 803 | for (int idx = 0; idx < len; idx++) |
| 804 | { |
| 805 | set_vim_var_nr(VV_KEY, idx); |
| 806 | if (filter_map_one(TUPLE_ITEM(tuple, idx), expr, filtermap, fc, |
| 807 | &newtv, &rem) == FAIL) |
| 808 | break; |
| 809 | } |
| 810 | |
| 811 | if (fc != NULL) |
| 812 | remove_funccal(); |
| 813 | } |
| 814 | |
| 815 | /* |
| 816 | * Count the number of times item "needle" occurs in Tuple "l" starting at index |
| 817 | * "idx". Case is ignored if "ic" is TRUE. |
| 818 | */ |
| 819 | long |
| 820 | tuple_count(tuple_T *tuple, typval_T *needle, long idx, int ic) |
| 821 | { |
| 822 | long n = 0; |
| 823 | |
| 824 | if (tuple == NULL) |
| 825 | return 0; |
| 826 | |
| 827 | int len = TUPLE_LEN(tuple); |
| 828 | if (len == 0) |
| 829 | return 0; |
| 830 | |
| 831 | if (idx < 0 || idx >= len) |
| 832 | { |
| 833 | semsg(_(e_tuple_index_out_of_range_nr), idx); |
| 834 | return 0; |
| 835 | } |
| 836 | |
| 837 | for (int i = idx; i < len; i++) |
| 838 | { |
| 839 | if (tv_equal(TUPLE_ITEM(tuple, i), needle, ic)) |
| 840 | ++n; |
| 841 | } |
| 842 | |
| 843 | return n; |
| 844 | } |
| 845 | |
| 846 | /* |
| 847 | * "items(tuple)" function |
| 848 | * Caller must have already checked that argvars[0] is a tuple. |
| 849 | */ |
| 850 | void |
| 851 | tuple2items(typval_T *argvars, typval_T *rettv) |
| 852 | { |
| 853 | tuple_T *tuple = argvars[0].vval.v_tuple; |
| 854 | varnumber_T idx; |
| 855 | |
| 856 | if (rettv_list_alloc(rettv) == FAIL) |
| 857 | return; |
| 858 | |
| 859 | if (tuple == NULL) |
| 860 | return; // null tuple behaves like an empty list |
| 861 | |
| 862 | for (idx = 0; idx < TUPLE_LEN(tuple); idx++) |
| 863 | { |
| 864 | list_T *l = list_alloc(); |
| 865 | |
| 866 | if (l == NULL) |
| 867 | break; |
| 868 | |
| 869 | if (list_append_list(rettv->vval.v_list, l) == FAIL) |
| 870 | { |
| 871 | vim_free(l); |
| 872 | break; |
| 873 | } |
| 874 | if (list_append_number(l, idx) == FAIL |
| 875 | || list_append_tv(l, TUPLE_ITEM(tuple, idx)) == FAIL) |
| 876 | break; |
| 877 | } |
| 878 | } |
| 879 | |
| 880 | /* |
| 881 | * Search for item "tv" in tuple "tuple" starting from index "start_idx". |
| 882 | * If "ic" is set to TRUE, then case is ignored. |
| 883 | * |
| 884 | * Returns the index where "tv" is present or -1 if it is not found. |
| 885 | */ |
| 886 | int |
| 887 | index_tuple(tuple_T *tuple, typval_T *tv, int start_idx, int ic) |
| 888 | { |
| 889 | if (start_idx < 0) |
| 890 | { |
| 891 | start_idx = TUPLE_LEN(tuple) + start_idx; |
| 892 | if (start_idx < 0) |
| 893 | start_idx = 0; |
| 894 | } |
| 895 | |
| 896 | for (int idx = start_idx; idx < TUPLE_LEN(tuple); idx++) |
| 897 | { |
| 898 | if (tv_equal(TUPLE_ITEM(tuple, idx), tv, ic)) |
| 899 | return idx; |
| 900 | } |
| 901 | |
| 902 | return -1; // "tv" not found |
| 903 | } |
| 904 | |
| 905 | /* |
| 906 | * Evaluate 'expr' for each item in the Tuple 'tuple' starting with the item at |
| 907 | * 'startidx' and return the index of the item where 'expr' is TRUE. Returns |
| 908 | * -1 if 'expr' doesn't evaluate to TRUE for any of the items. |
| 909 | */ |
| 910 | int |
| 911 | indexof_tuple(tuple_T *tuple, long startidx, typval_T *expr) |
| 912 | { |
| 913 | long idx = 0; |
| 914 | int len; |
| 915 | int found; |
| 916 | |
| 917 | if (tuple == NULL) |
| 918 | return -1; |
| 919 | |
| 920 | len = TUPLE_LEN(tuple); |
| 921 | |
| 922 | if (startidx < 0) |
| 923 | { |
| 924 | // negative index: index from the end |
| 925 | startidx = len + startidx; |
| 926 | if (startidx < 0) |
| 927 | startidx = 0; |
| 928 | } |
| 929 | |
| 930 | set_vim_var_type(VV_KEY, VAR_NUMBER); |
| 931 | |
| 932 | int called_emsg_start = called_emsg; |
| 933 | |
| 934 | for (idx = startidx; idx < len; idx++) |
| 935 | { |
| 936 | set_vim_var_nr(VV_KEY, idx); |
| 937 | copy_tv(TUPLE_ITEM(tuple, idx), get_vim_var_tv(VV_VAL)); |
| 938 | |
| 939 | found = indexof_eval_expr(expr); |
| 940 | clear_tv(get_vim_var_tv(VV_VAL)); |
| 941 | |
| 942 | if (found) |
| 943 | return idx; |
| 944 | |
| 945 | if (called_emsg != called_emsg_start) |
| 946 | return -1; |
| 947 | } |
| 948 | |
| 949 | return -1; |
| 950 | } |
| 951 | |
| 952 | /* |
| 953 | * Return the max or min of the items in tuple "tuple". |
| 954 | * If a tuple item is not a number, then "error" is set to TRUE. |
| 955 | */ |
| 956 | varnumber_T |
| 957 | tuple_max_min(tuple_T *tuple, int domax, int *error) |
| 958 | { |
| 959 | varnumber_T n = 0; |
| 960 | varnumber_T v; |
| 961 | |
| 962 | if (tuple == NULL || TUPLE_LEN(tuple) == 0) |
| 963 | return 0; |
| 964 | |
| 965 | n = tv_get_number_chk(TUPLE_ITEM(tuple, 0), error); |
| 966 | if (*error) |
| 967 | return n; // type error; errmsg already given |
| 968 | |
| 969 | for (int idx = 1; idx < TUPLE_LEN(tuple); idx++) |
| 970 | { |
| 971 | v = tv_get_number_chk(TUPLE_ITEM(tuple, idx), error); |
| 972 | if (*error) |
| 973 | return n; // type error; errmsg already given |
| 974 | if (domax ? v > n : v < n) |
| 975 | n = v; |
| 976 | } |
| 977 | |
| 978 | return n; |
| 979 | } |
| 980 | |
| 981 | /* |
| 982 | * Repeat the tuple "tuple" "n" times and set "rettv" to the new tuple. |
| 983 | */ |
| 984 | void |
| 985 | tuple_repeat(tuple_T *tuple, int n, typval_T *rettv) |
| 986 | { |
| 987 | rettv->v_type = VAR_TUPLE; |
| 988 | rettv->vval.v_tuple = NULL; |
| 989 | |
| 990 | if (tuple == NULL || TUPLE_LEN(tuple) == 0 || n <= 0) |
| 991 | return; |
| 992 | |
| 993 | if (rettv_tuple_set_with_items(rettv, TUPLE_LEN(tuple) * n) == FAIL) |
| 994 | return; |
| 995 | |
| 996 | tuple_T *new_tuple = rettv->vval.v_tuple; |
| 997 | for (int count = 0; count < n; count++) |
| 998 | { |
| 999 | for (int idx = 0; idx < TUPLE_LEN(tuple); idx++) |
| 1000 | { |
| 1001 | copy_tv(TUPLE_ITEM(tuple, idx), |
| 1002 | TUPLE_ITEM(new_tuple, TUPLE_LEN(new_tuple))); |
| 1003 | new_tuple->tv_items.ga_len++; |
| 1004 | } |
| 1005 | } |
| 1006 | } |
| 1007 | |
| 1008 | /* |
| 1009 | * Reverse "tuple" and return the new tuple in "rettv" |
| 1010 | */ |
| 1011 | void |
| 1012 | tuple_reverse(tuple_T *tuple, typval_T *rettv) |
| 1013 | { |
| 1014 | rettv->v_type = VAR_TUPLE; |
| 1015 | rettv->vval.v_tuple = NULL; |
| 1016 | |
| 1017 | int len = tuple_len(tuple); |
| 1018 | |
| 1019 | if (len == 0) |
| 1020 | return; |
| 1021 | |
| 1022 | if (rettv_tuple_set_with_items(rettv, len) == FAIL) |
| 1023 | return; |
| 1024 | |
| 1025 | tuple_T *new_tuple = rettv->vval.v_tuple; |
| 1026 | for (int i = 0; i < len; i++) |
| 1027 | copy_tv(TUPLE_ITEM(tuple, i), TUPLE_ITEM(new_tuple, len - i - 1)); |
| 1028 | new_tuple->tv_items.ga_len = tuple->tv_items.ga_len; |
| 1029 | } |
| 1030 | |
| 1031 | /* |
| 1032 | * Tuple reduce() function |
| 1033 | */ |
| 1034 | void |
| 1035 | tuple_reduce(typval_T *argvars, typval_T *expr, typval_T *rettv) |
| 1036 | { |
| 1037 | tuple_T *tuple = argvars[0].vval.v_tuple; |
| 1038 | int called_emsg_start = called_emsg; |
| 1039 | typval_T initial; |
| 1040 | int idx = 0; |
| 1041 | funccall_T *fc; |
| 1042 | typval_T argv[3]; |
| 1043 | int r; |
| 1044 | |
| 1045 | if (argvars[2].v_type == VAR_UNKNOWN) |
| 1046 | { |
| 1047 | if (tuple == NULL || TUPLE_LEN(tuple) == 0) |
| 1048 | { |
| 1049 | semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "Tuple"); |
| 1050 | return; |
| 1051 | } |
| 1052 | initial = *TUPLE_ITEM(tuple, 0); |
| 1053 | idx = 1; |
| 1054 | } |
| 1055 | else |
| 1056 | { |
| 1057 | initial = argvars[2]; |
| 1058 | idx = 0; |
| 1059 | } |
| 1060 | |
| 1061 | copy_tv(&initial, rettv); |
| 1062 | |
| 1063 | if (tuple == NULL) |
| 1064 | return; |
| 1065 | |
| 1066 | // Create one funccall_T for all eval_expr_typval() calls. |
| 1067 | fc = eval_expr_get_funccal(expr, rettv); |
| 1068 | |
| 1069 | for ( ; idx < TUPLE_LEN(tuple); idx++) |
| 1070 | { |
| 1071 | argv[0] = *rettv; |
| 1072 | rettv->v_type = VAR_UNKNOWN; |
| 1073 | argv[1] = *TUPLE_ITEM(tuple, idx); |
| 1074 | |
| 1075 | r = eval_expr_typval(expr, TRUE, argv, 2, fc, rettv); |
| 1076 | |
| 1077 | clear_tv(&argv[0]); |
| 1078 | |
| 1079 | if (r == FAIL || called_emsg != called_emsg_start) |
| 1080 | break; |
| 1081 | } |
| 1082 | |
| 1083 | if (fc != NULL) |
| 1084 | remove_funccal(); |
| 1085 | } |
| 1086 | |
| 1087 | /* |
| 1088 | * Returns TRUE if two tuples with types "type1" and "type2" are addable. |
| 1089 | * Otherwise returns FALSE. |
| 1090 | */ |
| 1091 | int |
| 1092 | check_tuples_addable(type_T *type1, type_T *type2) |
| 1093 | { |
| 1094 | int addable = TRUE; |
| 1095 | |
| 1096 | // If the first operand is a variadic tuple and the second argument is |
| 1097 | // non-variadic, then concatenation is not possible. |
| 1098 | if ((type1->tt_flags & TTFLAG_VARARGS) |
| 1099 | && !(type2->tt_flags & TTFLAG_VARARGS) |
| 1100 | && (type2->tt_argcount > 0)) |
| 1101 | addable = FALSE; |
| 1102 | |
| 1103 | if ((type1->tt_flags & TTFLAG_VARARGS) |
| 1104 | && (type2->tt_flags & TTFLAG_VARARGS)) |
| 1105 | { |
| 1106 | // two variadic tuples |
| 1107 | if (type1->tt_argcount > 1 || type2->tt_argcount > 1) |
| 1108 | // one of the variadic tuple has fixed number of items |
| 1109 | addable = FALSE; |
| 1110 | else if ((type1->tt_argcount == 1 && type2->tt_argcount == 1) |
| 1111 | && !equal_type(type1->tt_args[0], type2->tt_args[0], 0)) |
| 1112 | // the tuples have different item types |
| 1113 | addable = FALSE; |
| 1114 | } |
| 1115 | |
| 1116 | if (!addable) |
| 1117 | { |
| 1118 | emsg(_(e_cannot_use_variadic_tuple_in_concatenation)); |
| 1119 | return FAIL; |
| 1120 | } |
| 1121 | |
| 1122 | return OK; |
| 1123 | } |
| 1124 | |
| 1125 | #endif // defined(FEAT_EVAL) |