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