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