Add support for binary numbers

This commit is contained in:
Jason Schulz
2015-12-29 15:17:16 -08:00
parent dddbf9c5fa
commit 7ad3f077dc
17 changed files with 350 additions and 95 deletions

View File

@@ -122,6 +122,14 @@ static inline bool ascii_isxdigit(int c)
|| (c >= 'A' && c <= 'F');
}
/// Checks if `c` is a binary digit, that is, 0-1.
///
/// @see {ascii_isdigit}
static inline bool ascii_isbdigit(int c)
{
return (c == '0' || c == '1');
}
/// Checks if `c` is a white-space character, that is,
/// one of \f, \n, \r, \t, \v.
///

View File

@@ -1454,6 +1454,20 @@ char_u* skipdigits(char_u *q)
return p;
}
/// skip over binary digits
///
/// @param q
///
/// @return Pointer to the character after the skipped digits.
char_u* skipbin(char_u *q)
{
char_u *p = q;
while (ascii_isbdigit(*p)) /* skip to next non-digit */
++p;
return p;
}
/// skip over digits and hex characters
///
/// @param q
@@ -1485,6 +1499,20 @@ char_u* skiptodigit(char_u *q)
return p;
}
/// skip to binary character (or NUL after the string)
///
/// @param q
///
/// @return Pointer to the binary character or (NUL after the string).
char_u* skiptobin(char_u *q)
{
char_u *p = q;
while (*p != NUL && !ascii_isbdigit(*p)) /* skip to next digit */
++p;
return p;
}
/// skip to hex character (or NUL after the string)
///
/// @param q
@@ -1720,33 +1748,38 @@ int vim_isblankline(char_u *lbuf)
}
/// Convert a string into a long and/or unsigned long, taking care of
/// hexadecimal and octal numbers. Accepts a '-' sign.
/// If "hexp" is not NULL, returns a flag to indicate the type of the number:
/// hexadecimal, octal and binary numbers. Accepts a '-' sign.
/// If "prep" is not NULL, returns a flag to indicate the type of the number:
/// 0 decimal
/// '0' octal
/// 'B' bin
/// 'b' bin
/// 'X' hex
/// 'x' hex
/// If "len" is not NULL, the length of the number in characters is returned.
/// If "nptr" is not NULL, the signed result is returned in it.
/// If "unptr" is not NULL, the unsigned result is returned in it.
/// If "dobin" is non-zero recognize binary numbers, when > 1 always assume
/// binary number.
/// If "dooct" is non-zero recognize octal numbers, when > 1 always assume
/// octal number.
/// If "dohex" is non-zero recognize hex numbers, when > 1 always assume
/// hex number.
///
/// @param start
/// @param hexp Returns type of number 0 = decimal, 'x' or 'X' is hex,
// '0' = octal
/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex,
// '0' = octal, 'b' or 'B' is bin
/// @param len Returns the detected length of number.
/// @param dobin recognize binary number
/// @param dooct recognize octal number
/// @param dohex recognize hex number
/// @param nptr Returns the signed result.
/// @param unptr Returns the unsigned result.
void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex,
void vim_str2nr(char_u *start, int *prep, int *len, int dobin, int dooct, int dohex,
long *nptr, unsigned long *unptr)
{
char_u *ptr = start;
int hex = 0; // default is decimal
int pre = 0; // default is decimal
int negative = FALSE;
unsigned long un = 0;
int n;
@@ -1756,31 +1789,35 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex,
++ptr;
}
// Recognize hex and octal.
// Recognize hex, octal, and bin.
if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) {
hex = ptr[1];
pre = ptr[1];
if (dohex
&& ((hex == 'X') || (hex == 'x'))
&& ((pre == 'X') || (pre == 'x'))
&& ascii_isxdigit(ptr[2])) {
// hexadecimal
ptr += 2;
} else if (dobin
&& ((pre == 'B') || (pre == 'b'))
&& ascii_isbdigit(ptr[2])) {
// binary
ptr += 2;
} else {
// default is decimal
hex = 0;
pre = 0;
if (dooct) {
// Don't interpret "0", "08" or "0129" as octal.
for (n = 1; ascii_isdigit(ptr[n]); ++n) {
if (ptr[n] > '7') {
// can't be octal
hex = 0;
pre = 0;
break;
}
if (ptr[n] >= '0') {
// assume octal
hex = '0';
pre = '0';
}
}
}
@@ -1788,28 +1825,38 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex,
}
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
if ((hex == '0') || (dooct > 1)) {
if ((pre == 'B') || (pre == 'b') || (dobin > 1)) {
// bin
if (pre != 0)
n += 2; // skip over "0b"
while ('0' <= *ptr && *ptr <= '1') {
un = 2 * un + (unsigned long)(*ptr - '0');
++ptr;
}
} else if ((pre == '0') || (dooct > 1)) {
// octal
while ('0' <= *ptr && *ptr <= '7') {
un = 8 * un + (unsigned long)(*ptr - '0');
ptr++;
}
} else if ((hex != 0) || (dohex > 1)) {
} else if (pre != 0 || dohex > 1) {
// hex
if (pre != 0)
n += 2; // skip over "0x"
while (ascii_isxdigit(*ptr)) {
un = 16 * un + (unsigned long)hex2nr(*ptr);
ptr++;
++ptr;
}
} else {
// decimal
while (ascii_isdigit(*ptr)) {
un = 10 * un + (unsigned long)(*ptr - '0');
ptr++;
++ptr;
}
}
if (hexp != NULL) {
*hexp = hex;
if (prep != NULL) {
*prep = pre;
}
if (len != NULL) {

View File

@@ -1149,7 +1149,7 @@ call_vim_function (
len = 0;
else
/* Recognize a number argument, the others must be strings. */
vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, &n, NULL);
vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, TRUE, &n, NULL);
if (len != 0 && len == (int)STRLEN(argv[i])) {
argvars[i].v_type = VAR_NUMBER;
argvars[i].vval.v_number = n;
@@ -4127,7 +4127,7 @@ eval7 (
rettv->vval.v_float = f;
}
} else {
vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL);
vim_str2nr(*arg, NULL, &len, TRUE, TRUE, TRUE, &n, NULL);
*arg += len;
if (evaluate) {
rettv->v_type = VAR_NUMBER;
@@ -15982,7 +15982,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN) {
base = get_tv_number(&argvars[1]);
if (base != 8 && base != 10 && base != 16) {
if (base != 2 && base != 8 && base != 10 && base != 16) {
EMSG(_(e_invarg));
return;
}
@@ -15991,7 +15991,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv)
p = skipwhite(get_tv_string(&argvars[0]));
if (*p == '+')
p = skipwhite(p + 1);
vim_str2nr(p, NULL, NULL, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL);
vim_str2nr(p, NULL, NULL, base == 2 ? 2 : 0, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL);
rettv->vval.v_number = n;
}
@@ -18273,7 +18273,7 @@ long get_tv_number_chk(typval_T *varp, int *denote)
case VAR_STRING:
if (varp->vval.v_string != NULL)
vim_str2nr(varp->vval.v_string, NULL, NULL,
TRUE, TRUE, &n, NULL);
TRUE, TRUE, TRUE, &n, NULL);
return n;
case VAR_LIST:
EMSG(_("E745: Using a List as a Number"));

View File

@@ -348,6 +348,7 @@ void ex_sort(exarg_T *eap)
long deleted;
colnr_T start_col;
colnr_T end_col;
int sort_bin; /* sort on bin number */
int sort_oct; /* sort on octal number */
int sort_hex; /* sort on hex number */
@@ -362,7 +363,7 @@ void ex_sort(exarg_T *eap)
regmatch.regprog = NULL;
sorti_T *nrs = xmalloc(count * sizeof(sorti_T));
sort_abort = sort_ic = sort_rx = sort_nr = sort_oct = sort_hex = 0;
sort_abort = sort_ic = sort_rx = sort_nr = sort_bin = sort_oct = sort_hex = 0;
for (p = eap->arg; *p != NUL; ++p) {
if (ascii_iswhite(*p))
@@ -373,6 +374,8 @@ void ex_sort(exarg_T *eap)
sort_rx = TRUE;
else if (*p == 'n')
sort_nr = 2;
else if (*p == 'b')
sort_bin = 2;
else if (*p == 'o')
sort_oct = 2;
else if (*p == 'x')
@@ -410,14 +413,14 @@ void ex_sort(exarg_T *eap)
}
}
/* Can only have one of 'n', 'o' and 'x'. */
if (sort_nr + sort_oct + sort_hex > 2) {
/* Can only have one of 'n', 'b', 'o' and 'x'. */
if (sort_nr + sort_bin + sort_oct + sort_hex > 2) {
EMSG(_(e_invarg));
goto sortend;
}
/* From here on "sort_nr" is used as a flag for any number sorting. */
sort_nr += sort_oct + sort_hex;
sort_nr += sort_bin + sort_oct + sort_hex;
/*
* Make an array with all line numbers. This avoids having to copy all
@@ -454,6 +457,8 @@ void ex_sort(exarg_T *eap)
p = s + start_col;
if (sort_hex)
s = skiptohex(p);
else if (sort_bin)
s = skiptobin(p);
else
s = skiptodigit(p);
if (s > p && s[-1] == '-')
@@ -462,7 +467,7 @@ void ex_sort(exarg_T *eap)
/* empty line should sort before any number */
nrs[lnum - eap->line1].start_col_nr = -MAXLNUM;
else
vim_str2nr(s, NULL, NULL, sort_oct, sort_hex,
vim_str2nr(s, NULL, NULL, sort_bin, sort_oct, sort_hex,
&nrs[lnum - eap->line1].start_col_nr, NULL);
*s2 = c;
} else {

View File

@@ -4780,7 +4780,7 @@ int get_list_range(char_u **str, int *num1, int *num2)
*str = skipwhite(*str);
if (**str == '-' || ascii_isdigit(**str)) { /* parse "from" part of range */
vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL);
vim_str2nr(*str, NULL, &len, FALSE, FALSE, FALSE, &num, NULL);
*str += len;
*num1 = (int)num;
first = TRUE;
@@ -4788,7 +4788,7 @@ int get_list_range(char_u **str, int *num1, int *num2)
*str = skipwhite(*str);
if (**str == ',') { /* parse "to" part of range */
*str = skipwhite(*str + 1);
vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL);
vim_str2nr(*str, NULL, &len, FALSE, FALSE, FALSE, &num, NULL);
if (len > 0) {
*num2 = (int)num;
*str = skipwhite(*str + len);

View File

@@ -575,7 +575,7 @@ find_special_key (
if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3])
bp += 3; /* skip t_xx, xx may be '-' or '>' */
else if (STRNICMP(bp, "char-", 5) == 0) {
vim_str2nr(bp + 5, NULL, &l, TRUE, TRUE, NULL, NULL);
vim_str2nr(bp + 5, NULL, &l, TRUE, TRUE, TRUE, NULL, NULL);
bp += l + 5;
break;
}
@@ -602,7 +602,7 @@ find_special_key (
if (STRNICMP(last_dash + 1, "char-", 5) == 0
&& ascii_isdigit(last_dash[6])) {
/* <Char-123> or <Char-033> or <Char-0x33> */
vim_str2nr(last_dash + 6, NULL, NULL, TRUE, TRUE, NULL, &n);
vim_str2nr(last_dash + 6, NULL, NULL, TRUE, TRUE, TRUE, NULL, &n);
key = (int)n;
} else {
/*

View File

@@ -3037,7 +3037,7 @@ static double tv_float(typval_T *tvs, int *idxp)
* http://www.ijs.si/software/snprintf/
*
* This snprintf() only supports the following conversion specifiers:
* s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
* s, c, b, B, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
* with flags: '-', '+', ' ', '0' and '#'.
* An asterisk is supported for field width as well as precision.
*
@@ -3295,8 +3295,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
}
break;
case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
// u, o, x, X and p conversion specifiers imply the value is unsigned;
case 'd': case 'u': case 'b': case 'B': case 'o': case 'x': case 'X': case 'p': {
// u, b, B, o, x, X and p conversion specifiers imply the value is unsigned;
// d implies a signed value
// 0 if numeric argument is zero (or if pointer is NULL for 'p'),
@@ -3399,7 +3399,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
// leave negative numbers for sprintf to handle, to
// avoid handling tricky cases like (short int)-32768
} else if (alternate_form) {
if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) {
if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' ||
fmt_spec == 'b' || fmt_spec == 'B')) {
tmp[str_arg_l++] = '0';
tmp[str_arg_l++] = fmt_spec;
}
@@ -3411,7 +3412,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
precision = 1; // default precision is 1
if (precision == 0 && arg_sign == 0) {
// when zero value is formatted with an explicit precision 0,
// resulting formatted string is empty (d, i, u, o, x, X, p)
// resulting formatted string is empty (d, i, u, b, B, o, x, X, p)
} else {
char f[5];
int f_l = 0;
@@ -3441,6 +3442,36 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
case '2': str_arg_l += sprintf(tmp + str_arg_l, f, long_long_arg);
break;
}
} else if (fmt_spec == 'b' || fmt_spec == 'B') {
//binary
size_t bits = 0;
switch (length_modifier) {
case '\0':
case 'h': for (bits = sizeof(unsigned) * 8; bits > 0; bits--)
if ((uint_arg >> (bits - 1)) & 0x1) break;
while (bits > 0)
tmp[str_arg_l++] = ((uint_arg >> --bits) & 0x1) ? '1' : '0';
break;
case 'l': for (bits = sizeof(unsigned long) * 8; bits > 0; bits--)
if ((ulong_arg >> (bits - 1)) & 0x1) break;
while (bits > 0)
tmp[str_arg_l++] = ((ulong_arg >> --bits) & 0x1) ? '1' : '0';
break;
case '2': for (bits = sizeof(unsigned long long) * 8; bits > 0; bits--)
if ((ulong_long_arg >> (bits - 1)) & 0x1) break;
while (bits > 0)
tmp[str_arg_l++] = ((ulong_long_arg >> --bits) & 0x1) ? '1' : '0';
break;
case 'z': for (bits = sizeof(size_t) * 8; bits > 0; bits--)
if ((size_t_arg >> (bits - 1)) & 0x1) break;
while (bits > 0)
tmp[str_arg_l++] = ((size_t_arg >> --bits) & 0x1) ? '1' : '0';
break;
}
} else {
// unsigned
switch (length_modifier) {
@@ -3464,7 +3495,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (zero_padding_insertion_ind + 1 < str_arg_l
&& tmp[zero_padding_insertion_ind] == '0'
&& (tmp[zero_padding_insertion_ind + 1] == 'x'
|| tmp[zero_padding_insertion_ind + 1] == 'X'))
|| tmp[zero_padding_insertion_ind + 1] == 'X'
|| tmp[zero_padding_insertion_ind + 1] == 'b'
|| tmp[zero_padding_insertion_ind + 1] == 'B'))
zero_padding_insertion_ind += 2;
}

View File

@@ -4197,7 +4197,7 @@ int do_addsub(int command, linenr_T Prenum1)
int col;
char_u *buf1;
char_u buf2[NUMBUFLEN];
int hex; /* 'X' or 'x': hex; '0': octal */
int pre; /* 'X' or 'x': hex; '0': octal; 'B' or 'b': bin */
static int hexupper = FALSE; /* 0xABC */
unsigned long n, oldn;
char_u *ptr;
@@ -4206,6 +4206,7 @@ int do_addsub(int command, linenr_T Prenum1)
int todel;
int dohex;
int dooct;
int dobin;
int doalp;
int firstdigit;
int negative;
@@ -4213,6 +4214,7 @@ int do_addsub(int command, linenr_T Prenum1)
dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); /* "heX" */
dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); /* "Octal" */
dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); /* "Bin" */
doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); /* "alPha" */
ptr = get_cursor_line_ptr();
@@ -4222,19 +4224,44 @@ int do_addsub(int command, linenr_T Prenum1)
* First check if we are on a hexadecimal number, after the "0x".
*/
col = curwin->w_cursor.col;
if (dobin)
while (col > 0 && ascii_isbdigit(ptr[col]))
--col;
if (dohex)
while (col > 0 && ascii_isxdigit(ptr[col]))
--col;
if ( dohex
&& col > 0
if ( dobin
&& dohex
&& ! ((col > 0
&& (ptr[col] == 'X'
|| ptr[col] == 'x')
&& ptr[col - 1] == '0'
&& ascii_isxdigit(ptr[col + 1])) {
/*
* Found hexadecimal number, move to its start.
*/
--col;
&& ascii_isxdigit(ptr[col + 1])))) {
/* In case of binary/hexadecimal pattern overlap match, rescan */
col = curwin->w_cursor.col;
while (col > 0 && ascii_isdigit(ptr[col]))
col--;
}
if (( dohex
&& col > 0
&& (ptr[col] == 'X'
|| ptr[col] == 'x')
&& ptr[col - 1] == '0'
&& ascii_isxdigit(ptr[col + 1])) ||
( dobin
&& col > 0
&& (ptr[col] == 'B'
|| ptr[col] == 'b')
&& ptr[col - 1] == '0'
&& ascii_isbdigit(ptr[col + 1]))) {
/* Found hexadecimal or binary number, move to its start. */
--col;
} else {
/*
* Search forward and then backward to find the start of number.
@@ -4297,10 +4324,10 @@ int do_addsub(int command, linenr_T Prenum1)
}
/* get the number value (unsigned) */
vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n);
vim_str2nr(ptr + col, &pre, &length, dobin, dooct, dohex, NULL, &n);
/* ignore leading '-' for hex and octal numbers */
if (hex && negative) {
/* ignore leading '-' for hex, octal and bin numbers */
if (pre && negative) {
++col;
--length;
negative = FALSE;
@@ -4320,7 +4347,7 @@ int do_addsub(int command, linenr_T Prenum1)
n += (unsigned long)Prenum1;
/* handle wraparound for decimal numbers */
if (!hex) {
if (!pre) {
if (subtract) {
if (n > oldn) {
n = 1 + (n ^ (unsigned long)-1);
@@ -4370,23 +4397,38 @@ int do_addsub(int command, linenr_T Prenum1)
if (negative) {
*ptr++ = '-';
}
if (hex) {
if (pre) {
*ptr++ = '0';
--length;
}
if (hex == 'x' || hex == 'X') {
*ptr++ = hex;
if (pre == 'b' || pre == 'B'
|| pre == 'x' || pre == 'X') {
*ptr++ = pre;
--length;
}
/*
* Put the number characters in buf2[].
*/
if (hex == 0)
if (pre == 'b' || pre == 'B') {
size_t bits = 0;
size_t pos = 0;
/* leading zeros */
for (bits = 8 * sizeof(unsigned long); bits > 0; bits--)
if ((n >> (bits - 1)) & 0x1) break;
while (bits > 0)
buf2[pos++] = ((n >> --bits) & 0x1) ? '1' : '0';
buf2[pos] = '\0';
} else if (pre == 0)
sprintf((char *)buf2, "%" PRIu64, (uint64_t)n);
else if (hex == '0')
else if (pre == '0')
sprintf((char *)buf2, "%" PRIo64, (uint64_t)n);
else if (hex && hexupper)
else if (pre && hexupper)
sprintf((char *)buf2, "%" PRIX64, (uint64_t)n);
else
sprintf((char *)buf2, "%" PRIx64, (uint64_t)n);
@@ -4398,7 +4440,7 @@ int do_addsub(int command, linenr_T Prenum1)
* Don't do this when
* the result may look like an octal number.
*/
if (firstdigit == '0' && !(dooct && hex == 0))
if (firstdigit == '0' && !(dooct && pre == 0))
while (length-- > 0)
*ptr++ = '0';
*ptr = NUL;

View File

@@ -260,7 +260,7 @@ typedef struct vimoption {
static char *(p_ambw_values[]) = {"single", "double", NULL};
static char *(p_bg_values[]) = {"light", "dark", NULL};
static char *(p_nf_values[]) = {"octal", "hex", "alpha", NULL};
static char *(p_nf_values[]) = {"bin", "octal", "hex", "alpha", NULL};
static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL};
static char *(p_wop_values[]) = {"tagfile", NULL};
static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
@@ -1431,7 +1431,7 @@ do_set (
} else if (*arg == '-' || ascii_isdigit(*arg)) {
// Allow negative (for 'undolevels'), octal and
// hex numbers.
vim_str2nr(arg, NULL, &i, true, true, &value, NULL);
vim_str2nr(arg, NULL, &i, true, true, true, &value, NULL);
if (arg[i] != NUL && !ascii_iswhite(arg[i])) {
errmsg = e_invarg;
goto skip;

View File

@@ -1599,7 +1599,7 @@ return {
deny_duplicates=true,
alloced=true,
varname='p_nf',
defaults={if_true={vi="octal,hex", vim="hex"}}
defaults={if_true={vi="bin,octal,hex", vim="bin,hex"}}
},
{
full_name='number', abbreviation='nu',

View File

@@ -1095,7 +1095,9 @@ spell_check (
// 0X99FF. But always do check spelling to find "3GPP" and "11
// julifeest".
if (*ptr >= '0' && *ptr <= '9') {
if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
if (*ptr == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
mi.mi_end = skipbin(ptr + 2);
else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
mi.mi_end = skiphex(ptr + 2);
else
mi.mi_end = skipdigits(ptr);

View File

@@ -35,7 +35,7 @@ Error: configure did not run properly.Check auto/config.log.
#include "nvim/os/os_defs.h" /* bring lots of system header files */
#define NUMBUFLEN 30 /* length of a buffer to store a number in ASCII */
#define NUMBUFLEN 65 /* length of a buffer to store a number in ASCII */
#define MAX_TYPENR 65535