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