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