xref: /vim-8.2.3635/src/vim9script.c (revision 7ff78465)
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: 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;
36 
37     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
38     {
39 	emsg(_("E1038: vim9script can only be used in a script"));
40 	return;
41     }
42     si = SCRIPT_ITEM(current_sctx.sc_sid);
43     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
68 ex_export(exarg_T *eap)
69 {
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 *
105 new_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
116 free_imports(int sid)
117 {
118     scriptitem_T    *si = SCRIPT_ITEM(sid);
119     int		    idx;
120 
121     for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
122     {
123 	imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx;
124 
125 	vim_free(imp->imp_name);
126     }
127     ga_clear(&si->sn_imports);
128     ga_clear(&si->sn_var_vals);
129     clear_type_list(&si->sn_type_list);
130 }
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
143 ex_import(exarg_T *eap)
144 {
145     char_u	*cmd_end;
146     evalarg_T	evalarg;
147 
148     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
149     {
150 	emsg(_("E1094: import can only be used in a script"));
151 	return;
152     }
153     fill_evalarg_from_eap(&evalarg, eap, eap->skip);
154 
155     cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid,
156 							       &evalarg, NULL);
157     if (cmd_end != NULL)
158 	eap->nextcmd = check_nextcmd(cmd_end);
159     clear_evalarg(&evalarg, eap);
160 }
161 
162 /*
163  * Find an exported item in "sid" matching the name at "*argp".
164  * When it is a variable return the index.
165  * When it is a user function return "*ufunc".
166  * When not found returns -1 and "*ufunc" is NULL.
167  */
168     int
169 find_exported(
170 	int	    sid,
171 	char_u	    *name,
172 	ufunc_T	    **ufunc,
173 	type_T	    **type)
174 {
175     int		idx = -1;
176     svar_T	*sv;
177     scriptitem_T *script = SCRIPT_ITEM(sid);
178 
179     // find name in "script"
180     // TODO: also find script-local user function
181     idx = get_script_item_idx(sid, name, FALSE);
182     if (idx >= 0)
183     {
184 	sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
185 	if (!sv->sv_export)
186 	{
187 	    semsg(_("E1049: Item not exported in script: %s"), name);
188 	    return -1;
189 	}
190 	*type = sv->sv_type;
191 	*ufunc = NULL;
192     }
193     else
194     {
195 	char_u	buffer[200];
196 	char_u	*funcname;
197 
198 	// it could be a user function.
199 	if (STRLEN(name) < sizeof(buffer) - 10)
200 	    funcname = buffer;
201 	else
202 	{
203 	    funcname = alloc(STRLEN(name) + 10);
204 	    if (funcname == NULL)
205 		return -1;
206 	}
207 	funcname[0] = K_SPECIAL;
208 	funcname[1] = KS_EXTRA;
209 	funcname[2] = (int)KE_SNR;
210 	sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
211 	*ufunc = find_func(funcname, FALSE, NULL);
212 	if (funcname != buffer)
213 	    vim_free(funcname);
214 
215 	if (*ufunc == NULL)
216 	{
217 	    semsg(_("E1048: Item not found in script: %s"), name);
218 	    return -1;
219 	}
220     }
221 
222     return idx;
223 }
224 
225 /*
226  * Handle an ":import" command and add the resulting imported_T to "gap", when
227  * not NULL, or script "import_sid" sn_imports.
228  * Returns a pointer to after the command or NULL in case of failure
229  */
230     char_u *
231 handle_import(
232 	char_u	    *arg_start,
233 	garray_T    *gap,
234 	int	    import_sid,
235 	evalarg_T   *evalarg,
236 	void	    *cctx)
237 {
238     char_u	*arg = arg_start;
239     char_u	*cmd_end = NULL;
240     char_u	*as_name = NULL;
241     int		ret = FAIL;
242     typval_T	tv;
243     int		sid = -1;
244     int		res;
245     garray_T	names;
246     static char e_import_syntax[] = N_("E1047: syntax error in import");
247 
248     ga_init2(&names, sizeof(char_u *), 10);
249     if (*arg == '{')
250     {
251 	// "import {item, item} from ..."
252 	arg = skipwhite_and_linebreak(arg + 1, evalarg);
253 	for (;;)
254 	{
255 	    char_u  *p = arg;
256 	    int	    had_comma = FALSE;
257 
258 	    while (eval_isnamec(*arg))
259 		++arg;
260 	    if (p == arg)
261 		break;
262 	    if (ga_grow(&names, 1) == FAIL)
263 		goto erret;
264 	    ((char_u **)names.ga_data)[names.ga_len] =
265 						      vim_strnsave(p, arg - p);
266 	    ++names.ga_len;
267 	    if (*arg == ',')
268 	    {
269 		had_comma = TRUE;
270 		++arg;
271 	    }
272 	    arg = skipwhite_and_linebreak(arg, evalarg);
273 	    if (*arg == '}')
274 	    {
275 		arg = skipwhite_and_linebreak(arg + 1, evalarg);
276 		break;
277 	    }
278 	    if (!had_comma)
279 	    {
280 		emsg(_("E1046: Missing comma in import"));
281 		goto erret;
282 	    }
283 	}
284 	if (names.ga_len == 0)
285 	{
286 	    emsg(_(e_import_syntax));
287 	    goto erret;
288 	}
289     }
290     else
291     {
292 	// "import Name from ..."
293 	// "import * as Name from ..."
294 	// "import item [as Name] from ..."
295 	arg = skipwhite_and_linebreak(arg, evalarg);
296 	if (arg[0] == '*' && IS_WHITE_OR_NUL(arg[1]))
297 	    arg = skipwhite_and_linebreak(arg + 1, evalarg);
298 	else if (eval_isnamec1(*arg))
299 	{
300 	    char_u  *p = arg;
301 
302 	    while (eval_isnamec(*arg))
303 		++arg;
304 	    if (ga_grow(&names, 1) == FAIL)
305 		goto erret;
306 	    ((char_u **)names.ga_data)[names.ga_len] =
307 						      vim_strnsave(p, arg - p);
308 	    ++names.ga_len;
309 	    arg = skipwhite_and_linebreak(arg, evalarg);
310 	}
311 	else
312 	{
313 	    emsg(_(e_import_syntax));
314 	    goto erret;
315 	}
316 
317 	if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2]))
318 	{
319 	    char_u *p;
320 
321 	    // skip over "as Name "; no line break allowed after "as"
322 	    arg = skipwhite(arg + 2);
323 	    p = arg;
324 	    if (eval_isnamec1(*arg))
325 		while (eval_isnamec(*arg))
326 		    ++arg;
327 	    if (check_defined(p, arg - p, cctx) == FAIL)
328 		goto erret;
329 	    as_name = vim_strnsave(p, arg - p);
330 	    arg = skipwhite_and_linebreak(arg, evalarg);
331 	}
332 	else if (*arg_start == '*')
333 	{
334 	    emsg(_("E1045: Missing \"as\" after *"));
335 	    goto erret;
336 	}
337     }
338 
339     if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4]))
340     {
341 	emsg(_("E1070: Missing \"from\""));
342 	goto erret;
343     }
344 
345     arg = skipwhite_and_linebreak(arg + 4, evalarg);
346     tv.v_type = VAR_UNKNOWN;
347     // TODO: should we accept any expression?
348     if (*arg == '\'')
349 	ret = eval_lit_string(&arg, &tv, TRUE);
350     else if (*arg == '"')
351 	ret = eval_string(&arg, &tv, TRUE);
352     if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
353     {
354 	emsg(_("E1071: Invalid string after \"from\""));
355 	goto erret;
356     }
357     cmd_end = arg;
358 
359     /*
360      * find script file
361      */
362     if (*tv.vval.v_string == '.')
363     {
364 	size_t		len;
365 	scriptitem_T	*si = SCRIPT_ITEM(current_sctx.sc_sid);
366 	char_u		*tail = gettail(si->sn_name);
367 	char_u		*from_name;
368 
369 	// Relative to current script: "./name.vim", "../../name.vim".
370 	len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
371 	from_name = alloc((int)len);
372 	if (from_name == NULL)
373 	{
374 	    clear_tv(&tv);
375 	    goto erret;
376 	}
377 	vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
378 	add_pathsep(from_name);
379 	STRCAT(from_name, tv.vval.v_string);
380 	simplify_filename(from_name);
381 
382 	res = do_source(from_name, FALSE, DOSO_NONE, &sid);
383 	vim_free(from_name);
384     }
385     else if (mch_isFullName(tv.vval.v_string))
386     {
387 	// Absolute path: "/tmp/name.vim"
388 	res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
389     }
390     else
391     {
392 	size_t	    len = 7 + STRLEN(tv.vval.v_string) + 1;
393 	char_u	    *from_name;
394 
395 	// Find file in "import" subdirs in 'runtimepath'.
396 	from_name = alloc((int)len);
397 	if (from_name == NULL)
398 	{
399 	    clear_tv(&tv);
400 	    goto erret;
401 	}
402 	vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
403 	res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
404 	vim_free(from_name);
405     }
406 
407     if (res == FAIL || sid <= 0)
408     {
409 	semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
410 	clear_tv(&tv);
411 	goto erret;
412     }
413     clear_tv(&tv);
414 
415     if (*arg_start == '*')
416     {
417 	imported_T *imported = new_imported(gap != NULL ? gap
418 					: &SCRIPT_ITEM(import_sid)->sn_imports);
419 
420 	if (imported == NULL)
421 	    goto erret;
422 	imported->imp_name = as_name;
423 	as_name = NULL;
424 	imported->imp_sid = sid;
425 	imported->imp_all = TRUE;
426     }
427     else
428     {
429 	int i;
430 
431 	arg = arg_start;
432 	if (*arg == '{')
433 	    arg = skipwhite(arg + 1);
434 	for (i = 0; i < names.ga_len; ++i)
435 	{
436 	    char_u	*name = ((char_u **)names.ga_data)[i];
437 	    int		idx;
438 	    imported_T	*imported;
439 	    ufunc_T	*ufunc = NULL;
440 	    type_T	*type;
441 
442 	    idx = find_exported(sid, name, &ufunc, &type);
443 
444 	    if (idx < 0 && ufunc == NULL)
445 		goto erret;
446 
447 	    if (check_defined(name, STRLEN(name), cctx) == FAIL)
448 		goto erret;
449 
450 	    imported = new_imported(gap != NULL ? gap
451 				       : &SCRIPT_ITEM(import_sid)->sn_imports);
452 	    if (imported == NULL)
453 		goto erret;
454 
455 	    // TODO: check for "as" following
456 	    // imported->imp_name = vim_strsave(as_name);
457 	    imported->imp_name = name;
458 	    ((char_u **)names.ga_data)[i] = NULL;
459 	    imported->imp_sid = sid;
460 	    if (idx >= 0)
461 	    {
462 		imported->imp_type = type;
463 		imported->imp_var_vals_idx = idx;
464 	    }
465 	    else
466 		imported->imp_funcname = ufunc->uf_name;
467 	}
468     }
469 erret:
470     ga_clear_strings(&names);
471     vim_free(as_name);
472     return cmd_end;
473 }
474 
475 /*
476  * Declare a script-local variable without init: "let var: type".
477  * "const" is an error since the value is missing.
478  * Returns a pointer to after the type.
479  */
480     char_u *
481 vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
482 {
483     char_u	    *p;
484     char_u	    *name;
485     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
486     type_T	    *type;
487     int		    called_emsg_before = called_emsg;
488     typval_T	    init_tv;
489 
490     if (eap->cmdidx == CMD_const)
491     {
492 	emsg(_(e_const_req_value));
493 	return arg + STRLEN(arg);
494     }
495 
496     // Check for valid starting character.
497     if (!eval_isnamec1(*arg))
498     {
499 	semsg(_(e_invarg2), arg);
500 	return arg + STRLEN(arg);
501     }
502 
503     for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p))
504 	if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1))
505 	    break;
506 
507     if (*p != ':')
508     {
509 	emsg(_(e_type_req));
510 	return arg + STRLEN(arg);
511     }
512     if (!VIM_ISWHITE(p[1]))
513     {
514 	semsg(_(e_white_after), ":");
515 	return arg + STRLEN(arg);
516     }
517     name = vim_strnsave(arg, p - arg);
518 
519     // parse type
520     p = skipwhite(p + 1);
521     type = parse_type(&p, &si->sn_type_list);
522     if (called_emsg != called_emsg_before)
523     {
524 	vim_free(name);
525 	return p;
526     }
527 
528     // Create the variable with 0/NULL value.
529     CLEAR_FIELD(init_tv);
530     init_tv.v_type = type->tt_type;
531     set_var_const(name, type, &init_tv, FALSE, 0);
532 
533     vim_free(name);
534     return p;
535 }
536 
537 /*
538  * Check if the type of script variable "dest" allows assigning "value".
539  */
540     int
541 check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
542 {
543     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
544     int		    idx;
545 
546     // Find the svar_T in sn_var_vals.
547     for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
548     {
549 	svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
550 
551 	if (sv->sv_tv == dest)
552 	{
553 	    if (sv->sv_const)
554 	    {
555 		semsg(_(e_readonlyvar), name);
556 		return FAIL;
557 	    }
558 	    return check_type(sv->sv_type, typval2type(value), TRUE);
559 	}
560     }
561     iemsg("check_script_var_type(): not found");
562     return OK; // not really
563 }
564 
565 #endif // FEAT_EVAL
566