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