xref: /vim-8.2.3635/src/vim9script.c (revision 1d59aa1f)
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     int
21 in_vim9script(void)
22 {
23     // Do not go up the stack, a ":function" inside vim9script uses legacy
24     // syntax.  "sc_version" is also set when compiling a ":def" function in
25     // legacy script.
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(_(e_vim9script_can_only_be_used_in_script));
40 	return;
41     }
42     si = SCRIPT_ITEM(current_sctx.sc_sid);
43     if (si->sn_had_command)
44     {
45 	emsg(_(e_vim9script_must_be_first_command_in_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  * When in Vim9 script give an error and return FAIL.
61  */
62     int
63 not_in_vim9(exarg_T *eap)
64 {
65     if (in_vim9script())
66 	switch (eap->cmdidx)
67 	{
68 	    case CMD_append:
69 	    case CMD_change:
70 	    case CMD_insert:
71 	    case CMD_t:
72 	    case CMD_xit:
73 		semsg(_(e_missing_let_str), eap->cmd);
74 		return FAIL;
75 	    default: break;
76 	}
77     return OK;
78 }
79 
80 /*
81  * ":export let Name: type"
82  * ":export const Name: type"
83  * ":export def Name(..."
84  * ":export class Name ..."
85  */
86     void
87 ex_export(exarg_T *eap)
88 {
89     if (!in_vim9script())
90     {
91 	emsg(_(e_export_can_only_be_used_in_vim9script));
92 	return;
93     }
94 
95     eap->cmd = eap->arg;
96     (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL);
97     switch (eap->cmdidx)
98     {
99 	case CMD_let:
100 	case CMD_const:
101 	case CMD_def:
102 	// case CMD_class:
103 	    is_export = TRUE;
104 	    do_cmdline(eap->cmd, eap->getline, eap->cookie,
105 						DOCMD_VERBOSE + DOCMD_NOWAIT);
106 
107 	    // The command will reset "is_export" when exporting an item.
108 	    if (is_export)
109 	    {
110 		emsg(_(e_export_with_invalid_argument));
111 		is_export = FALSE;
112 	    }
113 	    break;
114 	default:
115 	    emsg(_(e_invalid_command_after_export));
116 	    break;
117     }
118 }
119 
120 /*
121  * Add a new imported item entry to the current script.
122  */
123     static imported_T *
124 new_imported(garray_T *gap)
125 {
126     if (ga_grow(gap, 1) == OK)
127 	return ((imported_T *)gap->ga_data + gap->ga_len++);
128     return NULL;
129 }
130 
131 /*
132  * Free all imported items in script "sid".
133  */
134     void
135 free_imports(int sid)
136 {
137     scriptitem_T    *si = SCRIPT_ITEM(sid);
138     int		    idx;
139 
140     for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
141     {
142 	imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx;
143 
144 	vim_free(imp->imp_name);
145     }
146     ga_clear(&si->sn_imports);
147     ga_clear(&si->sn_var_vals);
148     clear_type_list(&si->sn_type_list);
149 }
150 
151 /*
152  * ":import Item from 'filename'"
153  * ":import Item as Alias from 'filename'"
154  * ":import {Item} from 'filename'".
155  * ":import {Item as Alias} from 'filename'"
156  * ":import {Item, Item} from 'filename'"
157  * ":import {Item, Item as Alias} from 'filename'"
158  *
159  * ":import * as Name from 'filename'"
160  */
161     void
162 ex_import(exarg_T *eap)
163 {
164     char_u	*cmd_end;
165     evalarg_T	evalarg;
166 
167     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
168     {
169 	emsg(_(e_import_can_only_be_used_in_script));
170 	return;
171     }
172     fill_evalarg_from_eap(&evalarg, eap, eap->skip);
173 
174     cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid,
175 							       &evalarg, NULL);
176     if (cmd_end != NULL)
177 	eap->nextcmd = check_nextcmd(cmd_end);
178     clear_evalarg(&evalarg, eap);
179 }
180 
181 /*
182  * Find an exported item in "sid" matching the name at "*argp".
183  * When it is a variable return the index.
184  * When it is a user function return "*ufunc".
185  * When not found returns -1 and "*ufunc" is NULL.
186  */
187     int
188 find_exported(
189 	int	    sid,
190 	char_u	    *name,
191 	ufunc_T	    **ufunc,
192 	type_T	    **type)
193 {
194     int		idx = -1;
195     svar_T	*sv;
196     scriptitem_T *script = SCRIPT_ITEM(sid);
197 
198     // find name in "script"
199     // TODO: also find script-local user function
200     idx = get_script_item_idx(sid, name, FALSE);
201     if (idx >= 0)
202     {
203 	sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
204 	if (!sv->sv_export)
205 	{
206 	    semsg(_(e_item_not_exported_in_script_str), name);
207 	    return -1;
208 	}
209 	*type = sv->sv_type;
210 	*ufunc = NULL;
211     }
212     else
213     {
214 	char_u	buffer[200];
215 	char_u	*funcname;
216 
217 	// it could be a user function.
218 	if (STRLEN(name) < sizeof(buffer) - 15)
219 	    funcname = buffer;
220 	else
221 	{
222 	    funcname = alloc(STRLEN(name) + 15);
223 	    if (funcname == NULL)
224 		return -1;
225 	}
226 	funcname[0] = K_SPECIAL;
227 	funcname[1] = KS_EXTRA;
228 	funcname[2] = (int)KE_SNR;
229 	sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
230 	*ufunc = find_func(funcname, FALSE, NULL);
231 	if (funcname != buffer)
232 	    vim_free(funcname);
233 
234 	if (*ufunc == NULL)
235 	{
236 	    semsg(_(e_item_not_found_in_script_str), name);
237 	    return -1;
238 	}
239     }
240 
241     return idx;
242 }
243 
244 /*
245  * Handle an ":import" command and add the resulting imported_T to "gap", when
246  * not NULL, or script "import_sid" sn_imports.
247  * Returns a pointer to after the command or NULL in case of failure
248  */
249     char_u *
250 handle_import(
251 	char_u	    *arg_start,
252 	garray_T    *gap,
253 	int	    import_sid,
254 	evalarg_T   *evalarg,
255 	void	    *cctx)
256 {
257     char_u	*arg = arg_start;
258     char_u	*cmd_end = NULL;
259     char_u	*as_name = NULL;
260     int		ret = FAIL;
261     typval_T	tv;
262     int		sid = -1;
263     int		res;
264     garray_T	names;
265 
266     ga_init2(&names, sizeof(char_u *), 10);
267     if (*arg == '{')
268     {
269 	// "import {item, item} from ..."
270 	arg = skipwhite_and_linebreak(arg + 1, evalarg);
271 	for (;;)
272 	{
273 	    char_u  *p = arg;
274 	    int	    had_comma = FALSE;
275 
276 	    while (eval_isnamec(*arg))
277 		++arg;
278 	    if (p == arg)
279 		break;
280 	    if (ga_grow(&names, 1) == FAIL)
281 		goto erret;
282 	    ((char_u **)names.ga_data)[names.ga_len] =
283 						      vim_strnsave(p, arg - p);
284 	    ++names.ga_len;
285 	    if (*arg == ',')
286 	    {
287 		had_comma = TRUE;
288 		++arg;
289 	    }
290 	    arg = skipwhite_and_linebreak(arg, evalarg);
291 	    if (*arg == '}')
292 	    {
293 		arg = skipwhite_and_linebreak(arg + 1, evalarg);
294 		break;
295 	    }
296 	    if (!had_comma)
297 	    {
298 		emsg(_(e_missing_comma_in_import));
299 		goto erret;
300 	    }
301 	}
302 	if (names.ga_len == 0)
303 	{
304 	    emsg(_(e_syntax_error_in_import));
305 	    goto erret;
306 	}
307     }
308     else
309     {
310 	// "import Name from ..."
311 	// "import * as Name from ..."
312 	// "import item [as Name] from ..."
313 	arg = skipwhite_and_linebreak(arg, evalarg);
314 	if (arg[0] == '*' && IS_WHITE_OR_NUL(arg[1]))
315 	    arg = skipwhite_and_linebreak(arg + 1, evalarg);
316 	else if (eval_isnamec1(*arg))
317 	{
318 	    char_u  *p = arg;
319 
320 	    while (eval_isnamec(*arg))
321 		++arg;
322 	    if (ga_grow(&names, 1) == FAIL)
323 		goto erret;
324 	    ((char_u **)names.ga_data)[names.ga_len] =
325 						      vim_strnsave(p, arg - p);
326 	    ++names.ga_len;
327 	    arg = skipwhite_and_linebreak(arg, evalarg);
328 	}
329 	else
330 	{
331 	    emsg(_(e_syntax_error_in_import));
332 	    goto erret;
333 	}
334 
335 	if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2]))
336 	{
337 	    char_u *p;
338 
339 	    // skip over "as Name "; no line break allowed after "as"
340 	    arg = skipwhite(arg + 2);
341 	    p = arg;
342 	    if (eval_isnamec1(*arg))
343 		while (eval_isnamec(*arg))
344 		    ++arg;
345 	    if (check_defined(p, arg - p, cctx) == FAIL)
346 		goto erret;
347 	    as_name = vim_strnsave(p, arg - p);
348 	    arg = skipwhite_and_linebreak(arg, evalarg);
349 	}
350 	else if (*arg_start == '*')
351 	{
352 	    emsg(_(e_missing_as_after_star));
353 	    goto erret;
354 	}
355     }
356 
357     if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4]))
358     {
359 	emsg(_(e_missing_from));
360 	goto erret;
361     }
362 
363     arg = skipwhite_and_linebreak(arg + 4, evalarg);
364     tv.v_type = VAR_UNKNOWN;
365     // TODO: should we accept any expression?
366     if (*arg == '\'')
367 	ret = eval_lit_string(&arg, &tv, TRUE);
368     else if (*arg == '"')
369 	ret = eval_string(&arg, &tv, TRUE);
370     if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
371     {
372 	emsg(_(e_invalid_string_after_from));
373 	goto erret;
374     }
375     cmd_end = arg;
376 
377     /*
378      * find script file
379      */
380     if (*tv.vval.v_string == '.')
381     {
382 	size_t		len;
383 	scriptitem_T	*si = SCRIPT_ITEM(current_sctx.sc_sid);
384 	char_u		*tail = gettail(si->sn_name);
385 	char_u		*from_name;
386 
387 	// Relative to current script: "./name.vim", "../../name.vim".
388 	len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
389 	from_name = alloc((int)len);
390 	if (from_name == NULL)
391 	{
392 	    clear_tv(&tv);
393 	    goto erret;
394 	}
395 	vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
396 	add_pathsep(from_name);
397 	STRCAT(from_name, tv.vval.v_string);
398 	simplify_filename(from_name);
399 
400 	res = do_source(from_name, FALSE, DOSO_NONE, &sid);
401 	vim_free(from_name);
402     }
403     else if (mch_isFullName(tv.vval.v_string))
404     {
405 	// Absolute path: "/tmp/name.vim"
406 	res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
407     }
408     else
409     {
410 	size_t	    len = 7 + STRLEN(tv.vval.v_string) + 1;
411 	char_u	    *from_name;
412 
413 	// Find file in "import" subdirs in 'runtimepath'.
414 	from_name = alloc((int)len);
415 	if (from_name == NULL)
416 	{
417 	    clear_tv(&tv);
418 	    goto erret;
419 	}
420 	vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
421 	res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
422 	vim_free(from_name);
423     }
424 
425     if (res == FAIL || sid <= 0)
426     {
427 	semsg(_(e_could_not_import_str), tv.vval.v_string);
428 	clear_tv(&tv);
429 	goto erret;
430     }
431     clear_tv(&tv);
432 
433     if (*arg_start == '*')
434     {
435 	imported_T *imported = new_imported(gap != NULL ? gap
436 					: &SCRIPT_ITEM(import_sid)->sn_imports);
437 
438 	if (imported == NULL)
439 	    goto erret;
440 	imported->imp_name = as_name;
441 	as_name = NULL;
442 	imported->imp_sid = sid;
443 	imported->imp_all = TRUE;
444     }
445     else
446     {
447 	int i;
448 
449 	arg = arg_start;
450 	if (*arg == '{')
451 	    arg = skipwhite(arg + 1);
452 	for (i = 0; i < names.ga_len; ++i)
453 	{
454 	    char_u	*name = ((char_u **)names.ga_data)[i];
455 	    int		idx;
456 	    imported_T	*imported;
457 	    ufunc_T	*ufunc = NULL;
458 	    type_T	*type;
459 
460 	    idx = find_exported(sid, name, &ufunc, &type);
461 
462 	    if (idx < 0 && ufunc == NULL)
463 		goto erret;
464 
465 	    if (check_defined(name, STRLEN(name), cctx) == FAIL)
466 		goto erret;
467 
468 	    imported = new_imported(gap != NULL ? gap
469 				       : &SCRIPT_ITEM(import_sid)->sn_imports);
470 	    if (imported == NULL)
471 		goto erret;
472 
473 	    // TODO: check for "as" following
474 	    // imported->imp_name = vim_strsave(as_name);
475 	    imported->imp_name = name;
476 	    ((char_u **)names.ga_data)[i] = NULL;
477 	    imported->imp_sid = sid;
478 	    if (idx >= 0)
479 	    {
480 		imported->imp_type = type;
481 		imported->imp_var_vals_idx = idx;
482 	    }
483 	    else
484 	    {
485 		imported->imp_type = ufunc->uf_func_type;
486 		imported->imp_funcname = ufunc->uf_name;
487 	    }
488 	}
489     }
490 erret:
491     ga_clear_strings(&names);
492     vim_free(as_name);
493     return cmd_end;
494 }
495 
496 /*
497  * Declare a script-local variable without init: "let var: type".
498  * "const" is an error since the value is missing.
499  * Returns a pointer to after the type.
500  */
501     char_u *
502 vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
503 {
504     char_u	    *p;
505     char_u	    *name;
506     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
507     type_T	    *type;
508     int		    called_emsg_before = called_emsg;
509     typval_T	    init_tv;
510 
511     if (eap->cmdidx == CMD_const)
512     {
513 	emsg(_(e_const_requires_a_value));
514 	return arg + STRLEN(arg);
515     }
516 
517     // Check for valid starting character.
518     if (!eval_isnamec1(*arg))
519     {
520 	semsg(_(e_invarg2), arg);
521 	return arg + STRLEN(arg);
522     }
523 
524     for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p))
525 	if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1))
526 	    break;
527 
528     if (*p != ':')
529     {
530 	emsg(_(e_type_or_initialization_required));
531 	return arg + STRLEN(arg);
532     }
533     if (!VIM_ISWHITE(p[1]))
534     {
535 	semsg(_(e_white_space_required_after_str), ":");
536 	return arg + STRLEN(arg);
537     }
538     name = vim_strnsave(arg, p - arg);
539 
540     // parse type
541     p = skipwhite(p + 1);
542     type = parse_type(&p, &si->sn_type_list);
543     if (called_emsg != called_emsg_before)
544     {
545 	vim_free(name);
546 	return p;
547     }
548 
549     // Create the variable with 0/NULL value.
550     CLEAR_FIELD(init_tv);
551     if (type->tt_type == VAR_ANY)
552 	// A variable of type "any" is not possible, just use zero instead
553 	init_tv.v_type = VAR_NUMBER;
554     else
555 	init_tv.v_type = type->tt_type;
556     set_var_const(name, type, &init_tv, FALSE, 0);
557 
558     vim_free(name);
559     return p;
560 }
561 
562 /*
563  * Check if the type of script variable "dest" allows assigning "value".
564  * If needed convert "value" to a bool.
565  */
566     int
567 check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
568 {
569     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
570     int		    idx;
571 
572     if (si->sn_version != SCRIPT_VERSION_VIM9)
573 	// legacy script doesn't store variable types
574 	return OK;
575 
576     // Find the svar_T in sn_var_vals.
577     for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
578     {
579 	svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
580 
581 	if (sv->sv_tv == dest)
582 	{
583 	    int	    ret;
584 
585 	    if (sv->sv_const)
586 	    {
587 		semsg(_(e_readonlyvar), name);
588 		return FAIL;
589 	    }
590 	    ret = check_typval_type(sv->sv_type, value, 0);
591 	    if (ret == OK && need_convert_to_bool(sv->sv_type, value))
592 	    {
593 		int	val = tv2bool(value);
594 
595 		clear_tv(value);
596 		value->v_type = VAR_BOOL;
597 		value->v_lock = 0;
598 		value->vval.v_number = val ? VVAL_TRUE : VVAL_FALSE;
599 	    }
600 	    return ret;
601 	}
602     }
603     iemsg("check_script_var_type(): not found");
604     return OK; // not really
605 }
606 
607 #endif // FEAT_EVAL
608