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 = p_cpo; 55 p_cpo = vim_strsave((char_u *)CPO_VIM); 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(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 ga_clear(&si->sn_var_vals); 150 clear_type_list(&si->sn_type_list); 151 } 152 153 /* 154 * ":import Item from 'filename'" 155 * ":import Item as Alias from 'filename'" 156 * ":import {Item} from 'filename'". 157 * ":import {Item as Alias} from 'filename'" 158 * ":import {Item, Item} from 'filename'" 159 * ":import {Item, Item as Alias} from 'filename'" 160 * 161 * ":import * as Name from 'filename'" 162 */ 163 void 164 ex_import(exarg_T *eap) 165 { 166 char_u *cmd_end; 167 evalarg_T evalarg; 168 169 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) 170 { 171 emsg(_(e_import_can_only_be_used_in_script)); 172 return; 173 } 174 fill_evalarg_from_eap(&evalarg, eap, eap->skip); 175 176 cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, 177 &evalarg, NULL); 178 if (cmd_end != NULL) 179 eap->nextcmd = check_nextcmd(cmd_end); 180 clear_evalarg(&evalarg, eap); 181 } 182 183 /* 184 * Find an exported item in "sid" matching the name at "*argp". 185 * When it is a variable return the index. 186 * When it is a user function return "*ufunc". 187 * When not found returns -1 and "*ufunc" is NULL. 188 */ 189 int 190 find_exported( 191 int sid, 192 char_u *name, 193 ufunc_T **ufunc, 194 type_T **type) 195 { 196 int idx = -1; 197 svar_T *sv; 198 scriptitem_T *script = SCRIPT_ITEM(sid); 199 200 // find name in "script" 201 // TODO: also find script-local user function 202 idx = get_script_item_idx(sid, name, FALSE); 203 if (idx >= 0) 204 { 205 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; 206 if (!sv->sv_export) 207 { 208 semsg(_(e_item_not_exported_in_script_str), name); 209 return -1; 210 } 211 *type = sv->sv_type; 212 *ufunc = NULL; 213 } 214 else 215 { 216 char_u buffer[200]; 217 char_u *funcname; 218 219 // it could be a user function. 220 if (STRLEN(name) < sizeof(buffer) - 15) 221 funcname = buffer; 222 else 223 { 224 funcname = alloc(STRLEN(name) + 15); 225 if (funcname == NULL) 226 return -1; 227 } 228 funcname[0] = K_SPECIAL; 229 funcname[1] = KS_EXTRA; 230 funcname[2] = (int)KE_SNR; 231 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); 232 *ufunc = find_func(funcname, FALSE, NULL); 233 if (funcname != buffer) 234 vim_free(funcname); 235 236 if (*ufunc == NULL) 237 { 238 semsg(_(e_item_not_found_in_script_str), name); 239 return -1; 240 } 241 } 242 243 return idx; 244 } 245 246 /* 247 * Handle an ":import" command and add the resulting imported_T to "gap", when 248 * not NULL, or script "import_sid" sn_imports. 249 * Returns a pointer to after the command or NULL in case of failure 250 */ 251 char_u * 252 handle_import( 253 char_u *arg_start, 254 garray_T *gap, 255 int import_sid, 256 evalarg_T *evalarg, 257 void *cctx) 258 { 259 char_u *arg = arg_start; 260 char_u *cmd_end = NULL; 261 char_u *as_name = NULL; 262 int ret = FAIL; 263 typval_T tv; 264 int sid = -1; 265 int res; 266 garray_T names; 267 268 ga_init2(&names, sizeof(char_u *), 10); 269 if (*arg == '{') 270 { 271 // "import {item, item} from ..." 272 arg = skipwhite_and_linebreak(arg + 1, evalarg); 273 for (;;) 274 { 275 char_u *p = arg; 276 int had_comma = FALSE; 277 278 while (eval_isnamec(*arg)) 279 ++arg; 280 if (p == arg) 281 break; 282 if (ga_grow(&names, 1) == FAIL) 283 goto erret; 284 ((char_u **)names.ga_data)[names.ga_len] = 285 vim_strnsave(p, arg - p); 286 ++names.ga_len; 287 if (*arg == ',') 288 { 289 had_comma = TRUE; 290 ++arg; 291 } 292 arg = skipwhite_and_linebreak(arg, evalarg); 293 if (*arg == '}') 294 { 295 arg = skipwhite_and_linebreak(arg + 1, evalarg); 296 break; 297 } 298 if (!had_comma) 299 { 300 emsg(_(e_missing_comma_in_import)); 301 goto erret; 302 } 303 } 304 if (names.ga_len == 0) 305 { 306 emsg(_(e_syntax_error_in_import)); 307 goto erret; 308 } 309 } 310 else 311 { 312 // "import Name from ..." 313 // "import * as Name from ..." 314 // "import item [as Name] from ..." 315 arg = skipwhite_and_linebreak(arg, evalarg); 316 if (arg[0] == '*' && IS_WHITE_OR_NUL(arg[1])) 317 arg = skipwhite_and_linebreak(arg + 1, evalarg); 318 else if (eval_isnamec1(*arg)) 319 { 320 char_u *p = arg; 321 322 while (eval_isnamec(*arg)) 323 ++arg; 324 if (ga_grow(&names, 1) == FAIL) 325 goto erret; 326 ((char_u **)names.ga_data)[names.ga_len] = 327 vim_strnsave(p, arg - p); 328 ++names.ga_len; 329 arg = skipwhite_and_linebreak(arg, evalarg); 330 } 331 else 332 { 333 emsg(_(e_syntax_error_in_import)); 334 goto erret; 335 } 336 337 if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2])) 338 { 339 char_u *p; 340 341 // skip over "as Name "; no line break allowed after "as" 342 arg = skipwhite(arg + 2); 343 p = arg; 344 if (eval_isnamec1(*arg)) 345 while (eval_isnamec(*arg)) 346 ++arg; 347 if (check_defined(p, arg - p, cctx) == FAIL) 348 goto erret; 349 as_name = vim_strnsave(p, arg - p); 350 arg = skipwhite_and_linebreak(arg, evalarg); 351 } 352 else if (*arg_start == '*') 353 { 354 emsg(_(e_missing_as_after_star)); 355 goto erret; 356 } 357 } 358 359 if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4])) 360 { 361 emsg(_(e_missing_from)); 362 goto erret; 363 } 364 365 arg = skipwhite_and_linebreak(arg + 4, evalarg); 366 tv.v_type = VAR_UNKNOWN; 367 // TODO: should we accept any expression? 368 if (*arg == '\'') 369 ret = eval_lit_string(&arg, &tv, TRUE); 370 else if (*arg == '"') 371 ret = eval_string(&arg, &tv, TRUE); 372 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) 373 { 374 emsg(_(e_invalid_string_after_from)); 375 goto erret; 376 } 377 cmd_end = arg; 378 379 /* 380 * find script file 381 */ 382 if (*tv.vval.v_string == '.') 383 { 384 size_t len; 385 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 386 char_u *tail = gettail(si->sn_name); 387 char_u *from_name; 388 389 // Relative to current script: "./name.vim", "../../name.vim". 390 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; 391 from_name = alloc((int)len); 392 if (from_name == NULL) 393 { 394 clear_tv(&tv); 395 goto erret; 396 } 397 vim_strncpy(from_name, si->sn_name, tail - si->sn_name); 398 add_pathsep(from_name); 399 STRCAT(from_name, tv.vval.v_string); 400 simplify_filename(from_name); 401 402 res = do_source(from_name, FALSE, DOSO_NONE, &sid); 403 vim_free(from_name); 404 } 405 else if (mch_isFullName(tv.vval.v_string)) 406 { 407 // Absolute path: "/tmp/name.vim" 408 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); 409 } 410 else 411 { 412 size_t len = 7 + STRLEN(tv.vval.v_string) + 1; 413 char_u *from_name; 414 415 // Find file in "import" subdirs in 'runtimepath'. 416 from_name = alloc((int)len); 417 if (from_name == NULL) 418 { 419 clear_tv(&tv); 420 goto erret; 421 } 422 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); 423 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); 424 vim_free(from_name); 425 } 426 427 if (res == FAIL || sid <= 0) 428 { 429 semsg(_(e_could_not_import_str), tv.vval.v_string); 430 clear_tv(&tv); 431 goto erret; 432 } 433 clear_tv(&tv); 434 435 if (*arg_start == '*') 436 { 437 imported_T *imported = new_imported(gap != NULL ? gap 438 : &SCRIPT_ITEM(import_sid)->sn_imports); 439 440 if (imported == NULL) 441 goto erret; 442 imported->imp_name = as_name; 443 as_name = NULL; 444 imported->imp_sid = sid; 445 imported->imp_all = TRUE; 446 } 447 else 448 { 449 int i; 450 451 arg = arg_start; 452 if (*arg == '{') 453 arg = skipwhite(arg + 1); 454 for (i = 0; i < names.ga_len; ++i) 455 { 456 char_u *name = ((char_u **)names.ga_data)[i]; 457 int idx; 458 imported_T *imported; 459 ufunc_T *ufunc = NULL; 460 type_T *type; 461 462 idx = find_exported(sid, name, &ufunc, &type); 463 464 if (idx < 0 && ufunc == NULL) 465 goto erret; 466 467 if (check_defined(name, STRLEN(name), cctx) == FAIL) 468 goto erret; 469 470 imported = new_imported(gap != NULL ? gap 471 : &SCRIPT_ITEM(import_sid)->sn_imports); 472 if (imported == NULL) 473 goto erret; 474 475 // TODO: check for "as" following 476 // imported->imp_name = vim_strsave(as_name); 477 imported->imp_name = name; 478 ((char_u **)names.ga_data)[i] = NULL; 479 imported->imp_sid = sid; 480 if (idx >= 0) 481 { 482 imported->imp_type = type; 483 imported->imp_var_vals_idx = idx; 484 } 485 else 486 { 487 imported->imp_type = ufunc->uf_func_type; 488 imported->imp_funcname = ufunc->uf_name; 489 } 490 } 491 } 492 erret: 493 ga_clear_strings(&names); 494 vim_free(as_name); 495 return cmd_end; 496 } 497 498 /* 499 * Declare a script-local variable without init: "let var: type". 500 * "const" is an error since the value is missing. 501 * Returns a pointer to after the type. 502 */ 503 char_u * 504 vim9_declare_scriptvar(exarg_T *eap, char_u *arg) 505 { 506 char_u *p; 507 char_u *name; 508 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 509 type_T *type; 510 int called_emsg_before = called_emsg; 511 typval_T init_tv; 512 513 if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const) 514 { 515 if (eap->cmdidx == CMD_final) 516 emsg(_(e_final_requires_a_value)); 517 else 518 emsg(_(e_const_requires_a_value)); 519 return arg + STRLEN(arg); 520 } 521 522 // Check for valid starting character. 523 if (!eval_isnamec1(*arg)) 524 { 525 semsg(_(e_invarg2), arg); 526 return arg + STRLEN(arg); 527 } 528 529 for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) 530 if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1)) 531 break; 532 533 if (*p != ':') 534 { 535 emsg(_(e_type_or_initialization_required)); 536 return arg + STRLEN(arg); 537 } 538 if (!VIM_ISWHITE(p[1])) 539 { 540 semsg(_(e_white_space_required_after_str), ":"); 541 return arg + STRLEN(arg); 542 } 543 name = vim_strnsave(arg, p - arg); 544 545 // parse type 546 p = skipwhite(p + 1); 547 type = parse_type(&p, &si->sn_type_list); 548 if (called_emsg != called_emsg_before) 549 { 550 vim_free(name); 551 return p; 552 } 553 554 // Create the variable with 0/NULL value. 555 CLEAR_FIELD(init_tv); 556 if (type->tt_type == VAR_ANY) 557 // A variable of type "any" is not possible, just use zero instead 558 init_tv.v_type = VAR_NUMBER; 559 else 560 init_tv.v_type = type->tt_type; 561 set_var_const(name, type, &init_tv, FALSE, 0); 562 563 vim_free(name); 564 return p; 565 } 566 567 /* 568 * Check if the type of script variable "dest" allows assigning "value". 569 * If needed convert "value" to a bool. 570 */ 571 int 572 check_script_var_type(typval_T *dest, typval_T *value, char_u *name) 573 { 574 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 575 int idx; 576 577 if (si->sn_version != SCRIPT_VERSION_VIM9) 578 // legacy script doesn't store variable types 579 return OK; 580 581 // Find the svar_T in sn_var_vals. 582 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) 583 { 584 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 585 586 if (sv->sv_tv == dest) 587 { 588 int ret; 589 590 if (sv->sv_const) 591 { 592 semsg(_(e_readonlyvar), name); 593 return FAIL; 594 } 595 ret = check_typval_type(sv->sv_type, value, 0); 596 if (ret == OK && need_convert_to_bool(sv->sv_type, value)) 597 { 598 int val = tv2bool(value); 599 600 clear_tv(value); 601 value->v_type = VAR_BOOL; 602 value->v_lock = 0; 603 value->vval.v_number = val ? VVAL_TRUE : VVAL_FALSE; 604 } 605 return ret; 606 } 607 } 608 iemsg("check_script_var_type(): not found"); 609 return OK; // not really 610 } 611 612 #endif // FEAT_EVAL 613