charset: Refactor vim_str2nr

This commit is contained in:
ZyX
2017-11-02 10:44:20 +03:00
parent 3ecb95298f
commit d98199de9c
3 changed files with 173 additions and 92 deletions

View File

@@ -1611,8 +1611,9 @@ bool vim_isblankline(char_u *lbuf)
/// If maxlen > 0, check at a maximum maxlen chars. /// If maxlen > 0, check at a maximum maxlen chars.
/// ///
/// @param start /// @param start
/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex, /// @param prep Returns guessed type of number 0 = decimal, 'x' or 'X' is
/// '0' = octal, 'b' or 'B' is bin /// hexadecimal, '0' = octal, 'b' or 'B' is binary. When using
/// STR2NR_FORCE is always zero.
/// @param len Returns the detected length of number. /// @param len Returns the detected length of number.
/// @param what Recognizes what number passed. /// @param what Recognizes what number passed.
/// @param nptr Returns the signed result. /// @param nptr Returns the signed result.
@@ -1627,55 +1628,84 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
#define STRING_ENDED(ptr) \ #define STRING_ENDED(ptr) \
(!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen)) (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
int pre = 0; // default is decimal int pre = 0; // default is decimal
bool negative = false; const bool negative = (ptr[0] == '-');
uvarnumber_T un = 0;
if (ptr[0] == '-') { if (negative) {
negative = true;
ptr++; ptr++;
} }
// Recognize hex, octal and bin. if (what & STR2NR_FORCE) {
if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) // When forcing main consideration is skipping the prefix. Octal and decimal
&& !STRING_ENDED(ptr + 1) // numbers have no prefixes to skip. pre is not set.
&& ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) {
case STR2NR_HEX: {
if (!STRING_ENDED(ptr + 2)
&& ptr[0] == '0'
&& (ptr[1] == 'x' || ptr[1] == 'X')
&& ascii_isxdigit(ptr[2])) {
ptr += 2;
}
goto vim_str2nr_hex;
}
case STR2NR_BIN: {
if (!STRING_ENDED(ptr + 2)
&& ptr[0] == '0'
&& (ptr[1] == 'b' || ptr[1] == 'B')
&& ascii_isbdigit(ptr[2])) {
ptr += 2;
}
goto vim_str2nr_bin;
}
case STR2NR_OCT: {
goto vim_str2nr_oct;
}
case 0: {
goto vim_str2nr_dec;
}
default: {
assert(false);
}
}
} else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
&& !STRING_ENDED(ptr + 1)
&& ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
pre = ptr[1]; pre = ptr[1];
// Detect hexadecimal: 0x or 0X follwed by hex digit
if ((what & STR2NR_HEX) if ((what & STR2NR_HEX)
&& !STRING_ENDED(ptr + 2) && !STRING_ENDED(ptr + 2)
&& (pre == 'X' || pre == 'x') && (pre == 'X' || pre == 'x')
&& ascii_isxdigit(ptr[2])) { && ascii_isxdigit(ptr[2])) {
// hexadecimal
ptr += 2; ptr += 2;
} else if ((what & STR2NR_BIN) goto vim_str2nr_hex;
&& !STRING_ENDED(ptr + 2) }
&& (pre == 'B' || pre == 'b') // Detect binary: 0b or 0B follwed by 0 or 1
&& ascii_isbdigit(ptr[2])) { if ((what & STR2NR_BIN)
// binary && !STRING_ENDED(ptr + 2)
&& (pre == 'B' || pre == 'b')
&& ascii_isbdigit(ptr[2])) {
ptr += 2; ptr += 2;
} else { goto vim_str2nr_bin;
// decimal or octal, default is decimal }
pre = 0; // Detect octal number: zero followed by octal digits without '8' or '9'
pre = 0;
if (what & STR2NR_OCT if (!(what & STR2NR_OCT)
&& !STRING_ENDED(ptr + 1) || !('0' <= ptr[1] && ptr[1] <= '7')) {
&& ('0' <= ptr[1] && ptr[1] <= '7')) { goto vim_str2nr_dec;
// Assume octal now: what we already know is that string starts with }
// zero and some octal digit. for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
pre = '0'; if (ptr[i] > '7') {
// Dont interpret "0", "008" or "0129" as octal. goto vim_str2nr_dec;
for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
if (ptr[i] > '7') {
// Cant be octal.
pre = 0;
break;
}
}
} }
} }
pre = '0';
goto vim_str2nr_oct;
} else {
goto vim_str2nr_dec;
} }
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks. // Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
uvarnumber_T un = 0; assert(false); // Shouldve used goto earlier.
#define PARSE_NUMBER(base, cond, conv) \ #define PARSE_NUMBER(base, cond, conv) \
do { \ do { \
while (!STRING_ENDED(ptr) && (cond)) { \ while (!STRING_ENDED(ptr) && (cond)) { \
@@ -1688,18 +1718,29 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
ptr++; \ ptr++; \
} \ } \
} while (0) } while (0)
if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) { switch (pre) {
// Binary number. case 'b':
PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); case 'B': {
} else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) { vim_str2nr_bin:
// Octal number. PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); break;
} else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) { }
// Hexadecimal number. case '0': {
PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr))); vim_str2nr_oct:
} else { PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
// Decimal number. break;
PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); }
case 0: {
vim_str2nr_dec:
PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
break;
}
case 'x':
case 'X': {
vim_str2nr_hex:
PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
break;
}
} }
#undef PARSE_NUMBER #undef PARSE_NUMBER

View File

@@ -31,55 +31,83 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
#define STRING_ENDED(ptr) \ #define STRING_ENDED(ptr) \
(!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen)) (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
int pre = 0; // default is decimal int pre = 0; // default is decimal
bool negative = false; const bool negative = (ptr[0] == '-');
uvarnumber_T un = 0;
if (ptr[0] == '-') { if (negative) {
negative = true;
ptr++; ptr++;
} }
// Recognize hex, octal and bin. if (what & STR2NR_FORCE) {
if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) // When forcing main consideration is skipping the prefix. Octal and decimal
&& !STRING_ENDED(ptr + 1) // numbers have no prefixes to skip. pre is not set.
&& ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) {
case STR2NR_HEX: {
if (!STRING_ENDED(ptr + 2)
&& ptr[0] == '0'
&& (ptr[1] == 'x' || ptr[1] == 'X')
&& ascii_isxdigit(ptr[2])) {
ptr += 2;
}
goto vim_str2nr_hex;
}
case STR2NR_BIN: {
if (!STRING_ENDED(ptr + 2)
&& ptr[0] == '0'
&& (ptr[1] == 'b' || ptr[1] == 'B')
&& ascii_isbdigit(ptr[2])) {
ptr += 2;
}
goto vim_str2nr_bin;
}
case STR2NR_OCT: {
goto vim_str2nr_oct;
}
case 0: {
goto vim_str2nr_dec;
}
default: {
assert(false);
}
}
} else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
&& !STRING_ENDED(ptr + 1)
&& ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
pre = ptr[1]; pre = ptr[1];
// Detect hexadecimal: 0x or 0X follwed by hex digit
if ((what & STR2NR_HEX) if ((what & STR2NR_HEX)
&& !STRING_ENDED(ptr + 2) && !STRING_ENDED(ptr + 2)
&& (pre == 'X' || pre == 'x') && (pre == 'X' || pre == 'x')
&& ascii_isxdigit(ptr[2])) { && ascii_isxdigit(ptr[2])) {
// hexadecimal
ptr += 2; ptr += 2;
} else if ((what & STR2NR_BIN) goto vim_str2nr_hex;
&& !STRING_ENDED(ptr + 2) }
&& (pre == 'B' || pre == 'b') // Detect binary: 0b or 0B follwed by 0 or 1
&& ascii_isbdigit(ptr[2])) { if ((what & STR2NR_BIN)
// binary && !STRING_ENDED(ptr + 2)
&& (pre == 'B' || pre == 'b')
&& ascii_isbdigit(ptr[2])) {
ptr += 2; ptr += 2;
} else { goto vim_str2nr_bin;
// decimal or octal, default is decimal }
pre = 0; // Detect octal number: zero followed by octal digits without '8' or '9'
pre = 0;
if (what & STR2NR_OCT if (!(what & STR2NR_OCT)) {
&& !STRING_ENDED(ptr + 1) goto vim_str2nr_dec;
&& ('0' <= ptr[1] && ptr[1] <= '7')) { }
// Assume octal now: what we already know is that string starts with for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
// zero and some octal digit. if (ptr[i] > '7') {
pre = '0'; goto vim_str2nr_dec;
// Dont interpret "0", "008" or "0129" as octal.
for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
if (ptr[i] > '7') {
// Cant be octal.
pre = 0;
break;
}
}
} }
} }
pre = '0';
goto vim_str2nr_oct;
} else {
goto vim_str2nr_dec;
} }
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks. // Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
uvarnumber_T un = 0; assert(false); // Shouldve used goto earlier.
#define PARSE_NUMBER(base, cond, conv) \ #define PARSE_NUMBER(base, cond, conv) \
do { \ do { \
while (!STRING_ENDED(ptr) && (cond)) { \ while (!STRING_ENDED(ptr) && (cond)) { \
@@ -92,18 +120,29 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
ptr++; \ ptr++; \
} \ } \
} while (0) } while (0)
if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) { switch (pre) {
// Binary number. case 'b':
PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); case 'B': {
} else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) { vim_str2nr_bin:
// Octal number. PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); break;
} else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) { }
// Hexadecimal number. case '0': {
PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr))); vim_str2nr_oct:
} else { PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
// Decimal number. break;
PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); }
case 0: {
vim_str2nr_dec:
PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
break;
}
case 'x':
case 'X': {
vim_str2nr_hex:
PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
break;
}
} }
#undef PARSE_NUMBER #undef PARSE_NUMBER

View File

@@ -0,0 +1 @@
-- FIXME