Skip to content

Commit 60a8de2

Browse files
committed
patch 8.1.2035: recognizing octal numbers is confusing
Problem: Recognizing octal numbers is confusing. Solution: Introduce scriptversion 4: do not use octal and allow for single quote inside numbers.
1 parent 50bf7ce commit 60a8de2

File tree

8 files changed

+78
-27
lines changed

8 files changed

+78
-27
lines changed

runtime/doc/eval.txt

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*eval.txt* For Vim version 8.1. Last change: 2019 Sep 10
1+
*eval.txt* For Vim version 8.1. Last change: 2019 Sep 15
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -92,7 +92,8 @@ the Number. Examples:
9292
*octal*
9393
Conversion from a String to a Number is done by converting the first digits to
9494
a number. Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are
95-
recognized. If the String doesn't start with digits, the result is zero.
95+
recognized (NOTE: when using |scriptversion-4| octal is not recognized). If
96+
the String doesn't start with digits, the result is zero.
9697
Examples:
9798
String "456" --> Number 456 ~
9899
String "6bar" --> Number 6 ~
@@ -2757,7 +2758,8 @@ sqrt({expr}) Float square root of {expr}
27572758
str2float({expr}) Float convert String to Float
27582759
str2list({expr} [, {utf8}]) List convert each character of {expr} to
27592760
ASCII/UTF8 value
2760-
str2nr({expr} [, {base}]) Number convert String to Number
2761+
str2nr({expr} [, {base} [, {quoted}]])
2762+
Number convert String to Number
27612763
strchars({expr} [, {skipcc}]) Number character length of the String {expr}
27622764
strcharpart({str}, {start} [, {len}])
27632765
String {len} characters of {str} at {start}
@@ -9075,9 +9077,11 @@ str2list({expr} [, {utf8}]) *str2list()*
90759077
GetString()->str2list()
90769078
90779079
9078-
str2nr({expr} [, {base}]) *str2nr()*
9080+
str2nr({expr} [, {base} [, {quoted}]]) *str2nr()*
90799081
Convert string {expr} to a number.
90809082
{base} is the conversion base, it can be 2, 8, 10 or 16.
9083+
When {quoted} is present and non-zero then embedded single
9084+
quotes are ignored, thus "1'000'000" is a million.
90819085

90829086
When {base} is omitted base 10 is used. This also means that
90839087
a leading zero doesn't cause octal conversion to be used, as
@@ -12937,6 +12941,23 @@ instead of failing in mysterious ways.
1293712941

1293812942
Test for support with: >
1293912943
has('vimscript-3')
12944+
<
12945+
*scriptversion-4* >
12946+
:scriptversion 4
12947+
< Numbers with a leading zero are not recognized as octal. With the
12948+
previous version you get: >
12949+
echo 017 " displays 15
12950+
echo 018 " displays 18
12951+
< with script version 4: >
12952+
echo 017 " displays 17
12953+
echo 018 " displays 18
12954+
< Also, it is possible to use single quotes inside numbers to make them
12955+
easier to read: >
12956+
echo 1'000'000
12957+
< The quotes must be surrounded by digits.
12958+
12959+
Test for support with: >
12960+
has('vimscript-4')
1294012961
1294112962
==============================================================================
1294212963
11. No +eval feature *no-eval-feature*

src/eval.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2617,7 +2617,9 @@ eval7(
26172617
else
26182618
{
26192619
// decimal, hex or octal number
2620-
vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, TRUE);
2620+
vim_str2nr(*arg, NULL, &len, current_sctx.sc_version >= 4
2621+
? STR2NR_NO_OCT + STR2NR_QUOTE
2622+
: STR2NR_ALL, &n, NULL, 0, TRUE);
26212623
if (len == 0)
26222624
{
26232625
semsg(_(e_invexpr2), *arg);

src/evalfunc.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ static funcentry_T global_functions[] =
728728
{"str2float", 1, 1, FEARG_1, f_str2float},
729729
#endif
730730
{"str2list", 1, 2, FEARG_1, f_str2list},
731-
{"str2nr", 1, 2, FEARG_1, f_str2nr},
731+
{"str2nr", 1, 3, FEARG_1, f_str2nr},
732732
{"strcharpart", 2, 3, FEARG_1, f_strcharpart},
733733
{"strchars", 1, 2, FEARG_1, f_strchars},
734734
{"strdisplaywidth", 1, 2, FEARG_1, f_strdisplaywidth},
@@ -7323,7 +7323,7 @@ f_str2nr(typval_T *argvars, typval_T *rettv)
73237323
int base = 10;
73247324
char_u *p;
73257325
varnumber_T n;
7326-
int what;
7326+
int what = 0;
73277327
int isneg;
73287328

73297329
if (argvars[1].v_type != VAR_UNKNOWN)
@@ -7334,6 +7334,8 @@ f_str2nr(typval_T *argvars, typval_T *rettv)
73347334
emsg(_(e_invarg));
73357335
return;
73367336
}
7337+
if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2]))
7338+
what |= STR2NR_QUOTE;
73377339
}
73387340

73397341
p = skipwhite(tv_get_string(&argvars[0]));
@@ -7342,10 +7344,9 @@ f_str2nr(typval_T *argvars, typval_T *rettv)
73427344
p = skipwhite(p + 1);
73437345
switch (base)
73447346
{
7345-
case 2: what = STR2NR_BIN + STR2NR_FORCE; break;
7346-
case 8: what = STR2NR_OCT + STR2NR_FORCE; break;
7347-
case 16: what = STR2NR_HEX + STR2NR_FORCE; break;
7348-
default: what = 0;
7347+
case 2: what |= STR2NR_BIN + STR2NR_FORCE; break;
7348+
case 8: what |= STR2NR_OCT + STR2NR_FORCE; break;
7349+
case 16: what |= STR2NR_HEX + STR2NR_FORCE; break;
73497350
}
73507351
vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, FALSE);
73517352
// Text after the number is silently ignored.

src/scriptfile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,7 @@ ex_scriptversion(exarg_T *eap UNUSED)
16591659
nr = getdigits(&eap->arg);
16601660
if (nr == 0 || *eap->arg != NUL)
16611661
emsg(_(e_invarg));
1662-
else if (nr > 3)
1662+
else if (nr > 4)
16631663
semsg(_("E999: scriptversion not supported: %d"), nr);
16641664
else
16651665
current_sctx.sc_version = nr;

src/testdir/test_eval_stuff.vim

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func Test_readfile_binary()
7474
new
7575
call setline(1, ['one', 'two', 'three'])
7676
setlocal ff=dos
77-
write XReadfile
77+
silent write XReadfile
7878
let lines = 'XReadfile'->readfile()
7979
call assert_equal(['one', 'two', 'three'], lines)
8080
let lines = readfile('XReadfile', '', 2)
@@ -124,6 +124,15 @@ func Test_string_concatenation()
124124
call assert_equal('ab', a)
125125
endfunc
126126

127+
" Test fix for issue #4507
128+
func Test_skip_after_throw()
129+
try
130+
throw 'something'
131+
let x = wincol() || &ts
132+
catch /something/
133+
endtry
134+
endfunc
135+
127136
scriptversion 2
128137
func Test_string_concat_scriptversion2()
129138
call assert_true(has('vimscript-2'))
@@ -183,17 +192,23 @@ func Test_dict_access_scriptversion2()
183192
call assert_true(1 && l:x.foo)
184193
endfunc
185194

186-
func Test_scriptversion()
195+
scriptversion 4
196+
func Test_vvar_scriptversion4()
197+
call assert_equal(17, 017)
198+
call assert_equal(18, 018)
199+
call assert_equal(64, 0b1'00'00'00)
200+
call assert_equal(1048576, 0x10'00'00)
201+
call assert_equal(1000000, 1'000'000)
202+
endfunc
203+
204+
scriptversion 1
205+
func Test_vvar_scriptversion1()
206+
call assert_equal(15, 017)
207+
call assert_equal(18, 018)
208+
endfunc
209+
210+
func Test_scriptversion_fail()
187211
call writefile(['scriptversion 9'], 'Xversionscript')
188212
call assert_fails('source Xversionscript', 'E999:')
189213
call delete('Xversionscript')
190214
endfunc
191-
192-
" Test fix for issue #4507
193-
func Test_skip_after_throw()
194-
try
195-
throw 'something'
196-
let x = wincol() || &ts
197-
catch /something/
198-
endtry
199-
endfunc

src/testdir/test_functions.vim

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ func Test_str2nr()
157157
call assert_equal(11259375, str2nr('0XABCDEF', 16))
158158
call assert_equal(-11259375, str2nr('-0xABCDEF', 16))
159159

160+
call assert_equal(1, str2nr("1'000'000", 10, 0))
161+
call assert_equal(256, str2nr("1'0000'0000", 2, 1))
162+
call assert_equal(262144, str2nr("1'000'000", 8, 1))
163+
call assert_equal(1000000, str2nr("1'000'000", 10, 1))
164+
call assert_equal(65536, str2nr("1'00'00", 16, 1))
165+
160166
call assert_equal(0, str2nr('0x10'))
161167
call assert_equal(0, str2nr('0b10'))
162168
call assert_equal(1, str2nr('12', 2))

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,8 @@ static char *(features[]) =
757757

758758
static int included_patches[] =
759759
{ /* Add new patch number below this line */
760+
/**/
761+
2035,
760762
/**/
761763
2034,
762764
/**/

src/vim.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,11 +307,15 @@
307307
#define NUMBUFLEN 65
308308

309309
// flags for vim_str2nr()
310-
#define STR2NR_BIN 1
311-
#define STR2NR_OCT 2
312-
#define STR2NR_HEX 4
310+
#define STR2NR_BIN 0x01
311+
#define STR2NR_OCT 0x02
312+
#define STR2NR_HEX 0x04
313313
#define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX)
314-
#define STR2NR_FORCE 8 // only when ONE of the above is used
314+
#define STR2NR_NO_OCT (STR2NR_BIN + STR2NR_HEX)
315+
316+
#define STR2NR_FORCE 0x80 // only when ONE of the above is used
317+
318+
#define STR2NR_QUOTE 0x10 // ignore embedded single quotes
315319

316320
/*
317321
* Shorthand for unsigned variables. Many systems, but not all, have u_char

0 commit comments

Comments
 (0)