blob: 64c9d5f9e179ea6dbd4ab83a18bd5e5aa4e1cdaa [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaarcd524592016-07-17 14:57:05 +02002 *
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 * dict.c: Dictionary support
12 */
Bram Moolenaarcd524592016-07-17 14:57:05 +020013
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
Bram Moolenaar5d18efe2019-12-01 21:11:22 +010018// List head for garbage collection. Although there can be a reference loop
19// from partial to dict to partial, we don't need to keep track of the partial,
20// since it will get freed when the dict is unused and gets freed.
21static dict_T *first_dict = NULL;
Bram Moolenaarcd524592016-07-17 14:57:05 +020022
23/*
24 * Allocate an empty header for a dictionary.
Bram Moolenaar22286892020-11-05 20:50:51 +010025 * Caller should take care of the reference count.
Bram Moolenaarcd524592016-07-17 14:57:05 +020026 */
27 dict_T *
28dict_alloc(void)
29{
30 dict_T *d;
31
Bram Moolenaaradc67142019-06-22 01:40:42 +020032 d = ALLOC_CLEAR_ONE(dict_T);
Bram Moolenaarcd524592016-07-17 14:57:05 +020033 if (d != NULL)
34 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +010035 // Add the dict to the list of dicts for garbage collection.
Bram Moolenaarcd524592016-07-17 14:57:05 +020036 if (first_dict != NULL)
37 first_dict->dv_used_prev = d;
38 d->dv_used_next = first_dict;
39 d->dv_used_prev = NULL;
40 first_dict = d;
41
42 hash_init(&d->dv_hashtab);
43 d->dv_lock = 0;
44 d->dv_scope = 0;
45 d->dv_refcount = 0;
46 d->dv_copyID = 0;
47 }
48 return d;
49}
50
Bram Moolenaarf49cc602018-11-11 15:21:05 +010051/*
52 * dict_alloc() with an ID for alloc_fail().
53 */
54 dict_T *
55dict_alloc_id(alloc_id_T id UNUSED)
56{
57#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +020058 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaarf49cc602018-11-11 15:21:05 +010059 return NULL;
60#endif
61 return (dict_alloc());
62}
63
Bram Moolenaar7e1652c2017-12-16 18:27:02 +010064 dict_T *
65dict_alloc_lock(int lock)
66{
67 dict_T *d = dict_alloc();
68
69 if (d != NULL)
70 d->dv_lock = lock;
71 return d;
72}
73
Bram Moolenaarcd524592016-07-17 14:57:05 +020074/*
75 * Allocate an empty dict for a return value.
76 * Returns OK or FAIL.
77 */
78 int
79rettv_dict_alloc(typval_T *rettv)
80{
Bram Moolenaar7e1652c2017-12-16 18:27:02 +010081 dict_T *d = dict_alloc_lock(0);
Bram Moolenaarcd524592016-07-17 14:57:05 +020082
83 if (d == NULL)
84 return FAIL;
85
Bram Moolenaar45cf6e92017-04-30 20:25:19 +020086 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +020087 return OK;
88}
89
90/*
Bram Moolenaar45cf6e92017-04-30 20:25:19 +020091 * Set a dictionary as the return value
92 */
93 void
94rettv_dict_set(typval_T *rettv, dict_T *d)
95{
96 rettv->v_type = VAR_DICT;
97 rettv->vval.v_dict = d;
98 if (d != NULL)
99 ++d->dv_refcount;
100}
101
102/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200103 * Free a Dictionary, including all non-container items it contains.
104 * Ignores the reference count.
105 */
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100106 void
Bram Moolenaarcd524592016-07-17 14:57:05 +0200107dict_free_contents(dict_T *d)
108{
Bram Moolenaar89483d42020-05-10 15:24:44 +0200109 hashtab_free_contents(&d->dv_hashtab);
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100110 free_type(d->dv_type);
111 d->dv_type = NULL;
Bram Moolenaar89483d42020-05-10 15:24:44 +0200112}
113
114/*
115 * Clear hashtab "ht" and dict items it contains.
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100116 * If "ht" is not freed then you should call hash_init() next!
Bram Moolenaar89483d42020-05-10 15:24:44 +0200117 */
118 void
119hashtab_free_contents(hashtab_T *ht)
120{
Bram Moolenaarcd524592016-07-17 14:57:05 +0200121 int todo;
122 hashitem_T *hi;
123 dictitem_T *di;
124
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100125 // Lock the hashtab, we don't want it to resize while freeing items.
Bram Moolenaar89483d42020-05-10 15:24:44 +0200126 hash_lock(ht);
127 todo = (int)ht->ht_used;
128 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200129 {
130 if (!HASHITEM_EMPTY(hi))
131 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100132 // Remove the item before deleting it, just in case there is
133 // something recursive causing trouble.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200134 di = HI2DI(hi);
Bram Moolenaar89483d42020-05-10 15:24:44 +0200135 hash_remove(ht, hi);
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100136 dictitem_free(di);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200137 --todo;
138 }
139 }
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100140
Bram Moolenaar89483d42020-05-10 15:24:44 +0200141 // The hashtab is still locked, it has to be re-initialized anyway.
142 hash_clear(ht);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200143}
144
145 static void
146dict_free_dict(dict_T *d)
147{
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100148 // Remove the dict from the list of dicts for garbage collection.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200149 if (d->dv_used_prev == NULL)
150 first_dict = d->dv_used_next;
151 else
152 d->dv_used_prev->dv_used_next = d->dv_used_next;
153 if (d->dv_used_next != NULL)
154 d->dv_used_next->dv_used_prev = d->dv_used_prev;
155 vim_free(d);
156}
157
158 static void
159dict_free(dict_T *d)
160{
161 if (!in_free_unref_items)
162 {
163 dict_free_contents(d);
164 dict_free_dict(d);
165 }
166}
167
168/*
169 * Unreference a Dictionary: decrement the reference count and free it when it
170 * becomes zero.
171 */
172 void
173dict_unref(dict_T *d)
174{
175 if (d != NULL && --d->dv_refcount <= 0)
176 dict_free(d);
177}
178
179/*
180 * Go through the list of dicts and free items without the copyID.
181 * Returns TRUE if something was freed.
182 */
183 int
184dict_free_nonref(int copyID)
185{
186 dict_T *dd;
187 int did_free = FALSE;
188
189 for (dd = first_dict; dd != NULL; dd = dd->dv_used_next)
190 if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
191 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100192 // Free the Dictionary and ordinary items it contains, but don't
193 // recurse into Lists and Dictionaries, they will be in the list
194 // of dicts or list of lists.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200195 dict_free_contents(dd);
196 did_free = TRUE;
197 }
198 return did_free;
199}
200
201 void
202dict_free_items(int copyID)
203{
204 dict_T *dd, *dd_next;
205
206 for (dd = first_dict; dd != NULL; dd = dd_next)
207 {
208 dd_next = dd->dv_used_next;
209 if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
210 dict_free_dict(dd);
211 }
212}
213
214/*
215 * Allocate a Dictionary item.
216 * The "key" is copied to the new item.
Bram Moolenaarc89d4b32018-07-08 17:19:02 +0200217 * Note that the type and value of the item "di_tv" still needs to be
218 * initialized!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200219 * Returns NULL when out of memory.
220 */
221 dictitem_T *
222dictitem_alloc(char_u *key)
223{
224 dictitem_T *di;
Bram Moolenaarb4168fd2021-12-22 20:29:09 +0000225 size_t len = STRLEN(key);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200226
Bram Moolenaarb4168fd2021-12-22 20:29:09 +0000227 di = alloc(offsetof(dictitem_T, di_key) + len + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200228 if (di != NULL)
229 {
Bram Moolenaarb4168fd2021-12-22 20:29:09 +0000230 mch_memmove(di->di_key, key, len + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200231 di->di_flags = DI_FLAGS_ALLOC;
Bram Moolenaarc89d4b32018-07-08 17:19:02 +0200232 di->di_tv.v_lock = 0;
Bram Moolenaar3b318512021-09-06 19:19:45 +0200233 di->di_tv.v_type = VAR_UNKNOWN;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200234 }
235 return di;
236}
237
238/*
239 * Make a copy of a Dictionary item.
240 */
241 static dictitem_T *
242dictitem_copy(dictitem_T *org)
243{
244 dictitem_T *di;
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200245 size_t len = STRLEN(org->di_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200246
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200247 di = alloc(offsetof(dictitem_T, di_key) + len + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200248 if (di != NULL)
249 {
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200250 mch_memmove(di->di_key, org->di_key, len + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200251 di->di_flags = DI_FLAGS_ALLOC;
252 copy_tv(&org->di_tv, &di->di_tv);
253 }
254 return di;
255}
256
257/*
258 * Remove item "item" from Dictionary "dict" and free it.
259 */
260 void
261dictitem_remove(dict_T *dict, dictitem_T *item)
262{
263 hashitem_T *hi;
264
265 hi = hash_find(&dict->dv_hashtab, item->di_key);
266 if (HASHITEM_EMPTY(hi))
Bram Moolenaar95f09602016-11-10 20:01:45 +0100267 internal_error("dictitem_remove()");
Bram Moolenaarcd524592016-07-17 14:57:05 +0200268 else
269 hash_remove(&dict->dv_hashtab, hi);
270 dictitem_free(item);
271}
272
273/*
274 * Free a dict item. Also clears the value.
275 */
276 void
277dictitem_free(dictitem_T *item)
278{
279 clear_tv(&item->di_tv);
280 if (item->di_flags & DI_FLAGS_ALLOC)
281 vim_free(item);
282}
283
284/*
285 * Make a copy of dict "d". Shallow if "deep" is FALSE.
286 * The refcount of the new dict is set to 1.
287 * See item_copy() for "copyID".
288 * Returns NULL when out of memory.
289 */
290 dict_T *
291dict_copy(dict_T *orig, int deep, int copyID)
292{
293 dict_T *copy;
294 dictitem_T *di;
295 int todo;
296 hashitem_T *hi;
297
298 if (orig == NULL)
299 return NULL;
300
301 copy = dict_alloc();
302 if (copy != NULL)
303 {
304 if (copyID != 0)
305 {
306 orig->dv_copyID = copyID;
307 orig->dv_copydict = copy;
308 }
309 todo = (int)orig->dv_hashtab.ht_used;
310 for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
311 {
312 if (!HASHITEM_EMPTY(hi))
313 {
314 --todo;
315
316 di = dictitem_alloc(hi->hi_key);
317 if (di == NULL)
318 break;
319 if (deep)
320 {
321 if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep,
322 copyID) == FAIL)
323 {
324 vim_free(di);
325 break;
326 }
327 }
328 else
329 copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
330 if (dict_add(copy, di) == FAIL)
331 {
332 dictitem_free(di);
333 break;
334 }
335 }
336 }
337
338 ++copy->dv_refcount;
339 if (todo > 0)
340 {
341 dict_unref(copy);
342 copy = NULL;
343 }
344 }
345
346 return copy;
347}
348
349/*
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200350 * Check for adding a function to g: or s:.
351 * If the name is wrong give an error message and return TRUE.
352 */
353 int
354dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name)
355{
356 return (d == get_globvar_dict()
Bram Moolenaarb54abee2021-06-02 11:49:23 +0200357 || (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid)
358 && d == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict)
359 || &d->dv_hashtab == get_funccal_local_ht())
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200360 && (tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
361 && var_wrong_func_name(name, TRUE);
362}
363
364/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200365 * Add item "item" to Dictionary "d".
366 * Returns FAIL when out of memory and when key already exists.
367 */
368 int
369dict_add(dict_T *d, dictitem_T *item)
370{
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200371 if (dict_wrong_func_name(d, &item->di_tv, item->di_key))
372 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200373 return hash_add(&d->dv_hashtab, item->di_key);
374}
375
376/*
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200377 * Add a number or special entry to dictionary "d".
Bram Moolenaarcd524592016-07-17 14:57:05 +0200378 * Returns FAIL when out of memory and when key already exists.
379 */
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200380 static int
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100381dict_add_number_special(dict_T *d, char *key, varnumber_T nr, vartype_T vartype)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200382{
383 dictitem_T *item;
384
385 item = dictitem_alloc((char_u *)key);
386 if (item == NULL)
387 return FAIL;
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100388 item->di_tv.v_type = vartype;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200389 item->di_tv.vval.v_number = nr;
390 if (dict_add(d, item) == FAIL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200391 {
Bram Moolenaare0be1672018-07-08 16:50:37 +0200392 dictitem_free(item);
393 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200394 }
Bram Moolenaare0be1672018-07-08 16:50:37 +0200395 return OK;
396}
397
398/*
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200399 * Add a number entry to dictionary "d".
400 * Returns FAIL when out of memory and when key already exists.
401 */
402 int
403dict_add_number(dict_T *d, char *key, varnumber_T nr)
404{
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100405 return dict_add_number_special(d, key, nr, VAR_NUMBER);
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200406}
407
408/*
409 * Add a special entry to dictionary "d".
410 * Returns FAIL when out of memory and when key already exists.
411 */
412 int
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100413dict_add_bool(dict_T *d, char *key, varnumber_T nr)
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200414{
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100415 return dict_add_number_special(d, key, nr, VAR_BOOL);
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200416}
417
418/*
Bram Moolenaare0be1672018-07-08 16:50:37 +0200419 * Add a string entry to dictionary "d".
420 * Returns FAIL when out of memory and when key already exists.
421 */
422 int
423dict_add_string(dict_T *d, char *key, char_u *str)
424{
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100425 return dict_add_string_len(d, key, str, -1);
426}
427
428/*
429 * Add a string entry to dictionary "d".
430 * "str" will be copied to allocated memory.
431 * When "len" is -1 use the whole string, otherwise only this many bytes.
432 * Returns FAIL when out of memory and when key already exists.
433 */
434 int
435dict_add_string_len(dict_T *d, char *key, char_u *str, int len)
436{
Bram Moolenaare0be1672018-07-08 16:50:37 +0200437 dictitem_T *item;
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100438 char_u *val = NULL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200439
440 item = dictitem_alloc((char_u *)key);
441 if (item == NULL)
442 return FAIL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200443 item->di_tv.v_type = VAR_STRING;
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100444 if (str != NULL)
445 {
446 if (len == -1)
447 val = vim_strsave(str);
448 else
449 val = vim_strnsave(str, len);
450 }
451 item->di_tv.vval.v_string = val;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200452 if (dict_add(d, item) == FAIL)
453 {
454 dictitem_free(item);
455 return FAIL;
456 }
457 return OK;
458}
459
460/*
461 * Add a list entry to dictionary "d".
462 * Returns FAIL when out of memory and when key already exists.
463 */
464 int
465dict_add_list(dict_T *d, char *key, list_T *list)
466{
467 dictitem_T *item;
468
469 item = dictitem_alloc((char_u *)key);
470 if (item == NULL)
471 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200472 item->di_tv.v_type = VAR_LIST;
473 item->di_tv.vval.v_list = list;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100474 ++list->lv_refcount;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200475 if (dict_add(d, item) == FAIL)
476 {
477 dictitem_free(item);
478 return FAIL;
479 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200480 return OK;
481}
482
483/*
Bram Moolenaar08928322020-01-04 14:32:48 +0100484 * Add a typval_T entry to dictionary "d".
485 * Returns FAIL when out of memory and when key already exists.
486 */
487 int
488dict_add_tv(dict_T *d, char *key, typval_T *tv)
489{
490 dictitem_T *item;
491
492 item = dictitem_alloc((char_u *)key);
493 if (item == NULL)
494 return FAIL;
495 copy_tv(tv, &item->di_tv);
496 if (dict_add(d, item) == FAIL)
497 {
498 dictitem_free(item);
499 return FAIL;
500 }
501 return OK;
502}
503
504/*
Bram Moolenaarae943152019-06-16 22:54:14 +0200505 * Add a callback to dictionary "d".
506 * Returns FAIL when out of memory and when key already exists.
507 */
508 int
509dict_add_callback(dict_T *d, char *key, callback_T *cb)
510{
511 dictitem_T *item;
512
513 item = dictitem_alloc((char_u *)key);
514 if (item == NULL)
515 return FAIL;
516 put_callback(cb, &item->di_tv);
517 if (dict_add(d, item) == FAIL)
518 {
519 dictitem_free(item);
520 return FAIL;
521 }
522 return OK;
523}
524
525/*
Bram Moolenaar45e18cb2019-04-28 18:05:35 +0200526 * Initializes "iter" for iterating over dictionary items with
527 * dict_iterate_next().
528 * If "var" is not a Dict or an empty Dict then there will be nothing to
529 * iterate over, no error is given.
530 * NOTE: The dictionary must not change until iterating is finished!
531 */
532 void
533dict_iterate_start(typval_T *var, dict_iterator_T *iter)
534{
535 if (var->v_type != VAR_DICT || var->vval.v_dict == NULL)
536 iter->dit_todo = 0;
537 else
538 {
539 dict_T *d = var->vval.v_dict;
540
541 iter->dit_todo = d->dv_hashtab.ht_used;
542 iter->dit_hi = d->dv_hashtab.ht_array;
543 }
544}
545
546/*
547 * Iterate over the items referred to by "iter". It should be initialized with
548 * dict_iterate_start().
549 * Returns a pointer to the key.
550 * "*tv_result" is set to point to the value for that key.
551 * If there are no more items, NULL is returned.
552 */
553 char_u *
554dict_iterate_next(dict_iterator_T *iter, typval_T **tv_result)
555{
556 dictitem_T *di;
557 char_u *result;
558
559 if (iter->dit_todo == 0)
560 return NULL;
561
562 while (HASHITEM_EMPTY(iter->dit_hi))
563 ++iter->dit_hi;
564
565 di = HI2DI(iter->dit_hi);
566 result = di->di_key;
567 *tv_result = &di->di_tv;
568
569 --iter->dit_todo;
570 ++iter->dit_hi;
571 return result;
572}
573
574/*
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200575 * Add a dict entry to dictionary "d".
576 * Returns FAIL when out of memory and when key already exists.
577 */
578 int
579dict_add_dict(dict_T *d, char *key, dict_T *dict)
580{
581 dictitem_T *item;
582
583 item = dictitem_alloc((char_u *)key);
584 if (item == NULL)
585 return FAIL;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200586 item->di_tv.v_type = VAR_DICT;
587 item->di_tv.vval.v_dict = dict;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100588 ++dict->dv_refcount;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200589 if (dict_add(d, item) == FAIL)
590 {
591 dictitem_free(item);
592 return FAIL;
593 }
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200594 return OK;
595}
596
597/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200598 * Get the number of items in a Dictionary.
599 */
600 long
601dict_len(dict_T *d)
602{
603 if (d == NULL)
604 return 0L;
605 return (long)d->dv_hashtab.ht_used;
606}
607
608/*
609 * Find item "key[len]" in Dictionary "d".
610 * If "len" is negative use strlen(key).
611 * Returns NULL when not found.
612 */
613 dictitem_T *
614dict_find(dict_T *d, char_u *key, int len)
615{
616#define AKEYLEN 200
617 char_u buf[AKEYLEN];
618 char_u *akey;
619 char_u *tofree = NULL;
620 hashitem_T *hi;
621
622 if (d == NULL)
623 return NULL;
624 if (len < 0)
625 akey = key;
626 else if (len >= AKEYLEN)
627 {
628 tofree = akey = vim_strnsave(key, len);
629 if (akey == NULL)
630 return NULL;
631 }
632 else
633 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100634 // Avoid a malloc/free by using buf[].
Bram Moolenaarcd524592016-07-17 14:57:05 +0200635 vim_strncpy(buf, key, len);
636 akey = buf;
637 }
638
639 hi = hash_find(&d->dv_hashtab, akey);
640 vim_free(tofree);
641 if (HASHITEM_EMPTY(hi))
642 return NULL;
643 return HI2DI(hi);
644}
645
646/*
Bram Moolenaar08928322020-01-04 14:32:48 +0100647 * Get a typval_T item from a dictionary and copy it into "rettv".
648 * Returns FAIL if the entry doesn't exist or out of memory.
649 */
650 int
651dict_get_tv(dict_T *d, char_u *key, typval_T *rettv)
652{
653 dictitem_T *di;
Bram Moolenaar08928322020-01-04 14:32:48 +0100654
655 di = dict_find(d, key, -1);
656 if (di == NULL)
657 return FAIL;
658 copy_tv(&di->di_tv, rettv);
659 return OK;
660}
661
662/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200663 * Get a string item from a dictionary.
664 * When "save" is TRUE allocate memory for it.
Bram Moolenaar7dc5e2e2016-08-05 22:22:06 +0200665 * When FALSE a shared buffer is used, can only be used once!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200666 * Returns NULL if the entry doesn't exist or out of memory.
667 */
668 char_u *
Bram Moolenaar8f667172018-12-14 15:38:31 +0100669dict_get_string(dict_T *d, char_u *key, int save)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200670{
671 dictitem_T *di;
672 char_u *s;
673
674 di = dict_find(d, key, -1);
675 if (di == NULL)
676 return NULL;
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100677 s = tv_get_string(&di->di_tv);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200678 if (save && s != NULL)
679 s = vim_strsave(s);
680 return s;
681}
682
683/*
684 * Get a number item from a dictionary.
685 * Returns 0 if the entry doesn't exist.
686 */
687 varnumber_T
Bram Moolenaar8f667172018-12-14 15:38:31 +0100688dict_get_number(dict_T *d, char_u *key)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200689{
Bram Moolenaar8c6173c2019-08-30 22:08:34 +0200690 return dict_get_number_def(d, key, 0);
691}
692
693/*
694 * Get a number item from a dictionary.
695 * Returns "def" if the entry doesn't exist.
696 */
697 varnumber_T
698dict_get_number_def(dict_T *d, char_u *key, int def)
699{
Bram Moolenaarcd524592016-07-17 14:57:05 +0200700 dictitem_T *di;
701
702 di = dict_find(d, key, -1);
703 if (di == NULL)
Bram Moolenaar8c6173c2019-08-30 22:08:34 +0200704 return def;
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100705 return tv_get_number(&di->di_tv);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200706}
707
708/*
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +0200709 * Get a number item from a dictionary.
710 * Returns 0 if the entry doesn't exist.
711 * Give an error if the entry is not a number.
712 */
713 varnumber_T
714dict_get_number_check(dict_T *d, char_u *key)
715{
716 dictitem_T *di;
717
718 di = dict_find(d, key, -1);
719 if (di == NULL)
720 return 0;
721 if (di->di_tv.v_type != VAR_NUMBER)
722 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000723 semsg(_(e_invalid_argument_str), tv_get_string(&di->di_tv));
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +0200724 return 0;
725 }
726 return tv_get_number(&di->di_tv);
727}
728
729/*
Bram Moolenaar55881332020-08-18 13:04:15 +0200730 * Get a bool item (number or true/false) from a dictionary.
731 * Returns "def" if the entry doesn't exist.
732 */
733 varnumber_T
734dict_get_bool(dict_T *d, char_u *key, int def)
735{
736 dictitem_T *di;
737
738 di = dict_find(d, key, -1);
739 if (di == NULL)
740 return def;
741 return tv_get_bool(&di->di_tv);
742}
743
744/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200745 * Return an allocated string with the string representation of a Dictionary.
746 * May return NULL.
747 */
748 char_u *
749dict2string(typval_T *tv, int copyID, int restore_copyID)
750{
751 garray_T ga;
752 int first = TRUE;
753 char_u *tofree;
754 char_u numbuf[NUMBUFLEN];
755 hashitem_T *hi;
756 char_u *s;
757 dict_T *d;
758 int todo;
759
760 if ((d = tv->vval.v_dict) == NULL)
761 return NULL;
762 ga_init2(&ga, (int)sizeof(char), 80);
763 ga_append(&ga, '{');
764
765 todo = (int)d->dv_hashtab.ht_used;
766 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
767 {
768 if (!HASHITEM_EMPTY(hi))
769 {
770 --todo;
771
772 if (first)
773 first = FALSE;
774 else
775 ga_concat(&ga, (char_u *)", ");
776
777 tofree = string_quote(hi->hi_key, FALSE);
778 if (tofree != NULL)
779 {
780 ga_concat(&ga, tofree);
781 vim_free(tofree);
782 }
783 ga_concat(&ga, (char_u *)": ");
784 s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID,
785 FALSE, restore_copyID, TRUE);
786 if (s != NULL)
787 ga_concat(&ga, s);
788 vim_free(tofree);
789 if (s == NULL || did_echo_string_emsg)
790 break;
791 line_breakcheck();
792
793 }
794 }
795 if (todo > 0)
796 {
797 vim_free(ga.ga_data);
798 return NULL;
799 }
800
801 ga_append(&ga, '}');
802 ga_append(&ga, NUL);
803 return (char_u *)ga.ga_data;
804}
805
806/*
Bram Moolenaare0de1712020-12-02 17:36:54 +0100807 * Advance over a literal key, including "-". If the first character is not a
808 * literal key character then "key" is returned.
809 */
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +0200810 static char_u *
Bram Moolenaare0de1712020-12-02 17:36:54 +0100811skip_literal_key(char_u *key)
812{
813 char_u *p;
814
815 for (p = key; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; ++p)
816 ;
817 return p;
818}
819
820/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200821 * Get the key for #{key: val} into "tv" and advance "arg".
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200822 * Return FAIL when there is no valid key.
823 */
824 static int
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100825get_literal_key_tv(char_u **arg, typval_T *tv)
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200826{
Bram Moolenaare0de1712020-12-02 17:36:54 +0100827 char_u *p = skip_literal_key(*arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200828
Bram Moolenaare0de1712020-12-02 17:36:54 +0100829 if (p == *arg)
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200830 return FAIL;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200831 tv->v_type = VAR_STRING;
Bram Moolenaardf44a272020-06-07 20:49:05 +0200832 tv->vval.v_string = vim_strnsave(*arg, p - *arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200833
Bram Moolenaardb199212020-08-12 18:01:53 +0200834 *arg = p;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200835 return OK;
836}
837
838/*
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100839 * Get a literal key for a Vim9 dict:
840 * {"name": value},
841 * {'name': value},
842 * {name: value} use "name" as a literal key
843 * Return the key in allocated memory or NULL in the case of an error.
844 * "arg" is advanced to just after the key.
845 */
846 char_u *
847get_literal_key(char_u **arg)
848{
849 char_u *key;
850 char_u *end;
851 typval_T rettv;
852
853 if (**arg == '\'')
854 {
855 if (eval_lit_string(arg, &rettv, TRUE) == FAIL)
856 return NULL;
857 key = rettv.vval.v_string;
858 }
859 else if (**arg == '"')
860 {
861 if (eval_string(arg, &rettv, TRUE) == FAIL)
862 return NULL;
863 key = rettv.vval.v_string;
864 }
865 else
866 {
867 end = skip_literal_key(*arg);
868 if (end == *arg)
869 {
870 semsg(_(e_invalid_key_str), *arg);
871 return NULL;
872 }
873 key = vim_strnsave(*arg, end - *arg);
874 *arg = end;
875 }
876 return key;
877}
878
879/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200880 * Allocate a variable for a Dictionary and fill it from "*arg".
Bram Moolenaar962d7212020-07-04 14:15:00 +0200881 * "*arg" points to the "{".
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200882 * "literal" is TRUE for #{key: val}
Bram Moolenaarcd524592016-07-17 14:57:05 +0200883 * Return OK or FAIL. Returns NOTDONE for {expr}.
884 */
885 int
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200886eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200887{
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200888 int evaluate = evalarg == NULL ? FALSE
889 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200890 dict_T *d = NULL;
891 typval_T tvkey;
892 typval_T tv;
893 char_u *key = NULL;
894 dictitem_T *item;
Bram Moolenaar98cb90e2021-11-30 11:56:22 +0000895 char_u *curly_expr = skipwhite(*arg + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200896 char_u buf[NUMBUFLEN];
Bram Moolenaareb6880b2020-07-12 17:07:05 +0200897 int vim9script = in_vim9script();
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200898 int had_comma;
Bram Moolenaar5409f5d2020-06-24 18:37:35 +0200899
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000900 // First check if it's not a curly-braces thing: {expr}.
901 // Must do this without evaluating, otherwise a function may be called
902 // twice. Unfortunately this means we need to call eval1() twice for the
903 // first item.
904 // But {} is an empty Dictionary.
Bram Moolenaar98cb90e2021-11-30 11:56:22 +0000905 if (!vim9script
906 && *curly_expr != '}'
907 && eval1(&curly_expr, &tv, NULL) == OK
908 && *skipwhite(curly_expr) == '}')
909 return NOTDONE;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200910
911 if (evaluate)
912 {
913 d = dict_alloc();
914 if (d == NULL)
915 return FAIL;
916 }
917 tvkey.v_type = VAR_UNKNOWN;
918 tv.v_type = VAR_UNKNOWN;
919
Bram Moolenaar962d7212020-07-04 14:15:00 +0200920 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200921 while (**arg != '}' && **arg != NUL)
922 {
Bram Moolenaare0de1712020-12-02 17:36:54 +0100923 int has_bracket = vim9script && **arg == '[';
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100924
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100925 if (literal)
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100926 {
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100927 if (get_literal_key_tv(arg, &tvkey) == FAIL)
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100928 goto failret;
929 }
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100930 else if (vim9script && !has_bracket)
931 {
932 tvkey.vval.v_string = get_literal_key(arg);
933 if (tvkey.vval.v_string == NULL)
934 goto failret;
935 tvkey.v_type = VAR_STRING;
936 }
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100937 else
938 {
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100939 if (has_bracket)
940 *arg = skipwhite(*arg + 1);
941 if (eval1(arg, &tvkey, evalarg) == FAIL) // recursive!
942 goto failret;
943 if (has_bracket)
944 {
945 *arg = skipwhite(*arg);
946 if (**arg != ']')
947 {
948 emsg(_(e_missing_matching_bracket_after_dict_key));
Bram Moolenaar8bb0f542020-12-06 16:03:55 +0100949 clear_tv(&tvkey);
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100950 return FAIL;
951 }
952 ++*arg;
953 }
954 }
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200955
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +0200956 // the colon should come right after the key, but this wasn't checked
Bram Moolenaar9d489562020-07-30 20:08:50 +0200957 // previously, so only require it in Vim9 script.
958 if (!vim9script)
959 *arg = skipwhite(*arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200960 if (**arg != ':')
961 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200962 if (*skipwhite(*arg) == ':')
Bram Moolenaarba98fb52021-02-07 18:06:29 +0100963 semsg(_(e_no_white_space_allowed_before_str_str), ":", *arg);
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200964 else
Bram Moolenaar74409f62022-01-01 15:58:22 +0000965 semsg(_(e_missing_colon_in_dictionary), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200966 clear_tv(&tvkey);
967 goto failret;
968 }
969 if (evaluate)
970 {
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100971#ifdef FEAT_FLOAT
972 if (tvkey.v_type == VAR_FLOAT)
Bram Moolenaar9987fb02020-12-15 21:41:56 +0100973 {
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100974 tvkey.vval.v_string = typval_tostring(&tvkey, TRUE);
975 tvkey.v_type = VAR_STRING;
Bram Moolenaar9987fb02020-12-15 21:41:56 +0100976 }
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100977#endif
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100978 key = tv_get_string_buf_chk(&tvkey, buf);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200979 if (key == NULL)
980 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100981 // "key" is NULL when tv_get_string_buf_chk() gave an errmsg
Bram Moolenaarcd524592016-07-17 14:57:05 +0200982 clear_tv(&tvkey);
983 goto failret;
984 }
985 }
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200986 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
987 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +0100988 semsg(_(e_white_space_required_after_str_str), ":", *arg);
Bram Moolenaarab19d492020-06-27 17:04:05 +0200989 clear_tv(&tvkey);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200990 goto failret;
991 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200992
Bram Moolenaar962d7212020-07-04 14:15:00 +0200993 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200994 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200995 {
996 if (evaluate)
997 clear_tv(&tvkey);
998 goto failret;
999 }
1000 if (evaluate)
1001 {
1002 item = dict_find(d, key, -1);
1003 if (item != NULL)
1004 {
Bram Moolenaar74409f62022-01-01 15:58:22 +00001005 semsg(_(e_duplicate_key_in_dicitonary), key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001006 clear_tv(&tvkey);
1007 clear_tv(&tv);
1008 goto failret;
1009 }
1010 item = dictitem_alloc(key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001011 if (item != NULL)
1012 {
1013 item->di_tv = tv;
1014 item->di_tv.v_lock = 0;
1015 if (dict_add(d, item) == FAIL)
1016 dictitem_free(item);
1017 }
1018 }
Bram Moolenaara8931942019-09-28 17:25:10 +02001019 clear_tv(&tvkey);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001020
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +02001021 // the comma should come right after the value, but this wasn't checked
1022 // previously, so only require it in Vim9 script.
1023 if (!vim9script)
1024 *arg = skipwhite(*arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001025 had_comma = **arg == ',';
1026 if (had_comma)
1027 {
1028 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
1029 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +01001030 semsg(_(e_white_space_required_after_str_str), ",", *arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001031 goto failret;
1032 }
1033 *arg = skipwhite(*arg + 1);
1034 }
1035
1036 // the "}" can be on the next line
Bram Moolenaar962d7212020-07-04 14:15:00 +02001037 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001038 if (**arg == '}')
1039 break;
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001040 if (!had_comma)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001041 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001042 if (**arg == ',')
Bram Moolenaarba98fb52021-02-07 18:06:29 +01001043 semsg(_(e_no_white_space_allowed_before_str_str), ",", *arg);
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001044 else
Bram Moolenaar74409f62022-01-01 15:58:22 +00001045 semsg(_(e_missing_comma_in_dictionary), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001046 goto failret;
1047 }
Bram Moolenaarcd524592016-07-17 14:57:05 +02001048 }
1049
1050 if (**arg != '}')
1051 {
Bram Moolenaar4bce26b2021-01-22 22:06:56 +01001052 if (evalarg != NULL)
1053 semsg(_(e_missing_dict_end), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001054failret:
Bram Moolenaaradc67142019-06-22 01:40:42 +02001055 if (d != NULL)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001056 dict_free(d);
1057 return FAIL;
1058 }
1059
Bram Moolenaarb07a39d2020-10-22 19:00:01 +02001060 *arg = *arg + 1;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001061 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001062 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001063
1064 return OK;
1065}
1066
1067/*
1068 * Go over all entries in "d2" and add them to "d1".
1069 * When "action" is "error" then a duplicate key is an error.
1070 * When "action" is "force" then a duplicate key is overwritten.
1071 * Otherwise duplicate keys are ignored ("action" is "keep").
1072 */
1073 void
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02001074dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001075{
1076 dictitem_T *di1;
1077 hashitem_T *hi2;
1078 int todo;
1079 char_u *arg_errmsg = (char_u *)N_("extend() argument");
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001080 type_T *type;
1081
1082 if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL)
1083 type = d1->dv_type->tt_member;
1084 else
1085 type = NULL;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001086
1087 todo = (int)d2->dv_hashtab.ht_used;
1088 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
1089 {
1090 if (!HASHITEM_EMPTY(hi2))
1091 {
1092 --todo;
1093 di1 = dict_find(d1, hi2->hi_key, -1);
1094 if (d1->dv_scope != 0)
1095 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001096 // Disallow replacing a builtin function in l: and g:.
1097 // Check the key to be valid when adding to any scope.
Bram Moolenaarcd524592016-07-17 14:57:05 +02001098 if (d1->dv_scope == VAR_DEF_SCOPE
1099 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
Bram Moolenaar98b4f142020-08-08 15:46:01 +02001100 && var_wrong_func_name(hi2->hi_key, di1 == NULL))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001101 break;
Bram Moolenaar3b3755f2021-11-22 20:10:18 +00001102 if (!valid_varname(hi2->hi_key, -1, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001103 break;
1104 }
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001105
1106 if (type != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02001107 && check_typval_arg_type(type, &HI2DI(hi2)->di_tv,
1108 func_name, 0) == FAIL)
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001109 break;
1110
Bram Moolenaarcd524592016-07-17 14:57:05 +02001111 if (di1 == NULL)
1112 {
1113 di1 = dictitem_copy(HI2DI(hi2));
1114 if (di1 != NULL && dict_add(d1, di1) == FAIL)
1115 dictitem_free(di1);
1116 }
1117 else if (*action == 'e')
1118 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00001119 semsg(_(e_key_already_exists_str), hi2->hi_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001120 break;
1121 }
1122 else if (*action == 'f' && HI2DI(hi2) != di1)
1123 {
Bram Moolenaara187c432020-09-16 21:08:28 +02001124 if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
Bram Moolenaar05c00c02019-02-11 22:00:11 +01001125 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001126 break;
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +02001127 if (dict_wrong_func_name(d1, &HI2DI(hi2)->di_tv, hi2->hi_key))
1128 break;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001129 clear_tv(&di1->di_tv);
1130 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
1131 }
1132 }
1133 }
1134}
1135
1136/*
1137 * Return the dictitem that an entry in a hashtable points to.
1138 */
1139 dictitem_T *
1140dict_lookup(hashitem_T *hi)
1141{
1142 return HI2DI(hi);
1143}
1144
1145/*
1146 * Return TRUE when two dictionaries have exactly the same key/values.
1147 */
1148 int
1149dict_equal(
1150 dict_T *d1,
1151 dict_T *d2,
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001152 int ic, // ignore case for strings
1153 int recursive) // TRUE when used recursively
Bram Moolenaarcd524592016-07-17 14:57:05 +02001154{
1155 hashitem_T *hi;
1156 dictitem_T *item2;
1157 int todo;
1158
Bram Moolenaarcd524592016-07-17 14:57:05 +02001159 if (d1 == d2)
1160 return TRUE;
1161 if (dict_len(d1) != dict_len(d2))
1162 return FALSE;
Bram Moolenaarea04a6e2020-04-23 13:38:02 +02001163 if (dict_len(d1) == 0)
1164 // empty and NULL dicts are considered equal
1165 return TRUE;
1166 if (d1 == NULL || d2 == NULL)
1167 return FALSE;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001168
1169 todo = (int)d1->dv_hashtab.ht_used;
1170 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
1171 {
1172 if (!HASHITEM_EMPTY(hi))
1173 {
1174 item2 = dict_find(d2, hi->hi_key, -1);
1175 if (item2 == NULL)
1176 return FALSE;
1177 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
1178 return FALSE;
1179 --todo;
1180 }
1181 }
1182 return TRUE;
1183}
1184
1185/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001186 * Count the number of times item "needle" occurs in Dict "d". Case is ignored
1187 * if "ic" is TRUE.
1188 */
1189 long
1190dict_count(dict_T *d, typval_T *needle, int ic)
1191{
1192 int todo;
1193 hashitem_T *hi;
1194 long n = 0;
1195
1196 if (d == NULL)
1197 return 0;
1198
1199 todo = (int)d->dv_hashtab.ht_used;
1200 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1201 {
1202 if (!HASHITEM_EMPTY(hi))
1203 {
1204 --todo;
1205 if (tv_equal(&HI2DI(hi)->di_tv, needle, ic, FALSE))
1206 ++n;
1207 }
1208 }
1209
1210 return n;
1211}
1212
1213/*
1214 * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
1215 * resulting Dict in "rettv". "is_new" is TRUE for extendnew().
1216 */
1217 void
1218dict_extend_func(
1219 typval_T *argvars,
1220 type_T *type,
1221 char *func_name,
1222 char_u *arg_errmsg,
1223 int is_new,
1224 typval_T *rettv)
1225{
1226 dict_T *d1, *d2;
1227 char_u *action;
1228 int i;
1229
1230 d1 = argvars[0].vval.v_dict;
1231 if (d1 == NULL)
1232 {
1233 emsg(_(e_cannot_extend_null_dict));
1234 return;
1235 }
1236 d2 = argvars[1].vval.v_dict;
1237 if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
1238 && d2 != NULL)
1239 {
1240 if (is_new)
1241 {
1242 d1 = dict_copy(d1, FALSE, get_copyID());
1243 if (d1 == NULL)
1244 return;
1245 }
1246
1247 // Check the third argument.
1248 if (argvars[2].v_type != VAR_UNKNOWN)
1249 {
1250 static char *(av[]) = {"keep", "force", "error"};
1251
1252 action = tv_get_string_chk(&argvars[2]);
1253 if (action == NULL)
1254 return;
1255 for (i = 0; i < 3; ++i)
1256 if (STRCMP(action, av[i]) == 0)
1257 break;
1258 if (i == 3)
1259 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001260 semsg(_(e_invalid_argument_str), action);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001261 return;
1262 }
1263 }
1264 else
1265 action = (char_u *)"force";
1266
1267 if (type != NULL && check_typval_arg_type(type, &argvars[1],
1268 func_name, 2) == FAIL)
1269 return;
1270 dict_extend(d1, d2, action, func_name);
1271
1272 if (is_new)
1273 {
1274 rettv->v_type = VAR_DICT;
1275 rettv->vval.v_dict = d1;
1276 rettv->v_lock = FALSE;
1277 }
1278 else
1279 copy_tv(&argvars[0], rettv);
1280 }
1281}
1282
1283/*
1284 * Implementation of map() and filter() for a Dict. Apply "expr" to every
1285 * item in Dict "d" and return the result in "rettv".
1286 */
1287 void
1288dict_filter_map(
1289 dict_T *d,
1290 filtermap_T filtermap,
1291 type_T *argtype,
1292 char *func_name,
1293 char_u *arg_errmsg,
1294 typval_T *expr,
1295 typval_T *rettv)
1296{
1297 int prev_lock;
1298 dict_T *d_ret = NULL;
1299 hashtab_T *ht;
1300 hashitem_T *hi;
1301 dictitem_T *di;
1302 int todo;
1303 int rem;
1304
1305 if (filtermap == FILTERMAP_MAPNEW)
1306 {
1307 rettv->v_type = VAR_DICT;
1308 rettv->vval.v_dict = NULL;
1309 }
1310 if (d == NULL
1311 || (filtermap == FILTERMAP_FILTER
1312 && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
1313 return;
1314
1315 prev_lock = d->dv_lock;
1316
1317 if (filtermap == FILTERMAP_MAPNEW)
1318 {
1319 if (rettv_dict_alloc(rettv) == FAIL)
1320 return;
1321 d_ret = rettv->vval.v_dict;
1322 }
1323
1324 if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
1325 d->dv_lock = VAR_LOCKED;
1326 ht = &d->dv_hashtab;
1327 hash_lock(ht);
1328 todo = (int)ht->ht_used;
1329 for (hi = ht->ht_array; todo > 0; ++hi)
1330 {
1331 if (!HASHITEM_EMPTY(hi))
1332 {
1333 int r;
1334 typval_T newtv;
1335
1336 --todo;
1337 di = HI2DI(hi);
1338 if (filtermap == FILTERMAP_MAP
1339 && (value_check_lock(di->di_tv.v_lock,
1340 arg_errmsg, TRUE)
1341 || var_check_ro(di->di_flags,
1342 arg_errmsg, TRUE)))
1343 break;
1344 set_vim_var_string(VV_KEY, di->di_key, -1);
1345 newtv.v_type = VAR_UNKNOWN;
1346 r = filter_map_one(&di->di_tv, expr, filtermap,
1347 &newtv, &rem);
1348 clear_tv(get_vim_var_tv(VV_KEY));
1349 if (r == FAIL || did_emsg)
1350 {
1351 clear_tv(&newtv);
1352 break;
1353 }
1354 if (filtermap == FILTERMAP_MAP)
1355 {
1356 if (argtype != NULL && check_typval_arg_type(
Bram Moolenaar078a4612022-01-04 15:17:03 +00001357 argtype->tt_member, &newtv, func_name, 0) == FAIL)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001358 {
1359 clear_tv(&newtv);
1360 break;
1361 }
1362 // map(): replace the dict item value
1363 clear_tv(&di->di_tv);
1364 newtv.v_lock = 0;
1365 di->di_tv = newtv;
1366 }
1367 else if (filtermap == FILTERMAP_MAPNEW)
1368 {
1369 // mapnew(): add the item value to the new dict
1370 r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
1371 clear_tv(&newtv);
1372 if (r == FAIL)
1373 break;
1374 }
1375 else if (filtermap == FILTERMAP_FILTER && rem)
1376 {
1377 // filter(false): remove the item from the dict
1378 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1379 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1380 break;
1381 dictitem_remove(d, di);
1382 }
1383 }
1384 }
1385 hash_unlock(ht);
1386 d->dv_lock = prev_lock;
1387}
1388
1389/*
1390 * "remove({dict})" function
1391 */
1392 void
1393dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1394{
1395 dict_T *d;
1396 char_u *key;
1397 dictitem_T *di;
1398
1399 if (argvars[2].v_type != VAR_UNKNOWN)
1400 {
1401 semsg(_(e_too_many_arguments_for_function_str), "remove()");
1402 return;
1403 }
1404
1405 d = argvars[0].vval.v_dict;
1406 if (d == NULL || value_check_lock(d->dv_lock, arg_errmsg, TRUE))
1407 return;
1408
1409 key = tv_get_string_chk(&argvars[1]);
1410 if (key == NULL)
1411 return;
1412
1413 di = dict_find(d, key, -1);
1414 if (di == NULL)
1415 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001416 semsg(_(e_key_not_present_in_dictionary), key);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001417 return;
1418 }
1419
1420 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1421 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1422 return;
1423
1424 *rettv = di->di_tv;
1425 init_tv(&di->di_tv);
1426 dictitem_remove(d, di);
1427}
1428
1429/*
Bram Moolenaarcd524592016-07-17 14:57:05 +02001430 * Turn a dict into a list:
1431 * "what" == 0: list of keys
1432 * "what" == 1: list of values
1433 * "what" == 2: list of items
1434 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001435 static void
Bram Moolenaarcd524592016-07-17 14:57:05 +02001436dict_list(typval_T *argvars, typval_T *rettv, int what)
1437{
1438 list_T *l2;
1439 dictitem_T *di;
1440 hashitem_T *hi;
1441 listitem_T *li;
1442 listitem_T *li2;
1443 dict_T *d;
1444 int todo;
1445
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001446 if (in_vim9script() && check_for_dict_arg(argvars, 0) == FAIL)
1447 return;
1448
Bram Moolenaarcd524592016-07-17 14:57:05 +02001449 if (argvars[0].v_type != VAR_DICT)
1450 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001451 emsg(_(e_dictionary_required));
Bram Moolenaarcd524592016-07-17 14:57:05 +02001452 return;
1453 }
Bram Moolenaarcd524592016-07-17 14:57:05 +02001454
1455 if (rettv_list_alloc(rettv) == FAIL)
1456 return;
Bram Moolenaaref982572021-08-12 19:27:57 +02001457 if ((d = argvars[0].vval.v_dict) == NULL)
1458 // empty dict behaves like an empty dict
1459 return;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001460
1461 todo = (int)d->dv_hashtab.ht_used;
1462 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1463 {
1464 if (!HASHITEM_EMPTY(hi))
1465 {
1466 --todo;
1467 di = HI2DI(hi);
1468
1469 li = listitem_alloc();
1470 if (li == NULL)
1471 break;
1472 list_append(rettv->vval.v_list, li);
1473
1474 if (what == 0)
1475 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001476 // keys()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001477 li->li_tv.v_type = VAR_STRING;
1478 li->li_tv.v_lock = 0;
1479 li->li_tv.vval.v_string = vim_strsave(di->di_key);
1480 }
1481 else if (what == 1)
1482 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001483 // values()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001484 copy_tv(&di->di_tv, &li->li_tv);
1485 }
1486 else
1487 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001488 // items()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001489 l2 = list_alloc();
1490 li->li_tv.v_type = VAR_LIST;
1491 li->li_tv.v_lock = 0;
1492 li->li_tv.vval.v_list = l2;
1493 if (l2 == NULL)
1494 break;
1495 ++l2->lv_refcount;
1496
1497 li2 = listitem_alloc();
1498 if (li2 == NULL)
1499 break;
1500 list_append(l2, li2);
1501 li2->li_tv.v_type = VAR_STRING;
1502 li2->li_tv.v_lock = 0;
1503 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
1504
1505 li2 = listitem_alloc();
1506 if (li2 == NULL)
1507 break;
1508 list_append(l2, li2);
1509 copy_tv(&di->di_tv, &li2->li_tv);
1510 }
1511 }
1512 }
1513}
1514
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001515/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001516 * "items(dict)" function
1517 */
1518 void
1519f_items(typval_T *argvars, typval_T *rettv)
1520{
1521 dict_list(argvars, rettv, 2);
1522}
1523
1524/*
1525 * "keys()" function
1526 */
1527 void
1528f_keys(typval_T *argvars, typval_T *rettv)
1529{
1530 dict_list(argvars, rettv, 0);
1531}
1532
1533/*
1534 * "values(dict)" function
1535 */
1536 void
1537f_values(typval_T *argvars, typval_T *rettv)
1538{
1539 dict_list(argvars, rettv, 1);
1540}
1541
1542/*
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001543 * Make each item in the dict readonly (not the value of the item).
1544 */
1545 void
1546dict_set_items_ro(dict_T *di)
1547{
1548 int todo = (int)di->dv_hashtab.ht_used;
1549 hashitem_T *hi;
1550
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001551 // Set readonly
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001552 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
1553 {
1554 if (HASHITEM_EMPTY(hi))
1555 continue;
1556 --todo;
1557 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
1558 }
1559}
1560
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001561/*
1562 * "has_key()" function
1563 */
1564 void
1565f_has_key(typval_T *argvars, typval_T *rettv)
1566{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001567 if (in_vim9script()
1568 && (check_for_dict_arg(argvars, 0) == FAIL
1569 || check_for_string_or_number_arg(argvars, 1) == FAIL))
1570 return;
1571
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001572 if (argvars[0].v_type != VAR_DICT)
1573 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001574 emsg(_(e_dictionary_required));
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001575 return;
1576 }
1577 if (argvars[0].vval.v_dict == NULL)
1578 return;
1579
1580 rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
1581 tv_get_string(&argvars[1]), -1) != NULL;
1582}
1583
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001584#endif // defined(FEAT_EVAL)