xref: /vim-8.2.3635/src/vim9script.c (revision 92c098d1)
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: import/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     if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
145 	emsg(_(e_needs_vim9));
146     else
147     {
148 	char_u *cmd_end = handle_import(eap->arg, NULL,
149 						    current_sctx.sc_sid, NULL);
150 
151 	if (cmd_end != NULL)
152 	    eap->nextcmd = check_nextcmd(cmd_end);
153     }
154 }
155 
156 /*
157  * Find an exported item in "sid" matching the name at "*argp".
158  * When it is a variable return the index.
159  * When it is a user function return "*ufunc".
160  * When not found returns -1 and "*ufunc" is NULL.
161  */
162     int
163 find_exported(
164 	int	    sid,
165 	char_u	    **argp,
166 	int	    *name_len,
167 	ufunc_T	    **ufunc,
168 	type_T	    **type)
169 {
170     char_u	*name = *argp;
171     char_u	*arg = *argp;
172     int		cc;
173     int		idx = -1;
174     svar_T	*sv;
175     scriptitem_T *script = SCRIPT_ITEM(sid);
176 
177     // isolate one name
178     while (eval_isnamec(*arg))
179 	++arg;
180     *name_len = (int)(arg - name);
181 
182     // find name in "script"
183     // TODO: also find script-local user function
184     cc = *arg;
185     *arg = NUL;
186     idx = get_script_item_idx(sid, name, FALSE);
187     if (idx >= 0)
188     {
189 	sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
190 	if (!sv->sv_export)
191 	{
192 	    semsg(_("E1049: Item not exported in script: %s"), name);
193 	    *arg = cc;
194 	    return -1;
195 	}
196 	*type = sv->sv_type;
197 	*ufunc = NULL;
198     }
199     else
200     {
201 	char_u	buffer[200];
202 	char_u	*funcname;
203 
204 	// it could be a user function.
205 	if (STRLEN(name) < sizeof(buffer) - 10)
206 	    funcname = buffer;
207 	else
208 	{
209 	    funcname = alloc(STRLEN(name) + 10);
210 	    if (funcname == NULL)
211 	    {
212 		*arg = cc;
213 		return -1;
214 	    }
215 	}
216 	funcname[0] = K_SPECIAL;
217 	funcname[1] = KS_EXTRA;
218 	funcname[2] = (int)KE_SNR;
219 	sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
220 	*ufunc = find_func(funcname, FALSE, NULL);
221 	if (funcname != buffer)
222 	    vim_free(funcname);
223 
224 	if (*ufunc == NULL)
225 	{
226 	    semsg(_("E1048: Item not found in script: %s"), name);
227 	    *arg = cc;
228 	    return -1;
229 	}
230     }
231     *arg = cc;
232     arg = skipwhite(arg);
233     *argp = arg;
234 
235     return idx;
236 }
237 
238 /*
239  * Handle an ":import" command and add the resulting imported_T to "gap", when
240  * not NULL, or script "import_sid" sn_imports.
241  * Returns a pointer to after the command or NULL in case of failure
242  */
243     char_u *
244 handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
245 {
246     char_u	*arg = arg_start;
247     char_u	*cmd_end;
248     char_u	*as_ptr = NULL;
249     char_u	*from_ptr;
250     int		as_len = 0;
251     int		ret = FAIL;
252     typval_T	tv;
253     int		sid = -1;
254     int		res;
255 
256     if (*arg == '{')
257     {
258 	// skip over {item} list
259 	while (*arg != NUL && *arg != '}')
260 	    ++arg;
261 	if (*arg == '}')
262 	    arg = skipwhite(arg + 1);
263     }
264     else
265     {
266 	if (*arg == '*')
267 	    arg = skipwhite(arg + 1);
268 	else if (eval_isnamec1(*arg))
269 	{
270 	    while (eval_isnamec(*arg))
271 		++arg;
272 	    arg = skipwhite(arg);
273 	}
274 	if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
275 	{
276 	    // skip over "as Name "
277 	    arg = skipwhite(arg + 2);
278 	    as_ptr = arg;
279 	    if (eval_isnamec1(*arg))
280 		while (eval_isnamec(*arg))
281 		    ++arg;
282 	    as_len = (int)(arg - as_ptr);
283 	    arg = skipwhite(arg);
284 	    if (check_defined(as_ptr, as_len, cctx) == FAIL)
285 		return NULL;
286 	}
287 	else if (*arg_start == '*')
288 	{
289 	    emsg(_("E1045: Missing \"as\" after *"));
290 	    return NULL;
291 	}
292     }
293     if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
294     {
295 	emsg(_("E1070: Missing \"from\""));
296 	return NULL;
297     }
298     from_ptr = arg;
299     arg = skipwhite(arg + 4);
300     tv.v_type = VAR_UNKNOWN;
301     // TODO: should we accept any expression?
302     if (*arg == '\'')
303 	ret = get_lit_string_tv(&arg, &tv, TRUE);
304     else if (*arg == '"')
305 	ret = get_string_tv(&arg, &tv, TRUE);
306     if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
307     {
308 	emsg(_("E1071: Invalid string after \"from\""));
309 	return NULL;
310     }
311     cmd_end = arg;
312 
313     // find script tv.vval.v_string
314     if (*tv.vval.v_string == '.')
315     {
316 	size_t		len;
317 	scriptitem_T	*si = SCRIPT_ITEM(current_sctx.sc_sid);
318 	char_u		*tail = gettail(si->sn_name);
319 	char_u		*from_name;
320 
321 	// Relative to current script: "./name.vim", "../../name.vim".
322 	len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
323 	from_name = alloc((int)len);
324 	if (from_name == NULL)
325 	{
326 	    clear_tv(&tv);
327 	    return NULL;
328 	}
329 	vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
330 	add_pathsep(from_name);
331 	STRCAT(from_name, tv.vval.v_string);
332 	simplify_filename(from_name);
333 
334 	res = do_source(from_name, FALSE, DOSO_NONE, &sid);
335 	vim_free(from_name);
336     }
337     else if (mch_isFullName(tv.vval.v_string))
338     {
339 	// Absolute path: "/tmp/name.vim"
340 	res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
341     }
342     else
343     {
344 	size_t	    len = 7 + STRLEN(tv.vval.v_string) + 1;
345 	char_u	    *from_name;
346 
347 	// Find file in "import" subdirs in 'runtimepath'.
348 	from_name = alloc((int)len);
349 	if (from_name == NULL)
350 	{
351 	    clear_tv(&tv);
352 	    return NULL;
353 	}
354 	vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
355 	res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
356 	vim_free(from_name);
357     }
358 
359     if (res == FAIL || sid <= 0)
360     {
361 	semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
362 	clear_tv(&tv);
363 	return NULL;
364     }
365     clear_tv(&tv);
366 
367     if (*arg_start == '*')
368     {
369 	imported_T *imported = new_imported(gap != NULL ? gap
370 					: &SCRIPT_ITEM(import_sid)->sn_imports);
371 
372 	if (imported == NULL)
373 	    return NULL;
374 	imported->imp_name = vim_strnsave(as_ptr, as_len);
375 	imported->imp_sid = sid;
376 	imported->imp_all = TRUE;
377     }
378     else
379     {
380 	arg = arg_start;
381 	if (*arg == '{')
382 	    arg = skipwhite(arg + 1);
383 	for (;;)
384 	{
385 	    char_u	*name = arg;
386 	    int		name_len;
387 	    int		idx;
388 	    imported_T	*imported;
389 	    ufunc_T	*ufunc = NULL;
390 	    type_T	*type;
391 
392 	    idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
393 
394 	    if (idx < 0 && ufunc == NULL)
395 		return NULL;
396 
397 	    if (check_defined(name, name_len, cctx) == FAIL)
398 		return NULL;
399 
400 	    imported = new_imported(gap != NULL ? gap
401 				       : &SCRIPT_ITEM(import_sid)->sn_imports);
402 	    if (imported == NULL)
403 		return NULL;
404 
405 	    // TODO: check for "as" following
406 	    // imported->imp_name = vim_strnsave(as_ptr, as_len);
407 	    imported->imp_name = vim_strnsave(name, name_len);
408 	    imported->imp_sid = sid;
409 	    if (idx >= 0)
410 	    {
411 		imported->imp_type = type;
412 		imported->imp_var_vals_idx = idx;
413 	    }
414 	    else
415 		imported->imp_funcname = ufunc->uf_name;
416 
417 	    arg = skipwhite(arg);
418 	    if (*arg_start != '{')
419 		break;
420 	    if (*arg == '}')
421 	    {
422 		arg = skipwhite(arg + 1);
423 		break;
424 	    }
425 
426 	    if (*arg != ',')
427 	    {
428 		emsg(_("E1046: Missing comma in import"));
429 		return NULL;
430 	    }
431 	    arg = skipwhite(arg + 1);
432 	}
433 	if (arg != from_ptr)
434 	{
435 	    // cannot happen, just in case the above has a flaw
436 	    emsg(_("E1047: syntax error in import"));
437 	    return NULL;
438 	}
439     }
440     return cmd_end;
441 }
442 
443 #endif // FEAT_EVAL
444