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