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 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 UNUSED) 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 if (current_sctx.sc_version != SCRIPT_VERSION_VIM9) 145 emsg(_(e_needs_vim9)); 146 else 147 { 148 char_u *cmd_end = handle_import(eap->arg, NULL, 149 current_sctx.sc_sid, NULL); 150 151 if (cmd_end != NULL) 152 eap->nextcmd = check_nextcmd(cmd_end); 153 } 154 } 155 156 /* 157 * Find an exported item in "sid" matching the name at "*argp". 158 * When it is a variable return the index. 159 * When it is a user function return "*ufunc". 160 * When not found returns -1 and "*ufunc" is NULL. 161 */ 162 int 163 find_exported( 164 int sid, 165 char_u **argp, 166 int *name_len, 167 ufunc_T **ufunc, 168 type_T **type) 169 { 170 char_u *name = *argp; 171 char_u *arg = *argp; 172 int cc; 173 int idx = -1; 174 svar_T *sv; 175 scriptitem_T *script = SCRIPT_ITEM(sid); 176 177 // isolate one name 178 while (eval_isnamec(*arg)) 179 ++arg; 180 *name_len = (int)(arg - name); 181 182 // find name in "script" 183 // TODO: also find script-local user function 184 cc = *arg; 185 *arg = NUL; 186 idx = get_script_item_idx(sid, name, FALSE); 187 if (idx >= 0) 188 { 189 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; 190 if (!sv->sv_export) 191 { 192 semsg(_("E1049: Item not exported in script: %s"), name); 193 *arg = cc; 194 return -1; 195 } 196 *type = sv->sv_type; 197 *ufunc = NULL; 198 } 199 else 200 { 201 char_u buffer[200]; 202 char_u *funcname; 203 204 // it could be a user function. 205 if (STRLEN(name) < sizeof(buffer) - 10) 206 funcname = buffer; 207 else 208 { 209 funcname = alloc(STRLEN(name) + 10); 210 if (funcname == NULL) 211 { 212 *arg = cc; 213 return -1; 214 } 215 } 216 funcname[0] = K_SPECIAL; 217 funcname[1] = KS_EXTRA; 218 funcname[2] = (int)KE_SNR; 219 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); 220 *ufunc = find_func(funcname, FALSE, NULL); 221 if (funcname != buffer) 222 vim_free(funcname); 223 224 if (*ufunc == NULL) 225 { 226 semsg(_("E1048: Item not found in script: %s"), name); 227 *arg = cc; 228 return -1; 229 } 230 } 231 *arg = cc; 232 arg = skipwhite(arg); 233 *argp = arg; 234 235 return idx; 236 } 237 238 /* 239 * Handle an ":import" command and add the resulting imported_T to "gap", when 240 * not NULL, or script "import_sid" sn_imports. 241 * Returns a pointer to after the command or NULL in case of failure 242 */ 243 char_u * 244 handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) 245 { 246 char_u *arg = arg_start; 247 char_u *cmd_end; 248 char_u *as_ptr = NULL; 249 char_u *from_ptr; 250 int as_len = 0; 251 int ret = FAIL; 252 typval_T tv; 253 int sid = -1; 254 int res; 255 256 if (*arg == '{') 257 { 258 // skip over {item} list 259 while (*arg != NUL && *arg != '}') 260 ++arg; 261 if (*arg == '}') 262 arg = skipwhite(arg + 1); 263 } 264 else 265 { 266 if (*arg == '*') 267 arg = skipwhite(arg + 1); 268 else if (eval_isnamec1(*arg)) 269 { 270 while (eval_isnamec(*arg)) 271 ++arg; 272 arg = skipwhite(arg); 273 } 274 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2])) 275 { 276 // skip over "as Name " 277 arg = skipwhite(arg + 2); 278 as_ptr = arg; 279 if (eval_isnamec1(*arg)) 280 while (eval_isnamec(*arg)) 281 ++arg; 282 as_len = (int)(arg - as_ptr); 283 arg = skipwhite(arg); 284 if (check_defined(as_ptr, as_len, cctx) == FAIL) 285 return NULL; 286 } 287 else if (*arg_start == '*') 288 { 289 emsg(_("E1045: Missing \"as\" after *")); 290 return NULL; 291 } 292 } 293 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4])) 294 { 295 emsg(_("E1070: Missing \"from\"")); 296 return NULL; 297 } 298 from_ptr = arg; 299 arg = skipwhite(arg + 4); 300 tv.v_type = VAR_UNKNOWN; 301 // TODO: should we accept any expression? 302 if (*arg == '\'') 303 ret = get_lit_string_tv(&arg, &tv, TRUE); 304 else if (*arg == '"') 305 ret = get_string_tv(&arg, &tv, TRUE); 306 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) 307 { 308 emsg(_("E1071: Invalid string after \"from\"")); 309 return NULL; 310 } 311 cmd_end = arg; 312 313 // find script tv.vval.v_string 314 if (*tv.vval.v_string == '.') 315 { 316 size_t len; 317 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); 318 char_u *tail = gettail(si->sn_name); 319 char_u *from_name; 320 321 // Relative to current script: "./name.vim", "../../name.vim". 322 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; 323 from_name = alloc((int)len); 324 if (from_name == NULL) 325 { 326 clear_tv(&tv); 327 return NULL; 328 } 329 vim_strncpy(from_name, si->sn_name, tail - si->sn_name); 330 add_pathsep(from_name); 331 STRCAT(from_name, tv.vval.v_string); 332 simplify_filename(from_name); 333 334 res = do_source(from_name, FALSE, DOSO_NONE, &sid); 335 vim_free(from_name); 336 } 337 else if (mch_isFullName(tv.vval.v_string)) 338 { 339 // Absolute path: "/tmp/name.vim" 340 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); 341 } 342 else 343 { 344 size_t len = 7 + STRLEN(tv.vval.v_string) + 1; 345 char_u *from_name; 346 347 // Find file in "import" subdirs in 'runtimepath'. 348 from_name = alloc((int)len); 349 if (from_name == NULL) 350 { 351 clear_tv(&tv); 352 return NULL; 353 } 354 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); 355 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); 356 vim_free(from_name); 357 } 358 359 if (res == FAIL || sid <= 0) 360 { 361 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string); 362 clear_tv(&tv); 363 return NULL; 364 } 365 clear_tv(&tv); 366 367 if (*arg_start == '*') 368 { 369 imported_T *imported = new_imported(gap != NULL ? gap 370 : &SCRIPT_ITEM(import_sid)->sn_imports); 371 372 if (imported == NULL) 373 return NULL; 374 imported->imp_name = vim_strnsave(as_ptr, as_len); 375 imported->imp_sid = sid; 376 imported->imp_all = TRUE; 377 } 378 else 379 { 380 arg = arg_start; 381 if (*arg == '{') 382 arg = skipwhite(arg + 1); 383 for (;;) 384 { 385 char_u *name = arg; 386 int name_len; 387 int idx; 388 imported_T *imported; 389 ufunc_T *ufunc = NULL; 390 type_T *type; 391 392 idx = find_exported(sid, &arg, &name_len, &ufunc, &type); 393 394 if (idx < 0 && ufunc == NULL) 395 return NULL; 396 397 if (check_defined(name, name_len, cctx) == FAIL) 398 return NULL; 399 400 imported = new_imported(gap != NULL ? gap 401 : &SCRIPT_ITEM(import_sid)->sn_imports); 402 if (imported == NULL) 403 return NULL; 404 405 // TODO: check for "as" following 406 // imported->imp_name = vim_strnsave(as_ptr, as_len); 407 imported->imp_name = vim_strnsave(name, name_len); 408 imported->imp_sid = sid; 409 if (idx >= 0) 410 { 411 imported->imp_type = type; 412 imported->imp_var_vals_idx = idx; 413 } 414 else 415 imported->imp_funcname = ufunc->uf_name; 416 417 arg = skipwhite(arg); 418 if (*arg_start != '{') 419 break; 420 if (*arg == '}') 421 { 422 arg = skipwhite(arg + 1); 423 break; 424 } 425 426 if (*arg != ',') 427 { 428 emsg(_("E1046: Missing comma in import")); 429 return NULL; 430 } 431 arg = skipwhite(arg + 1); 432 } 433 if (arg != from_ptr) 434 { 435 // cannot happen, just in case the above has a flaw 436 emsg(_("E1047: syntax error in import")); 437 return NULL; 438 } 439 } 440 return cmd_end; 441 } 442 443 #endif // FEAT_EVAL 444