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