• Subject: PATCH: hex numbers and bit ops
• From: Mike Pall <mikelu-0509@...>
• Date: Sun, 4 Sep 2005 23:52:39 +0200

```Hi,

this is a patch relative to Lua 5.1 alpha:

- The lexer now parses hex numbers (e.g. 0x1234 == 4660).

- luaO_str2d has a fallback for non C99 compliant systems
(used by tonumber() and by the lexer).

- String literals allow hex escape sequences (e.g. "\x40" == "@").

- New bit operations:

y = math.bitor(x1, x2)

y = math.bitand(x1 [, x2])   x2 = -1 if omitted (i.e. tobit)

y = math.bitxor(x1 [, x2])   x2 = -1 if omitted (i.e. bitnot)

y... = math.bitfield(x, start [, len] ...)
'start' is the bit position relative to the lsb (bit 0)
It's a right/left shift for a positive/negative 'start'.
It's a pure shift if 'len' is omitted.
Otherwise the result is masked to 'len' valid bits.

Examples:

bitand(120.123)                     --> 120
bitxor(120)                         --> -121
bitor(0x5aff, 0xa5f0)               --> 0xffff
bitand(0x5aff, 0xa5f0)              --> 0x00f0
bitxor(0x5aff, 0xa5f0)              --> 0xff0f
bitfield(0x345678, 8)               --> 0x3456
bitfield(0x345678, -8)              --> 0x34567800
bitfield(0x345678, 8, 8)            --> 0x56
bitfield(0x345678, -8, 8)           --> 0x7800
bitfield(0x345678, 3, 10)           --> 0x2cf
bitfield(0x345678, 0,8, 8,8, 16,8)  --> 0x78 0x56 0x34

- Restrictions:

- Non C99 compliant systems only support hex integer syntax
but not hex FP syntax (0xa.1p4).

- The precision of bit operations is limited by the minimum
precision of lua_Number and lua_Integer (ptrdiff_t). The
practical limit is usually at least 32 bits (only 24 bits
when lua_Number is a float). Bit operations work fine with
negative numbers and may produce negative numbers, too.

Bye,
Mike
```
```diff -u lua-5.1-alpha/src/llex.c lua51alpha-hex/src/llex.c
--- lua-5.1-alpha/src/llex.c	2005-05-17 21:49:15 +0200
+++ lua51alpha-hex/src/llex.c	2005-09-04 22:46:44 +0200
@@ -158,9 +158,17 @@

/* LUA_NUMBER */
static void read_numeral (LexState *ls, SemInfo *seminfo) {
+  int hex = 0;
while (isdigit(ls->current)) {
save_and_next(ls);
}
+  if (ls->current == 'x' || ls->current == 'X') {
+    save_and_next(ls);  /* read `x' */
+    while (isxdigit(ls->current)) {
+      save_and_next(ls);
+    }
+    hex = 1;
+  }
if (ls->current == '.') {
save_and_next(ls);
if (ls->current == '.') {
@@ -170,14 +178,15 @@
TK_NUMBER);
}
}
-  while (isdigit(ls->current)) {
+  while (hex ? isxdigit(ls->current) : isdigit(ls->current)) {
save_and_next(ls);
}
-  if (ls->current == 'e' || ls->current == 'E') {
+  if (hex ? (ls->current == 'p' || ls->current == 'P') :
+            (ls->current == 'e' || ls->current == 'E')) {
save_and_next(ls);  /* read `E' */
if (ls->current == '+' || ls->current == '-')
save_and_next(ls);  /* optional exponent sign */
-    while (isdigit(ls->current)) {
+    while (hex ? isxdigit(ls->current) : isdigit(ls->current)) {
save_and_next(ls);
}
}
@@ -277,6 +286,18 @@
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
+          case 'x':
+            next(ls);
+            if (isxdigit(ls->current)) {
+              c = ((ls->current & 15) + (isdigit(ls->current) ? 0 : 9)) << 4;
+              next(ls);
+              if (isxdigit(ls->current)) {
+                c += (ls->current & 15) + (isdigit(ls->current) ? 0 : 9);
+                break;
+              }
+            }
+            luaX_lexerror(ls, "bad hex escape sequence", TK_STRING);
+            return;
case '\n':  /* go through */
case '\r': save(ls, '\n'); inclinenumber(ls); continue;
case EOZ: continue;  /* will raise an error next loop */
diff -u lua-5.1-alpha/src/lobject.c lua51alpha-hex/src/lobject.c
--- lua-5.1-alpha/src/lobject.c	2005-08-01 06:22:23 +0200
+++ lua51alpha-hex/src/lobject.c	2005-09-04 22:46:44 +0200
@@ -90,11 +90,22 @@
int luaO_str2d (const char *s, lua_Number *result) {
char *endptr;
*result = lua_str2number(s, &endptr);
-  if (endptr == s) return 0;  /* conversion failed */
-  if (*endptr == '\0') return 1;  /* most common case */
-  while (isspace(cast(unsigned char, *endptr))) endptr++;
-  if (*endptr != '\0') return 0;  /* invalid trailing characters? */
-  return 1;
+  if (endptr != s) {  /* at least one char was accepted */
+    if (*endptr == '\0') return 1;  /* most common case */
+    while (isspace(cast(unsigned char, *endptr))) endptr++;
+    if (*endptr == '\0') return 1;  /* ok, only trailing spaces */
+  }
+  /* fallback for non C99 compliant systems */
+  if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+    lua_Number n = 0;
+    for (s += 2; isxdigit(*s); s++)
+      n = n * 16 + (*s & 15) + (isdigit(*s) ? 0 : 9);
+    if (*s == '\0') {
+      *result = n;
+      return 1;  /* ok */
+    }
+  }
+  return 0;  /* conversion failed */
}

diff -u lua-5.1-alpha/src/lmathlib.c lua51alpha-hex/src/lmathlib.c
--- lua-5.1-alpha/src/lmathlib.c	2005-08-26 19:36:32 +0200
+++ lua51alpha-hex/src/lmathlib.c	2005-09-04 22:46:44 +0200
@@ -212,6 +212,43 @@
}

+static int bit_and (lua_State *L) {  /* aka tobit */
+  lua_pushinteger(L, luaL_checkinteger(L, 1) & luaL_optinteger(L, 2, -1));
+  return 1;
+}
+
+
+static int bit_or (lua_State *L) {
+  lua_pushinteger(L, luaL_checkinteger(L, 1) | luaL_checkinteger(L, 2));
+  return 1;
+}
+
+
+static int bit_xor (lua_State *L) {  /* aka bitnot */
+  lua_pushinteger(L, luaL_checkinteger(L, 1) ^ luaL_optinteger(L, 2, -1));
+  return 1;
+}
+
+
+#define LI_BITS         (sizeof(lua_Integer)*8)
+#define LI_MASK(m)      (~(((lua_Integer)-1) << (m)))
+
+static int bit_field (lua_State *L) {  /* aka bitshift */
+  lua_Integer ii = luaL_checkinteger(L, 1);
+  int idx, num = lua_gettop(L);
+  for (idx = 2; idx <= num; idx += 2) {
+    lua_Integer i = ii;
+    int start = luaL_checkinteger(L, idx);
+    int len = luaL_optinteger(L, idx+1, LI_BITS);
+    if (start > 0) i = (i >> start) & LI_MASK(LI_BITS - start);
+    if (len < LI_BITS) i &= LI_MASK(len);
+    if (start < 0) i <<= -start;
+    lua_pushinteger(L, i);
+  }
+  return num >> 1;
+}
+
+
static const luaL_Reg mathlib[] = {
{"abs",   math_abs},
{"acos",  math_acos},
@@ -241,6 +278,10 @@
{"sqrt",  math_sqrt},
{"tanh",   math_tanh},
{"tan",   math_tan},
+  {"bitand",   bit_and},
+  {"bitor",    bit_or},
+  {"bitxor",   bit_xor},
+  {"bitfield", bit_field},
{NULL, NULL}
};

```