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