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 ga_clear(&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 147 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) 148 { 149 emsg(_("E1094: import can only be used in a script")); 150 return; 151 } 152 153 cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL); 154 if (cmd_end != NULL) 155 eap->nextcmd = check_nextcmd(cmd_end); 156 } 157 158 /* 159 * Find an exported item in "sid" matching the name at "*argp". 160 * When it is a variable return the index. 161 * When it is a user function return "*ufunc". 162 * When not found returns -1 and "*ufunc" is NULL. 163 */ 164 int 165 find_exported( 166 int sid, 167 char_u **argp, 168 int *name_len, 169 ufunc_T **ufunc, 170 type_T **type) 171 { 172 char_u *name = *argp; 173 char_u *arg = *argp; 174 int cc; 175 int idx = -1; 176 svar_T *sv; 177 scriptitem_T *script = SCRIPT_ITEM(sid); 178 179 // isolate one name 180 while (eval_isnamec(*arg)) 181 ++arg; 182 *name_len = (int)(arg - name); 183 184 // find name in "script" 185 // TODO: also find script-local user function 186 cc = *arg; 187 *arg = NUL; 188 idx = get_script_item_idx(sid, name, FALSE); 189 if (idx >= 0) 190 { 191 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; 192 if (!sv->sv_export) 193 { 194 semsg(_("E1049: Item not exported in script: %s"), name); 195 *arg = cc; 196 return -1; 197 } 198 *type = sv->sv_type; 199 *ufunc = NULL; 200 } 201 else 202 { 203 char_u buffer[200]; 204 char_u *funcname; 205 206 // it could be a user function. 207 if (STRLEN(name) < sizeof(buffer) - 10) 208 funcname = buffer; 209 else 210 { 211 funcname = alloc(STRLEN(name) + 10); 212 if (funcname == NULL) 213 { 214 *arg = cc; 215 return -1; 216 } 217 } 218 funcname[0] = K_SPECIAL; 219 funcname[1] = KS_EXTRA; 220 funcname[2] = (int)KE_SNR; 221 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); 222 *ufunc = find_func(funcname, FALSE, NULL); 223 if (funcname != buffer) 224 vim_free(funcname); 225 226 if (*ufunc == NULL) 227 { 228 semsg(_("E1048: Item not found in script: %s"), name); 229 *arg = cc; 230 return -1; 231 } 232 } 233 *arg = cc; 234 arg = skipwhite(arg); 235 *argp = arg; 236 237 return idx; 238 } 239 240 /* 241 * Handle an ":import" command and add the resulting imported_T to "gap", when 242 * not NULL, or script "import_sid" sn_imports. 243 * Returns a pointer to after the command or NULL in case of failure 244 */ 245 char_u * 246 handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) 247 { 248 char_u *arg = arg_start; 249 char_u *cmd_end; 250 char_u *as_ptr = NULL; 251 char_u *from_ptr; 252 int as_len = 0; 253 int ret = FAIL; 254 typval_T tv; 255 int sid = -1; 256 int res; 257 258 if (*arg == '{') 259 { 260 // skip over {item} list 261 while (*arg != NUL && *arg != '}') 262 ++arg; 263 if (*arg == '}') 264 arg = skipwhite(arg + 1); 265 } 266 else 267 { 268 if (*arg == '*') 269 arg = skipwhite(arg + 1); 270 else if (eval_isnamec1(*arg)) 271 { 272 while (eval_isnamec(*arg)) 273 ++arg; 274 arg = skipwhite(arg); 275 } 276 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2])) 277 { 278 // skip over "as Name " 279 arg = skipwhite(arg + 2); 280 as_ptr = arg; 281 if (eval_isnamec1(*arg)) 282 while (eval_isnamec(*arg)) 283 ++arg; 284 as_len = (int)(arg - as_ptr); 285 arg = skipwhite(arg); 286 if (check_defined(as_ptr, as_len, cctx) == FAIL) 287 return NULL; 288 } 289 else if (*arg_start == '*') 290 { 291 emsg(_("E1045: Missing \"as\" after *")); 292 return NULL; 293 } 294 } 295 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4])) 296 { 297 emsg(_("E1070: Missing \"from\"")); 298 return NULL; 299 } 300 from_ptr = arg; 301 arg = skipwhite(arg + 4); 302 tv.v_type = VAR_UNKNOWN; 303 // TODO: should we accept any expression? 304 if (*arg == '\'') 305 ret = get_lit_string_tv(&arg, &tv, TRUE); 306 else if (*arg == '"') 307 ret = get_string_tv(&arg, &tv, TRUE); 308 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) 309 { 310 emsg(_("E1071: Invalid string after \"from\"")); 311 return NULL; 312 } 313 cmd_end = arg; 314 315 // find script tv.vval.v_string 316 if (*tv.vval.v_string == '.') 317 { 318 size_t len; 319 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 320 char_u *tail = gettail(si->sn_name); 321 char_u *from_name; 322 323 // Relative to current script: "./name.vim", "../../name.vim". 324 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; 325 from_name = alloc((int)len); 326 if (from_name == NULL) 327 { 328 clear_tv(&tv); 329 return NULL; 330 } 331 vim_strncpy(from_name, si->sn_name, tail - si->sn_name); 332 add_pathsep(from_name); 333 STRCAT(from_name, tv.vval.v_string); 334 simplify_filename(from_name); 335 336 res = do_source(from_name, FALSE, DOSO_NONE, &sid); 337 vim_free(from_name); 338 } 339 else if (mch_isFullName(tv.vval.v_string)) 340 { 341 // Absolute path: "/tmp/name.vim" 342 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); 343 } 344 else 345 { 346 size_t len = 7 + STRLEN(tv.vval.v_string) + 1; 347 char_u *from_name; 348 349 // Find file in "import" subdirs in 'runtimepath'. 350 from_name = alloc((int)len); 351 if (from_name == NULL) 352 { 353 clear_tv(&tv); 354 return NULL; 355 } 356 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); 357 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); 358 vim_free(from_name); 359 } 360 361 if (res == FAIL || sid <= 0) 362 { 363 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string); 364 clear_tv(&tv); 365 return NULL; 366 } 367 clear_tv(&tv); 368 369 if (*arg_start == '*') 370 { 371 imported_T *imported = new_imported(gap != NULL ? gap 372 : &SCRIPT_ITEM(import_sid)->sn_imports); 373 374 if (imported == NULL) 375 return NULL; 376 imported->imp_name = vim_strnsave(as_ptr, as_len); 377 imported->imp_sid = sid; 378 imported->imp_all = TRUE; 379 } 380 else 381 { 382 arg = arg_start; 383 if (*arg == '{') 384 arg = skipwhite(arg + 1); 385 for (;;) 386 { 387 char_u *name = arg; 388 int name_len; 389 int idx; 390 imported_T *imported; 391 ufunc_T *ufunc = NULL; 392 type_T *type; 393 394 idx = find_exported(sid, &arg, &name_len, &ufunc, &type); 395 396 if (idx < 0 && ufunc == NULL) 397 return NULL; 398 399 if (check_defined(name, name_len, cctx) == FAIL) 400 return NULL; 401 402 imported = new_imported(gap != NULL ? gap 403 : &SCRIPT_ITEM(import_sid)->sn_imports); 404 if (imported == NULL) 405 return NULL; 406 407 // TODO: check for "as" following 408 // imported->imp_name = vim_strnsave(as_ptr, as_len); 409 imported->imp_name = vim_strnsave(name, name_len); 410 imported->imp_sid = sid; 411 if (idx >= 0) 412 { 413 imported->imp_type = type; 414 imported->imp_var_vals_idx = idx; 415 } 416 else 417 imported->imp_funcname = ufunc->uf_name; 418 419 arg = skipwhite(arg); 420 if (*arg_start != '{') 421 break; 422 if (*arg == '}') 423 { 424 arg = skipwhite(arg + 1); 425 break; 426 } 427 428 if (*arg != ',') 429 { 430 emsg(_("E1046: Missing comma in import")); 431 return NULL; 432 } 433 arg = skipwhite(arg + 1); 434 } 435 if (arg != from_ptr) 436 { 437 // cannot happen, just in case the above has a flaw 438 emsg(_("E1047: syntax error in import")); 439 return NULL; 440 } 441 } 442 return cmd_end; 443 } 444 445 /* 446 * Declare a script-local variable without init: "let var: type". 447 * "const" is an error since the value is missing. 448 * Returns a pointer to after the type. 449 */ 450 char_u * 451 vim9_declare_scriptvar(exarg_T *eap, char_u *arg) 452 { 453 char_u *p; 454 char_u *name; 455 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 456 type_T *type; 457 int called_emsg_before = called_emsg; 458 typval_T init_tv; 459 460 if (eap->cmdidx == CMD_const) 461 { 462 emsg(_(e_const_req_value)); 463 return arg + STRLEN(arg); 464 } 465 466 // Check for valid starting character. 467 if (!eval_isnamec1(*arg)) 468 { 469 semsg(_(e_invarg2), arg); 470 return arg + STRLEN(arg); 471 } 472 473 for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) 474 if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1)) 475 break; 476 477 if (*p != ':') 478 { 479 emsg(_(e_type_req)); 480 return arg + STRLEN(arg); 481 } 482 if (!VIM_ISWHITE(p[1])) 483 { 484 semsg(_(e_white_after), ":"); 485 return arg + STRLEN(arg); 486 } 487 name = vim_strnsave(arg, p - arg); 488 489 // parse type 490 p = skipwhite(p + 1); 491 type = parse_type(&p, &si->sn_type_list); 492 if (called_emsg != called_emsg_before) 493 { 494 vim_free(name); 495 return p; 496 } 497 498 // Create the variable with 0/NULL value. 499 CLEAR_FIELD(init_tv); 500 init_tv.v_type = type->tt_type; 501 set_var_const(name, type, &init_tv, FALSE, 0); 502 503 vim_free(name); 504 return p; 505 } 506 507 /* 508 * Check if the type of script variable "dest" allows assigning "value". 509 */ 510 int 511 check_script_var_type(typval_T *dest, typval_T *value, char_u *name) 512 { 513 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 514 int idx; 515 516 // Find the svar_T in sn_var_vals. 517 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) 518 { 519 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; 520 521 if (sv->sv_tv == dest) 522 { 523 if (sv->sv_const) 524 { 525 semsg(_(e_readonlyvar), name); 526 return FAIL; 527 } 528 return check_type(sv->sv_type, typval2type(value), TRUE); 529 } 530 } 531 iemsg("check_script_var_type(): not found"); 532 return OK; // not really 533 } 534 535 #endif // FEAT_EVAL 536