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