1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2520e1e41SBram Moolenaar *
3520e1e41SBram Moolenaar * VIM - Vi IMproved by Bram Moolenaar
4520e1e41SBram Moolenaar *
5520e1e41SBram Moolenaar * Do ":help uganda" in Vim to read copying and usage conditions.
6520e1e41SBram Moolenaar * Do ":help credits" in Vim to see a list of people who contributed.
7520e1e41SBram Moolenaar * See README.txt for an overview of the Vim source code.
8520e1e41SBram Moolenaar */
9520e1e41SBram Moolenaar
10520e1e41SBram Moolenaar /*
11520e1e41SBram Moolenaar * json.c: Encoding and decoding JSON.
12520e1e41SBram Moolenaar *
13009d84a3SBram Moolenaar * Follows this standard: https://tools.ietf.org/html/rfc7159.html
14520e1e41SBram Moolenaar */
15fefecb0fSBram Moolenaar #define USING_FLOAT_STUFF
16520e1e41SBram Moolenaar
17520e1e41SBram Moolenaar #include "vim.h"
18520e1e41SBram Moolenaar
19520e1e41SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
20f1b6ac72SBram Moolenaar
21595e64e2SBram Moolenaar static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options);
22520e1e41SBram Moolenaar
23a09195f2SBram Moolenaar static char e_json_error[] = N_("E491: json decode error at '%s'");
24a09195f2SBram Moolenaar
25520e1e41SBram Moolenaar /*
26520e1e41SBram Moolenaar * Encode "val" into a JSON format string.
27f1f0792eSBram Moolenaar * The result is added to "gap"
28f1f0792eSBram Moolenaar * Returns FAIL on failure and makes gap->ga_data empty.
29f1f0792eSBram Moolenaar */
30f1f0792eSBram Moolenaar static int
json_encode_gap(garray_T * gap,typval_T * val,int options)31f1f0792eSBram Moolenaar json_encode_gap(garray_T *gap, typval_T *val, int options)
32f1f0792eSBram Moolenaar {
33f1f0792eSBram Moolenaar if (json_encode_item(gap, val, get_copyID(), options) == FAIL)
34f1f0792eSBram Moolenaar {
35f1f0792eSBram Moolenaar ga_clear(gap);
36f1f0792eSBram Moolenaar gap->ga_data = vim_strsave((char_u *)"");
37f1f0792eSBram Moolenaar return FAIL;
38f1f0792eSBram Moolenaar }
39f1f0792eSBram Moolenaar return OK;
40f1f0792eSBram Moolenaar }
41f1f0792eSBram Moolenaar
42f1f0792eSBram Moolenaar /*
43f1f0792eSBram Moolenaar * Encode "val" into a JSON format string.
4455fab439SBram Moolenaar * The result is in allocated memory.
4555fab439SBram Moolenaar * The result is empty when encoding fails.
46f1f0792eSBram Moolenaar * "options" can contain JSON_JS, JSON_NO_NONE and JSON_NL.
47520e1e41SBram Moolenaar */
48520e1e41SBram Moolenaar char_u *
json_encode(typval_T * val,int options)49595e64e2SBram Moolenaar json_encode(typval_T *val, int options)
50520e1e41SBram Moolenaar {
51520e1e41SBram Moolenaar garray_T ga;
52520e1e41SBram Moolenaar
534ba37b58SBram Moolenaar // Store bytes in the growarray.
54520e1e41SBram Moolenaar ga_init2(&ga, 1, 4000);
55f1f0792eSBram Moolenaar json_encode_gap(&ga, val, options);
5604af1963SBram Moolenaar ga_append(&ga, NUL);
57520e1e41SBram Moolenaar return ga.ga_data;
58520e1e41SBram Moolenaar }
59520e1e41SBram Moolenaar
60113e1072SBram Moolenaar #if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
61fb1f6269SBram Moolenaar /*
6255fab439SBram Moolenaar * Encode ["nr", "val"] into a JSON format string in allocated memory.
63f1f0792eSBram Moolenaar * "options" can contain JSON_JS, JSON_NO_NONE and JSON_NL.
64fb1f6269SBram Moolenaar * Returns NULL when out of memory.
65fb1f6269SBram Moolenaar */
66fb1f6269SBram Moolenaar char_u *
json_encode_nr_expr(int nr,typval_T * val,int options)67595e64e2SBram Moolenaar json_encode_nr_expr(int nr, typval_T *val, int options)
68fb1f6269SBram Moolenaar {
69fb1f6269SBram Moolenaar typval_T listtv;
70fb1f6269SBram Moolenaar typval_T nrtv;
71f1f0792eSBram Moolenaar garray_T ga;
72fb1f6269SBram Moolenaar
73fb1f6269SBram Moolenaar nrtv.v_type = VAR_NUMBER;
74fb1f6269SBram Moolenaar nrtv.vval.v_number = nr;
75fb1f6269SBram Moolenaar if (rettv_list_alloc(&listtv) == FAIL)
76fb1f6269SBram Moolenaar return NULL;
77fb1f6269SBram Moolenaar if (list_append_tv(listtv.vval.v_list, &nrtv) == FAIL
78fb1f6269SBram Moolenaar || list_append_tv(listtv.vval.v_list, val) == FAIL)
79fb1f6269SBram Moolenaar {
80fb1f6269SBram Moolenaar list_unref(listtv.vval.v_list);
81fb1f6269SBram Moolenaar return NULL;
82fb1f6269SBram Moolenaar }
83fb1f6269SBram Moolenaar
84f1f0792eSBram Moolenaar ga_init2(&ga, 1, 4000);
85f1f0792eSBram Moolenaar if (json_encode_gap(&ga, &listtv, options) == OK && (options & JSON_NL))
86f1f0792eSBram Moolenaar ga_append(&ga, '\n');
87fb1f6269SBram Moolenaar list_unref(listtv.vval.v_list);
8804af1963SBram Moolenaar ga_append(&ga, NUL);
89f1f0792eSBram Moolenaar return ga.ga_data;
90fb1f6269SBram Moolenaar }
91113e1072SBram Moolenaar #endif
92fb1f6269SBram Moolenaar
93520e1e41SBram Moolenaar static void
write_string(garray_T * gap,char_u * str)94520e1e41SBram Moolenaar write_string(garray_T *gap, char_u *str)
95520e1e41SBram Moolenaar {
96520e1e41SBram Moolenaar char_u *res = str;
97520e1e41SBram Moolenaar char_u numbuf[NUMBUFLEN];
98520e1e41SBram Moolenaar
99520e1e41SBram Moolenaar if (res == NULL)
100b29d328eSBram Moolenaar ga_concat(gap, (char_u *)"\"\"");
101520e1e41SBram Moolenaar else
102520e1e41SBram Moolenaar {
103fc3abf47SBram Moolenaar #if defined(USE_ICONV)
104b6ff8118SBram Moolenaar vimconv_T conv;
105b6ff8118SBram Moolenaar char_u *converted = NULL;
106b6ff8118SBram Moolenaar
107f97ddbebSBram Moolenaar if (!enc_utf8)
108f97ddbebSBram Moolenaar {
1094ba37b58SBram Moolenaar // Convert the text from 'encoding' to utf-8, the JSON string is
1104ba37b58SBram Moolenaar // always utf-8.
111f97ddbebSBram Moolenaar conv.vc_type = CONV_NONE;
112b6ff8118SBram Moolenaar convert_setup(&conv, p_enc, (char_u*)"utf-8");
113b6ff8118SBram Moolenaar if (conv.vc_type != CONV_NONE)
114b6ff8118SBram Moolenaar converted = res = string_convert(&conv, res, NULL);
115b6ff8118SBram Moolenaar convert_setup(&conv, NULL, NULL);
116f97ddbebSBram Moolenaar }
117b6ff8118SBram Moolenaar #endif
118520e1e41SBram Moolenaar ga_append(gap, '"');
119520e1e41SBram Moolenaar while (*res != NUL)
120520e1e41SBram Moolenaar {
121b6ff8118SBram Moolenaar int c;
1224ba37b58SBram Moolenaar // always use utf-8 encoding, ignore 'encoding'
123b6ff8118SBram Moolenaar c = utf_ptr2char(res);
124520e1e41SBram Moolenaar
125520e1e41SBram Moolenaar switch (c)
126520e1e41SBram Moolenaar {
127520e1e41SBram Moolenaar case 0x08:
128520e1e41SBram Moolenaar ga_append(gap, '\\'); ga_append(gap, 'b'); break;
129520e1e41SBram Moolenaar case 0x09:
130520e1e41SBram Moolenaar ga_append(gap, '\\'); ga_append(gap, 't'); break;
131520e1e41SBram Moolenaar case 0x0a:
132520e1e41SBram Moolenaar ga_append(gap, '\\'); ga_append(gap, 'n'); break;
133520e1e41SBram Moolenaar case 0x0c:
134520e1e41SBram Moolenaar ga_append(gap, '\\'); ga_append(gap, 'f'); break;
135520e1e41SBram Moolenaar case 0x0d:
136520e1e41SBram Moolenaar ga_append(gap, '\\'); ga_append(gap, 'r'); break;
1374ba37b58SBram Moolenaar case 0x22: // "
1384ba37b58SBram Moolenaar case 0x5c: // backslash
139520e1e41SBram Moolenaar ga_append(gap, '\\');
140520e1e41SBram Moolenaar ga_append(gap, c);
141520e1e41SBram Moolenaar break;
142520e1e41SBram Moolenaar default:
143520e1e41SBram Moolenaar if (c >= 0x20)
144520e1e41SBram Moolenaar {
145b6ff8118SBram Moolenaar numbuf[utf_char2bytes(c, numbuf)] = NUL;
146520e1e41SBram Moolenaar ga_concat(gap, numbuf);
147520e1e41SBram Moolenaar }
148520e1e41SBram Moolenaar else
149520e1e41SBram Moolenaar {
150520e1e41SBram Moolenaar vim_snprintf((char *)numbuf, NUMBUFLEN,
151520e1e41SBram Moolenaar "\\u%04lx", (long)c);
152520e1e41SBram Moolenaar ga_concat(gap, numbuf);
153520e1e41SBram Moolenaar }
154520e1e41SBram Moolenaar }
155b6ff8118SBram Moolenaar res += utf_ptr2len(res);
156520e1e41SBram Moolenaar }
157520e1e41SBram Moolenaar ga_append(gap, '"');
158fc3abf47SBram Moolenaar #if defined(USE_ICONV)
159b6ff8118SBram Moolenaar vim_free(converted);
160b6ff8118SBram Moolenaar #endif
161520e1e41SBram Moolenaar }
162520e1e41SBram Moolenaar }
163520e1e41SBram Moolenaar
164fcaaae6bSBram Moolenaar /*
165595e64e2SBram Moolenaar * Return TRUE if "key" can be used without quotes.
166595e64e2SBram Moolenaar * That is when it starts with a letter and only contains letters, digits and
167595e64e2SBram Moolenaar * underscore.
168595e64e2SBram Moolenaar */
169595e64e2SBram Moolenaar static int
is_simple_key(char_u * key)170595e64e2SBram Moolenaar is_simple_key(char_u *key)
171595e64e2SBram Moolenaar {
172595e64e2SBram Moolenaar char_u *p;
173595e64e2SBram Moolenaar
174595e64e2SBram Moolenaar if (!ASCII_ISALPHA(*key))
175595e64e2SBram Moolenaar return FALSE;
176595e64e2SBram Moolenaar for (p = key + 1; *p != NUL; ++p)
177595e64e2SBram Moolenaar if (!ASCII_ISALPHA(*p) && *p != '_' && !vim_isdigit(*p))
178595e64e2SBram Moolenaar return FALSE;
179595e64e2SBram Moolenaar return TRUE;
180595e64e2SBram Moolenaar }
181595e64e2SBram Moolenaar
182595e64e2SBram Moolenaar /*
183fcaaae6bSBram Moolenaar * Encode "val" into "gap".
184fcaaae6bSBram Moolenaar * Return FAIL or OK.
185fcaaae6bSBram Moolenaar */
186fcaaae6bSBram Moolenaar static int
json_encode_item(garray_T * gap,typval_T * val,int copyID,int options)187595e64e2SBram Moolenaar json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
188520e1e41SBram Moolenaar {
189520e1e41SBram Moolenaar char_u numbuf[NUMBUFLEN];
190520e1e41SBram Moolenaar char_u *res;
1916e5ea8d2SBram Moolenaar blob_T *b;
192520e1e41SBram Moolenaar list_T *l;
193520e1e41SBram Moolenaar dict_T *d;
1946e5ea8d2SBram Moolenaar int i;
195520e1e41SBram Moolenaar
196520e1e41SBram Moolenaar switch (val->v_type)
197520e1e41SBram Moolenaar {
1989b4a15d5SBram Moolenaar case VAR_BOOL:
199c593bec4SBram Moolenaar switch ((long)val->vval.v_number)
200520e1e41SBram Moolenaar {
201520e1e41SBram Moolenaar case VVAL_FALSE: ga_concat(gap, (char_u *)"false"); break;
202520e1e41SBram Moolenaar case VVAL_TRUE: ga_concat(gap, (char_u *)"true"); break;
2039b4a15d5SBram Moolenaar }
2049b4a15d5SBram Moolenaar break;
2059b4a15d5SBram Moolenaar
2069b4a15d5SBram Moolenaar case VAR_SPECIAL:
207c593bec4SBram Moolenaar switch ((long)val->vval.v_number)
2089b4a15d5SBram Moolenaar {
209595e64e2SBram Moolenaar case VVAL_NONE: if ((options & JSON_JS) != 0
210595e64e2SBram Moolenaar && (options & JSON_NO_NONE) == 0)
2114ba37b58SBram Moolenaar // empty item
2124f8b8faeSBram Moolenaar break;
2134ba37b58SBram Moolenaar // FALLTHROUGH
214520e1e41SBram Moolenaar case VVAL_NULL: ga_concat(gap, (char_u *)"null"); break;
215520e1e41SBram Moolenaar }
216520e1e41SBram Moolenaar break;
217520e1e41SBram Moolenaar
218520e1e41SBram Moolenaar case VAR_NUMBER:
21922fcfad2SBram Moolenaar vim_snprintf((char *)numbuf, NUMBUFLEN, "%lld",
220f9706e9dSBram Moolenaar (varnumber_T)val->vval.v_number);
221520e1e41SBram Moolenaar ga_concat(gap, numbuf);
222520e1e41SBram Moolenaar break;
223520e1e41SBram Moolenaar
224520e1e41SBram Moolenaar case VAR_STRING:
225520e1e41SBram Moolenaar res = val->vval.v_string;
226520e1e41SBram Moolenaar write_string(gap, res);
227520e1e41SBram Moolenaar break;
228520e1e41SBram Moolenaar
229520e1e41SBram Moolenaar case VAR_FUNC:
2301735bc98SBram Moolenaar case VAR_PARTIAL:
23155fab439SBram Moolenaar case VAR_JOB:
2327707344dSBram Moolenaar case VAR_CHANNEL:
233f18332fbSBram Moolenaar case VAR_INSTR:
234a8530894SBram Moolenaar semsg(_(e_cannot_json_encode_str), vartype_name(val->v_type));
235fcaaae6bSBram Moolenaar return FAIL;
236520e1e41SBram Moolenaar
2376e5ea8d2SBram Moolenaar case VAR_BLOB:
2386e5ea8d2SBram Moolenaar b = val->vval.v_blob;
2396e5ea8d2SBram Moolenaar if (b == NULL || b->bv_ga.ga_len == 0)
2406e5ea8d2SBram Moolenaar ga_concat(gap, (char_u *)"[]");
2416e5ea8d2SBram Moolenaar else
2426e5ea8d2SBram Moolenaar {
2436e5ea8d2SBram Moolenaar ga_append(gap, '[');
2446e5ea8d2SBram Moolenaar for (i = 0; i < b->bv_ga.ga_len; i++)
2456e5ea8d2SBram Moolenaar {
2466e5ea8d2SBram Moolenaar if (i > 0)
2476e5ea8d2SBram Moolenaar ga_concat(gap, (char_u *)",");
2486e5ea8d2SBram Moolenaar vim_snprintf((char *)numbuf, NUMBUFLEN, "%d",
2496e5ea8d2SBram Moolenaar (int)blob_get(b, i));
2506e5ea8d2SBram Moolenaar ga_concat(gap, numbuf);
2516e5ea8d2SBram Moolenaar }
2526e5ea8d2SBram Moolenaar ga_append(gap, ']');
2536e5ea8d2SBram Moolenaar }
2546e5ea8d2SBram Moolenaar break;
2556e5ea8d2SBram Moolenaar
256520e1e41SBram Moolenaar case VAR_LIST:
257520e1e41SBram Moolenaar l = val->vval.v_list;
258520e1e41SBram Moolenaar if (l == NULL)
259b29d328eSBram Moolenaar ga_concat(gap, (char_u *)"[]");
260520e1e41SBram Moolenaar else
261520e1e41SBram Moolenaar {
262520e1e41SBram Moolenaar if (l->lv_copyID == copyID)
263520e1e41SBram Moolenaar ga_concat(gap, (char_u *)"[]");
264520e1e41SBram Moolenaar else
265520e1e41SBram Moolenaar {
266520e1e41SBram Moolenaar listitem_T *li;
267520e1e41SBram Moolenaar
268520e1e41SBram Moolenaar l->lv_copyID = copyID;
269520e1e41SBram Moolenaar ga_append(gap, '[');
2707e9f351bSBram Moolenaar CHECK_LIST_MATERIALIZE(l);
271520e1e41SBram Moolenaar for (li = l->lv_first; li != NULL && !got_int; )
272520e1e41SBram Moolenaar {
273595e64e2SBram Moolenaar if (json_encode_item(gap, &li->li_tv, copyID,
274595e64e2SBram Moolenaar options & JSON_JS) == FAIL)
275fcaaae6bSBram Moolenaar return FAIL;
276595e64e2SBram Moolenaar if ((options & JSON_JS)
277595e64e2SBram Moolenaar && li->li_next == NULL
278595e64e2SBram Moolenaar && li->li_tv.v_type == VAR_SPECIAL
279595e64e2SBram Moolenaar && li->li_tv.vval.v_number == VVAL_NONE)
2804ba37b58SBram Moolenaar // add an extra comma if the last item is v:none
281595e64e2SBram Moolenaar ga_append(gap, ',');
282520e1e41SBram Moolenaar li = li->li_next;
283520e1e41SBram Moolenaar if (li != NULL)
284520e1e41SBram Moolenaar ga_append(gap, ',');
285520e1e41SBram Moolenaar }
286520e1e41SBram Moolenaar ga_append(gap, ']');
287fcaaae6bSBram Moolenaar l->lv_copyID = 0;
288520e1e41SBram Moolenaar }
289520e1e41SBram Moolenaar }
290520e1e41SBram Moolenaar break;
291520e1e41SBram Moolenaar
292520e1e41SBram Moolenaar case VAR_DICT:
293520e1e41SBram Moolenaar d = val->vval.v_dict;
294520e1e41SBram Moolenaar if (d == NULL)
295b29d328eSBram Moolenaar ga_concat(gap, (char_u *)"{}");
296520e1e41SBram Moolenaar else
297520e1e41SBram Moolenaar {
298520e1e41SBram Moolenaar if (d->dv_copyID == copyID)
299520e1e41SBram Moolenaar ga_concat(gap, (char_u *)"{}");
300520e1e41SBram Moolenaar else
301520e1e41SBram Moolenaar {
302520e1e41SBram Moolenaar int first = TRUE;
303520e1e41SBram Moolenaar int todo = (int)d->dv_hashtab.ht_used;
304520e1e41SBram Moolenaar hashitem_T *hi;
305520e1e41SBram Moolenaar
306520e1e41SBram Moolenaar d->dv_copyID = copyID;
307520e1e41SBram Moolenaar ga_append(gap, '{');
308520e1e41SBram Moolenaar
309520e1e41SBram Moolenaar for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int;
310520e1e41SBram Moolenaar ++hi)
311520e1e41SBram Moolenaar if (!HASHITEM_EMPTY(hi))
312520e1e41SBram Moolenaar {
313520e1e41SBram Moolenaar --todo;
314520e1e41SBram Moolenaar if (first)
315520e1e41SBram Moolenaar first = FALSE;
316520e1e41SBram Moolenaar else
317520e1e41SBram Moolenaar ga_append(gap, ',');
318595e64e2SBram Moolenaar if ((options & JSON_JS)
319595e64e2SBram Moolenaar && is_simple_key(hi->hi_key))
320595e64e2SBram Moolenaar ga_concat(gap, hi->hi_key);
321595e64e2SBram Moolenaar else
322520e1e41SBram Moolenaar write_string(gap, hi->hi_key);
323520e1e41SBram Moolenaar ga_append(gap, ':');
324fcaaae6bSBram Moolenaar if (json_encode_item(gap, &dict_lookup(hi)->di_tv,
325595e64e2SBram Moolenaar copyID, options | JSON_NO_NONE) == FAIL)
326fcaaae6bSBram Moolenaar return FAIL;
327520e1e41SBram Moolenaar }
328520e1e41SBram Moolenaar ga_append(gap, '}');
329fcaaae6bSBram Moolenaar d->dv_copyID = 0;
330520e1e41SBram Moolenaar }
331520e1e41SBram Moolenaar }
332520e1e41SBram Moolenaar break;
333520e1e41SBram Moolenaar
334520e1e41SBram Moolenaar case VAR_FLOAT:
33555fab439SBram Moolenaar #ifdef FEAT_FLOAT
336f1b6ac72SBram Moolenaar # if defined(HAVE_MATH_H)
3377ce686c9SBram Moolenaar if (isnan(val->vval.v_float))
338f1b6ac72SBram Moolenaar ga_concat(gap, (char_u *)"NaN");
3397ce686c9SBram Moolenaar else if (isinf(val->vval.v_float))
3405f6b379fSBram Moolenaar {
3415f6b379fSBram Moolenaar if (val->vval.v_float < 0.0)
3425f6b379fSBram Moolenaar ga_concat(gap, (char_u *)"-Infinity");
3435f6b379fSBram Moolenaar else
344f1b6ac72SBram Moolenaar ga_concat(gap, (char_u *)"Infinity");
3455f6b379fSBram Moolenaar }
346f1b6ac72SBram Moolenaar else
347f1b6ac72SBram Moolenaar # endif
348f1b6ac72SBram Moolenaar {
349f1b6ac72SBram Moolenaar vim_snprintf((char *)numbuf, NUMBUFLEN, "%g",
350f1b6ac72SBram Moolenaar val->vval.v_float);
351520e1e41SBram Moolenaar ga_concat(gap, numbuf);
352f1b6ac72SBram Moolenaar }
353520e1e41SBram Moolenaar break;
354520e1e41SBram Moolenaar #endif
35555fab439SBram Moolenaar case VAR_UNKNOWN:
3564c683750SBram Moolenaar case VAR_ANY:
3578a7d6542SBram Moolenaar case VAR_VOID:
358dd58923cSBram Moolenaar internal_error_no_abort("json_encode_item()");
359fcaaae6bSBram Moolenaar return FAIL;
360520e1e41SBram Moolenaar }
361fcaaae6bSBram Moolenaar return OK;
362520e1e41SBram Moolenaar }
363520e1e41SBram Moolenaar
364520e1e41SBram Moolenaar /*
36556ead341SBram Moolenaar * When "reader" has less than NUMBUFLEN bytes available, call the fill
36656ead341SBram Moolenaar * callback to get more.
36756ead341SBram Moolenaar */
36856ead341SBram Moolenaar static void
fill_numbuflen(js_read_T * reader)36956ead341SBram Moolenaar fill_numbuflen(js_read_T *reader)
37056ead341SBram Moolenaar {
37156ead341SBram Moolenaar if (reader->js_fill != NULL && (int)(reader->js_end - reader->js_buf)
37256ead341SBram Moolenaar - reader->js_used < NUMBUFLEN)
37356ead341SBram Moolenaar {
37456ead341SBram Moolenaar if (reader->js_fill(reader))
37556ead341SBram Moolenaar reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
37656ead341SBram Moolenaar }
37756ead341SBram Moolenaar }
37856ead341SBram Moolenaar
37956ead341SBram Moolenaar /*
380595e64e2SBram Moolenaar * Skip white space in "reader". All characters <= space are considered white
381595e64e2SBram Moolenaar * space.
38256ead341SBram Moolenaar * Also tops up readahead when needed.
383520e1e41SBram Moolenaar */
384520e1e41SBram Moolenaar static void
json_skip_white(js_read_T * reader)385520e1e41SBram Moolenaar json_skip_white(js_read_T *reader)
386520e1e41SBram Moolenaar {
387520e1e41SBram Moolenaar int c;
388520e1e41SBram Moolenaar
38956ead341SBram Moolenaar for (;;)
39056ead341SBram Moolenaar {
39156ead341SBram Moolenaar c = reader->js_buf[reader->js_used];
39256ead341SBram Moolenaar if (reader->js_fill != NULL && c == NUL)
39356ead341SBram Moolenaar {
39456ead341SBram Moolenaar if (reader->js_fill(reader))
39546c00a65SBram Moolenaar {
39656ead341SBram Moolenaar reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
39756ead341SBram Moolenaar continue;
39856ead341SBram Moolenaar }
39946c00a65SBram Moolenaar }
400595e64e2SBram Moolenaar if (c == NUL || c > ' ')
40156ead341SBram Moolenaar break;
402520e1e41SBram Moolenaar ++reader->js_used;
403520e1e41SBram Moolenaar }
40456ead341SBram Moolenaar fill_numbuflen(reader);
405520e1e41SBram Moolenaar }
406520e1e41SBram Moolenaar
40756ead341SBram Moolenaar static int
json_decode_string(js_read_T * reader,typval_T * res,int quote)408ee142addSBram Moolenaar json_decode_string(js_read_T *reader, typval_T *res, int quote)
409520e1e41SBram Moolenaar {
410520e1e41SBram Moolenaar garray_T ga;
411520e1e41SBram Moolenaar int len;
41256ead341SBram Moolenaar char_u *p;
413520e1e41SBram Moolenaar int c;
41422fcfad2SBram Moolenaar varnumber_T nr;
415520e1e41SBram Moolenaar
41656ead341SBram Moolenaar if (res != NULL)
417520e1e41SBram Moolenaar ga_init2(&ga, 1, 200);
418520e1e41SBram Moolenaar
4194ba37b58SBram Moolenaar p = reader->js_buf + reader->js_used + 1; // skip over " or '
420ee142addSBram Moolenaar while (*p != quote)
421520e1e41SBram Moolenaar {
4224ba37b58SBram Moolenaar // The JSON is always expected to be utf-8, thus use utf functions
4234ba37b58SBram Moolenaar // here. The string is converted below if needed.
424fc3abf47SBram Moolenaar if (*p == NUL || p[1] == NUL || utf_ptr2len(p) < utf_byte2len(*p))
42556ead341SBram Moolenaar {
4264ba37b58SBram Moolenaar // Not enough bytes to make a character or end of the string. Get
4274ba37b58SBram Moolenaar // more if possible.
42856ead341SBram Moolenaar if (reader->js_fill == NULL)
42956ead341SBram Moolenaar break;
43056ead341SBram Moolenaar len = (int)(reader->js_end - p);
43156ead341SBram Moolenaar reader->js_used = (int)(p - reader->js_buf);
43256ead341SBram Moolenaar if (!reader->js_fill(reader))
4334ba37b58SBram Moolenaar break; // didn't get more
43456ead341SBram Moolenaar p = reader->js_buf + reader->js_used;
43556ead341SBram Moolenaar reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
43656ead341SBram Moolenaar continue;
43756ead341SBram Moolenaar }
43856ead341SBram Moolenaar
439520e1e41SBram Moolenaar if (*p == '\\')
440520e1e41SBram Moolenaar {
441520e1e41SBram Moolenaar c = -1;
442520e1e41SBram Moolenaar switch (p[1])
443520e1e41SBram Moolenaar {
44456ead341SBram Moolenaar case '\\': c = '\\'; break;
44556ead341SBram Moolenaar case '"': c = '"'; break;
446520e1e41SBram Moolenaar case 'b': c = BS; break;
447520e1e41SBram Moolenaar case 't': c = TAB; break;
448520e1e41SBram Moolenaar case 'n': c = NL; break;
449520e1e41SBram Moolenaar case 'f': c = FF; break;
450520e1e41SBram Moolenaar case 'r': c = CAR; break;
451520e1e41SBram Moolenaar case 'u':
45256ead341SBram Moolenaar if (reader->js_fill != NULL
45356ead341SBram Moolenaar && (int)(reader->js_end - p) < NUMBUFLEN)
45456ead341SBram Moolenaar {
45556ead341SBram Moolenaar reader->js_used = (int)(p - reader->js_buf);
45656ead341SBram Moolenaar if (reader->js_fill(reader))
45756ead341SBram Moolenaar {
45856ead341SBram Moolenaar p = reader->js_buf + reader->js_used;
45956ead341SBram Moolenaar reader->js_end = reader->js_buf
46056ead341SBram Moolenaar + STRLEN(reader->js_buf);
46156ead341SBram Moolenaar }
46256ead341SBram Moolenaar }
463b6ff8118SBram Moolenaar nr = 0;
464b6ff8118SBram Moolenaar len = 0;
465520e1e41SBram Moolenaar vim_str2nr(p + 2, NULL, &len,
46616e9b851SBram Moolenaar STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4, TRUE);
46716e9b851SBram Moolenaar if (len == 0)
46816e9b851SBram Moolenaar {
469b4368372SBram Moolenaar if (res != NULL)
47016e9b851SBram Moolenaar ga_clear(&ga);
47116e9b851SBram Moolenaar return FAIL;
47216e9b851SBram Moolenaar }
473520e1e41SBram Moolenaar p += len + 2;
474b6ff8118SBram Moolenaar if (0xd800 <= nr && nr <= 0xdfff
475b6ff8118SBram Moolenaar && (int)(reader->js_end - p) >= 6
476b6ff8118SBram Moolenaar && *p == '\\' && *(p+1) == 'u')
477b6ff8118SBram Moolenaar {
47822fcfad2SBram Moolenaar varnumber_T nr2 = 0;
479b6ff8118SBram Moolenaar
4804ba37b58SBram Moolenaar // decode surrogate pair: \ud812\u3456
481b6ff8118SBram Moolenaar len = 0;
482b6ff8118SBram Moolenaar vim_str2nr(p + 2, NULL, &len,
48316e9b851SBram Moolenaar STR2NR_HEX + STR2NR_FORCE, &nr2, NULL, 4, TRUE);
48416e9b851SBram Moolenaar if (len == 0)
48516e9b851SBram Moolenaar {
486b4368372SBram Moolenaar if (res != NULL)
48716e9b851SBram Moolenaar ga_clear(&ga);
48816e9b851SBram Moolenaar return FAIL;
48916e9b851SBram Moolenaar }
490b6ff8118SBram Moolenaar if (0xdc00 <= nr2 && nr2 <= 0xdfff)
491b6ff8118SBram Moolenaar {
492b6ff8118SBram Moolenaar p += len + 2;
493b6ff8118SBram Moolenaar nr = (((nr - 0xd800) << 10) |
494b6ff8118SBram Moolenaar ((nr2 - 0xdc00) & 0x3ff)) + 0x10000;
495b6ff8118SBram Moolenaar }
496b6ff8118SBram Moolenaar }
49756ead341SBram Moolenaar if (res != NULL)
49856ead341SBram Moolenaar {
499dc633cf8SBram Moolenaar char_u buf[NUMBUFLEN];
500b4368372SBram Moolenaar
501b6ff8118SBram Moolenaar buf[utf_char2bytes((int)nr, buf)] = NUL;
502520e1e41SBram Moolenaar ga_concat(&ga, buf);
50356ead341SBram Moolenaar }
504520e1e41SBram Moolenaar break;
50556ead341SBram Moolenaar default:
5064ba37b58SBram Moolenaar // not a special char, skip over backslash
50756ead341SBram Moolenaar ++p;
50856ead341SBram Moolenaar continue;
509520e1e41SBram Moolenaar }
510520e1e41SBram Moolenaar if (c > 0)
511520e1e41SBram Moolenaar {
512520e1e41SBram Moolenaar p += 2;
51356ead341SBram Moolenaar if (res != NULL)
514520e1e41SBram Moolenaar ga_append(&ga, c);
515520e1e41SBram Moolenaar }
516520e1e41SBram Moolenaar }
517520e1e41SBram Moolenaar else
518520e1e41SBram Moolenaar {
519b6ff8118SBram Moolenaar len = utf_ptr2len(p);
52056ead341SBram Moolenaar if (res != NULL)
521520e1e41SBram Moolenaar {
52256ead341SBram Moolenaar if (ga_grow(&ga, len) == FAIL)
52356ead341SBram Moolenaar {
52456ead341SBram Moolenaar ga_clear(&ga);
52556ead341SBram Moolenaar return FAIL;
52656ead341SBram Moolenaar }
527520e1e41SBram Moolenaar mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
528520e1e41SBram Moolenaar ga.ga_len += len;
529520e1e41SBram Moolenaar }
530520e1e41SBram Moolenaar p += len;
531520e1e41SBram Moolenaar }
532520e1e41SBram Moolenaar }
53356ead341SBram Moolenaar
534520e1e41SBram Moolenaar reader->js_used = (int)(p - reader->js_buf);
535ee142addSBram Moolenaar if (*p == quote)
536520e1e41SBram Moolenaar {
537520e1e41SBram Moolenaar ++reader->js_used;
53856ead341SBram Moolenaar if (res != NULL)
53956ead341SBram Moolenaar {
54080e78847SBram Moolenaar ga_append(&ga, NUL);
541520e1e41SBram Moolenaar res->v_type = VAR_STRING;
542fc3abf47SBram Moolenaar #if defined(USE_ICONV)
543b3628728SBram Moolenaar if (!enc_utf8)
544b3628728SBram Moolenaar {
545b3628728SBram Moolenaar vimconv_T conv;
546b3628728SBram Moolenaar
5474ba37b58SBram Moolenaar // Convert the utf-8 string to 'encoding'.
548b3628728SBram Moolenaar conv.vc_type = CONV_NONE;
549b3628728SBram Moolenaar convert_setup(&conv, (char_u*)"utf-8", p_enc);
550b3628728SBram Moolenaar if (conv.vc_type != CONV_NONE)
551b3628728SBram Moolenaar {
552b3628728SBram Moolenaar res->vval.v_string =
553b3628728SBram Moolenaar string_convert(&conv, ga.ga_data, NULL);
554b3628728SBram Moolenaar vim_free(ga.ga_data);
555b3628728SBram Moolenaar }
556b3628728SBram Moolenaar convert_setup(&conv, NULL, NULL);
557b3628728SBram Moolenaar }
558b3628728SBram Moolenaar else
559b3628728SBram Moolenaar #endif
5604b6a6dcbSBram Moolenaar res->vval.v_string = ga.ga_data;
561520e1e41SBram Moolenaar }
56256ead341SBram Moolenaar return OK;
56356ead341SBram Moolenaar }
56456ead341SBram Moolenaar if (res != NULL)
565520e1e41SBram Moolenaar {
566520e1e41SBram Moolenaar res->v_type = VAR_SPECIAL;
567520e1e41SBram Moolenaar res->vval.v_number = VVAL_NONE;
568520e1e41SBram Moolenaar ga_clear(&ga);
569520e1e41SBram Moolenaar }
57056ead341SBram Moolenaar return MAYBE;
57156ead341SBram Moolenaar }
572520e1e41SBram Moolenaar
5738b2f1953SBram Moolenaar typedef enum {
5744ba37b58SBram Moolenaar JSON_ARRAY, // parsing items in an array
5754ba37b58SBram Moolenaar JSON_OBJECT_KEY, // parsing key of an object
5764ba37b58SBram Moolenaar JSON_OBJECT // parsing item in an object, after the key
5778b2f1953SBram Moolenaar } json_decode_T;
5788b2f1953SBram Moolenaar
5798b2f1953SBram Moolenaar typedef struct {
5808b2f1953SBram Moolenaar json_decode_T jd_type;
5814ba37b58SBram Moolenaar typval_T jd_tv; // the list or dict
5828b2f1953SBram Moolenaar typval_T jd_key_tv;
5838b2f1953SBram Moolenaar char_u *jd_key;
5848b2f1953SBram Moolenaar } json_dec_item_T;
5858b2f1953SBram Moolenaar
586520e1e41SBram Moolenaar /*
58756ead341SBram Moolenaar * Decode one item and put it in "res". If "res" is NULL only advance.
588520e1e41SBram Moolenaar * Must already have skipped white space.
58956ead341SBram Moolenaar *
59003c60c15SBram Moolenaar * Return FAIL for a decoding error (and give an error).
59156ead341SBram Moolenaar * Return MAYBE for an incomplete message.
592520e1e41SBram Moolenaar */
59356ead341SBram Moolenaar static int
json_decode_item(js_read_T * reader,typval_T * res,int options)594595e64e2SBram Moolenaar json_decode_item(js_read_T *reader, typval_T *res, int options)
595520e1e41SBram Moolenaar {
59656ead341SBram Moolenaar char_u *p;
5976d3a7213SBram Moolenaar int i;
59856ead341SBram Moolenaar int len;
5998b2f1953SBram Moolenaar int retval;
6008b2f1953SBram Moolenaar garray_T stack;
6018b2f1953SBram Moolenaar typval_T item;
6028b2f1953SBram Moolenaar typval_T *cur_item;
6038b2f1953SBram Moolenaar json_dec_item_T *top_item;
6048b2f1953SBram Moolenaar char_u key_buf[NUMBUFLEN];
6058b2f1953SBram Moolenaar
6068b2f1953SBram Moolenaar ga_init2(&stack, sizeof(json_dec_item_T), 100);
6078b2f1953SBram Moolenaar cur_item = res;
6088b2f1953SBram Moolenaar init_tv(&item);
609e32abbe4SBram Moolenaar if (res != NULL)
610e32abbe4SBram Moolenaar init_tv(res);
611520e1e41SBram Moolenaar
61256ead341SBram Moolenaar fill_numbuflen(reader);
61356ead341SBram Moolenaar p = reader->js_buf + reader->js_used;
6148b2f1953SBram Moolenaar for (;;)
6158b2f1953SBram Moolenaar {
6168b2f1953SBram Moolenaar top_item = NULL;
6178b2f1953SBram Moolenaar if (stack.ga_len > 0)
6188b2f1953SBram Moolenaar {
6198b2f1953SBram Moolenaar top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len - 1;
6208b2f1953SBram Moolenaar json_skip_white(reader);
6218b2f1953SBram Moolenaar p = reader->js_buf + reader->js_used;
6228b2f1953SBram Moolenaar if (*p == NUL)
6238b2f1953SBram Moolenaar {
6248b2f1953SBram Moolenaar retval = MAYBE;
6258b2f1953SBram Moolenaar goto theend;
6268b2f1953SBram Moolenaar }
6278b2f1953SBram Moolenaar if (top_item->jd_type == JSON_OBJECT_KEY
6288b2f1953SBram Moolenaar || top_item->jd_type == JSON_ARRAY)
6298b2f1953SBram Moolenaar {
6304ba37b58SBram Moolenaar // Check for end of object or array.
6318b2f1953SBram Moolenaar if (*p == (top_item->jd_type == JSON_ARRAY ? ']' : '}'))
6328b2f1953SBram Moolenaar {
6334ba37b58SBram Moolenaar ++reader->js_used; // consume the ']' or '}'
6348b2f1953SBram Moolenaar --stack.ga_len;
6358b2f1953SBram Moolenaar if (stack.ga_len == 0)
6368b2f1953SBram Moolenaar {
6378b2f1953SBram Moolenaar retval = OK;
6388b2f1953SBram Moolenaar goto theend;
6398b2f1953SBram Moolenaar }
6408b2f1953SBram Moolenaar if (cur_item != NULL)
6418b2f1953SBram Moolenaar cur_item = &top_item->jd_tv;
6428b2f1953SBram Moolenaar goto item_end;
6438b2f1953SBram Moolenaar }
6448b2f1953SBram Moolenaar }
6458b2f1953SBram Moolenaar }
6468b2f1953SBram Moolenaar
6478b2f1953SBram Moolenaar if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY
6488b2f1953SBram Moolenaar && (options & JSON_JS)
649ee142addSBram Moolenaar && reader->js_buf[reader->js_used] != '"'
650625f0c1eSBram Moolenaar && reader->js_buf[reader->js_used] != '\''
651625f0c1eSBram Moolenaar && reader->js_buf[reader->js_used] != '['
652625f0c1eSBram Moolenaar && reader->js_buf[reader->js_used] != '{')
6538b2f1953SBram Moolenaar {
6548b2f1953SBram Moolenaar char_u *key;
6558b2f1953SBram Moolenaar
6564ba37b58SBram Moolenaar // accept an object key that is not in quotes
6578b2f1953SBram Moolenaar key = p = reader->js_buf + reader->js_used;
6588b2f1953SBram Moolenaar while (*p != NUL && *p != ':' && *p > ' ')
6598b2f1953SBram Moolenaar ++p;
660e2c6037dSBram Moolenaar if (cur_item != NULL)
661e2c6037dSBram Moolenaar {
6628b2f1953SBram Moolenaar cur_item->v_type = VAR_STRING;
66371ccd03eSBram Moolenaar cur_item->vval.v_string = vim_strnsave(key, p - key);
6648b2f1953SBram Moolenaar top_item->jd_key = cur_item->vval.v_string;
6658b2f1953SBram Moolenaar }
666e2c6037dSBram Moolenaar reader->js_used += (int)(p - key);
667e2c6037dSBram Moolenaar }
6688b2f1953SBram Moolenaar else
6698b2f1953SBram Moolenaar {
670520e1e41SBram Moolenaar switch (*p)
671520e1e41SBram Moolenaar {
6724ba37b58SBram Moolenaar case '[': // start of array
673625f0c1eSBram Moolenaar if (top_item && top_item->jd_type == JSON_OBJECT_KEY)
674625f0c1eSBram Moolenaar {
675625f0c1eSBram Moolenaar retval = FAIL;
676625f0c1eSBram Moolenaar break;
677625f0c1eSBram Moolenaar }
6788b2f1953SBram Moolenaar if (ga_grow(&stack, 1) == FAIL)
6798b2f1953SBram Moolenaar {
6808b2f1953SBram Moolenaar retval = FAIL;
6818b2f1953SBram Moolenaar break;
6828b2f1953SBram Moolenaar }
6838b2f1953SBram Moolenaar if (cur_item != NULL && rettv_list_alloc(cur_item) == FAIL)
6848b2f1953SBram Moolenaar {
6858b2f1953SBram Moolenaar cur_item->v_type = VAR_SPECIAL;
6868b2f1953SBram Moolenaar cur_item->vval.v_number = VVAL_NONE;
6878b2f1953SBram Moolenaar retval = FAIL;
6888b2f1953SBram Moolenaar break;
6898b2f1953SBram Moolenaar }
690520e1e41SBram Moolenaar
6914ba37b58SBram Moolenaar ++reader->js_used; // consume the '['
6928b2f1953SBram Moolenaar top_item = ((json_dec_item_T *)stack.ga_data)
6938b2f1953SBram Moolenaar + stack.ga_len;
6948b2f1953SBram Moolenaar top_item->jd_type = JSON_ARRAY;
6958b2f1953SBram Moolenaar ++stack.ga_len;
6968b2f1953SBram Moolenaar if (cur_item != NULL)
6978b2f1953SBram Moolenaar {
6988b2f1953SBram Moolenaar top_item->jd_tv = *cur_item;
6998b2f1953SBram Moolenaar cur_item = &item;
7008b2f1953SBram Moolenaar }
7018b2f1953SBram Moolenaar continue;
7028b2f1953SBram Moolenaar
7034ba37b58SBram Moolenaar case '{': // start of object
704625f0c1eSBram Moolenaar if (top_item && top_item->jd_type == JSON_OBJECT_KEY)
705625f0c1eSBram Moolenaar {
706625f0c1eSBram Moolenaar retval = FAIL;
707625f0c1eSBram Moolenaar break;
708625f0c1eSBram Moolenaar }
7098b2f1953SBram Moolenaar if (ga_grow(&stack, 1) == FAIL)
7108b2f1953SBram Moolenaar {
7118b2f1953SBram Moolenaar retval = FAIL;
7128b2f1953SBram Moolenaar break;
7138b2f1953SBram Moolenaar }
7148b2f1953SBram Moolenaar if (cur_item != NULL && rettv_dict_alloc(cur_item) == FAIL)
7158b2f1953SBram Moolenaar {
7168b2f1953SBram Moolenaar cur_item->v_type = VAR_SPECIAL;
7178b2f1953SBram Moolenaar cur_item->vval.v_number = VVAL_NONE;
7188b2f1953SBram Moolenaar retval = FAIL;
7198b2f1953SBram Moolenaar break;
7208b2f1953SBram Moolenaar }
7218b2f1953SBram Moolenaar
7224ba37b58SBram Moolenaar ++reader->js_used; // consume the '{'
7238b2f1953SBram Moolenaar top_item = ((json_dec_item_T *)stack.ga_data)
7248b2f1953SBram Moolenaar + stack.ga_len;
7258b2f1953SBram Moolenaar top_item->jd_type = JSON_OBJECT_KEY;
7268b2f1953SBram Moolenaar ++stack.ga_len;
7278b2f1953SBram Moolenaar if (cur_item != NULL)
7288b2f1953SBram Moolenaar {
7298b2f1953SBram Moolenaar top_item->jd_tv = *cur_item;
7308b2f1953SBram Moolenaar cur_item = &top_item->jd_key_tv;
7318b2f1953SBram Moolenaar }
7328b2f1953SBram Moolenaar continue;
733520e1e41SBram Moolenaar
7344ba37b58SBram Moolenaar case '"': // string
735ee142addSBram Moolenaar retval = json_decode_string(reader, cur_item, *p);
736ee142addSBram Moolenaar break;
737ee142addSBram Moolenaar
738ee142addSBram Moolenaar case '\'':
739ee142addSBram Moolenaar if (options & JSON_JS)
740ee142addSBram Moolenaar retval = json_decode_string(reader, cur_item, *p);
741ee142addSBram Moolenaar else
742ee142addSBram Moolenaar {
743a09195f2SBram Moolenaar semsg(_(e_json_error), p);
744ee142addSBram Moolenaar retval = FAIL;
745ee142addSBram Moolenaar }
7468b2f1953SBram Moolenaar break;
747520e1e41SBram Moolenaar
7484ba37b58SBram Moolenaar case ',': // comma: empty item
749595e64e2SBram Moolenaar if ((options & JSON_JS) == 0)
75003c60c15SBram Moolenaar {
751a09195f2SBram Moolenaar semsg(_(e_json_error), p);
7528b2f1953SBram Moolenaar retval = FAIL;
7538b2f1953SBram Moolenaar break;
75403c60c15SBram Moolenaar }
7554ba37b58SBram Moolenaar // FALLTHROUGH
7564ba37b58SBram Moolenaar case NUL: // empty
7578b2f1953SBram Moolenaar if (cur_item != NULL)
75856ead341SBram Moolenaar {
7598b2f1953SBram Moolenaar cur_item->v_type = VAR_SPECIAL;
7608b2f1953SBram Moolenaar cur_item->vval.v_number = VVAL_NONE;
76156ead341SBram Moolenaar }
7628b2f1953SBram Moolenaar retval = OK;
7638b2f1953SBram Moolenaar break;
764520e1e41SBram Moolenaar
765520e1e41SBram Moolenaar default:
766a5d5953dSBram Moolenaar if (VIM_ISDIGIT(*p) || (*p == '-'
767a5d5953dSBram Moolenaar && (VIM_ISDIGIT(p[1]) || p[1] == NUL)))
768520e1e41SBram Moolenaar {
769520e1e41SBram Moolenaar char_u *sp = p;
77056ead341SBram Moolenaar
771520e1e41SBram Moolenaar if (*sp == '-')
77256ead341SBram Moolenaar {
773520e1e41SBram Moolenaar ++sp;
77456ead341SBram Moolenaar if (*sp == NUL)
7758b2f1953SBram Moolenaar {
7768b2f1953SBram Moolenaar retval = MAYBE;
7778b2f1953SBram Moolenaar break;
7788b2f1953SBram Moolenaar }
77956ead341SBram Moolenaar if (!VIM_ISDIGIT(*sp))
78003c60c15SBram Moolenaar {
781a09195f2SBram Moolenaar semsg(_(e_json_error), p);
7828b2f1953SBram Moolenaar retval = FAIL;
7838b2f1953SBram Moolenaar break;
78456ead341SBram Moolenaar }
78503c60c15SBram Moolenaar }
786520e1e41SBram Moolenaar sp = skipdigits(sp);
787a5d5953dSBram Moolenaar #ifdef FEAT_FLOAT
788520e1e41SBram Moolenaar if (*sp == '.' || *sp == 'e' || *sp == 'E')
789520e1e41SBram Moolenaar {
7908b2f1953SBram Moolenaar if (cur_item == NULL)
79156ead341SBram Moolenaar {
79256ead341SBram Moolenaar float_T f;
79356ead341SBram Moolenaar
794*2950065eSBram Moolenaar len = string2float(p, &f, FALSE);
79556ead341SBram Moolenaar }
79656ead341SBram Moolenaar else
79756ead341SBram Moolenaar {
7988b2f1953SBram Moolenaar cur_item->v_type = VAR_FLOAT;
799*2950065eSBram Moolenaar len = string2float(p, &cur_item->vval.v_float,
800*2950065eSBram Moolenaar FALSE);
801520e1e41SBram Moolenaar }
80256ead341SBram Moolenaar }
803520e1e41SBram Moolenaar else
804520e1e41SBram Moolenaar #endif
805520e1e41SBram Moolenaar {
80622fcfad2SBram Moolenaar varnumber_T nr;
807520e1e41SBram Moolenaar
808520e1e41SBram Moolenaar vim_str2nr(reader->js_buf + reader->js_used,
8094ba37b58SBram Moolenaar NULL, &len, 0, // what
81016e9b851SBram Moolenaar &nr, NULL, 0, TRUE);
81116e9b851SBram Moolenaar if (len == 0)
81216e9b851SBram Moolenaar {
813a09195f2SBram Moolenaar semsg(_(e_json_error), p);
81416e9b851SBram Moolenaar retval = FAIL;
81516e9b851SBram Moolenaar goto theend;
81616e9b851SBram Moolenaar }
8178b2f1953SBram Moolenaar if (cur_item != NULL)
81856ead341SBram Moolenaar {
8198b2f1953SBram Moolenaar cur_item->v_type = VAR_NUMBER;
8208b2f1953SBram Moolenaar cur_item->vval.v_number = nr;
821520e1e41SBram Moolenaar }
82256ead341SBram Moolenaar }
823520e1e41SBram Moolenaar reader->js_used += len;
8248b2f1953SBram Moolenaar retval = OK;
8258b2f1953SBram Moolenaar break;
826520e1e41SBram Moolenaar }
827520e1e41SBram Moolenaar if (STRNICMP((char *)p, "false", 5) == 0)
828520e1e41SBram Moolenaar {
829520e1e41SBram Moolenaar reader->js_used += 5;
8308b2f1953SBram Moolenaar if (cur_item != NULL)
83156ead341SBram Moolenaar {
8329b4a15d5SBram Moolenaar cur_item->v_type = VAR_BOOL;
8338b2f1953SBram Moolenaar cur_item->vval.v_number = VVAL_FALSE;
83456ead341SBram Moolenaar }
8358b2f1953SBram Moolenaar retval = OK;
8368b2f1953SBram Moolenaar break;
837520e1e41SBram Moolenaar }
838520e1e41SBram Moolenaar if (STRNICMP((char *)p, "true", 4) == 0)
839520e1e41SBram Moolenaar {
840520e1e41SBram Moolenaar reader->js_used += 4;
8418b2f1953SBram Moolenaar if (cur_item != NULL)
84256ead341SBram Moolenaar {
8439b4a15d5SBram Moolenaar cur_item->v_type = VAR_BOOL;
8448b2f1953SBram Moolenaar cur_item->vval.v_number = VVAL_TRUE;
84556ead341SBram Moolenaar }
8468b2f1953SBram Moolenaar retval = OK;
8478b2f1953SBram Moolenaar break;
848520e1e41SBram Moolenaar }
849520e1e41SBram Moolenaar if (STRNICMP((char *)p, "null", 4) == 0)
850520e1e41SBram Moolenaar {
851520e1e41SBram Moolenaar reader->js_used += 4;
8528b2f1953SBram Moolenaar if (cur_item != NULL)
85356ead341SBram Moolenaar {
8548b2f1953SBram Moolenaar cur_item->v_type = VAR_SPECIAL;
8558b2f1953SBram Moolenaar cur_item->vval.v_number = VVAL_NULL;
856520e1e41SBram Moolenaar }
8578b2f1953SBram Moolenaar retval = OK;
8588b2f1953SBram Moolenaar break;
85956ead341SBram Moolenaar }
860f1b6ac72SBram Moolenaar #ifdef FEAT_FLOAT
861f1b6ac72SBram Moolenaar if (STRNICMP((char *)p, "NaN", 3) == 0)
862f1b6ac72SBram Moolenaar {
863f1b6ac72SBram Moolenaar reader->js_used += 3;
8648b2f1953SBram Moolenaar if (cur_item != NULL)
865f1b6ac72SBram Moolenaar {
8668b2f1953SBram Moolenaar cur_item->v_type = VAR_FLOAT;
8678b2f1953SBram Moolenaar cur_item->vval.v_float = NAN;
868f1b6ac72SBram Moolenaar }
8698b2f1953SBram Moolenaar retval = OK;
8708b2f1953SBram Moolenaar break;
871f1b6ac72SBram Moolenaar }
8725f6b379fSBram Moolenaar if (STRNICMP((char *)p, "-Infinity", 9) == 0)
8735f6b379fSBram Moolenaar {
8745f6b379fSBram Moolenaar reader->js_used += 9;
8755f6b379fSBram Moolenaar if (cur_item != NULL)
8765f6b379fSBram Moolenaar {
8775f6b379fSBram Moolenaar cur_item->v_type = VAR_FLOAT;
8785f6b379fSBram Moolenaar cur_item->vval.v_float = -INFINITY;
8795f6b379fSBram Moolenaar }
8805f6b379fSBram Moolenaar retval = OK;
8815f6b379fSBram Moolenaar break;
8825f6b379fSBram Moolenaar }
883f1b6ac72SBram Moolenaar if (STRNICMP((char *)p, "Infinity", 8) == 0)
884f1b6ac72SBram Moolenaar {
885f1b6ac72SBram Moolenaar reader->js_used += 8;
8868b2f1953SBram Moolenaar if (cur_item != NULL)
887f1b6ac72SBram Moolenaar {
8888b2f1953SBram Moolenaar cur_item->v_type = VAR_FLOAT;
8898b2f1953SBram Moolenaar cur_item->vval.v_float = INFINITY;
890f1b6ac72SBram Moolenaar }
8918b2f1953SBram Moolenaar retval = OK;
8928b2f1953SBram Moolenaar break;
893f1b6ac72SBram Moolenaar }
894f1b6ac72SBram Moolenaar #endif
8954ba37b58SBram Moolenaar // check for truncated name
896a5d5953dSBram Moolenaar len = (int)(reader->js_end
897a5d5953dSBram Moolenaar - (reader->js_buf + reader->js_used));
898f1b6ac72SBram Moolenaar if (
899f1b6ac72SBram Moolenaar (len < 5 && STRNICMP((char *)p, "false", len) == 0)
900f1b6ac72SBram Moolenaar #ifdef FEAT_FLOAT
9015f6b379fSBram Moolenaar || (len < 9 && STRNICMP((char *)p, "-Infinity", len) == 0)
902f1b6ac72SBram Moolenaar || (len < 8 && STRNICMP((char *)p, "Infinity", len) == 0)
903f1b6ac72SBram Moolenaar || (len < 3 && STRNICMP((char *)p, "NaN", len) == 0)
904f1b6ac72SBram Moolenaar #endif
90556ead341SBram Moolenaar || (len < 4 && (STRNICMP((char *)p, "true", len) == 0
90656ead341SBram Moolenaar || STRNICMP((char *)p, "null", len) == 0)))
9078b2f1953SBram Moolenaar
9088b2f1953SBram Moolenaar retval = MAYBE;
9098b2f1953SBram Moolenaar else
9108b2f1953SBram Moolenaar retval = FAIL;
911520e1e41SBram Moolenaar break;
912520e1e41SBram Moolenaar }
913520e1e41SBram Moolenaar
9144ba37b58SBram Moolenaar // We are finished when retval is FAIL or MAYBE and when at the
9154ba37b58SBram Moolenaar // toplevel.
9168b2f1953SBram Moolenaar if (retval == FAIL)
9178b2f1953SBram Moolenaar break;
9188b2f1953SBram Moolenaar if (retval == MAYBE || stack.ga_len == 0)
9198b2f1953SBram Moolenaar goto theend;
9208b2f1953SBram Moolenaar
9218b2f1953SBram Moolenaar if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY
9228b2f1953SBram Moolenaar && cur_item != NULL)
9238b2f1953SBram Moolenaar {
9243cfa5b16SBram Moolenaar #ifdef FEAT_FLOAT
9253cfa5b16SBram Moolenaar if (cur_item->v_type == VAR_FLOAT)
9263cfa5b16SBram Moolenaar {
9273cfa5b16SBram Moolenaar // cannot use a float as a key
9283cfa5b16SBram Moolenaar emsg(_(e_float_as_string));
9293cfa5b16SBram Moolenaar retval = FAIL;
9303cfa5b16SBram Moolenaar goto theend;
9313cfa5b16SBram Moolenaar }
9323cfa5b16SBram Moolenaar #endif
933d155d7a8SBram Moolenaar top_item->jd_key = tv_get_string_buf_chk(cur_item, key_buf);
934059b7482SBram Moolenaar if (top_item->jd_key == NULL)
9358b2f1953SBram Moolenaar {
936f9e3e09fSBram Moolenaar emsg(_(e_invarg));
9378b2f1953SBram Moolenaar retval = FAIL;
9388b2f1953SBram Moolenaar goto theend;
9398b2f1953SBram Moolenaar }
9408b2f1953SBram Moolenaar }
9418b2f1953SBram Moolenaar }
9428b2f1953SBram Moolenaar
9438b2f1953SBram Moolenaar item_end:
9448b2f1953SBram Moolenaar top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len - 1;
9458b2f1953SBram Moolenaar switch (top_item->jd_type)
9468b2f1953SBram Moolenaar {
9478b2f1953SBram Moolenaar case JSON_ARRAY:
9487756e746SBram Moolenaar if (res != NULL)
94956ead341SBram Moolenaar {
9508b2f1953SBram Moolenaar listitem_T *li = listitem_alloc();
9518b2f1953SBram Moolenaar
9528b2f1953SBram Moolenaar if (li == NULL)
9538b2f1953SBram Moolenaar {
9548b2f1953SBram Moolenaar clear_tv(cur_item);
9558b2f1953SBram Moolenaar retval = FAIL;
9568b2f1953SBram Moolenaar goto theend;
9578b2f1953SBram Moolenaar }
9588b2f1953SBram Moolenaar li->li_tv = *cur_item;
9598b2f1953SBram Moolenaar list_append(top_item->jd_tv.vval.v_list, li);
9608b2f1953SBram Moolenaar }
9618b2f1953SBram Moolenaar if (cur_item != NULL)
9628b2f1953SBram Moolenaar cur_item = &item;
9638b2f1953SBram Moolenaar
9648b2f1953SBram Moolenaar json_skip_white(reader);
9658b2f1953SBram Moolenaar p = reader->js_buf + reader->js_used;
9668b2f1953SBram Moolenaar if (*p == ',')
9678b2f1953SBram Moolenaar ++reader->js_used;
9688b2f1953SBram Moolenaar else if (*p != ']')
9698b2f1953SBram Moolenaar {
9708b2f1953SBram Moolenaar if (*p == NUL)
9718b2f1953SBram Moolenaar retval = MAYBE;
9728b2f1953SBram Moolenaar else
9738b2f1953SBram Moolenaar {
974a09195f2SBram Moolenaar semsg(_(e_json_error), p);
9758b2f1953SBram Moolenaar retval = FAIL;
9768b2f1953SBram Moolenaar }
9778b2f1953SBram Moolenaar goto theend;
9788b2f1953SBram Moolenaar }
9798b2f1953SBram Moolenaar break;
9808b2f1953SBram Moolenaar
9818b2f1953SBram Moolenaar case JSON_OBJECT_KEY:
9828b2f1953SBram Moolenaar json_skip_white(reader);
9838b2f1953SBram Moolenaar p = reader->js_buf + reader->js_used;
9848b2f1953SBram Moolenaar if (*p != ':')
9858b2f1953SBram Moolenaar {
9868b2f1953SBram Moolenaar if (cur_item != NULL)
9878b2f1953SBram Moolenaar clear_tv(cur_item);
9888b2f1953SBram Moolenaar if (*p == NUL)
9898b2f1953SBram Moolenaar retval = MAYBE;
9908b2f1953SBram Moolenaar else
9918b2f1953SBram Moolenaar {
992a09195f2SBram Moolenaar semsg(_(e_json_error), p);
9938b2f1953SBram Moolenaar retval = FAIL;
9948b2f1953SBram Moolenaar }
9958b2f1953SBram Moolenaar goto theend;
9968b2f1953SBram Moolenaar }
9978b2f1953SBram Moolenaar ++reader->js_used;
9988b2f1953SBram Moolenaar json_skip_white(reader);
9998b2f1953SBram Moolenaar top_item->jd_type = JSON_OBJECT;
10008b2f1953SBram Moolenaar if (cur_item != NULL)
10018b2f1953SBram Moolenaar cur_item = &item;
10028b2f1953SBram Moolenaar break;
10038b2f1953SBram Moolenaar
10048b2f1953SBram Moolenaar case JSON_OBJECT:
10058b2f1953SBram Moolenaar if (cur_item != NULL
10068b2f1953SBram Moolenaar && dict_find(top_item->jd_tv.vval.v_dict,
10078b2f1953SBram Moolenaar top_item->jd_key, -1) != NULL)
10088b2f1953SBram Moolenaar {
1009f9e3e09fSBram Moolenaar semsg(_("E938: Duplicate key in JSON: \"%s\""),
10108b2f1953SBram Moolenaar top_item->jd_key);
10118b2f1953SBram Moolenaar clear_tv(cur_item);
10128b2f1953SBram Moolenaar retval = FAIL;
10138b2f1953SBram Moolenaar goto theend;
10148b2f1953SBram Moolenaar }
10158b2f1953SBram Moolenaar
10168b2f1953SBram Moolenaar if (cur_item != NULL)
10178b2f1953SBram Moolenaar {
10188b2f1953SBram Moolenaar dictitem_T *di = dictitem_alloc(top_item->jd_key);
10198b2f1953SBram Moolenaar
10208b2f1953SBram Moolenaar clear_tv(&top_item->jd_key_tv);
10218b2f1953SBram Moolenaar if (di == NULL)
10228b2f1953SBram Moolenaar {
10238b2f1953SBram Moolenaar clear_tv(cur_item);
10248b2f1953SBram Moolenaar retval = FAIL;
10258b2f1953SBram Moolenaar goto theend;
10268b2f1953SBram Moolenaar }
10278b2f1953SBram Moolenaar di->di_tv = *cur_item;
10288b2f1953SBram Moolenaar di->di_tv.v_lock = 0;
10298b2f1953SBram Moolenaar if (dict_add(top_item->jd_tv.vval.v_dict, di) == FAIL)
10308b2f1953SBram Moolenaar {
10318b2f1953SBram Moolenaar dictitem_free(di);
10328b2f1953SBram Moolenaar retval = FAIL;
10338b2f1953SBram Moolenaar goto theend;
10348b2f1953SBram Moolenaar }
10358b2f1953SBram Moolenaar }
10368b2f1953SBram Moolenaar
10378b2f1953SBram Moolenaar json_skip_white(reader);
10388b2f1953SBram Moolenaar p = reader->js_buf + reader->js_used;
10398b2f1953SBram Moolenaar if (*p == ',')
10408b2f1953SBram Moolenaar ++reader->js_used;
10418b2f1953SBram Moolenaar else if (*p != '}')
10428b2f1953SBram Moolenaar {
10438b2f1953SBram Moolenaar if (*p == NUL)
10448b2f1953SBram Moolenaar retval = MAYBE;
10458b2f1953SBram Moolenaar else
10468b2f1953SBram Moolenaar {
1047a09195f2SBram Moolenaar semsg(_(e_json_error), p);
10488b2f1953SBram Moolenaar retval = FAIL;
10498b2f1953SBram Moolenaar }
10508b2f1953SBram Moolenaar goto theend;
10518b2f1953SBram Moolenaar }
10528b2f1953SBram Moolenaar top_item->jd_type = JSON_OBJECT_KEY;
10538b2f1953SBram Moolenaar if (cur_item != NULL)
10548b2f1953SBram Moolenaar cur_item = &top_item->jd_key_tv;
10558b2f1953SBram Moolenaar break;
10568b2f1953SBram Moolenaar }
10578b2f1953SBram Moolenaar }
10588b2f1953SBram Moolenaar
10594ba37b58SBram Moolenaar // Get here when parsing failed.
10608b2f1953SBram Moolenaar if (res != NULL)
10618b2f1953SBram Moolenaar {
10628b2f1953SBram Moolenaar clear_tv(res);
1063520e1e41SBram Moolenaar res->v_type = VAR_SPECIAL;
1064520e1e41SBram Moolenaar res->vval.v_number = VVAL_NONE;
1065520e1e41SBram Moolenaar }
1066a09195f2SBram Moolenaar semsg(_(e_json_error), p);
10678b2f1953SBram Moolenaar
10688b2f1953SBram Moolenaar theend:
10696d3a7213SBram Moolenaar for (i = 0; i < stack.ga_len; i++)
10706d3a7213SBram Moolenaar clear_tv(&(((json_dec_item_T *)stack.ga_data) + i)->jd_key_tv);
10718b2f1953SBram Moolenaar ga_clear(&stack);
10726d3a7213SBram Moolenaar
10738b2f1953SBram Moolenaar return retval;
107456ead341SBram Moolenaar }
1075520e1e41SBram Moolenaar
1076520e1e41SBram Moolenaar /*
1077520e1e41SBram Moolenaar * Decode the JSON from "reader" and store the result in "res".
1078595e64e2SBram Moolenaar * "options" can be JSON_JS or zero;
107956ead341SBram Moolenaar * Return FAIL if not the whole message was consumed.
1080520e1e41SBram Moolenaar */
10815843f5f3SBram Moolenaar static int
json_decode_all(js_read_T * reader,typval_T * res,int options)1082595e64e2SBram Moolenaar json_decode_all(js_read_T *reader, typval_T *res, int options)
1083520e1e41SBram Moolenaar {
108456ead341SBram Moolenaar int ret;
108556ead341SBram Moolenaar
10864ba37b58SBram Moolenaar // We find the end once, to avoid calling strlen() many times.
108756ead341SBram Moolenaar reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
1088520e1e41SBram Moolenaar json_skip_white(reader);
1089595e64e2SBram Moolenaar ret = json_decode_item(reader, res, options);
109056ead341SBram Moolenaar if (ret != OK)
109103c60c15SBram Moolenaar {
109203c60c15SBram Moolenaar if (ret == MAYBE)
1093a09195f2SBram Moolenaar semsg(_(e_json_error), reader->js_buf);
109456ead341SBram Moolenaar return FAIL;
109503c60c15SBram Moolenaar }
1096520e1e41SBram Moolenaar json_skip_white(reader);
1097520e1e41SBram Moolenaar if (reader->js_buf[reader->js_used] != NUL)
109803c60c15SBram Moolenaar {
10992d06bfdeSBram Moolenaar semsg(_(e_trailing_arg), reader->js_buf + reader->js_used);
110019d2f158SBram Moolenaar return FAIL;
110103c60c15SBram Moolenaar }
110219d2f158SBram Moolenaar return OK;
1103520e1e41SBram Moolenaar }
110456ead341SBram Moolenaar
1105113e1072SBram Moolenaar #if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
110656ead341SBram Moolenaar /*
110756ead341SBram Moolenaar * Decode the JSON from "reader" and store the result in "res".
1108595e64e2SBram Moolenaar * "options" can be JSON_JS or zero;
1109ba61ac0dSBram Moolenaar * Return FAIL for a decoding error.
1110ba61ac0dSBram Moolenaar * Return MAYBE for an incomplete message.
1111ba61ac0dSBram Moolenaar * Consumes the message anyway.
111256ead341SBram Moolenaar */
111356ead341SBram Moolenaar int
json_decode(js_read_T * reader,typval_T * res,int options)1114595e64e2SBram Moolenaar json_decode(js_read_T *reader, typval_T *res, int options)
111556ead341SBram Moolenaar {
111656ead341SBram Moolenaar int ret;
111756ead341SBram Moolenaar
11184ba37b58SBram Moolenaar // We find the end once, to avoid calling strlen() many times.
111956ead341SBram Moolenaar reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
112056ead341SBram Moolenaar json_skip_white(reader);
1121595e64e2SBram Moolenaar ret = json_decode_item(reader, res, options);
112256ead341SBram Moolenaar json_skip_white(reader);
112356ead341SBram Moolenaar
1124ba61ac0dSBram Moolenaar return ret;
112556ead341SBram Moolenaar }
1126113e1072SBram Moolenaar #endif
112756ead341SBram Moolenaar
112856ead341SBram Moolenaar /*
112956ead341SBram Moolenaar * Decode the JSON from "reader" to find the end of the message.
1130e2c6037dSBram Moolenaar * "options" can be JSON_JS or zero.
1131e2c6037dSBram Moolenaar * This is only used for testing.
113256ead341SBram Moolenaar * Return FAIL if the message has a decoding error.
113356ead341SBram Moolenaar * Return MAYBE if the message is truncated, need to read more.
113456ead341SBram Moolenaar * This only works reliable if the message contains an object, array or
11355f6b379fSBram Moolenaar * string. A number might be truncated without knowing.
113656ead341SBram Moolenaar * Does not advance the reader.
113756ead341SBram Moolenaar */
113856ead341SBram Moolenaar int
json_find_end(js_read_T * reader,int options)1139595e64e2SBram Moolenaar json_find_end(js_read_T *reader, int options)
114056ead341SBram Moolenaar {
114156ead341SBram Moolenaar int used_save = reader->js_used;
114256ead341SBram Moolenaar int ret;
114356ead341SBram Moolenaar
11444ba37b58SBram Moolenaar // We find the end once, to avoid calling strlen() many times.
114556ead341SBram Moolenaar reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
114656ead341SBram Moolenaar json_skip_white(reader);
1147595e64e2SBram Moolenaar ret = json_decode_item(reader, NULL, options);
114856ead341SBram Moolenaar reader->js_used = used_save;
114956ead341SBram Moolenaar return ret;
115056ead341SBram Moolenaar }
115129b7d7a9SBram Moolenaar
115229b7d7a9SBram Moolenaar /*
115329b7d7a9SBram Moolenaar * "js_decode()" function
115429b7d7a9SBram Moolenaar */
115529b7d7a9SBram Moolenaar void
f_js_decode(typval_T * argvars,typval_T * rettv)115629b7d7a9SBram Moolenaar f_js_decode(typval_T *argvars, typval_T *rettv)
115729b7d7a9SBram Moolenaar {
115829b7d7a9SBram Moolenaar js_read_T reader;
115929b7d7a9SBram Moolenaar
11604490ec4eSYegappan Lakshmanan if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
11614490ec4eSYegappan Lakshmanan return;
11624490ec4eSYegappan Lakshmanan
116329b7d7a9SBram Moolenaar reader.js_buf = tv_get_string(&argvars[0]);
116429b7d7a9SBram Moolenaar reader.js_fill = NULL;
116529b7d7a9SBram Moolenaar reader.js_used = 0;
116629b7d7a9SBram Moolenaar if (json_decode_all(&reader, rettv, JSON_JS) != OK)
116729b7d7a9SBram Moolenaar emsg(_(e_invarg));
116829b7d7a9SBram Moolenaar }
116929b7d7a9SBram Moolenaar
117029b7d7a9SBram Moolenaar /*
117129b7d7a9SBram Moolenaar * "js_encode()" function
117229b7d7a9SBram Moolenaar */
117329b7d7a9SBram Moolenaar void
f_js_encode(typval_T * argvars,typval_T * rettv)117429b7d7a9SBram Moolenaar f_js_encode(typval_T *argvars, typval_T *rettv)
117529b7d7a9SBram Moolenaar {
117629b7d7a9SBram Moolenaar rettv->v_type = VAR_STRING;
117729b7d7a9SBram Moolenaar rettv->vval.v_string = json_encode(&argvars[0], JSON_JS);
117829b7d7a9SBram Moolenaar }
117929b7d7a9SBram Moolenaar
118029b7d7a9SBram Moolenaar /*
118129b7d7a9SBram Moolenaar * "json_decode()" function
118229b7d7a9SBram Moolenaar */
118329b7d7a9SBram Moolenaar void
f_json_decode(typval_T * argvars,typval_T * rettv)118429b7d7a9SBram Moolenaar f_json_decode(typval_T *argvars, typval_T *rettv)
118529b7d7a9SBram Moolenaar {
118629b7d7a9SBram Moolenaar js_read_T reader;
118729b7d7a9SBram Moolenaar
11884490ec4eSYegappan Lakshmanan if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
11894490ec4eSYegappan Lakshmanan return;
11904490ec4eSYegappan Lakshmanan
119129b7d7a9SBram Moolenaar reader.js_buf = tv_get_string(&argvars[0]);
119229b7d7a9SBram Moolenaar reader.js_fill = NULL;
119329b7d7a9SBram Moolenaar reader.js_used = 0;
119429b7d7a9SBram Moolenaar json_decode_all(&reader, rettv, 0);
119529b7d7a9SBram Moolenaar }
119629b7d7a9SBram Moolenaar
119729b7d7a9SBram Moolenaar /*
119829b7d7a9SBram Moolenaar * "json_encode()" function
119929b7d7a9SBram Moolenaar */
120029b7d7a9SBram Moolenaar void
f_json_encode(typval_T * argvars,typval_T * rettv)120129b7d7a9SBram Moolenaar f_json_encode(typval_T *argvars, typval_T *rettv)
120229b7d7a9SBram Moolenaar {
120329b7d7a9SBram Moolenaar rettv->v_type = VAR_STRING;
120429b7d7a9SBram Moolenaar rettv->vval.v_string = json_encode(&argvars[0], 0);
120529b7d7a9SBram Moolenaar }
1206c61a48d2SBram Moolenaar #endif
1207