To: vim_dev@googlegroups.com Subject: Patch 8.1.2035 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.2035 Problem: Recognizing octal numbers is confusing. Solution: Introduce scriptversion 4: do not use octal and allow for single quote inside numbers. Files: runtime/doc/eval.txt, src/vim.h, src/eval.c, src/scriptfile.c, src/evalfunc.c, src/testdir/test_eval_stuff.vim, src/testdir/test_functions.vim *** ../vim-8.1.2034/runtime/doc/eval.txt 2019-09-10 21:22:54.872629203 +0200 --- runtime/doc/eval.txt 2019-09-15 14:26:28.494040912 +0200 *************** *** 92,98 **** *octal* Conversion from a String to a Number is done by converting the first digits to a number. Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are ! recognized. If the String doesn't start with digits, the result is zero. Examples: String "456" --> Number 456 ~ String "6bar" --> Number 6 ~ --- 92,99 ---- *octal* Conversion from a String to a Number is done by converting the first digits to a number. Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are ! recognized (NOTE: when using |scriptversion-4| octal is not recognized). If ! the String doesn't start with digits, the result is zero. Examples: String "456" --> Number 456 ~ String "6bar" --> Number 6 ~ *************** *** 2543,2549 **** len({expr}) Number the length of {expr} libcall({lib}, {func}, {arg}) String call {func} in library {lib} with {arg} libcallnr({lib}, {func}, {arg}) Number idem, but return a Number ! line({expr}) Number line nr of cursor, last line or mark line2byte({lnum}) Number byte count of line {lnum} lispindent({lnum}) Number Lisp indent for line {lnum} list2str({list} [, {utf8}]) String turn numbers in {list} into a String --- 2549,2555 ---- len({expr}) Number the length of {expr} libcall({lib}, {func}, {arg}) String call {func} in library {lib} with {arg} libcallnr({lib}, {func}, {arg}) Number idem, but return a Number ! line({expr} [, {winid}]) Number line nr of cursor, last line or mark line2byte({lnum}) Number byte count of line {lnum} lispindent({lnum}) Number Lisp indent for line {lnum} list2str({list} [, {utf8}]) String turn numbers in {list} into a String *************** *** 2752,2758 **** str2float({expr}) Float convert String to Float str2list({expr} [, {utf8}]) List convert each character of {expr} to ASCII/UTF8 value ! str2nr({expr} [, {base}]) Number convert String to Number strchars({expr} [, {skipcc}]) Number character length of the String {expr} strcharpart({str}, {start} [, {len}]) String {len} characters of {str} at {start} --- 2758,2765 ---- str2float({expr}) Float convert String to Float str2list({expr} [, {utf8}]) List convert each character of {expr} to ASCII/UTF8 value ! str2nr({expr} [, {base} [, {quoted}]]) ! Number convert String to Number strchars({expr} [, {skipcc}]) Number character length of the String {expr} strcharpart({str}, {start} [, {len}]) String {len} characters of {str} at {start} *************** *** 6376,6383 **** the argument to the called function: > GetValue()->libcallnr("libc.so", "printf") < ! *line()* ! line({expr}) The result is a Number, which is the line number of the file position given with {expr}. The accepted positions are: . the cursor position $ the last line in the current buffer --- 6388,6396 ---- the argument to the called function: > GetValue()->libcallnr("libc.so", "printf") < ! ! line({expr} [, {winid}]) *line()* ! The result is a Number, which is the line number of the file position given with {expr}. The accepted positions are: . the cursor position $ the last line in the current buffer *************** *** 6395,6402 **** --- 6408,6418 ---- then applies to another buffer. To get the column number use |col()|. To get both use |getpos()|. + With the optional {winid} argument the values are obtained for + that window instead of the current window. Examples: > line(".") line number of the cursor + line(".", winid) idem, in window "winid" line("'t") line number of mark t line("'" . marker) line number of mark marker < *************** *** 9061,9069 **** GetString()->str2list() ! str2nr({expr} [, {base}]) *str2nr()* Convert string {expr} to a number. {base} is the conversion base, it can be 2, 8, 10 or 16. When {base} is omitted base 10 is used. This also means that a leading zero doesn't cause octal conversion to be used, as --- 9077,9087 ---- GetString()->str2list() ! str2nr({expr} [, {base} [, {quoted}]]) *str2nr()* Convert string {expr} to a number. {base} is the conversion base, it can be 2, 8, 10 or 16. + When {quoted} is present and non-zero then embedded single + quotes are ignored, thus "1'000'000" is a million. When {base} is omitted base 10 is used. This also means that a leading zero doesn't cause octal conversion to be used, as *************** *** 12916,12921 **** --- 12941,12963 ---- Test for support with: > has('vimscript-3') + < + *scriptversion-4* > + :scriptversion 4 + < Numbers with a leading zero are not recognized as octal. With the + previous version you get: > + echo 017 " displays 15 + echo 018 " displays 18 + < with script version 4: > + echo 017 " displays 17 + echo 018 " displays 18 + < Also, it is possible to use single quotes inside numbers to make them + easier to read: > + echo 1'000'000 + < The quotes must be surrounded by digits. + + Test for support with: > + has('vimscript-4') ============================================================================== 11. No +eval feature *no-eval-feature* *** ../vim-8.1.2034/src/vim.h 2019-09-04 20:59:10.491409987 +0200 --- src/vim.h 2019-09-15 13:49:47.753458236 +0200 *************** *** 307,317 **** #define NUMBUFLEN 65 // flags for vim_str2nr() ! #define STR2NR_BIN 1 ! #define STR2NR_OCT 2 ! #define STR2NR_HEX 4 #define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX) ! #define STR2NR_FORCE 8 // only when ONE of the above is used /* * Shorthand for unsigned variables. Many systems, but not all, have u_char --- 307,321 ---- #define NUMBUFLEN 65 // flags for vim_str2nr() ! #define STR2NR_BIN 0x01 ! #define STR2NR_OCT 0x02 ! #define STR2NR_HEX 0x04 #define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX) ! #define STR2NR_NO_OCT (STR2NR_BIN + STR2NR_HEX) ! ! #define STR2NR_FORCE 0x80 // only when ONE of the above is used ! ! #define STR2NR_QUOTE 0x10 // ignore embedded single quotes /* * Shorthand for unsigned variables. Many systems, but not all, have u_char *** ../vim-8.1.2034/src/eval.c 2019-09-04 15:54:23.912359700 +0200 --- src/eval.c 2019-09-15 13:50:06.149393853 +0200 *************** *** 2617,2623 **** else { // decimal, hex or octal number ! vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, TRUE); if (len == 0) { semsg(_(e_invexpr2), *arg); --- 2617,2625 ---- else { // decimal, hex or octal number ! vim_str2nr(*arg, NULL, &len, current_sctx.sc_version >= 4 ! ? STR2NR_NO_OCT + STR2NR_QUOTE ! : STR2NR_ALL, &n, NULL, 0, TRUE); if (len == 0) { semsg(_(e_invexpr2), *arg); *** ../vim-8.1.2034/src/scriptfile.c 2019-09-04 20:59:10.491409987 +0200 --- src/scriptfile.c 2019-09-15 13:41:54.911081578 +0200 *************** *** 1659,1665 **** nr = getdigits(&eap->arg); if (nr == 0 || *eap->arg != NUL) emsg(_(e_invarg)); ! else if (nr > 3) semsg(_("E999: scriptversion not supported: %d"), nr); else current_sctx.sc_version = nr; --- 1659,1665 ---- nr = getdigits(&eap->arg); if (nr == 0 || *eap->arg != NUL) emsg(_(e_invarg)); ! else if (nr > 4) semsg(_("E999: scriptversion not supported: %d"), nr); else current_sctx.sc_version = nr; *** ../vim-8.1.2034/src/evalfunc.c 2019-09-10 21:22:54.876629189 +0200 --- src/evalfunc.c 2019-09-15 14:18:51.327587656 +0200 *************** *** 728,734 **** {"str2float", 1, 1, FEARG_1, f_str2float}, #endif {"str2list", 1, 2, FEARG_1, f_str2list}, ! {"str2nr", 1, 2, FEARG_1, f_str2nr}, {"strcharpart", 2, 3, FEARG_1, f_strcharpart}, {"strchars", 1, 2, FEARG_1, f_strchars}, {"strdisplaywidth", 1, 2, FEARG_1, f_strdisplaywidth}, --- 728,734 ---- {"str2float", 1, 1, FEARG_1, f_str2float}, #endif {"str2list", 1, 2, FEARG_1, f_str2list}, ! {"str2nr", 1, 3, FEARG_1, f_str2nr}, {"strcharpart", 2, 3, FEARG_1, f_strcharpart}, {"strchars", 1, 2, FEARG_1, f_strchars}, {"strdisplaywidth", 1, 2, FEARG_1, f_strdisplaywidth}, *************** *** 7323,7329 **** int base = 10; char_u *p; varnumber_T n; ! int what; int isneg; if (argvars[1].v_type != VAR_UNKNOWN) --- 7323,7329 ---- int base = 10; char_u *p; varnumber_T n; ! int what = 0; int isneg; if (argvars[1].v_type != VAR_UNKNOWN) *************** *** 7334,7339 **** --- 7334,7341 ---- emsg(_(e_invarg)); return; } + if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2])) + what |= STR2NR_QUOTE; } p = skipwhite(tv_get_string(&argvars[0])); *************** *** 7342,7351 **** p = skipwhite(p + 1); switch (base) { ! case 2: what = STR2NR_BIN + STR2NR_FORCE; break; ! case 8: what = STR2NR_OCT + STR2NR_FORCE; break; ! case 16: what = STR2NR_HEX + STR2NR_FORCE; break; ! default: what = 0; } vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE); // Text after the number is silently ignored. --- 7344,7352 ---- p = skipwhite(p + 1); switch (base) { ! case 2: what |= STR2NR_BIN + STR2NR_FORCE; break; ! case 8: what |= STR2NR_OCT + STR2NR_FORCE; break; ! case 16: what |= STR2NR_HEX + STR2NR_FORCE; break; } vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE); // Text after the number is silently ignored. *** ../vim-8.1.2034/src/testdir/test_eval_stuff.vim 2019-09-04 22:28:53.061026888 +0200 --- src/testdir/test_eval_stuff.vim 2019-09-15 14:08:25.701595419 +0200 *************** *** 74,80 **** new call setline(1, ['one', 'two', 'three']) setlocal ff=dos ! write XReadfile let lines = 'XReadfile'->readfile() call assert_equal(['one', 'two', 'three'], lines) let lines = readfile('XReadfile', '', 2) --- 74,80 ---- new call setline(1, ['one', 'two', 'three']) setlocal ff=dos ! silent write XReadfile let lines = 'XReadfile'->readfile() call assert_equal(['one', 'two', 'three'], lines) let lines = readfile('XReadfile', '', 2) *************** *** 124,129 **** --- 124,138 ---- call assert_equal('ab', a) endfunc + " Test fix for issue #4507 + func Test_skip_after_throw() + try + throw 'something' + let x = wincol() || &ts + catch /something/ + endtry + endfunc + scriptversion 2 func Test_string_concat_scriptversion2() call assert_true(has('vimscript-2')) *************** *** 183,199 **** call assert_true(1 && l:x.foo) endfunc ! func Test_scriptversion() call writefile(['scriptversion 9'], 'Xversionscript') call assert_fails('source Xversionscript', 'E999:') call delete('Xversionscript') endfunc - - " Test fix for issue #4507 - func Test_skip_after_throw() - try - throw 'something' - let x = wincol() || &ts - catch /something/ - endtry - endfunc --- 192,214 ---- call assert_true(1 && l:x.foo) endfunc ! scriptversion 4 ! func Test_vvar_scriptversion4() ! call assert_equal(17, 017) ! call assert_equal(18, 018) ! call assert_equal(64, 0b1'00'00'00) ! call assert_equal(1048576, 0x10'00'00) ! call assert_equal(1000000, 1'000'000) ! endfunc ! ! scriptversion 1 ! func Test_vvar_scriptversion1() ! call assert_equal(15, 017) ! call assert_equal(18, 018) ! endfunc ! ! func Test_scriptversion_fail() call writefile(['scriptversion 9'], 'Xversionscript') call assert_fails('source Xversionscript', 'E999:') call delete('Xversionscript') endfunc *** ../vim-8.1.2034/src/testdir/test_functions.vim 2019-09-08 21:51:36.453843927 +0200 --- src/testdir/test_functions.vim 2019-09-15 14:16:32.940047034 +0200 *************** *** 157,162 **** --- 157,168 ---- call assert_equal(11259375, str2nr('0XABCDEF', 16)) call assert_equal(-11259375, str2nr('-0xABCDEF', 16)) + call assert_equal(1, str2nr("1'000'000", 10, 0)) + call assert_equal(256, str2nr("1'0000'0000", 2, 1)) + call assert_equal(262144, str2nr("1'000'000", 8, 1)) + call assert_equal(1000000, str2nr("1'000'000", 10, 1)) + call assert_equal(65536, str2nr("1'00'00", 16, 1)) + call assert_equal(0, str2nr('0x10')) call assert_equal(0, str2nr('0b10')) call assert_equal(1, str2nr('12', 2)) *** ../vim-8.1.2034/src/version.c 2019-09-15 13:16:55.208317441 +0200 --- src/version.c 2019-09-15 13:46:48.678081187 +0200 *************** *** 759,760 **** --- 759,762 ---- { /* Add new patch number below this line */ + /**/ + 2035, /**/ -- hundred-and-one symptoms of being an internet addict: 264. You turn to the teletext page "surfing report" and are surprised that it is about sizes of waves and a weather forecast for seaside resorts. /// 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 ///