To: vim_dev@googlegroups.com Subject: Patch 8.2.1124 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1124 Problem: Vim9: no line break allowed in :import command. Solution: Skip over line breaks. Files: src/vim9script.c, src/proto/vim9script.pro, src/vim9compile.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.1123/src/vim9script.c 2020-07-01 18:29:23.685143414 +0200 --- src/vim9script.c 2020-07-04 13:12:10.543506786 +0200 *************** *** 142,158 **** void ex_import(exarg_T *eap) { ! char_u *cmd_end; if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { emsg(_("E1094: import can only be used in a script")); return; } ! cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL); if (cmd_end != NULL) eap->nextcmd = check_nextcmd(cmd_end); } /* --- 142,162 ---- void ex_import(exarg_T *eap) { ! char_u *cmd_end; ! evalarg_T evalarg; if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { emsg(_("E1094: import can only be used in a script")); return; } + fill_evalarg_from_eap(&evalarg, eap, eap->skip); ! cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, ! &evalarg, NULL); if (cmd_end != NULL) eap->nextcmd = check_nextcmd(cmd_end); + clear_evalarg(&evalarg, eap); } /* *************** *** 164,190 **** int find_exported( int sid, ! char_u **argp, ! int *name_len, ufunc_T **ufunc, type_T **type) { - char_u *name = *argp; - char_u *arg = *argp; - int cc; int idx = -1; svar_T *sv; scriptitem_T *script = SCRIPT_ITEM(sid); - // isolate one name - while (eval_isnamec(*arg)) - ++arg; - *name_len = (int)(arg - name); - // find name in "script" // TODO: also find script-local user function - cc = *arg; - *arg = NUL; idx = get_script_item_idx(sid, name, FALSE); if (idx >= 0) { --- 168,183 ---- int find_exported( int sid, ! char_u *name, ufunc_T **ufunc, type_T **type) { int idx = -1; svar_T *sv; scriptitem_T *script = SCRIPT_ITEM(sid); // find name in "script" // TODO: also find script-local user function idx = get_script_item_idx(sid, name, FALSE); if (idx >= 0) { *************** *** 192,198 **** if (!sv->sv_export) { semsg(_("E1049: Item not exported in script: %s"), name); - *arg = cc; return -1; } *type = sv->sv_type; --- 185,190 ---- *************** *** 210,219 **** { funcname = alloc(STRLEN(name) + 10); if (funcname == NULL) - { - *arg = cc; return -1; - } } funcname[0] = K_SPECIAL; funcname[1] = KS_EXTRA; --- 202,208 ---- *************** *** 226,238 **** if (*ufunc == NULL) { semsg(_("E1048: Item not found in script: %s"), name); - *arg = cc; return -1; } } - *arg = cc; - arg = skipwhite(arg); - *argp = arg; return idx; } --- 215,223 ---- *************** *** 243,304 **** * Returns a pointer to after the command or NULL in case of failure */ char_u * ! handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx) { char_u *arg = arg_start; ! char_u *cmd_end; ! char_u *as_ptr = NULL; ! char_u *from_ptr; ! int as_len = 0; int ret = FAIL; typval_T tv; int sid = -1; int res; if (*arg == '{') { ! // skip over {item} list ! while (*arg != NUL && *arg != '}') ! ++arg; ! if (*arg == '}') ! arg = skipwhite(arg + 1); } else { ! if (*arg == '*') ! arg = skipwhite(arg + 1); else if (eval_isnamec1(*arg)) { while (eval_isnamec(*arg)) ++arg; ! arg = skipwhite(arg); } ! if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2])) { ! // skip over "as Name " arg = skipwhite(arg + 2); ! as_ptr = arg; if (eval_isnamec1(*arg)) while (eval_isnamec(*arg)) ++arg; ! as_len = (int)(arg - as_ptr); ! arg = skipwhite(arg); ! if (check_defined(as_ptr, as_len, cctx) == FAIL) ! return NULL; } else if (*arg_start == '*') { emsg(_("E1045: Missing \"as\" after *")); ! return NULL; } } ! if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4])) { emsg(_("E1070: Missing \"from\"")); ! return NULL; } ! from_ptr = arg; ! arg = skipwhite(arg + 4); tv.v_type = VAR_UNKNOWN; // TODO: should we accept any expression? if (*arg == '\'') --- 228,348 ---- * Returns a pointer to after the command or NULL in case of failure */ char_u * ! handle_import( ! char_u *arg_start, ! garray_T *gap, ! int import_sid, ! evalarg_T *evalarg, ! void *cctx) { char_u *arg = arg_start; ! char_u *cmd_end = NULL; ! char_u *as_name = NULL; int ret = FAIL; typval_T tv; int sid = -1; int res; + garray_T names; + static char e_import_syntax[] = N_("E1047: syntax error in import"); + ga_init2(&names, sizeof(char_u *), 10); if (*arg == '{') { ! // "import {item, item} from ..." ! arg = skipwhite_and_linebreak(arg + 1, evalarg); ! for (;;) ! { ! char_u *p = arg; ! int had_comma = FALSE; ! ! while (eval_isnamec(*arg)) ! ++arg; ! if (p == arg) ! break; ! if (ga_grow(&names, 1) == FAIL) ! goto erret; ! ((char_u **)names.ga_data)[names.ga_len] = ! vim_strnsave(p, arg - p); ! ++names.ga_len; ! if (*arg == ',') ! { ! had_comma = TRUE; ! ++arg; ! } ! arg = skipwhite_and_linebreak(arg, evalarg); ! if (*arg == '}') ! { ! arg = skipwhite_and_linebreak(arg + 1, evalarg); ! break; ! } ! if (!had_comma) ! { ! emsg(_("E1046: Missing comma in import")); ! goto erret; ! } ! } ! if (names.ga_len == 0) ! { ! emsg(_(e_import_syntax)); ! goto erret; ! } } else { ! // "import Name from ..." ! // "import * as Name from ..." ! // "import item [as Name] from ..." ! arg = skipwhite_and_linebreak(arg, evalarg); ! if (arg[0] == '*' && IS_WHITE_OR_NUL(arg[1])) ! arg = skipwhite_and_linebreak(arg + 1, evalarg); else if (eval_isnamec1(*arg)) { + char_u *p = arg; + while (eval_isnamec(*arg)) ++arg; ! if (ga_grow(&names, 1) == FAIL) ! goto erret; ! ((char_u **)names.ga_data)[names.ga_len] = ! vim_strnsave(p, arg - p); ! ++names.ga_len; ! arg = skipwhite_and_linebreak(arg, evalarg); ! } ! else ! { ! emsg(_(e_import_syntax)); ! goto erret; } ! ! if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2])) { ! char_u *p; ! ! // skip over "as Name "; no line break allowed after "as" arg = skipwhite(arg + 2); ! p = arg; if (eval_isnamec1(*arg)) while (eval_isnamec(*arg)) ++arg; ! if (check_defined(p, (int)(arg - p), cctx) == FAIL) ! goto erret; ! as_name = vim_strnsave(p, arg - p); ! arg = skipwhite_and_linebreak(arg, evalarg); } else if (*arg_start == '*') { emsg(_("E1045: Missing \"as\" after *")); ! goto erret; } } ! ! if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4])) { emsg(_("E1070: Missing \"from\"")); ! goto erret; } ! ! arg = skipwhite_and_linebreak_keep_string(arg + 4, evalarg); tv.v_type = VAR_UNKNOWN; // TODO: should we accept any expression? if (*arg == '\'') *************** *** 308,318 **** if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) { emsg(_("E1071: Invalid string after \"from\"")); ! return NULL; } cmd_end = arg; ! // find script tv.vval.v_string if (*tv.vval.v_string == '.') { size_t len; --- 352,364 ---- if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) { emsg(_("E1071: Invalid string after \"from\"")); ! goto erret; } cmd_end = arg; ! /* ! * find script file ! */ if (*tv.vval.v_string == '.') { size_t len; *************** *** 326,332 **** if (from_name == NULL) { clear_tv(&tv); ! return NULL; } vim_strncpy(from_name, si->sn_name, tail - si->sn_name); add_pathsep(from_name); --- 372,378 ---- if (from_name == NULL) { clear_tv(&tv); ! goto erret; } vim_strncpy(from_name, si->sn_name, tail - si->sn_name); add_pathsep(from_name); *************** *** 351,357 **** if (from_name == NULL) { clear_tv(&tv); ! return NULL; } vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); --- 397,403 ---- if (from_name == NULL) { clear_tv(&tv); ! goto erret; } vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); *************** *** 362,368 **** { semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string); clear_tv(&tv); ! return NULL; } clear_tv(&tv); --- 408,414 ---- { semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string); clear_tv(&tv); ! goto erret; } clear_tv(&tv); *************** *** 372,412 **** : &SCRIPT_ITEM(import_sid)->sn_imports); if (imported == NULL) ! return NULL; ! imported->imp_name = vim_strnsave(as_ptr, as_len); imported->imp_sid = sid; imported->imp_all = TRUE; } else { arg = arg_start; if (*arg == '{') arg = skipwhite(arg + 1); ! for (;;) { ! char_u *name = arg; ! int name_len; int idx; imported_T *imported; ufunc_T *ufunc = NULL; type_T *type; ! idx = find_exported(sid, &arg, &name_len, &ufunc, &type); if (idx < 0 && ufunc == NULL) ! return NULL; ! if (check_defined(name, name_len, cctx) == FAIL) ! return NULL; imported = new_imported(gap != NULL ? gap : &SCRIPT_ITEM(import_sid)->sn_imports); if (imported == NULL) ! return NULL; // TODO: check for "as" following ! // imported->imp_name = vim_strnsave(as_ptr, as_len); ! imported->imp_name = vim_strnsave(name, name_len); imported->imp_sid = sid; if (idx >= 0) { --- 418,461 ---- : &SCRIPT_ITEM(import_sid)->sn_imports); if (imported == NULL) ! goto erret; ! imported->imp_name = as_name; ! as_name = NULL; imported->imp_sid = sid; imported->imp_all = TRUE; } else { + int i; + arg = arg_start; if (*arg == '{') arg = skipwhite(arg + 1); ! for (i = 0; i < names.ga_len; ++i) { ! char_u *name = ((char_u **)names.ga_data)[i]; int idx; imported_T *imported; ufunc_T *ufunc = NULL; type_T *type; ! idx = find_exported(sid, name, &ufunc, &type); if (idx < 0 && ufunc == NULL) ! goto erret; ! if (check_defined(name, STRLEN(name), cctx) == FAIL) ! goto erret; imported = new_imported(gap != NULL ? gap : &SCRIPT_ITEM(import_sid)->sn_imports); if (imported == NULL) ! goto erret; // TODO: check for "as" following ! // imported->imp_name = vim_strsave(as_name); ! imported->imp_name = name; ! ((char_u **)names.ga_data)[i] = NULL; imported->imp_sid = sid; if (idx >= 0) { *************** *** 415,444 **** } else imported->imp_funcname = ufunc->uf_name; - - arg = skipwhite(arg); - if (*arg_start != '{') - break; - if (*arg == '}') - { - arg = skipwhite(arg + 1); - break; - } - - if (*arg != ',') - { - emsg(_("E1046: Missing comma in import")); - return NULL; - } - arg = skipwhite(arg + 1); - } - if (arg != from_ptr) - { - // cannot happen, just in case the above has a flaw - emsg(_("E1047: syntax error in import")); - return NULL; } } return cmd_end; } --- 464,474 ---- } else imported->imp_funcname = ufunc->uf_name; } } + erret: + ga_clear_strings(&names); + vim_free(as_name); return cmd_end; } *** ../vim-8.2.1123/src/proto/vim9script.pro 2020-06-19 18:34:10.989945091 +0200 --- src/proto/vim9script.pro 2020-07-03 22:57:15.354069062 +0200 *************** *** 4,11 **** void ex_export(exarg_T *eap); void free_imports(int sid); void ex_import(exarg_T *eap); ! int find_exported(int sid, char_u **argp, int *name_len, ufunc_T **ufunc, type_T **type); ! char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name); /* vim: set ft=c : */ --- 4,11 ---- void ex_export(exarg_T *eap); void free_imports(int sid); void ex_import(exarg_T *eap); ! int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type); ! char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name); /* vim: set ft=c : */ *** ../vim-8.2.1123/src/vim9compile.c 2020-07-01 18:29:23.681143435 +0200 --- src/vim9compile.c 2020-07-03 22:55:56.290395603 +0200 *************** *** 2613,2619 **** if (import->imp_all) { char_u *p = skipwhite(*end); ! int name_len; ufunc_T *ufunc; type_T *type; --- 2613,2620 ---- if (import->imp_all) { char_u *p = skipwhite(*end); ! char_u *exp_name; ! int cc; ufunc_T *ufunc; type_T *type; *************** *** 2630,2636 **** return FAIL; } ! idx = find_exported(import->imp_sid, &p, &name_len, &ufunc, &type); // TODO: what if it is a function? if (idx < 0) return FAIL; --- 2631,2647 ---- return FAIL; } ! // isolate one name ! exp_name = p; ! while (eval_isnamec(*p)) ! ++p; ! cc = *p; ! *p = NUL; ! ! idx = find_exported(import->imp_sid, exp_name, &ufunc, &type); ! *p = cc; ! p = skipwhite(p); ! // TODO: what if it is a function? if (idx < 0) return FAIL; *************** *** 2981,2986 **** --- 2992,2998 ---- /* * Like to_name_end() but also skip over a list or dict constant. + * This intentionally does not handle line continuation. */ char_u * to_name_const_end(char_u *arg) *************** *** 5632,5638 **** static char_u * compile_import(char_u *arg, cctx_T *cctx) { ! return handle_import(arg, &cctx->ctx_imports, 0, cctx); } /* --- 5644,5650 ---- static char_u * compile_import(char_u *arg, cctx_T *cctx) { ! return handle_import(arg, &cctx->ctx_imports, 0, NULL, cctx); } /* *** ../vim-8.2.1123/src/testdir/test_vim9_script.vim 2020-07-01 20:07:11.557328210 +0200 --- src/testdir/test_vim9_script.vim 2020-07-04 13:11:46.319570593 +0200 *************** *** 686,691 **** --- 686,720 ---- unlet g:imported_name g:imported_name_appended delete('Ximport.vim') + # similar, with line breaks + let import_line_break_script_lines =<< trim END + vim9script + import { + exported, + Exported, + } + from + './Xexport.vim' + g:imported = exported + exported += 5 + g:imported_added = exported + g:imported_func = Exported() + END + writefile(import_line_break_script_lines, 'Ximport_lbr.vim') + source Ximport_lbr.vim + + assert_equal(9876, g:imported) + assert_equal(9881, g:imported_added) + assert_equal('Exported', g:imported_func) + + # exported script not sourced again + assert_false(exists('g:result')) + unlet g:imported + unlet g:imported_added + unlet g:imported_func + delete('Ximport_lbr.vim') + + # import inside :def function let import_in_def_lines =<< trim END vim9script def ImportInDef() *************** *** 751,756 **** --- 780,800 ---- writefile(import_star_as_lines_missing_name, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1048:') + let import_star_as_lbr_lines =<< trim END + vim9script + import * + as Export + from + './Xexport.vim' + def UseExport() + g:imported = Export.exported + enddef + UseExport() + END + writefile(import_star_as_lbr_lines, 'Ximport.vim') + source Ximport.vim + assert_equal(9883, g:imported) + let import_star_lines =<< trim END vim9script import * from './Xexport.vim' *** ../vim-8.2.1123/src/version.c 2020-07-03 21:17:31.343914450 +0200 --- src/version.c 2020-07-04 13:13:32.219291562 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1124, /**/ -- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///