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