xref: /vim-8.2.3635/src/vim9script.c (revision a6e8bf2d)
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 = SCRIPT_ITEM(current_sctx.sc_sid);
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     if (si->sn_had_command)
43     {
44 	emsg(_("E1039: vim9script must be the first command in a script"));
45 	return;
46     }
47     current_sctx.sc_version = SCRIPT_VERSION_VIM9;
48     si->sn_version = SCRIPT_VERSION_VIM9;
49     si->sn_had_command = TRUE;
50 
51     if (STRCMP(p_cpo, CPO_VIM) != 0)
52     {
53 	si->sn_save_cpo = p_cpo;
54 	p_cpo = vim_strsave((char_u *)CPO_VIM);
55     }
56 }
57 
58 /*
59  * ":export let Name: type"
60  * ":export const Name: type"
61  * ":export def Name(..."
62  * ":export class Name ..."
63  *
64  * ":export {Name, ...}"
65  */
66     void
67 ex_export(exarg_T *eap)
68 {
69     if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
70     {
71 	emsg(_(e_needs_vim9));
72 	return;
73     }
74 
75     eap->cmd = eap->arg;
76     (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL);
77     switch (eap->cmdidx)
78     {
79 	case CMD_let:
80 	case CMD_const:
81 	case CMD_def:
82 	// case CMD_class:
83 	    is_export = TRUE;
84 	    do_cmdline(eap->cmd, eap->getline, eap->cookie,
85 						DOCMD_VERBOSE + DOCMD_NOWAIT);
86 
87 	    // The command will reset "is_export" when exporting an item.
88 	    if (is_export)
89 	    {
90 		emsg(_("E1044: export with invalid argument"));
91 		is_export = FALSE;
92 	    }
93 	    break;
94 	default:
95 	    emsg(_("E1043: Invalid command after :export"));
96 	    break;
97     }
98 }
99 
100 /*
101  * Add a new imported item entry to the current script.
102  */
103     static imported_T *
104 new_imported(garray_T *gap)
105 {
106     if (ga_grow(gap, 1) == OK)
107 	return ((imported_T *)gap->ga_data + gap->ga_len++);
108     return NULL;
109 }
110 
111 /*
112  * Free all imported items in script "sid".
113  */
114     void
115 free_imports(int sid)
116 {
117     scriptitem_T    *si = SCRIPT_ITEM(sid);
118     int		    idx;
119 
120     for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
121     {
122 	imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx;
123 
124 	vim_free(imp->imp_name);
125     }
126     ga_clear(&si->sn_imports);
127     ga_clear(&si->sn_var_vals);
128     ga_clear(&si->sn_type_list);
129 }
130 
131 /*
132  * ":import Item from 'filename'"
133  * ":import Item as Alias from 'filename'"
134  * ":import {Item} from 'filename'".
135  * ":import {Item as Alias} from 'filename'"
136  * ":import {Item, Item} from 'filename'"
137  * ":import {Item, Item as Alias} from 'filename'"
138  *
139  * ":import * as Name from 'filename'"
140  */
141     void
142 ex_import(exarg_T *eap)
143 {
144     char_u *cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL);
145 
146     if (cmd_end != NULL)
147 	eap->nextcmd = check_nextcmd(cmd_end);
148 }
149 
150 /*
151  * Find an exported item in "sid" matching the name at "*argp".
152  * When it is a variable return the index.
153  * When it is a user function return "*ufunc".
154  * When not found returns -1 and "*ufunc" is NULL.
155  */
156     int
157 find_exported(
158 	int	    sid,
159 	char_u	    **argp,
160 	int	    *name_len,
161 	ufunc_T	    **ufunc,
162 	type_T	    **type)
163 {
164     char_u	*name = *argp;
165     char_u	*arg = *argp;
166     int		cc;
167     int		idx = -1;
168     svar_T	*sv;
169     scriptitem_T *script = SCRIPT_ITEM(sid);
170 
171     // isolate one name
172     while (eval_isnamec(*arg))
173 	++arg;
174     *name_len = (int)(arg - name);
175 
176     // find name in "script"
177     // TODO: also find script-local user function
178     cc = *arg;
179     *arg = NUL;
180     idx = get_script_item_idx(sid, name, FALSE);
181     if (idx >= 0)
182     {
183 	sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
184 	if (!sv->sv_export)
185 	{
186 	    semsg(_("E1049: Item not exported in script: %s"), name);
187 	    *arg = cc;
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 	    {
206 		*arg = cc;
207 		return -1;
208 	    }
209 	}
210 	funcname[0] = K_SPECIAL;
211 	funcname[1] = KS_EXTRA;
212 	funcname[2] = (int)KE_SNR;
213 	sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
214 	*ufunc = find_func(funcname, FALSE, NULL);
215 	if (funcname != buffer)
216 	    vim_free(funcname);
217 
218 	if (*ufunc == NULL)
219 	{
220 	    semsg(_("E1048: Item not found in script: %s"), name);
221 	    *arg = cc;
222 	    return -1;
223 	}
224     }
225     *arg = cc;
226     arg = skipwhite(arg);
227     *argp = arg;
228 
229     return idx;
230 }
231 
232 /*
233  * Handle an ":import" command and add the resulting imported_T to "gap", when
234  * not NULL, or script "import_sid" sn_imports.
235  * Returns a pointer to after the command or NULL in case of failure
236  */
237     char_u *
238 handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
239 {
240     char_u	*arg = arg_start;
241     char_u	*cmd_end;
242     char_u	*as_ptr = NULL;
243     char_u	*from_ptr;
244     int		as_len = 0;
245     int		ret = FAIL;
246     typval_T	tv;
247     int		sid = -1;
248     int		res;
249 
250     if (*arg == '{')
251     {
252 	// skip over {item} list
253 	while (*arg != NUL && *arg != '}')
254 	    ++arg;
255 	if (*arg == '}')
256 	    arg = skipwhite(arg + 1);
257     }
258     else
259     {
260 	if (*arg == '*')
261 	    arg = skipwhite(arg + 1);
262 	else if (eval_isnamec1(*arg))
263 	{
264 	    while (eval_isnamec(*arg))
265 		++arg;
266 	    arg = skipwhite(arg);
267 	}
268 	if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
269 	{
270 	    // skip over "as Name "
271 	    arg = skipwhite(arg + 2);
272 	    as_ptr = arg;
273 	    if (eval_isnamec1(*arg))
274 		while (eval_isnamec(*arg))
275 		    ++arg;
276 	    as_len = (int)(arg - as_ptr);
277 	    arg = skipwhite(arg);
278 	    if (check_defined(as_ptr, as_len, cctx) == FAIL)
279 		return NULL;
280 	}
281 	else if (*arg_start == '*')
282 	{
283 	    emsg(_("E1045: Missing \"as\" after *"));
284 	    return NULL;
285 	}
286     }
287     if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
288     {
289 	emsg(_("E1070: Missing \"from\""));
290 	return NULL;
291     }
292     from_ptr = arg;
293     arg = skipwhite(arg + 4);
294     tv.v_type = VAR_UNKNOWN;
295     // TODO: should we accept any expression?
296     if (*arg == '\'')
297 	ret = get_lit_string_tv(&arg, &tv, TRUE);
298     else if (*arg == '"')
299 	ret = get_string_tv(&arg, &tv, TRUE);
300     if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
301     {
302 	emsg(_("E1071: Invalid string after \"from\""));
303 	return NULL;
304     }
305     cmd_end = arg;
306 
307     // find script tv.vval.v_string
308     if (*tv.vval.v_string == '.')
309     {
310 	size_t		len;
311 	scriptitem_T	*si = SCRIPT_ITEM(current_sctx.sc_sid);
312 	char_u		*tail = gettail(si->sn_name);
313 	char_u		*from_name;
314 
315 	// Relative to current script: "./name.vim", "../../name.vim".
316 	len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
317 	from_name = alloc((int)len);
318 	if (from_name == NULL)
319 	{
320 	    clear_tv(&tv);
321 	    return NULL;
322 	}
323 	vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
324 	add_pathsep(from_name);
325 	STRCAT(from_name, tv.vval.v_string);
326 	simplify_filename(from_name);
327 
328 	res = do_source(from_name, FALSE, DOSO_NONE, &sid);
329 	vim_free(from_name);
330     }
331     else if (mch_isFullName(tv.vval.v_string))
332     {
333 	// Absolute path: "/tmp/name.vim"
334 	res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
335     }
336     else
337     {
338 	size_t	    len = 7 + STRLEN(tv.vval.v_string) + 1;
339 	char_u	    *from_name;
340 
341 	// Find file in "import" subdirs in 'runtimepath'.
342 	from_name = alloc((int)len);
343 	if (from_name == NULL)
344 	{
345 	    clear_tv(&tv);
346 	    return NULL;
347 	}
348 	vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
349 	res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
350 	vim_free(from_name);
351     }
352 
353     if (res == FAIL || sid <= 0)
354     {
355 	semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
356 	clear_tv(&tv);
357 	return NULL;
358     }
359     clear_tv(&tv);
360 
361     if (*arg_start == '*')
362     {
363 	imported_T *imported = new_imported(gap != NULL ? gap
364 					: &SCRIPT_ITEM(import_sid)->sn_imports);
365 
366 	if (imported == NULL)
367 	    return NULL;
368 	imported->imp_name = vim_strnsave(as_ptr, as_len);
369 	imported->imp_sid = sid;
370 	imported->imp_all = TRUE;
371     }
372     else
373     {
374 	arg = arg_start;
375 	if (*arg == '{')
376 	    arg = skipwhite(arg + 1);
377 	for (;;)
378 	{
379 	    char_u	*name = arg;
380 	    int		name_len;
381 	    int		idx;
382 	    imported_T	*imported;
383 	    ufunc_T	*ufunc = NULL;
384 	    type_T	*type;
385 
386 	    idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
387 
388 	    if (idx < 0 && ufunc == NULL)
389 		return NULL;
390 
391 	    if (check_defined(name, name_len, cctx) == FAIL)
392 		return NULL;
393 
394 	    imported = new_imported(gap != NULL ? gap
395 				       : &SCRIPT_ITEM(import_sid)->sn_imports);
396 	    if (imported == NULL)
397 		return NULL;
398 
399 	    // TODO: check for "as" following
400 	    // imported->imp_name = vim_strnsave(as_ptr, as_len);
401 	    imported->imp_name = vim_strnsave(name, name_len);
402 	    imported->imp_sid = sid;
403 	    if (idx >= 0)
404 	    {
405 		imported->imp_type = type;
406 		imported->imp_var_vals_idx = idx;
407 	    }
408 	    else
409 		imported->imp_funcname = ufunc->uf_name;
410 
411 	    arg = skipwhite(arg);
412 	    if (*arg_start != '{')
413 		break;
414 	    if (*arg == '}')
415 	    {
416 		arg = skipwhite(arg + 1);
417 		break;
418 	    }
419 
420 	    if (*arg != ',')
421 	    {
422 		emsg(_("E1046: Missing comma in import"));
423 		return NULL;
424 	    }
425 	    arg = skipwhite(arg + 1);
426 	}
427 	if (arg != from_ptr)
428 	{
429 	    // cannot happen, just in case the above has a flaw
430 	    emsg(_("E1047: syntax error in import"));
431 	    return NULL;
432 	}
433     }
434     return cmd_end;
435 }
436 
437 /*
438  * Declare a script-local variable without init: "let var: type".
439  * "const" is an error since the value is missing.
440  * Returns a pointer to after the type.
441  */
442     char_u *
443 vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
444 {
445     char_u	    *p;
446     char_u	    *name;
447     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
448     type_T	    *type;
449     int		    called_emsg_before = called_emsg;
450     typval_T	    init_tv;
451 
452     if (eap->cmdidx == CMD_const)
453     {
454 	emsg(_(e_const_req_value));
455 	return arg + STRLEN(arg);
456     }
457 
458     // Check for valid starting character.
459     if (!eval_isnamec1(*arg))
460     {
461 	semsg(_(e_invarg2), arg);
462 	return arg + STRLEN(arg);
463     }
464 
465     for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p))
466 	if (*p == ':' && p != arg + 1)
467 	    break;
468 
469     if (*p != ':')
470     {
471 	emsg(_(e_type_req));
472 	return arg + STRLEN(arg);
473     }
474     if (!VIM_ISWHITE(p[1]))
475     {
476 	semsg(_(e_white_after), ":");
477 	return arg + STRLEN(arg);
478     }
479     name = vim_strnsave(arg, p - arg);
480 
481     // parse type
482     p = skipwhite(p + 1);
483     type = parse_type(&p, &si->sn_type_list);
484     if (called_emsg != called_emsg_before)
485     {
486 	vim_free(name);
487 	return p;
488     }
489 
490     // Create the variable with 0/NULL value.
491     CLEAR_FIELD(init_tv);
492     init_tv.v_type = type->tt_type;
493     set_var_const(name, type, &init_tv, FALSE, 0);
494 
495     vim_free(name);
496     return p;
497 }
498 
499 /*
500  * Check if the type of script variable "dest" allows assigning "value".
501  */
502     void
503 check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
504 {
505     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
506     int		    idx;
507 
508     // Find the svar_T in sn_var_vals.
509     for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
510     {
511 	svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
512 
513 	if (sv->sv_tv == dest)
514 	{
515 	    if (sv->sv_const)
516 		semsg(_(e_readonlyvar), name);
517 	    else
518 		check_type(sv->sv_type, typval2type(value), TRUE);
519 	    return;
520 	}
521     }
522     iemsg("check_script_var_type(): not found");
523 }
524 
525 #endif // FEAT_EVAL
526