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