blob: 6c4cbc4305b55ff1adad49f0bcb0be9817e8cb4e [file] [log] [blame]
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9script.c: :vim9script, :import, :export and friends
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
18#include "vim9.h"
19
Bram Moolenaar9721fb42020-06-11 23:10:46 +020020static char e_needs_vim9[] = N_("E1042: export can only be used in vim9script");
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010021
22 int
23in_vim9script(void)
24{
25 // TODO: go up the stack?
26 return current_sctx.sc_version == SCRIPT_VERSION_VIM9;
27}
28
29/*
30 * ":vim9script".
31 */
32 void
33ex_vim9script(exarg_T *eap)
34{
Bram Moolenaar101f4812020-06-16 23:18:51 +020035 scriptitem_T *si;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010036
37 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
38 {
39 emsg(_("E1038: vim9script can only be used in a script"));
40 return;
41 }
Bram Moolenaar101f4812020-06-16 23:18:51 +020042 si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010043 if (si->sn_had_command)
44 {
45 emsg(_("E1039: vim9script must be the first command in a script"));
46 return;
47 }
48 current_sctx.sc_version = SCRIPT_VERSION_VIM9;
49 si->sn_version = SCRIPT_VERSION_VIM9;
50 si->sn_had_command = TRUE;
51
52 if (STRCMP(p_cpo, CPO_VIM) != 0)
53 {
54 si->sn_save_cpo = p_cpo;
55 p_cpo = vim_strsave((char_u *)CPO_VIM);
56 }
57}
58
59/*
60 * ":export let Name: type"
61 * ":export const Name: type"
62 * ":export def Name(..."
63 * ":export class Name ..."
64 *
65 * ":export {Name, ...}"
66 */
67 void
Bram Moolenaar09689a02020-05-09 22:50:08 +020068ex_export(exarg_T *eap)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010069{
70 if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
71 {
72 emsg(_(e_needs_vim9));
73 return;
74 }
75
76 eap->cmd = eap->arg;
77 (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL);
78 switch (eap->cmdidx)
79 {
80 case CMD_let:
81 case CMD_const:
82 case CMD_def:
83 // case CMD_class:
84 is_export = TRUE;
85 do_cmdline(eap->cmd, eap->getline, eap->cookie,
86 DOCMD_VERBOSE + DOCMD_NOWAIT);
87
88 // The command will reset "is_export" when exporting an item.
89 if (is_export)
90 {
91 emsg(_("E1044: export with invalid argument"));
92 is_export = FALSE;
93 }
94 break;
95 default:
96 emsg(_("E1043: Invalid command after :export"));
97 break;
98 }
99}
100
101/*
102 * Add a new imported item entry to the current script.
103 */
104 static imported_T *
105new_imported(garray_T *gap)
106{
107 if (ga_grow(gap, 1) == OK)
108 return ((imported_T *)gap->ga_data + gap->ga_len++);
109 return NULL;
110}
111
112/*
113 * Free all imported items in script "sid".
114 */
115 void
116free_imports(int sid)
117{
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100118 scriptitem_T *si = SCRIPT_ITEM(sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100119 int idx;
120
121 for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
122 {
Bram Moolenaar20431c92020-03-20 18:39:46 +0100123 imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100124
125 vim_free(imp->imp_name);
126 }
127 ga_clear(&si->sn_imports);
Bram Moolenaar20431c92020-03-20 18:39:46 +0100128 ga_clear(&si->sn_var_vals);
129 ga_clear(&si->sn_type_list);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100130}
131
132/*
133 * ":import Item from 'filename'"
134 * ":import Item as Alias from 'filename'"
135 * ":import {Item} from 'filename'".
136 * ":import {Item as Alias} from 'filename'"
137 * ":import {Item, Item} from 'filename'"
138 * ":import {Item, Item as Alias} from 'filename'"
139 *
140 * ":import * as Name from 'filename'"
141 */
142 void
143ex_import(exarg_T *eap)
144{
Bram Moolenaar101f4812020-06-16 23:18:51 +0200145 char_u *cmd_end;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100146
Bram Moolenaar101f4812020-06-16 23:18:51 +0200147 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
148 {
149 emsg(_("E1094: import can only be used in a script"));
150 return;
151 }
152
153 cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL);
Bram Moolenaar9721fb42020-06-11 23:10:46 +0200154 if (cmd_end != NULL)
155 eap->nextcmd = check_nextcmd(cmd_end);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100156}
157
158/*
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100159 * Find an exported item in "sid" matching the name at "*argp".
160 * When it is a variable return the index.
161 * When it is a user function return "*ufunc".
162 * When not found returns -1 and "*ufunc" is NULL.
163 */
164 int
165find_exported(
166 int sid,
167 char_u **argp,
168 int *name_len,
169 ufunc_T **ufunc,
170 type_T **type)
171{
172 char_u *name = *argp;
173 char_u *arg = *argp;
174 int cc;
175 int idx = -1;
176 svar_T *sv;
177 scriptitem_T *script = SCRIPT_ITEM(sid);
178
179 // isolate one name
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100180 while (eval_isnamec(*arg))
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100181 ++arg;
182 *name_len = (int)(arg - name);
183
184 // find name in "script"
185 // TODO: also find script-local user function
186 cc = *arg;
187 *arg = NUL;
188 idx = get_script_item_idx(sid, name, FALSE);
189 if (idx >= 0)
190 {
191 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
192 if (!sv->sv_export)
193 {
194 semsg(_("E1049: Item not exported in script: %s"), name);
195 *arg = cc;
196 return -1;
197 }
198 *type = sv->sv_type;
199 *ufunc = NULL;
200 }
201 else
202 {
203 char_u buffer[200];
204 char_u *funcname;
205
206 // it could be a user function.
207 if (STRLEN(name) < sizeof(buffer) - 10)
208 funcname = buffer;
209 else
210 {
211 funcname = alloc(STRLEN(name) + 10);
212 if (funcname == NULL)
213 {
214 *arg = cc;
215 return -1;
216 }
217 }
218 funcname[0] = K_SPECIAL;
219 funcname[1] = KS_EXTRA;
220 funcname[2] = (int)KE_SNR;
221 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
Bram Moolenaar4c17ad92020-04-27 22:47:51 +0200222 *ufunc = find_func(funcname, FALSE, NULL);
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100223 if (funcname != buffer)
224 vim_free(funcname);
225
226 if (*ufunc == NULL)
227 {
228 semsg(_("E1048: Item not found in script: %s"), name);
229 *arg = cc;
230 return -1;
231 }
232 }
233 *arg = cc;
234 arg = skipwhite(arg);
235 *argp = arg;
236
237 return idx;
238}
239
240/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100241 * Handle an ":import" command and add the resulting imported_T to "gap", when
242 * not NULL, or script "import_sid" sn_imports.
243 * Returns a pointer to after the command or NULL in case of failure
244 */
245 char_u *
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100246handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100247{
248 char_u *arg = arg_start;
249 char_u *cmd_end;
250 char_u *as_ptr = NULL;
251 char_u *from_ptr;
252 int as_len = 0;
253 int ret = FAIL;
254 typval_T tv;
255 int sid = -1;
256 int res;
257
258 if (*arg == '{')
259 {
260 // skip over {item} list
261 while (*arg != NUL && *arg != '}')
262 ++arg;
263 if (*arg == '}')
264 arg = skipwhite(arg + 1);
265 }
266 else
267 {
268 if (*arg == '*')
269 arg = skipwhite(arg + 1);
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100270 else if (eval_isnamec1(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100271 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100272 while (eval_isnamec(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100273 ++arg;
274 arg = skipwhite(arg);
275 }
276 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
277 {
278 // skip over "as Name "
279 arg = skipwhite(arg + 2);
280 as_ptr = arg;
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100281 if (eval_isnamec1(*arg))
282 while (eval_isnamec(*arg))
283 ++arg;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100284 as_len = (int)(arg - as_ptr);
285 arg = skipwhite(arg);
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100286 if (check_defined(as_ptr, as_len, cctx) == FAIL)
287 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100288 }
289 else if (*arg_start == '*')
290 {
291 emsg(_("E1045: Missing \"as\" after *"));
292 return NULL;
293 }
294 }
295 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
296 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100297 emsg(_("E1070: Missing \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100298 return NULL;
299 }
300 from_ptr = arg;
301 arg = skipwhite(arg + 4);
302 tv.v_type = VAR_UNKNOWN;
303 // TODO: should we accept any expression?
304 if (*arg == '\'')
305 ret = get_lit_string_tv(&arg, &tv, TRUE);
306 else if (*arg == '"')
307 ret = get_string_tv(&arg, &tv, TRUE);
308 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
309 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100310 emsg(_("E1071: Invalid string after \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100311 return NULL;
312 }
313 cmd_end = arg;
314
315 // find script tv.vval.v_string
316 if (*tv.vval.v_string == '.')
317 {
318 size_t len;
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100319 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100320 char_u *tail = gettail(si->sn_name);
321 char_u *from_name;
322
323 // Relative to current script: "./name.vim", "../../name.vim".
324 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
325 from_name = alloc((int)len);
326 if (from_name == NULL)
327 {
328 clear_tv(&tv);
329 return NULL;
330 }
331 vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
332 add_pathsep(from_name);
333 STRCAT(from_name, tv.vval.v_string);
334 simplify_filename(from_name);
335
336 res = do_source(from_name, FALSE, DOSO_NONE, &sid);
337 vim_free(from_name);
338 }
339 else if (mch_isFullName(tv.vval.v_string))
340 {
341 // Absolute path: "/tmp/name.vim"
342 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
343 }
344 else
345 {
346 size_t len = 7 + STRLEN(tv.vval.v_string) + 1;
347 char_u *from_name;
348
349 // Find file in "import" subdirs in 'runtimepath'.
350 from_name = alloc((int)len);
351 if (from_name == NULL)
352 {
353 clear_tv(&tv);
354 return NULL;
355 }
356 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
357 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
358 vim_free(from_name);
359 }
360
361 if (res == FAIL || sid <= 0)
362 {
363 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
364 clear_tv(&tv);
365 return NULL;
366 }
367 clear_tv(&tv);
368
369 if (*arg_start == '*')
370 {
371 imported_T *imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100372 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100373
374 if (imported == NULL)
375 return NULL;
376 imported->imp_name = vim_strnsave(as_ptr, as_len);
377 imported->imp_sid = sid;
378 imported->imp_all = TRUE;
379 }
380 else
381 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100382 arg = arg_start;
383 if (*arg == '{')
384 arg = skipwhite(arg + 1);
385 for (;;)
386 {
387 char_u *name = arg;
388 int name_len;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100389 int idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100390 imported_T *imported;
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100391 ufunc_T *ufunc = NULL;
392 type_T *type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100393
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100394 idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100395
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100396 if (idx < 0 && ufunc == NULL)
397 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100398
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100399 if (check_defined(name, name_len, cctx) == FAIL)
400 return NULL;
401
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100402 imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100403 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100404 if (imported == NULL)
405 return NULL;
406
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100407 // TODO: check for "as" following
408 // imported->imp_name = vim_strnsave(as_ptr, as_len);
409 imported->imp_name = vim_strnsave(name, name_len);
410 imported->imp_sid = sid;
411 if (idx >= 0)
412 {
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100413 imported->imp_type = type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100414 imported->imp_var_vals_idx = idx;
415 }
416 else
417 imported->imp_funcname = ufunc->uf_name;
418
419 arg = skipwhite(arg);
420 if (*arg_start != '{')
421 break;
422 if (*arg == '}')
423 {
424 arg = skipwhite(arg + 1);
425 break;
426 }
427
428 if (*arg != ',')
429 {
430 emsg(_("E1046: Missing comma in import"));
431 return NULL;
432 }
433 arg = skipwhite(arg + 1);
434 }
435 if (arg != from_ptr)
436 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100437 // cannot happen, just in case the above has a flaw
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100438 emsg(_("E1047: syntax error in import"));
439 return NULL;
440 }
441 }
442 return cmd_end;
443}
444
Bram Moolenaarc82a5b52020-06-13 18:09:19 +0200445/*
446 * Declare a script-local variable without init: "let var: type".
447 * "const" is an error since the value is missing.
448 * Returns a pointer to after the type.
449 */
450 char_u *
451vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
452{
453 char_u *p;
454 char_u *name;
455 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
456 type_T *type;
457 int called_emsg_before = called_emsg;
458 typval_T init_tv;
459
460 if (eap->cmdidx == CMD_const)
461 {
462 emsg(_(e_const_req_value));
463 return arg + STRLEN(arg);
464 }
465
466 // Check for valid starting character.
467 if (!eval_isnamec1(*arg))
468 {
469 semsg(_(e_invarg2), arg);
470 return arg + STRLEN(arg);
471 }
472
Bram Moolenaar984dddb2020-06-14 12:50:24 +0200473 for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p))
474 if (*p == ':' && p != arg + 1)
475 break;
Bram Moolenaarc82a5b52020-06-13 18:09:19 +0200476
477 if (*p != ':')
478 {
479 emsg(_(e_type_req));
480 return arg + STRLEN(arg);
481 }
Bram Moolenaar984dddb2020-06-14 12:50:24 +0200482 if (!VIM_ISWHITE(p[1]))
483 {
484 semsg(_(e_white_after), ":");
485 return arg + STRLEN(arg);
486 }
Bram Moolenaarc82a5b52020-06-13 18:09:19 +0200487 name = vim_strnsave(arg, p - arg);
488
489 // parse type
490 p = skipwhite(p + 1);
491 type = parse_type(&p, &si->sn_type_list);
492 if (called_emsg != called_emsg_before)
Bram Moolenaarf3decc52020-06-13 19:56:38 +0200493 {
494 vim_free(name);
Bram Moolenaarc82a5b52020-06-13 18:09:19 +0200495 return p;
Bram Moolenaarf3decc52020-06-13 19:56:38 +0200496 }
Bram Moolenaarc82a5b52020-06-13 18:09:19 +0200497
498 // Create the variable with 0/NULL value.
499 CLEAR_FIELD(init_tv);
500 init_tv.v_type = type->tt_type;
501 set_var_const(name, type, &init_tv, FALSE, 0);
502
503 vim_free(name);
504 return p;
505}
506
Bram Moolenaar34db91f2020-06-13 19:00:10 +0200507/*
508 * Check if the type of script variable "dest" allows assigning "value".
509 */
Bram Moolenaarc785b9a2020-06-19 18:34:15 +0200510 int
Bram Moolenaar34db91f2020-06-13 19:00:10 +0200511check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
512{
513 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
514 int idx;
515
516 // Find the svar_T in sn_var_vals.
517 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
518 {
519 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
520
521 if (sv->sv_tv == dest)
522 {
523 if (sv->sv_const)
Bram Moolenaarc785b9a2020-06-19 18:34:15 +0200524 {
Bram Moolenaar34db91f2020-06-13 19:00:10 +0200525 semsg(_(e_readonlyvar), name);
Bram Moolenaarc785b9a2020-06-19 18:34:15 +0200526 return FAIL;
527 }
528 return check_type(sv->sv_type, typval2type(value), TRUE);
Bram Moolenaar34db91f2020-06-13 19:00:10 +0200529 }
530 }
531 iemsg("check_script_var_type(): not found");
Bram Moolenaarc785b9a2020-06-19 18:34:15 +0200532 return OK; // not really
Bram Moolenaar34db91f2020-06-13 19:00:10 +0200533}
Bram Moolenaarc82a5b52020-06-13 18:09:19 +0200534
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100535#endif // FEAT_EVAL