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