blob: 8bc1962bfd49d43935c5c33bc163da412b79c331 [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
20static char e_needs_vim9[] = N_("E1042: import/export can only be used in vim9script");
21
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 Moolenaar09689a02020-05-09 22:50:08 +020035 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
36 garray_T *gap;
37 garray_T func_ga;
38 int idx;
39 ufunc_T *ufunc;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010040
41 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
42 {
43 emsg(_("E1038: vim9script can only be used in a script"));
44 return;
45 }
46 if (si->sn_had_command)
47 {
48 emsg(_("E1039: vim9script must be the first command in a script"));
49 return;
50 }
51 current_sctx.sc_version = SCRIPT_VERSION_VIM9;
52 si->sn_version = SCRIPT_VERSION_VIM9;
53 si->sn_had_command = TRUE;
Bram Moolenaar09689a02020-05-09 22:50:08 +020054 ga_init2(&func_ga, sizeof(ufunc_T *), 20);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010055
56 if (STRCMP(p_cpo, CPO_VIM) != 0)
57 {
58 si->sn_save_cpo = p_cpo;
59 p_cpo = vim_strsave((char_u *)CPO_VIM);
60 }
Bram Moolenaar09689a02020-05-09 22:50:08 +020061
62 // Make a pass through the script to find:
63 // - function declarations
64 // - variable and constant declarations
65 // - imports
66 // The types are recognized, so that they can be used when compiling a
67 // function.
68 gap = source_get_line_ga(eap->cookie);
69 for (;;)
70 {
71 char_u *line;
72 char_u *p;
73
74 if (ga_grow(gap, 1) == FAIL)
75 return;
76 line = eap->getline(':', eap->cookie, 0, TRUE);
77 if (line == NULL)
78 break;
79 ((char_u **)(gap->ga_data))[gap->ga_len++] = line;
80 line = skipwhite(line);
81 p = line;
82 if (checkforcmd(&p, "function", 2) || checkforcmd(&p, "def", 3))
83 {
84 int lnum_start = SOURCING_LNUM - 1;
85
86 // Handle :function and :def by calling def_function().
87 // It will read upto the matching :endded or :endfunction.
88 eap->cmdidx = *line == 'f' ? CMD_function : CMD_def;
89 eap->cmd = line;
90 eap->arg = p;
91 eap->forceit = FALSE;
92 ufunc = def_function(eap, NULL, NULL, FALSE);
93
94 if (ufunc != NULL && *line == 'd' && ga_grow(&func_ga, 1) == OK)
95 {
96 // Add the function to the list of :def functions, so that it
97 // can be referenced by index. It's compiled below.
98 add_def_function(ufunc);
99 ((ufunc_T **)(func_ga.ga_data))[func_ga.ga_len++] = ufunc;
100 }
101
102 // Store empty lines in place of the function, we don't need to
103 // process it again.
104 vim_free(((char_u **)(gap->ga_data))[--gap->ga_len]);
105 if (ga_grow(gap, SOURCING_LNUM - lnum_start) == OK)
106 while (lnum_start < SOURCING_LNUM)
107 {
108 // getsourceline() will skip over NULL lines.
109 ((char_u **)(gap->ga_data))[gap->ga_len++] = NULL;
110 ++lnum_start;
111 }
112 }
113 else if (checkforcmd(&p, "let", 3) || checkforcmd(&p, "const", 4))
114 {
115 eap->cmd = line;
116 eap->arg = p;
117 eap->forceit = FALSE;
118 eap->cmdidx = *line == 'l' ? CMD_let: CMD_const;
119
120 // The command will be executed again, it's OK to redefine the
121 // variable then.
122 ex_let_const(eap, TRUE);
123 }
124 else if (checkforcmd(&p, "import", 3))
125 {
126 eap->arg = p;
127 ex_import(eap);
128
129 // Store empty line, we don't need to process the command again.
130 vim_free(((char_u **)(gap->ga_data))[--gap->ga_len]);
131 ((char_u **)(gap->ga_data))[gap->ga_len++] = NULL;
132 }
133 }
134
135 // Compile the :def functions.
136 for (idx = 0; idx < func_ga.ga_len; ++idx)
137 {
138 ufunc = ((ufunc_T **)(func_ga.ga_data))[idx];
139 compile_def_function(ufunc, FALSE, NULL);
140 }
141 ga_clear(&func_ga);
142
143 // Return to process the commands at the script level.
144 source_use_line_ga(eap->cookie);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100145}
146
147/*
148 * ":export let Name: type"
149 * ":export const Name: type"
150 * ":export def Name(..."
151 * ":export class Name ..."
152 *
153 * ":export {Name, ...}"
154 */
155 void
Bram Moolenaar09689a02020-05-09 22:50:08 +0200156ex_export(exarg_T *eap)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100157{
158 if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
159 {
160 emsg(_(e_needs_vim9));
161 return;
162 }
163
164 eap->cmd = eap->arg;
165 (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL);
166 switch (eap->cmdidx)
167 {
168 case CMD_let:
169 case CMD_const:
170 case CMD_def:
171 // case CMD_class:
172 is_export = TRUE;
173 do_cmdline(eap->cmd, eap->getline, eap->cookie,
174 DOCMD_VERBOSE + DOCMD_NOWAIT);
175
176 // The command will reset "is_export" when exporting an item.
177 if (is_export)
178 {
179 emsg(_("E1044: export with invalid argument"));
180 is_export = FALSE;
181 }
182 break;
183 default:
184 emsg(_("E1043: Invalid command after :export"));
185 break;
186 }
187}
188
189/*
190 * Add a new imported item entry to the current script.
191 */
192 static imported_T *
193new_imported(garray_T *gap)
194{
195 if (ga_grow(gap, 1) == OK)
196 return ((imported_T *)gap->ga_data + gap->ga_len++);
197 return NULL;
198}
199
200/*
201 * Free all imported items in script "sid".
202 */
203 void
204free_imports(int sid)
205{
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100206 scriptitem_T *si = SCRIPT_ITEM(sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100207 int idx;
208
209 for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
210 {
Bram Moolenaar20431c92020-03-20 18:39:46 +0100211 imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100212
213 vim_free(imp->imp_name);
214 }
215 ga_clear(&si->sn_imports);
Bram Moolenaar20431c92020-03-20 18:39:46 +0100216 ga_clear(&si->sn_var_vals);
217 ga_clear(&si->sn_type_list);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100218}
219
220/*
221 * ":import Item from 'filename'"
222 * ":import Item as Alias from 'filename'"
223 * ":import {Item} from 'filename'".
224 * ":import {Item as Alias} from 'filename'"
225 * ":import {Item, Item} from 'filename'"
226 * ":import {Item, Item as Alias} from 'filename'"
227 *
228 * ":import * as Name from 'filename'"
229 */
230 void
231ex_import(exarg_T *eap)
232{
233 if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
234 emsg(_(e_needs_vim9));
235 else
236 {
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100237 char_u *cmd_end = handle_import(eap->arg, NULL,
238 current_sctx.sc_sid, NULL);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100239
240 if (cmd_end != NULL)
241 eap->nextcmd = check_nextcmd(cmd_end);
242 }
243}
244
245/*
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100246 * Find an exported item in "sid" matching the name at "*argp".
247 * When it is a variable return the index.
248 * When it is a user function return "*ufunc".
249 * When not found returns -1 and "*ufunc" is NULL.
250 */
251 int
252find_exported(
253 int sid,
254 char_u **argp,
255 int *name_len,
256 ufunc_T **ufunc,
257 type_T **type)
258{
259 char_u *name = *argp;
260 char_u *arg = *argp;
261 int cc;
262 int idx = -1;
263 svar_T *sv;
264 scriptitem_T *script = SCRIPT_ITEM(sid);
265
266 // isolate one name
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100267 while (eval_isnamec(*arg))
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100268 ++arg;
269 *name_len = (int)(arg - name);
270
271 // find name in "script"
272 // TODO: also find script-local user function
273 cc = *arg;
274 *arg = NUL;
275 idx = get_script_item_idx(sid, name, FALSE);
276 if (idx >= 0)
277 {
278 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
279 if (!sv->sv_export)
280 {
281 semsg(_("E1049: Item not exported in script: %s"), name);
282 *arg = cc;
283 return -1;
284 }
285 *type = sv->sv_type;
286 *ufunc = NULL;
287 }
288 else
289 {
290 char_u buffer[200];
291 char_u *funcname;
292
293 // it could be a user function.
294 if (STRLEN(name) < sizeof(buffer) - 10)
295 funcname = buffer;
296 else
297 {
298 funcname = alloc(STRLEN(name) + 10);
299 if (funcname == NULL)
300 {
301 *arg = cc;
302 return -1;
303 }
304 }
305 funcname[0] = K_SPECIAL;
306 funcname[1] = KS_EXTRA;
307 funcname[2] = (int)KE_SNR;
308 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
Bram Moolenaar4c17ad92020-04-27 22:47:51 +0200309 *ufunc = find_func(funcname, FALSE, NULL);
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100310 if (funcname != buffer)
311 vim_free(funcname);
312
313 if (*ufunc == NULL)
314 {
315 semsg(_("E1048: Item not found in script: %s"), name);
316 *arg = cc;
317 return -1;
318 }
319 }
320 *arg = cc;
321 arg = skipwhite(arg);
322 *argp = arg;
323
324 return idx;
325}
326
327/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100328 * Handle an ":import" command and add the resulting imported_T to "gap", when
329 * not NULL, or script "import_sid" sn_imports.
330 * Returns a pointer to after the command or NULL in case of failure
331 */
332 char_u *
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100333handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100334{
335 char_u *arg = arg_start;
336 char_u *cmd_end;
337 char_u *as_ptr = NULL;
338 char_u *from_ptr;
339 int as_len = 0;
340 int ret = FAIL;
341 typval_T tv;
342 int sid = -1;
343 int res;
344
345 if (*arg == '{')
346 {
347 // skip over {item} list
348 while (*arg != NUL && *arg != '}')
349 ++arg;
350 if (*arg == '}')
351 arg = skipwhite(arg + 1);
352 }
353 else
354 {
355 if (*arg == '*')
356 arg = skipwhite(arg + 1);
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100357 else if (eval_isnamec1(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100358 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100359 while (eval_isnamec(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100360 ++arg;
361 arg = skipwhite(arg);
362 }
363 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
364 {
365 // skip over "as Name "
366 arg = skipwhite(arg + 2);
367 as_ptr = arg;
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100368 if (eval_isnamec1(*arg))
369 while (eval_isnamec(*arg))
370 ++arg;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100371 as_len = (int)(arg - as_ptr);
372 arg = skipwhite(arg);
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100373 if (check_defined(as_ptr, as_len, cctx) == FAIL)
374 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100375 }
376 else if (*arg_start == '*')
377 {
378 emsg(_("E1045: Missing \"as\" after *"));
379 return NULL;
380 }
381 }
382 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
383 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100384 emsg(_("E1070: Missing \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100385 return NULL;
386 }
387 from_ptr = arg;
388 arg = skipwhite(arg + 4);
389 tv.v_type = VAR_UNKNOWN;
390 // TODO: should we accept any expression?
391 if (*arg == '\'')
392 ret = get_lit_string_tv(&arg, &tv, TRUE);
393 else if (*arg == '"')
394 ret = get_string_tv(&arg, &tv, TRUE);
395 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
396 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100397 emsg(_("E1071: Invalid string after \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100398 return NULL;
399 }
400 cmd_end = arg;
401
402 // find script tv.vval.v_string
403 if (*tv.vval.v_string == '.')
404 {
405 size_t len;
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100406 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100407 char_u *tail = gettail(si->sn_name);
408 char_u *from_name;
409
410 // Relative to current script: "./name.vim", "../../name.vim".
411 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
412 from_name = alloc((int)len);
413 if (from_name == NULL)
414 {
415 clear_tv(&tv);
416 return NULL;
417 }
418 vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
419 add_pathsep(from_name);
420 STRCAT(from_name, tv.vval.v_string);
421 simplify_filename(from_name);
422
423 res = do_source(from_name, FALSE, DOSO_NONE, &sid);
424 vim_free(from_name);
425 }
426 else if (mch_isFullName(tv.vval.v_string))
427 {
428 // Absolute path: "/tmp/name.vim"
429 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
430 }
431 else
432 {
433 size_t len = 7 + STRLEN(tv.vval.v_string) + 1;
434 char_u *from_name;
435
436 // Find file in "import" subdirs in 'runtimepath'.
437 from_name = alloc((int)len);
438 if (from_name == NULL)
439 {
440 clear_tv(&tv);
441 return NULL;
442 }
443 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
444 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
445 vim_free(from_name);
446 }
447
448 if (res == FAIL || sid <= 0)
449 {
450 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
451 clear_tv(&tv);
452 return NULL;
453 }
454 clear_tv(&tv);
455
456 if (*arg_start == '*')
457 {
458 imported_T *imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100459 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100460
461 if (imported == NULL)
462 return NULL;
463 imported->imp_name = vim_strnsave(as_ptr, as_len);
464 imported->imp_sid = sid;
465 imported->imp_all = TRUE;
466 }
467 else
468 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100469 arg = arg_start;
470 if (*arg == '{')
471 arg = skipwhite(arg + 1);
472 for (;;)
473 {
474 char_u *name = arg;
475 int name_len;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100476 int idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100477 imported_T *imported;
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100478 ufunc_T *ufunc = NULL;
479 type_T *type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100480
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100481 idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100482
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100483 if (idx < 0 && ufunc == NULL)
484 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100485
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100486 if (check_defined(name, name_len, cctx) == FAIL)
487 return NULL;
488
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100489 imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100490 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100491 if (imported == NULL)
492 return NULL;
493
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100494 // TODO: check for "as" following
495 // imported->imp_name = vim_strnsave(as_ptr, as_len);
496 imported->imp_name = vim_strnsave(name, name_len);
497 imported->imp_sid = sid;
498 if (idx >= 0)
499 {
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100500 imported->imp_type = type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100501 imported->imp_var_vals_idx = idx;
502 }
503 else
504 imported->imp_funcname = ufunc->uf_name;
505
506 arg = skipwhite(arg);
507 if (*arg_start != '{')
508 break;
509 if (*arg == '}')
510 {
511 arg = skipwhite(arg + 1);
512 break;
513 }
514
515 if (*arg != ',')
516 {
517 emsg(_("E1046: Missing comma in import"));
518 return NULL;
519 }
520 arg = skipwhite(arg + 1);
521 }
522 if (arg != from_ptr)
523 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100524 // cannot happen, just in case the above has a flaw
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100525 emsg(_("E1047: syntax error in import"));
526 return NULL;
527 }
528 }
529 return cmd_end;
530}
531
532#endif // FEAT_EVAL