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: export can only be used in vim9script"); 21 22 int 23 in_vim9script(void) 24 { 25 // Do not go up the stack, a ":function" inside vim9script uses legacy 26 // syntax. "sc_version" is also set when compiling a ":def" function in 27 // legacy script. 28 return current_sctx.sc_version == SCRIPT_VERSION_VIM9; 29 } 30 31 /* 32 * ":vim9script". 33 */ 34 void 35 ex_vim9script(exarg_T *eap) 36 { 37 scriptitem_T *si; 38 39 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) 40 { 41 emsg(_("E1038: vim9script can only be used in a script")); 42 return; 43 } 44 si = SCRIPT_ITEM(current_sctx.sc_sid); 45 if (si->sn_had_command) 46 { 47 emsg(_("E1039: vim9script must be the first command in a script")); 48 return; 49 } 50 current_sctx.sc_version = SCRIPT_VERSION_VIM9; 51 si->sn_version = SCRIPT_VERSION_VIM9; 52 si->sn_had_command = TRUE; 53 54 if (STRCMP(p_cpo, CPO_VIM) != 0) 55 { 56 si->sn_save_cpo = p_cpo; 57 p_cpo = vim_strsave((char_u *)CPO_VIM); 58 } 59 } 60 61 /* 62 * When in Vim9 script give an error and return FAIL. 63 */ 64 int 65 not_in_vim9(exarg_T *eap) 66 { 67 if (in_vim9script()) 68 switch (eap->cmdidx) 69 { 70 case CMD_insert: 71 case CMD_append: 72 case CMD_change: 73 case CMD_xit: 74 semsg(_("E1100: Missing :let: %s"), eap->cmd); 75 return FAIL; 76 default: break; 77 } 78 return OK; 79 } 80 81 /* 82 * ":export let Name: type" 83 * ":export const Name: type" 84 * ":export def Name(..." 85 * ":export class Name ..." 86 */ 87 void 88 ex_export(exarg_T *eap) 89 { 90 if (!in_vim9script()) 91 { 92 emsg(_(e_needs_vim9)); 93 return; 94 } 95 96 eap->cmd = eap->arg; 97 (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL); 98 switch (eap->cmdidx) 99 { 100 case CMD_let: 101 case CMD_const: 102 case CMD_def: 103 // case CMD_class: 104 is_export = TRUE; 105 do_cmdline(eap->cmd, eap->getline, eap->cookie, 106 DOCMD_VERBOSE + DOCMD_NOWAIT); 107 108 // The command will reset "is_export" when exporting an item. 109 if (is_export) 110 { 111 emsg(_("E1044: export with invalid argument")); 112 is_export = FALSE; 113 } 114 break; 115 default: 116 emsg(_("E1043: Invalid command after :export")); 117 break; 118 } 119 } 120 121 /* 122 * Add a new imported item entry to the current script. 123 */ 124 static imported_T * 125 new_imported(garray_T *gap) 126 { 127 if (ga_grow(gap, 1) == OK) 128 return ((imported_T *)gap->ga_data + gap->ga_len++); 129 return NULL; 130 } 131 132 /* 133 * Free all imported items in script "sid". 134 */ 135 void 136 free_imports(int sid) 137 { 138 scriptitem_T *si = SCRIPT_ITEM(sid); 139 int idx; 140 141 for (idx = 0; idx < si->sn_imports.ga_len; ++idx) 142 { 143 imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx; 144 145 vim_free(imp->imp_name); 146 } 147 ga_clear(&si->sn_imports); 148 ga_clear(&si->sn_var_vals); 149 clear_type_list(&si->sn_type_list); 150 } 151 152 /* 153 * ":import Item from 'filename'" 154 * ":import Item as Alias from 'filename'" 155 * ":import {Item} from 'filename'". 156 * ":import {Item as Alias} from 'filename'" 157 * ":import {Item, Item} from 'filename'" 158 * ":import {Item, Item as Alias} from 'filename'" 159 * 160 * ":import * as Name from 'filename'" 161 */ 162 void 163 ex_import(exarg_T *eap) 164 { 165 char_u *cmd_end; 166 evalarg_T evalarg; 167 168 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) 169 { 170 emsg(_("E1094: import can only be used in a script")); 171 return; 172 } 173 fill_evalarg_from_eap(&evalarg, eap, eap->skip); 174 175 cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, 176 &evalarg, NULL); 177 if (cmd_end != NULL) 178 eap->nextcmd = check_nextcmd(cmd_end); 179 clear_evalarg(&evalarg, eap); 180 } 181 182 /* 183 * Find an exported item in "sid" matching the name at "*argp". 184 * When it is a variable return the index. 185 * When it is a user function return "*ufunc". 186 * When not found returns -1 and "*ufunc" is NULL. 187 */ 188 int 189 find_exported( 190 int sid, 191 char_u *name, 192 ufunc_T **ufunc, 193 type_T **type) 194 { 195 int idx = -1; 196 svar_T *sv; 197 scriptitem_T *script = SCRIPT_ITEM(sid); 198 199 // find name in "script" 200 // TODO: also find script-local user function 201 idx = get_script_item_idx(sid, name, FALSE); 202 if (idx >= 0) 203 { 204 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; 205 if (!sv->sv_export) 206 { 207 semsg(_("E1049: Item not exported in script: %s"), name); 208 return -1; 209 } 210 *type = sv->sv_type; 211 *ufunc = NULL; 212 } 213 else 214 { 215 char_u buffer[200]; 216 char_u *funcname; 217 218 // it could be a user function. 219 if (STRLEN(name) < sizeof(buffer) - 15) 220 funcname = buffer; 221 else 222 { 223 funcname = alloc(STRLEN(name) + 15); 224 if (funcname == NULL) 225 return -1; 226 } 227 funcname[0] = K_SPECIAL; 228 funcname[1] = KS_EXTRA; 229 funcname[2] = (int)KE_SNR; 230 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); 231 *ufunc = find_func(funcname, FALSE, NULL); 232 if (funcname != buffer) 233 vim_free(funcname); 234 235 if (*ufunc == NULL) 236 { 237 semsg(_("E1048: Item not found in script: %s"), name); 238 return -1; 239 } 240 } 241 242 return idx; 243 } 244 245 /* 246 * Handle an ":import" command and add the resulting imported_T to "gap", when 247 * not NULL, or script "import_sid" sn_imports. 248 * Returns a pointer to after the command or NULL in case of failure 249 */ 250 char_u * 251 handle_import( 252 char_u *arg_start, 253 garray_T *gap, 254 int import_sid, 255 evalarg_T *evalarg, 256 void *cctx) 257 { 258 char_u *arg = arg_start; 259 char_u *cmd_end = NULL; 260 char_u *as_name = NULL; 261 int ret = FAIL; 262 typval_T tv; 263 int sid = -1; 264 int res; 265 garray_T names; 266 static char e_import_syntax[] = N_("E1047: syntax error in import"); 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(_("E1046: Missing comma in import")); 301 goto erret; 302 } 303 } 304 if (names.ga_len == 0) 305 { 306 emsg(_(e_import_syntax)); 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_import_syntax)); 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(_("E1045: Missing \"as\" after *")); 355 goto erret; 356 } 357 } 358 359 if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4])) 360 { 361 emsg(_("E1070: 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(_("E1071: 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(_("E1053: Could not import \"%s\""), 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_const) 514 { 515 emsg(_(e_const_req_value)); 516 return arg + STRLEN(arg); 517 } 518 519 // Check for valid starting character. 520 if (!eval_isnamec1(*arg)) 521 { 522 semsg(_(e_invarg2), arg); 523 return arg + STRLEN(arg); 524 } 525 526 for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) 527 if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1)) 528 break; 529 530 if (*p != ':') 531 { 532 emsg(_(e_type_req)); 533 return arg + STRLEN(arg); 534 } 535 if (!VIM_ISWHITE(p[1])) 536 { 537 semsg(_(e_white_after), ":"); 538 return arg + STRLEN(arg); 539 } 540 name = vim_strnsave(arg, p - arg); 541 542 // parse type 543 p = skipwhite(p + 1); 544 type = parse_type(&p, &si->sn_type_list); 545 if (called_emsg != called_emsg_before) 546 { 547 vim_free(name); 548 return p; 549 } 550 551 // Create the variable with 0/NULL value. 552 CLEAR_FIELD(init_tv); 553 init_tv.v_type = type->tt_type; 554 set_var_const(name, type, &init_tv, FALSE, 0); 555 556 vim_free(name); 557 return p; 558 } 559 560 /* 561 * Check if the type of script variable "dest" allows assigning "value". 562 */ 563 int 564 check_script_var_type(typval_T *dest, typval_T *value, char_u *name) 565 { 566 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 567 int idx; 568 569 // Find the svar_T in sn_var_vals. 570 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) 571 { 572 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 573 574 if (sv->sv_tv == dest) 575 { 576 if (sv->sv_const) 577 { 578 semsg(_(e_readonlyvar), name); 579 return FAIL; 580 } 581 return check_typval_type(sv->sv_type, value); 582 } 583 } 584 iemsg("check_script_var_type(): not found"); 585 return OK; // not really 586 } 587 588 #endif // FEAT_EVAL 589