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