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_append: 71 case CMD_change: 72 case CMD_insert: 73 case CMD_t: 74 case CMD_xit: 75 semsg(_("E1100: Missing :let: %s"), eap->cmd); 76 return FAIL; 77 default: break; 78 } 79 return OK; 80 } 81 82 /* 83 * ":export let Name: type" 84 * ":export const Name: type" 85 * ":export def Name(..." 86 * ":export class Name ..." 87 */ 88 void 89 ex_export(exarg_T *eap) 90 { 91 if (!in_vim9script()) 92 { 93 emsg(_(e_needs_vim9)); 94 return; 95 } 96 97 eap->cmd = eap->arg; 98 (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL); 99 switch (eap->cmdidx) 100 { 101 case CMD_let: 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(_("E1044: export with invalid argument")); 113 is_export = FALSE; 114 } 115 break; 116 default: 117 emsg(_("E1043: 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(_("E1094: import can only be used in a 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(_("E1049: Item not exported in script: %s"), 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(_("E1048: Item not found in script: %s"), 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 static char e_import_syntax[] = N_("E1047: syntax error in import"); 268 269 ga_init2(&names, sizeof(char_u *), 10); 270 if (*arg == '{') 271 { 272 // "import {item, item} from ..." 273 arg = skipwhite_and_linebreak(arg + 1, evalarg); 274 for (;;) 275 { 276 char_u *p = arg; 277 int had_comma = FALSE; 278 279 while (eval_isnamec(*arg)) 280 ++arg; 281 if (p == arg) 282 break; 283 if (ga_grow(&names, 1) == FAIL) 284 goto erret; 285 ((char_u **)names.ga_data)[names.ga_len] = 286 vim_strnsave(p, arg - p); 287 ++names.ga_len; 288 if (*arg == ',') 289 { 290 had_comma = TRUE; 291 ++arg; 292 } 293 arg = skipwhite_and_linebreak(arg, evalarg); 294 if (*arg == '}') 295 { 296 arg = skipwhite_and_linebreak(arg + 1, evalarg); 297 break; 298 } 299 if (!had_comma) 300 { 301 emsg(_("E1046: Missing comma in import")); 302 goto erret; 303 } 304 } 305 if (names.ga_len == 0) 306 { 307 emsg(_(e_import_syntax)); 308 goto erret; 309 } 310 } 311 else 312 { 313 // "import Name from ..." 314 // "import * as Name from ..." 315 // "import item [as Name] from ..." 316 arg = skipwhite_and_linebreak(arg, evalarg); 317 if (arg[0] == '*' && IS_WHITE_OR_NUL(arg[1])) 318 arg = skipwhite_and_linebreak(arg + 1, evalarg); 319 else if (eval_isnamec1(*arg)) 320 { 321 char_u *p = arg; 322 323 while (eval_isnamec(*arg)) 324 ++arg; 325 if (ga_grow(&names, 1) == FAIL) 326 goto erret; 327 ((char_u **)names.ga_data)[names.ga_len] = 328 vim_strnsave(p, arg - p); 329 ++names.ga_len; 330 arg = skipwhite_and_linebreak(arg, evalarg); 331 } 332 else 333 { 334 emsg(_(e_import_syntax)); 335 goto erret; 336 } 337 338 if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2])) 339 { 340 char_u *p; 341 342 // skip over "as Name "; no line break allowed after "as" 343 arg = skipwhite(arg + 2); 344 p = arg; 345 if (eval_isnamec1(*arg)) 346 while (eval_isnamec(*arg)) 347 ++arg; 348 if (check_defined(p, arg - p, cctx) == FAIL) 349 goto erret; 350 as_name = vim_strnsave(p, arg - p); 351 arg = skipwhite_and_linebreak(arg, evalarg); 352 } 353 else if (*arg_start == '*') 354 { 355 emsg(_("E1045: Missing \"as\" after *")); 356 goto erret; 357 } 358 } 359 360 if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4])) 361 { 362 emsg(_("E1070: Missing \"from\"")); 363 goto erret; 364 } 365 366 arg = skipwhite_and_linebreak(arg + 4, evalarg); 367 tv.v_type = VAR_UNKNOWN; 368 // TODO: should we accept any expression? 369 if (*arg == '\'') 370 ret = eval_lit_string(&arg, &tv, TRUE); 371 else if (*arg == '"') 372 ret = eval_string(&arg, &tv, TRUE); 373 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) 374 { 375 emsg(_("E1071: Invalid string after \"from\"")); 376 goto erret; 377 } 378 cmd_end = arg; 379 380 /* 381 * find script file 382 */ 383 if (*tv.vval.v_string == '.') 384 { 385 size_t len; 386 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 387 char_u *tail = gettail(si->sn_name); 388 char_u *from_name; 389 390 // Relative to current script: "./name.vim", "../../name.vim". 391 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; 392 from_name = alloc((int)len); 393 if (from_name == NULL) 394 { 395 clear_tv(&tv); 396 goto erret; 397 } 398 vim_strncpy(from_name, si->sn_name, tail - si->sn_name); 399 add_pathsep(from_name); 400 STRCAT(from_name, tv.vval.v_string); 401 simplify_filename(from_name); 402 403 res = do_source(from_name, FALSE, DOSO_NONE, &sid); 404 vim_free(from_name); 405 } 406 else if (mch_isFullName(tv.vval.v_string)) 407 { 408 // Absolute path: "/tmp/name.vim" 409 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); 410 } 411 else 412 { 413 size_t len = 7 + STRLEN(tv.vval.v_string) + 1; 414 char_u *from_name; 415 416 // Find file in "import" subdirs in 'runtimepath'. 417 from_name = alloc((int)len); 418 if (from_name == NULL) 419 { 420 clear_tv(&tv); 421 goto erret; 422 } 423 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); 424 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); 425 vim_free(from_name); 426 } 427 428 if (res == FAIL || sid <= 0) 429 { 430 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string); 431 clear_tv(&tv); 432 goto erret; 433 } 434 clear_tv(&tv); 435 436 if (*arg_start == '*') 437 { 438 imported_T *imported = new_imported(gap != NULL ? gap 439 : &SCRIPT_ITEM(import_sid)->sn_imports); 440 441 if (imported == NULL) 442 goto erret; 443 imported->imp_name = as_name; 444 as_name = NULL; 445 imported->imp_sid = sid; 446 imported->imp_all = TRUE; 447 } 448 else 449 { 450 int i; 451 452 arg = arg_start; 453 if (*arg == '{') 454 arg = skipwhite(arg + 1); 455 for (i = 0; i < names.ga_len; ++i) 456 { 457 char_u *name = ((char_u **)names.ga_data)[i]; 458 int idx; 459 imported_T *imported; 460 ufunc_T *ufunc = NULL; 461 type_T *type; 462 463 idx = find_exported(sid, name, &ufunc, &type); 464 465 if (idx < 0 && ufunc == NULL) 466 goto erret; 467 468 if (check_defined(name, STRLEN(name), cctx) == FAIL) 469 goto erret; 470 471 imported = new_imported(gap != NULL ? gap 472 : &SCRIPT_ITEM(import_sid)->sn_imports); 473 if (imported == NULL) 474 goto erret; 475 476 // TODO: check for "as" following 477 // imported->imp_name = vim_strsave(as_name); 478 imported->imp_name = name; 479 ((char_u **)names.ga_data)[i] = NULL; 480 imported->imp_sid = sid; 481 if (idx >= 0) 482 { 483 imported->imp_type = type; 484 imported->imp_var_vals_idx = idx; 485 } 486 else 487 { 488 imported->imp_type = ufunc->uf_func_type; 489 imported->imp_funcname = ufunc->uf_name; 490 } 491 } 492 } 493 erret: 494 ga_clear_strings(&names); 495 vim_free(as_name); 496 return cmd_end; 497 } 498 499 /* 500 * Declare a script-local variable without init: "let var: type". 501 * "const" is an error since the value is missing. 502 * Returns a pointer to after the type. 503 */ 504 char_u * 505 vim9_declare_scriptvar(exarg_T *eap, char_u *arg) 506 { 507 char_u *p; 508 char_u *name; 509 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 510 type_T *type; 511 int called_emsg_before = called_emsg; 512 typval_T init_tv; 513 514 if (eap->cmdidx == CMD_const) 515 { 516 emsg(_(e_const_req_value)); 517 return arg + STRLEN(arg); 518 } 519 520 // Check for valid starting character. 521 if (!eval_isnamec1(*arg)) 522 { 523 semsg(_(e_invarg2), arg); 524 return arg + STRLEN(arg); 525 } 526 527 for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) 528 if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1)) 529 break; 530 531 if (*p != ':') 532 { 533 emsg(_(e_type_req)); 534 return arg + STRLEN(arg); 535 } 536 if (!VIM_ISWHITE(p[1])) 537 { 538 semsg(_(e_white_after), ":"); 539 return arg + STRLEN(arg); 540 } 541 name = vim_strnsave(arg, p - arg); 542 543 // parse type 544 p = skipwhite(p + 1); 545 type = parse_type(&p, &si->sn_type_list); 546 if (called_emsg != called_emsg_before) 547 { 548 vim_free(name); 549 return p; 550 } 551 552 // Create the variable with 0/NULL value. 553 CLEAR_FIELD(init_tv); 554 init_tv.v_type = type->tt_type; 555 set_var_const(name, type, &init_tv, FALSE, 0); 556 557 vim_free(name); 558 return p; 559 } 560 561 /* 562 * Check if the type of script variable "dest" allows assigning "value". 563 */ 564 int 565 check_script_var_type(typval_T *dest, typval_T *value, char_u *name) 566 { 567 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 568 int idx; 569 570 // Find the svar_T in sn_var_vals. 571 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) 572 { 573 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 574 575 if (sv->sv_tv == dest) 576 { 577 if (sv->sv_const) 578 { 579 semsg(_(e_readonlyvar), name); 580 return FAIL; 581 } 582 return check_typval_type(sv->sv_type, value); 583 } 584 } 585 iemsg("check_script_var_type(): not found"); 586 return OK; // not really 587 } 588 589 #endif // FEAT_EVAL 590