blob: 06f38716b57ac22dea484fbb49fe5a8304d65734 [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.
Bram Moolenaar381692b2022-02-02 20:01:27 +0000287 * See item_copy() for "top" and "copyID".
Bram Moolenaarcd524592016-07-17 14:57:05 +0200288 * Returns NULL when out of memory.
289 */
290 dict_T *
Bram Moolenaar381692b2022-02-02 20:01:27 +0000291dict_copy(dict_T *orig, int deep, int top, int copyID)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200292{
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 }
Bram Moolenaar381692b2022-02-02 20:01:27 +0000309 copy->dv_type = alloc_type(top || deep ? &t_dict_any : orig->dv_type);
310
Bram Moolenaarcd524592016-07-17 14:57:05 +0200311 todo = (int)orig->dv_hashtab.ht_used;
312 for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
313 {
314 if (!HASHITEM_EMPTY(hi))
315 {
316 --todo;
317
318 di = dictitem_alloc(hi->hi_key);
319 if (di == NULL)
320 break;
321 if (deep)
322 {
Bram Moolenaar381692b2022-02-02 20:01:27 +0000323 if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv,
324 deep, FALSE, copyID) == FAIL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200325 {
326 vim_free(di);
327 break;
328 }
329 }
330 else
331 copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
332 if (dict_add(copy, di) == FAIL)
333 {
334 dictitem_free(di);
335 break;
336 }
337 }
338 }
339
340 ++copy->dv_refcount;
341 if (todo > 0)
342 {
343 dict_unref(copy);
344 copy = NULL;
345 }
346 }
347
348 return copy;
349}
350
351/*
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200352 * Check for adding a function to g: or s:.
353 * If the name is wrong give an error message and return TRUE.
354 */
355 int
356dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name)
357{
358 return (d == get_globvar_dict()
Bram Moolenaarb54abee2021-06-02 11:49:23 +0200359 || (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid)
360 && d == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict)
361 || &d->dv_hashtab == get_funccal_local_ht())
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200362 && (tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
363 && var_wrong_func_name(name, TRUE);
364}
365
366/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200367 * Add item "item" to Dictionary "d".
368 * Returns FAIL when out of memory and when key already exists.
369 */
370 int
371dict_add(dict_T *d, dictitem_T *item)
372{
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200373 if (dict_wrong_func_name(d, &item->di_tv, item->di_key))
374 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200375 return hash_add(&d->dv_hashtab, item->di_key);
376}
377
378/*
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200379 * Add a number or special entry to dictionary "d".
Bram Moolenaarcd524592016-07-17 14:57:05 +0200380 * Returns FAIL when out of memory and when key already exists.
381 */
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200382 static int
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100383dict_add_number_special(dict_T *d, char *key, varnumber_T nr, vartype_T vartype)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200384{
385 dictitem_T *item;
386
387 item = dictitem_alloc((char_u *)key);
388 if (item == NULL)
389 return FAIL;
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100390 item->di_tv.v_type = vartype;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200391 item->di_tv.vval.v_number = nr;
392 if (dict_add(d, item) == FAIL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200393 {
Bram Moolenaare0be1672018-07-08 16:50:37 +0200394 dictitem_free(item);
395 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200396 }
Bram Moolenaare0be1672018-07-08 16:50:37 +0200397 return OK;
398}
399
400/*
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200401 * Add a number entry to dictionary "d".
402 * Returns FAIL when out of memory and when key already exists.
403 */
404 int
405dict_add_number(dict_T *d, char *key, varnumber_T nr)
406{
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100407 return dict_add_number_special(d, key, nr, VAR_NUMBER);
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200408}
409
410/*
411 * Add a special entry to dictionary "d".
412 * Returns FAIL when out of memory and when key already exists.
413 */
414 int
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100415dict_add_bool(dict_T *d, char *key, varnumber_T nr)
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200416{
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100417 return dict_add_number_special(d, key, nr, VAR_BOOL);
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200418}
419
420/*
Bram Moolenaare0be1672018-07-08 16:50:37 +0200421 * Add a string entry to dictionary "d".
422 * Returns FAIL when out of memory and when key already exists.
423 */
424 int
425dict_add_string(dict_T *d, char *key, char_u *str)
426{
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100427 return dict_add_string_len(d, key, str, -1);
428}
429
430/*
431 * Add a string entry to dictionary "d".
432 * "str" will be copied to allocated memory.
433 * When "len" is -1 use the whole string, otherwise only this many bytes.
434 * Returns FAIL when out of memory and when key already exists.
435 */
436 int
437dict_add_string_len(dict_T *d, char *key, char_u *str, int len)
438{
Bram Moolenaare0be1672018-07-08 16:50:37 +0200439 dictitem_T *item;
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100440 char_u *val = NULL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200441
442 item = dictitem_alloc((char_u *)key);
443 if (item == NULL)
444 return FAIL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200445 item->di_tv.v_type = VAR_STRING;
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100446 if (str != NULL)
447 {
448 if (len == -1)
449 val = vim_strsave(str);
450 else
451 val = vim_strnsave(str, len);
452 }
453 item->di_tv.vval.v_string = val;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200454 if (dict_add(d, item) == FAIL)
455 {
456 dictitem_free(item);
457 return FAIL;
458 }
459 return OK;
460}
461
462/*
463 * Add a list entry to dictionary "d".
464 * Returns FAIL when out of memory and when key already exists.
465 */
466 int
467dict_add_list(dict_T *d, char *key, list_T *list)
468{
469 dictitem_T *item;
470
471 item = dictitem_alloc((char_u *)key);
472 if (item == NULL)
473 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200474 item->di_tv.v_type = VAR_LIST;
475 item->di_tv.vval.v_list = list;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100476 ++list->lv_refcount;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200477 if (dict_add(d, item) == FAIL)
478 {
479 dictitem_free(item);
480 return FAIL;
481 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200482 return OK;
483}
484
485/*
Bram Moolenaar08928322020-01-04 14:32:48 +0100486 * Add a typval_T entry to dictionary "d".
487 * Returns FAIL when out of memory and when key already exists.
488 */
489 int
490dict_add_tv(dict_T *d, char *key, typval_T *tv)
491{
492 dictitem_T *item;
493
494 item = dictitem_alloc((char_u *)key);
495 if (item == NULL)
496 return FAIL;
497 copy_tv(tv, &item->di_tv);
498 if (dict_add(d, item) == FAIL)
499 {
500 dictitem_free(item);
501 return FAIL;
502 }
503 return OK;
504}
505
506/*
Bram Moolenaarae943152019-06-16 22:54:14 +0200507 * Add a callback to dictionary "d".
508 * Returns FAIL when out of memory and when key already exists.
509 */
510 int
511dict_add_callback(dict_T *d, char *key, callback_T *cb)
512{
513 dictitem_T *item;
514
515 item = dictitem_alloc((char_u *)key);
516 if (item == NULL)
517 return FAIL;
518 put_callback(cb, &item->di_tv);
519 if (dict_add(d, item) == FAIL)
520 {
521 dictitem_free(item);
522 return FAIL;
523 }
524 return OK;
525}
526
527/*
Bram Moolenaar45e18cb2019-04-28 18:05:35 +0200528 * Initializes "iter" for iterating over dictionary items with
529 * dict_iterate_next().
530 * If "var" is not a Dict or an empty Dict then there will be nothing to
531 * iterate over, no error is given.
532 * NOTE: The dictionary must not change until iterating is finished!
533 */
534 void
535dict_iterate_start(typval_T *var, dict_iterator_T *iter)
536{
537 if (var->v_type != VAR_DICT || var->vval.v_dict == NULL)
538 iter->dit_todo = 0;
539 else
540 {
541 dict_T *d = var->vval.v_dict;
542
543 iter->dit_todo = d->dv_hashtab.ht_used;
544 iter->dit_hi = d->dv_hashtab.ht_array;
545 }
546}
547
548/*
549 * Iterate over the items referred to by "iter". It should be initialized with
550 * dict_iterate_start().
551 * Returns a pointer to the key.
552 * "*tv_result" is set to point to the value for that key.
553 * If there are no more items, NULL is returned.
554 */
555 char_u *
556dict_iterate_next(dict_iterator_T *iter, typval_T **tv_result)
557{
558 dictitem_T *di;
559 char_u *result;
560
561 if (iter->dit_todo == 0)
562 return NULL;
563
564 while (HASHITEM_EMPTY(iter->dit_hi))
565 ++iter->dit_hi;
566
567 di = HI2DI(iter->dit_hi);
568 result = di->di_key;
569 *tv_result = &di->di_tv;
570
571 --iter->dit_todo;
572 ++iter->dit_hi;
573 return result;
574}
575
576/*
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200577 * Add a dict entry to dictionary "d".
578 * Returns FAIL when out of memory and when key already exists.
579 */
580 int
581dict_add_dict(dict_T *d, char *key, dict_T *dict)
582{
583 dictitem_T *item;
584
585 item = dictitem_alloc((char_u *)key);
586 if (item == NULL)
587 return FAIL;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200588 item->di_tv.v_type = VAR_DICT;
589 item->di_tv.vval.v_dict = dict;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100590 ++dict->dv_refcount;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200591 if (dict_add(d, item) == FAIL)
592 {
593 dictitem_free(item);
594 return FAIL;
595 }
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200596 return OK;
597}
598
599/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200600 * Get the number of items in a Dictionary.
601 */
602 long
603dict_len(dict_T *d)
604{
605 if (d == NULL)
606 return 0L;
607 return (long)d->dv_hashtab.ht_used;
608}
609
610/*
611 * Find item "key[len]" in Dictionary "d".
612 * If "len" is negative use strlen(key).
613 * Returns NULL when not found.
614 */
615 dictitem_T *
616dict_find(dict_T *d, char_u *key, int len)
617{
618#define AKEYLEN 200
619 char_u buf[AKEYLEN];
620 char_u *akey;
621 char_u *tofree = NULL;
622 hashitem_T *hi;
623
624 if (d == NULL)
625 return NULL;
626 if (len < 0)
627 akey = key;
628 else if (len >= AKEYLEN)
629 {
630 tofree = akey = vim_strnsave(key, len);
631 if (akey == NULL)
632 return NULL;
633 }
634 else
635 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100636 // Avoid a malloc/free by using buf[].
Bram Moolenaarcd524592016-07-17 14:57:05 +0200637 vim_strncpy(buf, key, len);
638 akey = buf;
639 }
640
641 hi = hash_find(&d->dv_hashtab, akey);
642 vim_free(tofree);
643 if (HASHITEM_EMPTY(hi))
644 return NULL;
645 return HI2DI(hi);
646}
647
648/*
Bram Moolenaar08928322020-01-04 14:32:48 +0100649 * Get a typval_T item from a dictionary and copy it into "rettv".
650 * Returns FAIL if the entry doesn't exist or out of memory.
651 */
652 int
653dict_get_tv(dict_T *d, char_u *key, typval_T *rettv)
654{
655 dictitem_T *di;
Bram Moolenaar08928322020-01-04 14:32:48 +0100656
657 di = dict_find(d, key, -1);
658 if (di == NULL)
659 return FAIL;
660 copy_tv(&di->di_tv, rettv);
661 return OK;
662}
663
664/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200665 * Get a string item from a dictionary.
666 * When "save" is TRUE allocate memory for it.
Bram Moolenaar7dc5e2e2016-08-05 22:22:06 +0200667 * When FALSE a shared buffer is used, can only be used once!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200668 * Returns NULL if the entry doesn't exist or out of memory.
669 */
670 char_u *
Bram Moolenaar8f667172018-12-14 15:38:31 +0100671dict_get_string(dict_T *d, char_u *key, int save)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200672{
673 dictitem_T *di;
674 char_u *s;
675
676 di = dict_find(d, key, -1);
677 if (di == NULL)
678 return NULL;
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100679 s = tv_get_string(&di->di_tv);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200680 if (save && s != NULL)
681 s = vim_strsave(s);
682 return s;
683}
684
685/*
686 * Get a number item from a dictionary.
687 * Returns 0 if the entry doesn't exist.
688 */
689 varnumber_T
Bram Moolenaar8f667172018-12-14 15:38:31 +0100690dict_get_number(dict_T *d, char_u *key)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200691{
Bram Moolenaar8c6173c2019-08-30 22:08:34 +0200692 return dict_get_number_def(d, key, 0);
693}
694
695/*
696 * Get a number item from a dictionary.
697 * Returns "def" if the entry doesn't exist.
698 */
699 varnumber_T
700dict_get_number_def(dict_T *d, char_u *key, int def)
701{
Bram Moolenaarcd524592016-07-17 14:57:05 +0200702 dictitem_T *di;
703
704 di = dict_find(d, key, -1);
705 if (di == NULL)
Bram Moolenaar8c6173c2019-08-30 22:08:34 +0200706 return def;
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100707 return tv_get_number(&di->di_tv);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200708}
709
710/*
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +0200711 * Get a number item from a dictionary.
712 * Returns 0 if the entry doesn't exist.
713 * Give an error if the entry is not a number.
714 */
715 varnumber_T
716dict_get_number_check(dict_T *d, char_u *key)
717{
718 dictitem_T *di;
719
720 di = dict_find(d, key, -1);
721 if (di == NULL)
722 return 0;
723 if (di->di_tv.v_type != VAR_NUMBER)
724 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000725 semsg(_(e_invalid_argument_str), tv_get_string(&di->di_tv));
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +0200726 return 0;
727 }
728 return tv_get_number(&di->di_tv);
729}
730
731/*
Bram Moolenaar55881332020-08-18 13:04:15 +0200732 * Get a bool item (number or true/false) from a dictionary.
733 * Returns "def" if the entry doesn't exist.
734 */
735 varnumber_T
736dict_get_bool(dict_T *d, char_u *key, int def)
737{
738 dictitem_T *di;
739
740 di = dict_find(d, key, -1);
741 if (di == NULL)
742 return def;
743 return tv_get_bool(&di->di_tv);
744}
745
746/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200747 * Return an allocated string with the string representation of a Dictionary.
748 * May return NULL.
749 */
750 char_u *
751dict2string(typval_T *tv, int copyID, int restore_copyID)
752{
753 garray_T ga;
754 int first = TRUE;
755 char_u *tofree;
756 char_u numbuf[NUMBUFLEN];
757 hashitem_T *hi;
758 char_u *s;
759 dict_T *d;
760 int todo;
761
762 if ((d = tv->vval.v_dict) == NULL)
763 return NULL;
Bram Moolenaar04935fb2022-01-08 16:19:22 +0000764 ga_init2(&ga, sizeof(char), 80);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200765 ga_append(&ga, '{');
766
767 todo = (int)d->dv_hashtab.ht_used;
768 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
769 {
770 if (!HASHITEM_EMPTY(hi))
771 {
772 --todo;
773
774 if (first)
775 first = FALSE;
776 else
777 ga_concat(&ga, (char_u *)", ");
778
779 tofree = string_quote(hi->hi_key, FALSE);
780 if (tofree != NULL)
781 {
782 ga_concat(&ga, tofree);
783 vim_free(tofree);
784 }
785 ga_concat(&ga, (char_u *)": ");
786 s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID,
787 FALSE, restore_copyID, TRUE);
788 if (s != NULL)
789 ga_concat(&ga, s);
790 vim_free(tofree);
791 if (s == NULL || did_echo_string_emsg)
792 break;
793 line_breakcheck();
794
795 }
796 }
797 if (todo > 0)
798 {
799 vim_free(ga.ga_data);
800 return NULL;
801 }
802
803 ga_append(&ga, '}');
804 ga_append(&ga, NUL);
805 return (char_u *)ga.ga_data;
806}
807
808/*
Bram Moolenaare0de1712020-12-02 17:36:54 +0100809 * Advance over a literal key, including "-". If the first character is not a
810 * literal key character then "key" is returned.
811 */
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +0200812 static char_u *
Bram Moolenaare0de1712020-12-02 17:36:54 +0100813skip_literal_key(char_u *key)
814{
815 char_u *p;
816
817 for (p = key; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; ++p)
818 ;
819 return p;
820}
821
822/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200823 * Get the key for #{key: val} into "tv" and advance "arg".
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200824 * Return FAIL when there is no valid key.
825 */
826 static int
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100827get_literal_key_tv(char_u **arg, typval_T *tv)
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200828{
Bram Moolenaare0de1712020-12-02 17:36:54 +0100829 char_u *p = skip_literal_key(*arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200830
Bram Moolenaare0de1712020-12-02 17:36:54 +0100831 if (p == *arg)
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200832 return FAIL;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200833 tv->v_type = VAR_STRING;
Bram Moolenaardf44a272020-06-07 20:49:05 +0200834 tv->vval.v_string = vim_strnsave(*arg, p - *arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200835
Bram Moolenaardb199212020-08-12 18:01:53 +0200836 *arg = p;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200837 return OK;
838}
839
840/*
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100841 * Get a literal key for a Vim9 dict:
842 * {"name": value},
843 * {'name': value},
844 * {name: value} use "name" as a literal key
845 * Return the key in allocated memory or NULL in the case of an error.
846 * "arg" is advanced to just after the key.
847 */
848 char_u *
849get_literal_key(char_u **arg)
850{
851 char_u *key;
852 char_u *end;
853 typval_T rettv;
854
855 if (**arg == '\'')
856 {
857 if (eval_lit_string(arg, &rettv, TRUE) == FAIL)
858 return NULL;
859 key = rettv.vval.v_string;
860 }
861 else if (**arg == '"')
862 {
863 if (eval_string(arg, &rettv, TRUE) == FAIL)
864 return NULL;
865 key = rettv.vval.v_string;
866 }
867 else
868 {
869 end = skip_literal_key(*arg);
870 if (end == *arg)
871 {
872 semsg(_(e_invalid_key_str), *arg);
873 return NULL;
874 }
875 key = vim_strnsave(*arg, end - *arg);
876 *arg = end;
877 }
878 return key;
879}
880
881/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200882 * Allocate a variable for a Dictionary and fill it from "*arg".
Bram Moolenaar962d7212020-07-04 14:15:00 +0200883 * "*arg" points to the "{".
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200884 * "literal" is TRUE for #{key: val}
Bram Moolenaarcd524592016-07-17 14:57:05 +0200885 * Return OK or FAIL. Returns NOTDONE for {expr}.
886 */
887 int
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200888eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200889{
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200890 int evaluate = evalarg == NULL ? FALSE
891 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200892 dict_T *d = NULL;
893 typval_T tvkey;
894 typval_T tv;
895 char_u *key = NULL;
896 dictitem_T *item;
Bram Moolenaar98cb90e2021-11-30 11:56:22 +0000897 char_u *curly_expr = skipwhite(*arg + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200898 char_u buf[NUMBUFLEN];
Bram Moolenaareb6880b2020-07-12 17:07:05 +0200899 int vim9script = in_vim9script();
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200900 int had_comma;
Bram Moolenaar5409f5d2020-06-24 18:37:35 +0200901
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000902 // First check if it's not a curly-braces thing: {expr}.
903 // Must do this without evaluating, otherwise a function may be called
904 // twice. Unfortunately this means we need to call eval1() twice for the
905 // first item.
906 // But {} is an empty Dictionary.
Bram Moolenaar98cb90e2021-11-30 11:56:22 +0000907 if (!vim9script
908 && *curly_expr != '}'
909 && eval1(&curly_expr, &tv, NULL) == OK
910 && *skipwhite(curly_expr) == '}')
911 return NOTDONE;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200912
913 if (evaluate)
914 {
915 d = dict_alloc();
916 if (d == NULL)
917 return FAIL;
918 }
919 tvkey.v_type = VAR_UNKNOWN;
920 tv.v_type = VAR_UNKNOWN;
921
Bram Moolenaar962d7212020-07-04 14:15:00 +0200922 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200923 while (**arg != '}' && **arg != NUL)
924 {
Bram Moolenaare0de1712020-12-02 17:36:54 +0100925 int has_bracket = vim9script && **arg == '[';
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100926
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100927 if (literal)
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100928 {
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100929 if (get_literal_key_tv(arg, &tvkey) == FAIL)
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100930 goto failret;
931 }
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100932 else if (vim9script && !has_bracket)
933 {
934 tvkey.vval.v_string = get_literal_key(arg);
935 if (tvkey.vval.v_string == NULL)
936 goto failret;
937 tvkey.v_type = VAR_STRING;
938 }
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100939 else
940 {
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100941 if (has_bracket)
942 *arg = skipwhite(*arg + 1);
943 if (eval1(arg, &tvkey, evalarg) == FAIL) // recursive!
944 goto failret;
945 if (has_bracket)
946 {
947 *arg = skipwhite(*arg);
948 if (**arg != ']')
949 {
950 emsg(_(e_missing_matching_bracket_after_dict_key));
Bram Moolenaar8bb0f542020-12-06 16:03:55 +0100951 clear_tv(&tvkey);
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100952 return FAIL;
953 }
954 ++*arg;
955 }
956 }
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200957
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +0200958 // the colon should come right after the key, but this wasn't checked
Bram Moolenaar9d489562020-07-30 20:08:50 +0200959 // previously, so only require it in Vim9 script.
960 if (!vim9script)
961 *arg = skipwhite(*arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200962 if (**arg != ':')
963 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200964 if (*skipwhite(*arg) == ':')
Bram Moolenaarba98fb52021-02-07 18:06:29 +0100965 semsg(_(e_no_white_space_allowed_before_str_str), ":", *arg);
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200966 else
Bram Moolenaar74409f62022-01-01 15:58:22 +0000967 semsg(_(e_missing_colon_in_dictionary), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200968 clear_tv(&tvkey);
969 goto failret;
970 }
971 if (evaluate)
972 {
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100973#ifdef FEAT_FLOAT
974 if (tvkey.v_type == VAR_FLOAT)
Bram Moolenaar9987fb02020-12-15 21:41:56 +0100975 {
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100976 tvkey.vval.v_string = typval_tostring(&tvkey, TRUE);
977 tvkey.v_type = VAR_STRING;
Bram Moolenaar9987fb02020-12-15 21:41:56 +0100978 }
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100979#endif
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100980 key = tv_get_string_buf_chk(&tvkey, buf);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200981 if (key == NULL)
982 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100983 // "key" is NULL when tv_get_string_buf_chk() gave an errmsg
Bram Moolenaarcd524592016-07-17 14:57:05 +0200984 clear_tv(&tvkey);
985 goto failret;
986 }
987 }
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200988 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
989 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +0100990 semsg(_(e_white_space_required_after_str_str), ":", *arg);
Bram Moolenaarab19d492020-06-27 17:04:05 +0200991 clear_tv(&tvkey);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200992 goto failret;
993 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200994
Bram Moolenaar962d7212020-07-04 14:15:00 +0200995 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200996 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200997 {
998 if (evaluate)
999 clear_tv(&tvkey);
1000 goto failret;
1001 }
1002 if (evaluate)
1003 {
1004 item = dict_find(d, key, -1);
1005 if (item != NULL)
1006 {
Bram Moolenaar74409f62022-01-01 15:58:22 +00001007 semsg(_(e_duplicate_key_in_dicitonary), key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001008 clear_tv(&tvkey);
1009 clear_tv(&tv);
1010 goto failret;
1011 }
1012 item = dictitem_alloc(key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001013 if (item != NULL)
1014 {
1015 item->di_tv = tv;
1016 item->di_tv.v_lock = 0;
1017 if (dict_add(d, item) == FAIL)
1018 dictitem_free(item);
1019 }
1020 }
Bram Moolenaara8931942019-09-28 17:25:10 +02001021 clear_tv(&tvkey);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001022
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +02001023 // the comma should come right after the value, but this wasn't checked
1024 // previously, so only require it in Vim9 script.
1025 if (!vim9script)
1026 *arg = skipwhite(*arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001027 had_comma = **arg == ',';
1028 if (had_comma)
1029 {
1030 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
1031 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +01001032 semsg(_(e_white_space_required_after_str_str), ",", *arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001033 goto failret;
1034 }
1035 *arg = skipwhite(*arg + 1);
1036 }
1037
1038 // the "}" can be on the next line
Bram Moolenaar962d7212020-07-04 14:15:00 +02001039 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001040 if (**arg == '}')
1041 break;
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001042 if (!had_comma)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001043 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001044 if (**arg == ',')
Bram Moolenaarba98fb52021-02-07 18:06:29 +01001045 semsg(_(e_no_white_space_allowed_before_str_str), ",", *arg);
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001046 else
Bram Moolenaar74409f62022-01-01 15:58:22 +00001047 semsg(_(e_missing_comma_in_dictionary), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001048 goto failret;
1049 }
Bram Moolenaarcd524592016-07-17 14:57:05 +02001050 }
1051
1052 if (**arg != '}')
1053 {
Bram Moolenaar4bce26b2021-01-22 22:06:56 +01001054 if (evalarg != NULL)
1055 semsg(_(e_missing_dict_end), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001056failret:
Bram Moolenaaradc67142019-06-22 01:40:42 +02001057 if (d != NULL)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001058 dict_free(d);
1059 return FAIL;
1060 }
1061
Bram Moolenaarb07a39d2020-10-22 19:00:01 +02001062 *arg = *arg + 1;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001063 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001064 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001065
1066 return OK;
1067}
1068
1069/*
1070 * Go over all entries in "d2" and add them to "d1".
1071 * When "action" is "error" then a duplicate key is an error.
1072 * When "action" is "force" then a duplicate key is overwritten.
1073 * Otherwise duplicate keys are ignored ("action" is "keep").
1074 */
1075 void
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02001076dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001077{
1078 dictitem_T *di1;
1079 hashitem_T *hi2;
1080 int todo;
1081 char_u *arg_errmsg = (char_u *)N_("extend() argument");
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001082 type_T *type;
1083
1084 if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL)
1085 type = d1->dv_type->tt_member;
1086 else
1087 type = NULL;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001088
1089 todo = (int)d2->dv_hashtab.ht_used;
1090 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
1091 {
1092 if (!HASHITEM_EMPTY(hi2))
1093 {
1094 --todo;
1095 di1 = dict_find(d1, hi2->hi_key, -1);
1096 if (d1->dv_scope != 0)
1097 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001098 // Disallow replacing a builtin function in l: and g:.
1099 // Check the key to be valid when adding to any scope.
Bram Moolenaarcd524592016-07-17 14:57:05 +02001100 if (d1->dv_scope == VAR_DEF_SCOPE
1101 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
Bram Moolenaar98b4f142020-08-08 15:46:01 +02001102 && var_wrong_func_name(hi2->hi_key, di1 == NULL))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001103 break;
Bram Moolenaar3b3755f2021-11-22 20:10:18 +00001104 if (!valid_varname(hi2->hi_key, -1, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001105 break;
1106 }
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001107
1108 if (type != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02001109 && check_typval_arg_type(type, &HI2DI(hi2)->di_tv,
1110 func_name, 0) == FAIL)
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001111 break;
1112
Bram Moolenaarcd524592016-07-17 14:57:05 +02001113 if (di1 == NULL)
1114 {
1115 di1 = dictitem_copy(HI2DI(hi2));
1116 if (di1 != NULL && dict_add(d1, di1) == FAIL)
1117 dictitem_free(di1);
1118 }
1119 else if (*action == 'e')
1120 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00001121 semsg(_(e_key_already_exists_str), hi2->hi_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001122 break;
1123 }
1124 else if (*action == 'f' && HI2DI(hi2) != di1)
1125 {
Bram Moolenaara187c432020-09-16 21:08:28 +02001126 if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
Bram Moolenaar05c00c02019-02-11 22:00:11 +01001127 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001128 break;
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +02001129 if (dict_wrong_func_name(d1, &HI2DI(hi2)->di_tv, hi2->hi_key))
1130 break;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001131 clear_tv(&di1->di_tv);
1132 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
1133 }
1134 }
1135 }
1136}
1137
1138/*
1139 * Return the dictitem that an entry in a hashtable points to.
1140 */
1141 dictitem_T *
1142dict_lookup(hashitem_T *hi)
1143{
1144 return HI2DI(hi);
1145}
1146
1147/*
1148 * Return TRUE when two dictionaries have exactly the same key/values.
1149 */
1150 int
1151dict_equal(
1152 dict_T *d1,
1153 dict_T *d2,
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001154 int ic, // ignore case for strings
1155 int recursive) // TRUE when used recursively
Bram Moolenaarcd524592016-07-17 14:57:05 +02001156{
1157 hashitem_T *hi;
1158 dictitem_T *item2;
1159 int todo;
1160
Bram Moolenaarcd524592016-07-17 14:57:05 +02001161 if (d1 == d2)
1162 return TRUE;
1163 if (dict_len(d1) != dict_len(d2))
1164 return FALSE;
Bram Moolenaarea04a6e2020-04-23 13:38:02 +02001165 if (dict_len(d1) == 0)
1166 // empty and NULL dicts are considered equal
1167 return TRUE;
1168 if (d1 == NULL || d2 == NULL)
1169 return FALSE;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001170
1171 todo = (int)d1->dv_hashtab.ht_used;
1172 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
1173 {
1174 if (!HASHITEM_EMPTY(hi))
1175 {
1176 item2 = dict_find(d2, hi->hi_key, -1);
1177 if (item2 == NULL)
1178 return FALSE;
1179 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
1180 return FALSE;
1181 --todo;
1182 }
1183 }
1184 return TRUE;
1185}
1186
1187/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001188 * Count the number of times item "needle" occurs in Dict "d". Case is ignored
1189 * if "ic" is TRUE.
1190 */
1191 long
1192dict_count(dict_T *d, typval_T *needle, int ic)
1193{
1194 int todo;
1195 hashitem_T *hi;
1196 long n = 0;
1197
1198 if (d == NULL)
1199 return 0;
1200
1201 todo = (int)d->dv_hashtab.ht_used;
1202 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1203 {
1204 if (!HASHITEM_EMPTY(hi))
1205 {
1206 --todo;
1207 if (tv_equal(&HI2DI(hi)->di_tv, needle, ic, FALSE))
1208 ++n;
1209 }
1210 }
1211
1212 return n;
1213}
1214
1215/*
1216 * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
1217 * resulting Dict in "rettv". "is_new" is TRUE for extendnew().
1218 */
1219 void
1220dict_extend_func(
1221 typval_T *argvars,
1222 type_T *type,
1223 char *func_name,
1224 char_u *arg_errmsg,
1225 int is_new,
1226 typval_T *rettv)
1227{
1228 dict_T *d1, *d2;
1229 char_u *action;
1230 int i;
1231
1232 d1 = argvars[0].vval.v_dict;
1233 if (d1 == NULL)
1234 {
1235 emsg(_(e_cannot_extend_null_dict));
1236 return;
1237 }
1238 d2 = argvars[1].vval.v_dict;
1239 if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
1240 && d2 != NULL)
1241 {
1242 if (is_new)
1243 {
Bram Moolenaar381692b2022-02-02 20:01:27 +00001244 d1 = dict_copy(d1, FALSE, TRUE, get_copyID());
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001245 if (d1 == NULL)
1246 return;
1247 }
1248
1249 // Check the third argument.
1250 if (argvars[2].v_type != VAR_UNKNOWN)
1251 {
1252 static char *(av[]) = {"keep", "force", "error"};
1253
1254 action = tv_get_string_chk(&argvars[2]);
1255 if (action == NULL)
1256 return;
1257 for (i = 0; i < 3; ++i)
1258 if (STRCMP(action, av[i]) == 0)
1259 break;
1260 if (i == 3)
1261 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001262 semsg(_(e_invalid_argument_str), action);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001263 return;
1264 }
1265 }
1266 else
1267 action = (char_u *)"force";
1268
1269 if (type != NULL && check_typval_arg_type(type, &argvars[1],
1270 func_name, 2) == FAIL)
1271 return;
1272 dict_extend(d1, d2, action, func_name);
1273
1274 if (is_new)
1275 {
1276 rettv->v_type = VAR_DICT;
1277 rettv->vval.v_dict = d1;
1278 rettv->v_lock = FALSE;
1279 }
1280 else
1281 copy_tv(&argvars[0], rettv);
1282 }
1283}
1284
1285/*
1286 * Implementation of map() and filter() for a Dict. Apply "expr" to every
1287 * item in Dict "d" and return the result in "rettv".
1288 */
1289 void
1290dict_filter_map(
1291 dict_T *d,
1292 filtermap_T filtermap,
1293 type_T *argtype,
1294 char *func_name,
1295 char_u *arg_errmsg,
1296 typval_T *expr,
1297 typval_T *rettv)
1298{
1299 int prev_lock;
1300 dict_T *d_ret = NULL;
1301 hashtab_T *ht;
1302 hashitem_T *hi;
1303 dictitem_T *di;
1304 int todo;
1305 int rem;
1306
1307 if (filtermap == FILTERMAP_MAPNEW)
1308 {
1309 rettv->v_type = VAR_DICT;
1310 rettv->vval.v_dict = NULL;
1311 }
1312 if (d == NULL
1313 || (filtermap == FILTERMAP_FILTER
1314 && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
1315 return;
1316
1317 prev_lock = d->dv_lock;
1318
1319 if (filtermap == FILTERMAP_MAPNEW)
1320 {
1321 if (rettv_dict_alloc(rettv) == FAIL)
1322 return;
1323 d_ret = rettv->vval.v_dict;
1324 }
1325
1326 if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
1327 d->dv_lock = VAR_LOCKED;
1328 ht = &d->dv_hashtab;
1329 hash_lock(ht);
1330 todo = (int)ht->ht_used;
1331 for (hi = ht->ht_array; todo > 0; ++hi)
1332 {
1333 if (!HASHITEM_EMPTY(hi))
1334 {
1335 int r;
1336 typval_T newtv;
1337
1338 --todo;
1339 di = HI2DI(hi);
1340 if (filtermap == FILTERMAP_MAP
1341 && (value_check_lock(di->di_tv.v_lock,
1342 arg_errmsg, TRUE)
1343 || var_check_ro(di->di_flags,
1344 arg_errmsg, TRUE)))
1345 break;
1346 set_vim_var_string(VV_KEY, di->di_key, -1);
1347 newtv.v_type = VAR_UNKNOWN;
1348 r = filter_map_one(&di->di_tv, expr, filtermap,
1349 &newtv, &rem);
1350 clear_tv(get_vim_var_tv(VV_KEY));
1351 if (r == FAIL || did_emsg)
1352 {
1353 clear_tv(&newtv);
1354 break;
1355 }
1356 if (filtermap == FILTERMAP_MAP)
1357 {
1358 if (argtype != NULL && check_typval_arg_type(
Bram Moolenaar078a4612022-01-04 15:17:03 +00001359 argtype->tt_member, &newtv, func_name, 0) == FAIL)
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001360 {
1361 clear_tv(&newtv);
1362 break;
1363 }
1364 // map(): replace the dict item value
1365 clear_tv(&di->di_tv);
1366 newtv.v_lock = 0;
1367 di->di_tv = newtv;
1368 }
1369 else if (filtermap == FILTERMAP_MAPNEW)
1370 {
1371 // mapnew(): add the item value to the new dict
1372 r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
1373 clear_tv(&newtv);
1374 if (r == FAIL)
1375 break;
1376 }
1377 else if (filtermap == FILTERMAP_FILTER && rem)
1378 {
1379 // filter(false): remove the item from the dict
1380 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1381 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1382 break;
1383 dictitem_remove(d, di);
1384 }
1385 }
1386 }
1387 hash_unlock(ht);
1388 d->dv_lock = prev_lock;
1389}
1390
1391/*
1392 * "remove({dict})" function
1393 */
1394 void
1395dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1396{
1397 dict_T *d;
1398 char_u *key;
1399 dictitem_T *di;
1400
1401 if (argvars[2].v_type != VAR_UNKNOWN)
1402 {
1403 semsg(_(e_too_many_arguments_for_function_str), "remove()");
1404 return;
1405 }
1406
1407 d = argvars[0].vval.v_dict;
1408 if (d == NULL || value_check_lock(d->dv_lock, arg_errmsg, TRUE))
1409 return;
1410
1411 key = tv_get_string_chk(&argvars[1]);
1412 if (key == NULL)
1413 return;
1414
1415 di = dict_find(d, key, -1);
1416 if (di == NULL)
1417 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001418 semsg(_(e_key_not_present_in_dictionary), key);
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001419 return;
1420 }
1421
1422 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1423 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1424 return;
1425
1426 *rettv = di->di_tv;
1427 init_tv(&di->di_tv);
1428 dictitem_remove(d, di);
1429}
1430
1431/*
Bram Moolenaarcd524592016-07-17 14:57:05 +02001432 * Turn a dict into a list:
1433 * "what" == 0: list of keys
1434 * "what" == 1: list of values
1435 * "what" == 2: list of items
1436 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001437 static void
Bram Moolenaarcd524592016-07-17 14:57:05 +02001438dict_list(typval_T *argvars, typval_T *rettv, int what)
1439{
1440 list_T *l2;
1441 dictitem_T *di;
1442 hashitem_T *hi;
1443 listitem_T *li;
1444 listitem_T *li2;
1445 dict_T *d;
1446 int todo;
1447
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001448 if (in_vim9script() && check_for_dict_arg(argvars, 0) == FAIL)
1449 return;
1450
Bram Moolenaarcd524592016-07-17 14:57:05 +02001451 if (argvars[0].v_type != VAR_DICT)
1452 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001453 emsg(_(e_dictionary_required));
Bram Moolenaarcd524592016-07-17 14:57:05 +02001454 return;
1455 }
Bram Moolenaarcd524592016-07-17 14:57:05 +02001456
1457 if (rettv_list_alloc(rettv) == FAIL)
1458 return;
Bram Moolenaaref982572021-08-12 19:27:57 +02001459 if ((d = argvars[0].vval.v_dict) == NULL)
1460 // empty dict behaves like an empty dict
1461 return;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001462
1463 todo = (int)d->dv_hashtab.ht_used;
1464 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1465 {
1466 if (!HASHITEM_EMPTY(hi))
1467 {
1468 --todo;
1469 di = HI2DI(hi);
1470
1471 li = listitem_alloc();
1472 if (li == NULL)
1473 break;
1474 list_append(rettv->vval.v_list, li);
1475
1476 if (what == 0)
1477 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001478 // keys()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001479 li->li_tv.v_type = VAR_STRING;
1480 li->li_tv.v_lock = 0;
1481 li->li_tv.vval.v_string = vim_strsave(di->di_key);
1482 }
1483 else if (what == 1)
1484 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001485 // values()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001486 copy_tv(&di->di_tv, &li->li_tv);
1487 }
1488 else
1489 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001490 // items()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001491 l2 = list_alloc();
1492 li->li_tv.v_type = VAR_LIST;
1493 li->li_tv.v_lock = 0;
1494 li->li_tv.vval.v_list = l2;
1495 if (l2 == NULL)
1496 break;
1497 ++l2->lv_refcount;
1498
1499 li2 = listitem_alloc();
1500 if (li2 == NULL)
1501 break;
1502 list_append(l2, li2);
1503 li2->li_tv.v_type = VAR_STRING;
1504 li2->li_tv.v_lock = 0;
1505 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
1506
1507 li2 = listitem_alloc();
1508 if (li2 == NULL)
1509 break;
1510 list_append(l2, li2);
1511 copy_tv(&di->di_tv, &li2->li_tv);
1512 }
1513 }
1514 }
1515}
1516
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001517/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001518 * "items(dict)" function
1519 */
1520 void
1521f_items(typval_T *argvars, typval_T *rettv)
1522{
1523 dict_list(argvars, rettv, 2);
1524}
1525
1526/*
1527 * "keys()" function
1528 */
1529 void
1530f_keys(typval_T *argvars, typval_T *rettv)
1531{
1532 dict_list(argvars, rettv, 0);
1533}
1534
1535/*
1536 * "values(dict)" function
1537 */
1538 void
1539f_values(typval_T *argvars, typval_T *rettv)
1540{
1541 dict_list(argvars, rettv, 1);
1542}
1543
1544/*
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001545 * Make each item in the dict readonly (not the value of the item).
1546 */
1547 void
1548dict_set_items_ro(dict_T *di)
1549{
1550 int todo = (int)di->dv_hashtab.ht_used;
1551 hashitem_T *hi;
1552
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001553 // Set readonly
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001554 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
1555 {
1556 if (HASHITEM_EMPTY(hi))
1557 continue;
1558 --todo;
1559 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
1560 }
1561}
1562
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001563/*
1564 * "has_key()" function
1565 */
1566 void
1567f_has_key(typval_T *argvars, typval_T *rettv)
1568{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001569 if (in_vim9script()
1570 && (check_for_dict_arg(argvars, 0) == FAIL
1571 || check_for_string_or_number_arg(argvars, 1) == FAIL))
1572 return;
1573
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001574 if (argvars[0].v_type != VAR_DICT)
1575 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001576 emsg(_(e_dictionary_required));
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001577 return;
1578 }
1579 if (argvars[0].vval.v_dict == NULL)
1580 return;
1581
1582 rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
1583 tv_get_string(&argvars[1]), -1) != NULL;
1584}
1585
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001586#endif // defined(FEAT_EVAL)