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 int 21 in_vim9script(void) 22 { 23 // Do not go up the stack, a ":function" inside vim9script uses legacy 24 // syntax. "sc_version" is also set when compiling a ":def" function in 25 // legacy script. 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; 36 37 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) 38 { 39 emsg(_(e_vim9script_can_only_be_used_in_script)); 40 return; 41 } 42 si = SCRIPT_ITEM(current_sctx.sc_sid); 43 if (si->sn_had_command) 44 { 45 emsg(_(e_vim9script_must_be_first_command_in_script)); 46 return; 47 } 48 current_sctx.sc_version = SCRIPT_VERSION_VIM9; 49 si->sn_version = SCRIPT_VERSION_VIM9; 50 si->sn_had_command = TRUE; 51 52 if (STRCMP(p_cpo, CPO_VIM) != 0) 53 { 54 si->sn_save_cpo = vim_strsave(p_cpo); 55 set_option_value((char_u *)"cpo", 0L, (char_u *)CPO_VIM, 0); 56 } 57 } 58 59 /* 60 * When in Vim9 script give an error and return FAIL. 61 */ 62 int 63 not_in_vim9(exarg_T *eap) 64 { 65 if (in_vim9script()) 66 switch (eap->cmdidx) 67 { 68 case CMD_append: 69 case CMD_change: 70 case CMD_insert: 71 case CMD_t: 72 case CMD_xit: 73 semsg(_(e_missing_let_str), eap->cmd); 74 return FAIL; 75 default: break; 76 } 77 return OK; 78 } 79 80 /* 81 * ":export let Name: type" 82 * ":export const Name: type" 83 * ":export def Name(..." 84 * ":export class Name ..." 85 */ 86 void 87 ex_export(exarg_T *eap) 88 { 89 if (!in_vim9script()) 90 { 91 emsg(_(e_export_can_only_be_used_in_vim9script)); 92 return; 93 } 94 95 eap->cmd = eap->arg; 96 (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL); 97 switch (eap->cmdidx) 98 { 99 case CMD_let: 100 case CMD_var: 101 case CMD_final: 102 case CMD_const: 103 case CMD_def: 104 // case CMD_class: 105 is_export = TRUE; 106 do_cmdline(eap->cmd, eap->getline, eap->cookie, 107 DOCMD_VERBOSE + DOCMD_NOWAIT); 108 109 // The command will reset "is_export" when exporting an item. 110 if (is_export) 111 { 112 emsg(_(e_export_with_invalid_argument)); 113 is_export = FALSE; 114 } 115 break; 116 default: 117 emsg(_(e_invalid_command_after_export)); 118 break; 119 } 120 } 121 122 /* 123 * Add a new imported item entry to the current script. 124 */ 125 static imported_T * 126 new_imported(garray_T *gap) 127 { 128 if (ga_grow(gap, 1) == OK) 129 return ((imported_T *)gap->ga_data + gap->ga_len++); 130 return NULL; 131 } 132 133 /* 134 * Free all imported items in script "sid". 135 */ 136 void 137 free_imports_and_script_vars(int sid) 138 { 139 scriptitem_T *si = SCRIPT_ITEM(sid); 140 int idx; 141 142 for (idx = 0; idx < si->sn_imports.ga_len; ++idx) 143 { 144 imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx; 145 146 vim_free(imp->imp_name); 147 } 148 ga_clear(&si->sn_imports); 149 150 free_all_script_vars(si); 151 152 clear_type_list(&si->sn_type_list); 153 } 154 155 /* 156 * ":import Item from 'filename'" 157 * ":import Item as Alias from 'filename'" 158 * ":import {Item} from 'filename'". 159 * ":import {Item as Alias} from 'filename'" 160 * ":import {Item, Item} from 'filename'" 161 * ":import {Item, Item as Alias} from 'filename'" 162 * 163 * ":import * as Name from 'filename'" 164 */ 165 void 166 ex_import(exarg_T *eap) 167 { 168 char_u *cmd_end; 169 evalarg_T evalarg; 170 171 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) 172 { 173 emsg(_(e_import_can_only_be_used_in_script)); 174 return; 175 } 176 fill_evalarg_from_eap(&evalarg, eap, eap->skip); 177 178 cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, 179 &evalarg, NULL); 180 if (cmd_end != NULL) 181 eap->nextcmd = check_nextcmd(cmd_end); 182 clear_evalarg(&evalarg, eap); 183 } 184 185 /* 186 * Find an exported item in "sid" matching the name at "*argp". 187 * When it is a variable return the index. 188 * When it is a user function return "*ufunc". 189 * When not found returns -1 and "*ufunc" is NULL. 190 */ 191 int 192 find_exported( 193 int sid, 194 char_u *name, 195 ufunc_T **ufunc, 196 type_T **type, 197 cctx_T *cctx) 198 { 199 int idx = -1; 200 svar_T *sv; 201 scriptitem_T *script = SCRIPT_ITEM(sid); 202 203 // find name in "script" 204 // TODO: also find script-local user function 205 idx = get_script_item_idx(sid, name, FALSE, cctx); 206 if (idx >= 0) 207 { 208 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; 209 if (!sv->sv_export) 210 { 211 semsg(_(e_item_not_exported_in_script_str), name); 212 return -1; 213 } 214 *type = sv->sv_type; 215 *ufunc = NULL; 216 } 217 else 218 { 219 char_u buffer[200]; 220 char_u *funcname; 221 222 // it could be a user function. 223 if (STRLEN(name) < sizeof(buffer) - 15) 224 funcname = buffer; 225 else 226 { 227 funcname = alloc(STRLEN(name) + 15); 228 if (funcname == NULL) 229 return -1; 230 } 231 funcname[0] = K_SPECIAL; 232 funcname[1] = KS_EXTRA; 233 funcname[2] = (int)KE_SNR; 234 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); 235 *ufunc = find_func(funcname, FALSE, NULL); 236 if (funcname != buffer) 237 vim_free(funcname); 238 239 if (*ufunc == NULL) 240 { 241 semsg(_(e_item_not_found_in_script_str), name); 242 return -1; 243 } 244 } 245 246 return idx; 247 } 248 249 /* 250 * Handle an ":import" command and add the resulting imported_T to "gap", when 251 * not NULL, or script "import_sid" sn_imports. 252 * "cctx" is NULL at the script level. 253 * Returns a pointer to after the command or NULL in case of failure 254 */ 255 char_u * 256 handle_import( 257 char_u *arg_start, 258 garray_T *gap, 259 int import_sid, 260 evalarg_T *evalarg, 261 void *cctx) 262 { 263 char_u *arg = arg_start; 264 char_u *cmd_end = NULL; 265 char_u *as_name = NULL; 266 int ret = FAIL; 267 typval_T tv; 268 int sid = -1; 269 int res; 270 garray_T names; 271 272 ga_init2(&names, sizeof(char_u *), 10); 273 if (*arg == '{') 274 { 275 // "import {item, item} from ..." 276 arg = skipwhite_and_linebreak(arg + 1, evalarg); 277 for (;;) 278 { 279 char_u *p = arg; 280 int had_comma = FALSE; 281 282 while (eval_isnamec(*arg)) 283 ++arg; 284 if (p == arg) 285 break; 286 if (ga_grow(&names, 1) == FAIL) 287 goto erret; 288 ((char_u **)names.ga_data)[names.ga_len] = 289 vim_strnsave(p, arg - p); 290 ++names.ga_len; 291 if (*arg == ',') 292 { 293 had_comma = TRUE; 294 ++arg; 295 } 296 arg = skipwhite_and_linebreak(arg, evalarg); 297 if (*arg == '}') 298 { 299 arg = skipwhite_and_linebreak(arg + 1, evalarg); 300 break; 301 } 302 if (!had_comma) 303 { 304 emsg(_(e_missing_comma_in_import)); 305 goto erret; 306 } 307 } 308 if (names.ga_len == 0) 309 { 310 emsg(_(e_syntax_error_in_import)); 311 goto erret; 312 } 313 } 314 else 315 { 316 // "import Name from ..." 317 // "import * as Name from ..." 318 // "import item [as Name] from ..." 319 arg = skipwhite_and_linebreak(arg, evalarg); 320 if (arg[0] == '*' && IS_WHITE_OR_NUL(arg[1])) 321 arg = skipwhite_and_linebreak(arg + 1, evalarg); 322 else if (eval_isnamec1(*arg)) 323 { 324 char_u *p = arg; 325 326 while (eval_isnamec(*arg)) 327 ++arg; 328 if (ga_grow(&names, 1) == FAIL) 329 goto erret; 330 ((char_u **)names.ga_data)[names.ga_len] = 331 vim_strnsave(p, arg - p); 332 ++names.ga_len; 333 arg = skipwhite_and_linebreak(arg, evalarg); 334 } 335 else 336 { 337 emsg(_(e_syntax_error_in_import)); 338 goto erret; 339 } 340 341 if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2])) 342 { 343 char_u *p; 344 345 // skip over "as Name "; no line break allowed after "as" 346 arg = skipwhite(arg + 2); 347 p = arg; 348 if (eval_isnamec1(*arg)) 349 while (eval_isnamec(*arg)) 350 ++arg; 351 if (check_defined(p, arg - p, cctx) == FAIL) 352 goto erret; 353 as_name = vim_strnsave(p, arg - p); 354 arg = skipwhite_and_linebreak(arg, evalarg); 355 } 356 else if (*arg_start == '*') 357 { 358 emsg(_(e_missing_as_after_star)); 359 goto erret; 360 } 361 } 362 363 if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4])) 364 { 365 emsg(_(e_missing_from)); 366 goto erret; 367 } 368 369 arg = skipwhite_and_linebreak(arg + 4, evalarg); 370 tv.v_type = VAR_UNKNOWN; 371 // TODO: should we accept any expression? 372 if (*arg == '\'') 373 ret = eval_lit_string(&arg, &tv, TRUE); 374 else if (*arg == '"') 375 ret = eval_string(&arg, &tv, TRUE); 376 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) 377 { 378 emsg(_(e_invalid_string_after_from)); 379 goto erret; 380 } 381 cmd_end = arg; 382 383 /* 384 * find script file 385 */ 386 if (*tv.vval.v_string == '.') 387 { 388 size_t len; 389 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 390 char_u *tail = gettail(si->sn_name); 391 char_u *from_name; 392 393 // Relative to current script: "./name.vim", "../../name.vim". 394 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; 395 from_name = alloc((int)len); 396 if (from_name == NULL) 397 { 398 clear_tv(&tv); 399 goto erret; 400 } 401 vim_strncpy(from_name, si->sn_name, tail - si->sn_name); 402 add_pathsep(from_name); 403 STRCAT(from_name, tv.vval.v_string); 404 simplify_filename(from_name); 405 406 res = do_source(from_name, FALSE, DOSO_NONE, &sid); 407 vim_free(from_name); 408 } 409 else if (mch_isFullName(tv.vval.v_string)) 410 { 411 // Absolute path: "/tmp/name.vim" 412 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); 413 } 414 else 415 { 416 size_t len = 7 + STRLEN(tv.vval.v_string) + 1; 417 char_u *from_name; 418 419 // Find file in "import" subdirs in 'runtimepath'. 420 from_name = alloc((int)len); 421 if (from_name == NULL) 422 { 423 clear_tv(&tv); 424 goto erret; 425 } 426 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); 427 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); 428 vim_free(from_name); 429 } 430 431 if (res == FAIL || sid <= 0) 432 { 433 semsg(_(e_could_not_import_str), tv.vval.v_string); 434 clear_tv(&tv); 435 goto erret; 436 } 437 clear_tv(&tv); 438 439 if (*arg_start == '*') 440 { 441 imported_T *imported = new_imported(gap != NULL ? gap 442 : &SCRIPT_ITEM(import_sid)->sn_imports); 443 444 if (imported == NULL) 445 goto erret; 446 imported->imp_name = as_name; 447 as_name = NULL; 448 imported->imp_sid = sid; 449 imported->imp_all = TRUE; 450 } 451 else 452 { 453 int i; 454 455 arg = arg_start; 456 if (*arg == '{') 457 arg = skipwhite(arg + 1); 458 for (i = 0; i < names.ga_len; ++i) 459 { 460 char_u *name = ((char_u **)names.ga_data)[i]; 461 int idx; 462 imported_T *imported; 463 ufunc_T *ufunc = NULL; 464 type_T *type; 465 466 idx = find_exported(sid, name, &ufunc, &type, cctx); 467 468 if (idx < 0 && ufunc == NULL) 469 goto erret; 470 471 if (check_defined(name, STRLEN(name), cctx) == FAIL) 472 goto erret; 473 474 imported = new_imported(gap != NULL ? gap 475 : &SCRIPT_ITEM(import_sid)->sn_imports); 476 if (imported == NULL) 477 goto erret; 478 479 // TODO: check for "as" following 480 // imported->imp_name = vim_strsave(as_name); 481 imported->imp_name = name; 482 ((char_u **)names.ga_data)[i] = NULL; 483 imported->imp_sid = sid; 484 if (idx >= 0) 485 { 486 imported->imp_type = type; 487 imported->imp_var_vals_idx = idx; 488 } 489 else 490 { 491 imported->imp_type = ufunc->uf_func_type; 492 imported->imp_funcname = ufunc->uf_name; 493 } 494 } 495 } 496 erret: 497 ga_clear_strings(&names); 498 vim_free(as_name); 499 return cmd_end; 500 } 501 502 /* 503 * Declare a script-local variable without init: "let var: type". 504 * "const" is an error since the value is missing. 505 * Returns a pointer to after the type. 506 */ 507 char_u * 508 vim9_declare_scriptvar(exarg_T *eap, char_u *arg) 509 { 510 char_u *p; 511 char_u *name; 512 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 513 type_T *type; 514 int called_emsg_before = called_emsg; 515 typval_T init_tv; 516 517 if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const) 518 { 519 if (eap->cmdidx == CMD_final) 520 emsg(_(e_final_requires_a_value)); 521 else 522 emsg(_(e_const_requires_a_value)); 523 return arg + STRLEN(arg); 524 } 525 526 // Check for valid starting character. 527 if (!eval_isnamec1(*arg)) 528 { 529 semsg(_(e_invarg2), arg); 530 return arg + STRLEN(arg); 531 } 532 533 for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) 534 if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1)) 535 break; 536 537 if (*p != ':') 538 { 539 emsg(_(e_type_or_initialization_required)); 540 return arg + STRLEN(arg); 541 } 542 if (!VIM_ISWHITE(p[1])) 543 { 544 semsg(_(e_white_space_required_after_str), ":"); 545 return arg + STRLEN(arg); 546 } 547 name = vim_strnsave(arg, p - arg); 548 549 // parse type 550 p = skipwhite(p + 1); 551 type = parse_type(&p, &si->sn_type_list); 552 if (called_emsg != called_emsg_before) 553 { 554 vim_free(name); 555 return p; 556 } 557 558 // Create the variable with 0/NULL value. 559 CLEAR_FIELD(init_tv); 560 if (type->tt_type == VAR_ANY) 561 // A variable of type "any" is not possible, just use zero instead 562 init_tv.v_type = VAR_NUMBER; 563 else 564 init_tv.v_type = type->tt_type; 565 set_var_const(name, type, &init_tv, FALSE, 0); 566 567 vim_free(name); 568 return p; 569 } 570 571 /* 572 * Vim9 part of adding a script variable: add it to sn_all_vars (lookup by name 573 * with a hashtable) and sn_var_vals (lookup by index). 574 * When "type" is NULL use "tv" for the type. 575 */ 576 void 577 add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type) 578 { 579 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 580 581 // Store a pointer to the typval_T, so that it can be found by 582 // index instead of using a hastab lookup. 583 if (ga_grow(&si->sn_var_vals, 1) == OK) 584 { 585 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) 586 + si->sn_var_vals.ga_len; 587 hashitem_T *hi; 588 sallvar_T *newsav = (sallvar_T *)alloc_clear( 589 sizeof(sallvar_T) + STRLEN(di->di_key)); 590 591 if (newsav == NULL) 592 return; 593 594 sv->sv_tv = &di->di_tv; 595 if (type == NULL) 596 sv->sv_type = typval2type(tv, &si->sn_type_list); 597 else 598 sv->sv_type = type; 599 sv->sv_const = (di->di_flags & DI_FLAGS_LOCK) ? ASSIGN_CONST : 0; 600 sv->sv_export = is_export; 601 newsav->sav_var_vals_idx = si->sn_var_vals.ga_len; 602 ++si->sn_var_vals.ga_len; 603 604 STRCPY(&newsav->sav_key, di->di_key); 605 sv->sv_name = newsav->sav_key; 606 newsav->sav_di = di; 607 newsav->sav_block_id = si->sn_current_block_id; 608 609 hi = hash_find(&si->sn_all_vars.dv_hashtab, newsav->sav_key); 610 if (!HASHITEM_EMPTY(hi)) 611 { 612 sallvar_T *sav = HI2SAV(hi); 613 614 // variable with this name exists in another block 615 while (sav->sav_next != NULL) 616 sav = sav->sav_next; 617 sav->sav_next = newsav; 618 } 619 else 620 // new variable name 621 hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key); 622 623 // let ex_export() know the export worked. 624 is_export = FALSE; 625 } 626 } 627 628 /* 629 * Hide a script variable when leaving a block. 630 * "idx" is de index in sn_var_vals. 631 * When "func_defined" is non-zero then a function was defined in this block, 632 * the variable may be accessed by it. Otherwise the variable can be cleared. 633 */ 634 void 635 hide_script_var(scriptitem_T *si, int idx, int func_defined) 636 { 637 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 638 hashtab_T *script_ht = get_script_local_ht(); 639 hashtab_T *all_ht = &si->sn_all_vars.dv_hashtab; 640 hashitem_T *script_hi; 641 hashitem_T *all_hi; 642 643 // Remove a variable declared inside the block, if it still exists. 644 // If it was added in a nested block it will already have been removed. 645 // The typval is moved into the sallvar_T. 646 script_hi = hash_find(script_ht, sv->sv_name); 647 all_hi = hash_find(all_ht, sv->sv_name); 648 if (!HASHITEM_EMPTY(script_hi) && !HASHITEM_EMPTY(all_hi)) 649 { 650 dictitem_T *di = HI2DI(script_hi); 651 sallvar_T *sav = HI2SAV(all_hi); 652 sallvar_T *sav_prev = NULL; 653 654 // There can be multiple entries with the same name in different 655 // blocks, find the right one. 656 while (sav != NULL && sav->sav_var_vals_idx != idx) 657 { 658 sav_prev = sav; 659 sav = sav->sav_next; 660 } 661 if (sav != NULL) 662 { 663 if (func_defined) 664 { 665 // move the typval from the dictitem to the sallvar 666 sav->sav_tv = di->di_tv; 667 di->di_tv.v_type = VAR_UNKNOWN; 668 sav->sav_flags = di->di_flags; 669 sav->sav_di = NULL; 670 sv->sv_tv = &sav->sav_tv; 671 } 672 else 673 { 674 if (sav_prev == NULL) 675 hash_remove(all_ht, all_hi); 676 else 677 sav_prev->sav_next = sav->sav_next; 678 sv->sv_name = NULL; 679 vim_free(sav); 680 } 681 delete_var(script_ht, script_hi); 682 } 683 } 684 } 685 686 /* 687 * Free the script variables from "sn_all_vars". 688 */ 689 void 690 free_all_script_vars(scriptitem_T *si) 691 { 692 int todo; 693 hashtab_T *ht = &si->sn_all_vars.dv_hashtab; 694 hashitem_T *hi; 695 sallvar_T *sav; 696 sallvar_T *sav_next; 697 698 hash_lock(ht); 699 todo = (int)ht->ht_used; 700 for (hi = ht->ht_array; todo > 0; ++hi) 701 { 702 if (!HASHITEM_EMPTY(hi)) 703 { 704 --todo; 705 706 // Free the variable. Don't remove it from the hashtab, ht_array 707 // might change then. hash_clear() takes care of it later. 708 sav = HI2SAV(hi); 709 while (sav != NULL) 710 { 711 sav_next = sav->sav_next; 712 if (sav->sav_di == NULL) 713 clear_tv(&sav->sav_tv); 714 vim_free(sav); 715 sav = sav_next; 716 } 717 } 718 } 719 hash_clear(ht); 720 hash_init(ht); 721 722 ga_clear(&si->sn_var_vals); 723 } 724 725 /* 726 * Find the script-local variable that links to "dest". 727 * Returns NULL if not found. 728 */ 729 svar_T * 730 find_typval_in_script(typval_T *dest) 731 { 732 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 733 int idx; 734 735 if (si->sn_version != SCRIPT_VERSION_VIM9) 736 // legacy script doesn't store variable types 737 return NULL; 738 739 // Find the svar_T in sn_var_vals. 740 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) 741 { 742 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 743 744 if (sv->sv_tv == dest) 745 return sv; 746 } 747 iemsg("check_script_var_type(): not found"); 748 return NULL; 749 } 750 751 /* 752 * Check if the type of script variable "dest" allows assigning "value". 753 * If needed convert "value" to a bool. 754 */ 755 int 756 check_script_var_type(typval_T *dest, typval_T *value, char_u *name) 757 { 758 svar_T *sv = find_typval_in_script(dest); 759 int ret; 760 761 if (sv != NULL) 762 { 763 if (sv->sv_const) 764 { 765 semsg(_(e_readonlyvar), name); 766 return FAIL; 767 } 768 ret = check_typval_type(sv->sv_type, value, 0); 769 if (ret == OK && need_convert_to_bool(sv->sv_type, value)) 770 { 771 int val = tv2bool(value); 772 773 clear_tv(value); 774 value->v_type = VAR_BOOL; 775 value->v_lock = 0; 776 value->vval.v_number = val ? VVAL_TRUE : VVAL_FALSE; 777 } 778 return ret; 779 } 780 781 return OK; // not really 782 } 783 784 #endif // FEAT_EVAL 785