xref: /vim-8.2.3635/src/vim9script.c (revision 47e13953)
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 
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;
54     ga_init2(&func_ga, sizeof(ufunc_T *), 20);
55 
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     }
61 
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 	else if (checkforcmd(&p, "finish", 4))
134 	{
135 	    // TODO: this should not happen below "if false".
136 	    // Use "if cond | finish | endif as a workaround.
137 	    break;
138 	}
139     }
140 
141     // Compile the :def functions.
142     for (idx = 0; idx < func_ga.ga_len; ++idx)
143     {
144 	ufunc = ((ufunc_T **)(func_ga.ga_data))[idx];
145 	compile_def_function(ufunc, FALSE, NULL);
146     }
147     ga_clear(&func_ga);
148 
149     // Return to process the commands at the script level.
150     source_use_line_ga(eap->cookie);
151 }
152 
153 /*
154  * ":export let Name: type"
155  * ":export const Name: type"
156  * ":export def Name(..."
157  * ":export class Name ..."
158  *
159  * ":export {Name, ...}"
160  */
161     void
162 ex_export(exarg_T *eap)
163 {
164     if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
165     {
166 	emsg(_(e_needs_vim9));
167 	return;
168     }
169 
170     eap->cmd = eap->arg;
171     (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL);
172     switch (eap->cmdidx)
173     {
174 	case CMD_let:
175 	case CMD_const:
176 	case CMD_def:
177 	// case CMD_class:
178 	    is_export = TRUE;
179 	    do_cmdline(eap->cmd, eap->getline, eap->cookie,
180 						DOCMD_VERBOSE + DOCMD_NOWAIT);
181 
182 	    // The command will reset "is_export" when exporting an item.
183 	    if (is_export)
184 	    {
185 		emsg(_("E1044: export with invalid argument"));
186 		is_export = FALSE;
187 	    }
188 	    break;
189 	default:
190 	    emsg(_("E1043: Invalid command after :export"));
191 	    break;
192     }
193 }
194 
195 /*
196  * Add a new imported item entry to the current script.
197  */
198     static imported_T *
199 new_imported(garray_T *gap)
200 {
201     if (ga_grow(gap, 1) == OK)
202 	return ((imported_T *)gap->ga_data + gap->ga_len++);
203     return NULL;
204 }
205 
206 /*
207  * Free all imported items in script "sid".
208  */
209     void
210 free_imports(int sid)
211 {
212     scriptitem_T    *si = SCRIPT_ITEM(sid);
213     int		    idx;
214 
215     for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
216     {
217 	imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx;
218 
219 	vim_free(imp->imp_name);
220     }
221     ga_clear(&si->sn_imports);
222     ga_clear(&si->sn_var_vals);
223     ga_clear(&si->sn_type_list);
224 }
225 
226 /*
227  * ":import Item from 'filename'"
228  * ":import Item as Alias from 'filename'"
229  * ":import {Item} from 'filename'".
230  * ":import {Item as Alias} from 'filename'"
231  * ":import {Item, Item} from 'filename'"
232  * ":import {Item, Item as Alias} from 'filename'"
233  *
234  * ":import * as Name from 'filename'"
235  */
236     void
237 ex_import(exarg_T *eap)
238 {
239     if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
240 	emsg(_(e_needs_vim9));
241     else
242     {
243 	char_u *cmd_end = handle_import(eap->arg, NULL,
244 						    current_sctx.sc_sid, NULL);
245 
246 	if (cmd_end != NULL)
247 	    eap->nextcmd = check_nextcmd(cmd_end);
248     }
249 }
250 
251 /*
252  * Find an exported item in "sid" matching the name at "*argp".
253  * When it is a variable return the index.
254  * When it is a user function return "*ufunc".
255  * When not found returns -1 and "*ufunc" is NULL.
256  */
257     int
258 find_exported(
259 	int	    sid,
260 	char_u	    **argp,
261 	int	    *name_len,
262 	ufunc_T	    **ufunc,
263 	type_T	    **type)
264 {
265     char_u	*name = *argp;
266     char_u	*arg = *argp;
267     int		cc;
268     int		idx = -1;
269     svar_T	*sv;
270     scriptitem_T *script = SCRIPT_ITEM(sid);
271 
272     // isolate one name
273     while (eval_isnamec(*arg))
274 	++arg;
275     *name_len = (int)(arg - name);
276 
277     // find name in "script"
278     // TODO: also find script-local user function
279     cc = *arg;
280     *arg = NUL;
281     idx = get_script_item_idx(sid, name, FALSE);
282     if (idx >= 0)
283     {
284 	sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
285 	if (!sv->sv_export)
286 	{
287 	    semsg(_("E1049: Item not exported in script: %s"), name);
288 	    *arg = cc;
289 	    return -1;
290 	}
291 	*type = sv->sv_type;
292 	*ufunc = NULL;
293     }
294     else
295     {
296 	char_u	buffer[200];
297 	char_u	*funcname;
298 
299 	// it could be a user function.
300 	if (STRLEN(name) < sizeof(buffer) - 10)
301 	    funcname = buffer;
302 	else
303 	{
304 	    funcname = alloc(STRLEN(name) + 10);
305 	    if (funcname == NULL)
306 	    {
307 		*arg = cc;
308 		return -1;
309 	    }
310 	}
311 	funcname[0] = K_SPECIAL;
312 	funcname[1] = KS_EXTRA;
313 	funcname[2] = (int)KE_SNR;
314 	sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
315 	*ufunc = find_func(funcname, FALSE, NULL);
316 	if (funcname != buffer)
317 	    vim_free(funcname);
318 
319 	if (*ufunc == NULL)
320 	{
321 	    semsg(_("E1048: Item not found in script: %s"), name);
322 	    *arg = cc;
323 	    return -1;
324 	}
325     }
326     *arg = cc;
327     arg = skipwhite(arg);
328     *argp = arg;
329 
330     return idx;
331 }
332 
333 /*
334  * Handle an ":import" command and add the resulting imported_T to "gap", when
335  * not NULL, or script "import_sid" sn_imports.
336  * Returns a pointer to after the command or NULL in case of failure
337  */
338     char_u *
339 handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
340 {
341     char_u	*arg = arg_start;
342     char_u	*cmd_end;
343     char_u	*as_ptr = NULL;
344     char_u	*from_ptr;
345     int		as_len = 0;
346     int		ret = FAIL;
347     typval_T	tv;
348     int		sid = -1;
349     int		res;
350 
351     if (*arg == '{')
352     {
353 	// skip over {item} list
354 	while (*arg != NUL && *arg != '}')
355 	    ++arg;
356 	if (*arg == '}')
357 	    arg = skipwhite(arg + 1);
358     }
359     else
360     {
361 	if (*arg == '*')
362 	    arg = skipwhite(arg + 1);
363 	else if (eval_isnamec1(*arg))
364 	{
365 	    while (eval_isnamec(*arg))
366 		++arg;
367 	    arg = skipwhite(arg);
368 	}
369 	if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
370 	{
371 	    // skip over "as Name "
372 	    arg = skipwhite(arg + 2);
373 	    as_ptr = arg;
374 	    if (eval_isnamec1(*arg))
375 		while (eval_isnamec(*arg))
376 		    ++arg;
377 	    as_len = (int)(arg - as_ptr);
378 	    arg = skipwhite(arg);
379 	    if (check_defined(as_ptr, as_len, cctx) == FAIL)
380 		return NULL;
381 	}
382 	else if (*arg_start == '*')
383 	{
384 	    emsg(_("E1045: Missing \"as\" after *"));
385 	    return NULL;
386 	}
387     }
388     if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
389     {
390 	emsg(_("E1070: Missing \"from\""));
391 	return NULL;
392     }
393     from_ptr = arg;
394     arg = skipwhite(arg + 4);
395     tv.v_type = VAR_UNKNOWN;
396     // TODO: should we accept any expression?
397     if (*arg == '\'')
398 	ret = get_lit_string_tv(&arg, &tv, TRUE);
399     else if (*arg == '"')
400 	ret = get_string_tv(&arg, &tv, TRUE);
401     if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
402     {
403 	emsg(_("E1071: Invalid string after \"from\""));
404 	return NULL;
405     }
406     cmd_end = arg;
407 
408     // find script tv.vval.v_string
409     if (*tv.vval.v_string == '.')
410     {
411 	size_t		len;
412 	scriptitem_T	*si = SCRIPT_ITEM(current_sctx.sc_sid);
413 	char_u		*tail = gettail(si->sn_name);
414 	char_u		*from_name;
415 
416 	// Relative to current script: "./name.vim", "../../name.vim".
417 	len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
418 	from_name = alloc((int)len);
419 	if (from_name == NULL)
420 	{
421 	    clear_tv(&tv);
422 	    return NULL;
423 	}
424 	vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
425 	add_pathsep(from_name);
426 	STRCAT(from_name, tv.vval.v_string);
427 	simplify_filename(from_name);
428 
429 	res = do_source(from_name, FALSE, DOSO_NONE, &sid);
430 	vim_free(from_name);
431     }
432     else if (mch_isFullName(tv.vval.v_string))
433     {
434 	// Absolute path: "/tmp/name.vim"
435 	res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
436     }
437     else
438     {
439 	size_t	    len = 7 + STRLEN(tv.vval.v_string) + 1;
440 	char_u	    *from_name;
441 
442 	// Find file in "import" subdirs in 'runtimepath'.
443 	from_name = alloc((int)len);
444 	if (from_name == NULL)
445 	{
446 	    clear_tv(&tv);
447 	    return NULL;
448 	}
449 	vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
450 	res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
451 	vim_free(from_name);
452     }
453 
454     if (res == FAIL || sid <= 0)
455     {
456 	semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
457 	clear_tv(&tv);
458 	return NULL;
459     }
460     clear_tv(&tv);
461 
462     if (*arg_start == '*')
463     {
464 	imported_T *imported = new_imported(gap != NULL ? gap
465 					: &SCRIPT_ITEM(import_sid)->sn_imports);
466 
467 	if (imported == NULL)
468 	    return NULL;
469 	imported->imp_name = vim_strnsave(as_ptr, as_len);
470 	imported->imp_sid = sid;
471 	imported->imp_all = TRUE;
472     }
473     else
474     {
475 	arg = arg_start;
476 	if (*arg == '{')
477 	    arg = skipwhite(arg + 1);
478 	for (;;)
479 	{
480 	    char_u	*name = arg;
481 	    int		name_len;
482 	    int		idx;
483 	    imported_T	*imported;
484 	    ufunc_T	*ufunc = NULL;
485 	    type_T	*type;
486 
487 	    idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
488 
489 	    if (idx < 0 && ufunc == NULL)
490 		return NULL;
491 
492 	    if (check_defined(name, name_len, cctx) == FAIL)
493 		return NULL;
494 
495 	    imported = new_imported(gap != NULL ? gap
496 				       : &SCRIPT_ITEM(import_sid)->sn_imports);
497 	    if (imported == NULL)
498 		return NULL;
499 
500 	    // TODO: check for "as" following
501 	    // imported->imp_name = vim_strnsave(as_ptr, as_len);
502 	    imported->imp_name = vim_strnsave(name, name_len);
503 	    imported->imp_sid = sid;
504 	    if (idx >= 0)
505 	    {
506 		imported->imp_type = type;
507 		imported->imp_var_vals_idx = idx;
508 	    }
509 	    else
510 		imported->imp_funcname = ufunc->uf_name;
511 
512 	    arg = skipwhite(arg);
513 	    if (*arg_start != '{')
514 		break;
515 	    if (*arg == '}')
516 	    {
517 		arg = skipwhite(arg + 1);
518 		break;
519 	    }
520 
521 	    if (*arg != ',')
522 	    {
523 		emsg(_("E1046: Missing comma in import"));
524 		return NULL;
525 	    }
526 	    arg = skipwhite(arg + 1);
527 	}
528 	if (arg != from_ptr)
529 	{
530 	    // cannot happen, just in case the above has a flaw
531 	    emsg(_("E1047: syntax error in import"));
532 	    return NULL;
533 	}
534     }
535     return cmd_end;
536 }
537 
538 #endif // FEAT_EVAL
539