diff --git a/include/luaconf.h b/include/luaconf.h index 6423bfe..f5dd8d2 100644 --- a/include/luaconf.h +++ b/include/luaconf.h @@ -40,11 +40,13 @@ #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #define LUA_USE_READLINE /* needs some extra libraries */ +#define LUA_USE_STRTODHEX #endif #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX #define LUA_DL_DYLD /* does not need extra library */ +#define LUA_USE_STRTODHEX #endif @@ -520,6 +522,11 @@ @@ lua_number2str converts a number to a string. @@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. @@ lua_str2number converts a string to a number. +@@ lua_strx2number converts an hexadecimal numeric string to a number. +** In C99, 'strtod' does both conversions. C89, however, has no function +** to convert floating hexadecimal strings to numbers. For these +** systems, you can leave 'lua_strx2number' undefined and Lua will +** provide its own implementation. */ #define LUA_NUMBER_SCAN "%lf" #define LUA_NUMBER_FMT "%.14g" @@ -527,6 +534,10 @@ #define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ #define lua_str2number(s,p) strtod((s), (p)) +#if defined(LUA_USE_STRTODHEX) +#define lua_strx2number(s,p) strtod((s), (p)) +#endif + /* @@ The luai_num* macros define the primitive operations over numbers. diff --git a/src/llex.c b/src/llex.c index 575e71f..94dc774 100644 --- a/src/llex.c +++ b/src/llex.c @@ -183,7 +183,7 @@ static void trydecpoint (LexState *ls, SemInfo *seminfo) { char old = ls->decpoint; ls->decpoint = (cv ? cv->decimal_point[0] : '.'); buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ - if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { + if (!luaO_str2d(luaZ_buffer(ls->buff), luaZ_sizebuffer(ls->buff), &seminfo->r)) { /* format error with correct decimal point: no more options */ buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ luaX_lexerror(ls, "malformed number", TK_NUMBER); @@ -193,17 +193,22 @@ static void trydecpoint (LexState *ls, SemInfo *seminfo) { /* LUA_NUMBER */ static void read_numeral (LexState *ls, SemInfo *seminfo) { + const char *expo = "Ee"; + int first = ls->current; lua_assert(isdigit(ls->current)); - do { - save_and_next(ls); - } while (isdigit(ls->current) || ls->current == '.'); - if (check_next(ls, "Ee")) /* `E'? */ - check_next(ls, "+-"); /* optional exponent sign */ - while (isalnum(ls->current) || ls->current == '_') - save_and_next(ls); + save_and_next(ls); + if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */ + expo = "Pp"; + for (;;) { + if (check_next(ls, expo)) /* exponent part? */ + check_next(ls, "+-"); /* optional exponent sign */ + if (isxdigit(ls->current) || ls->current == '.') + save_and_next(ls); + else break; + } save(ls, '\0'); buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ - if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */ + if (!luaO_str2d(luaZ_buffer(ls->buff), luaZ_bufflen(ls->buff) - 1, &seminfo->r)) /* format error? */ trydecpoint(ls, seminfo); /* try to update decimal point separator */ } diff --git a/src/lobject.c b/src/lobject.c index 5bf922f..1bd6ace 100644 --- a/src/lobject.c +++ b/src/lobject.c @@ -87,16 +87,88 @@ int luaO_rawequalObj (const TValue *t1, const TValue *t2) { } -int luaO_str2d (const char *s, lua_Number *result) { +int luaO_hexavalue (int c) { + if (isdigit(c)) return c - '0'; + else return tolower(c) - 'a' + 10; +} + + +#if !defined(lua_strx2number) + +#include + + +static int isneg (const char **s) { + if (**s == '-') { (*s)++; return 1; } + else if (**s == '+') (*s)++; + return 0; +} + + +static lua_Number readhexa (const char **s, lua_Number r, int *count) { + for (; isxdigit(cast(unsigned char, **s)); (*s)++) { /* read integer part */ + r = (r * cast_num(16.0)) + cast_num(luaO_hexavalue(cast(unsigned char, **s))); + (*count)++; + } + return r; +} + + +/* +** convert an hexadecimal numeric string to a number, following +** C99 specification for 'strtod' +*/ +static lua_Number lua_strx2number (const char *s, char **endptr) { + lua_Number r = 0.0; + int e = 0, i = 0; + int neg = 0; /* 1 if number is negative */ + *endptr = cast(char *, s); /* nothing is valid yet */ + while (isspace(cast(unsigned char, *s))) s++; /* skip initial spaces */ + neg = isneg(&s); /* check signal */ + if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ + return 0.0; /* invalid format (no '0x') */ + s += 2; /* skip '0x' */ + r = readhexa(&s, r, &i); /* read integer part */ + if (*s == '.') { + s++; /* skip dot */ + r = readhexa(&s, r, &e); /* read fractional part */ + } + if (i == 0 && e == 0) + return 0.0; /* invalid format (no digit) */ + e *= -4; /* each fractional digit divides value by 2^-4 */ + *endptr = cast(char *, s); /* valid up to here */ + if (*s == 'p' || *s == 'P') { /* exponent part? */ + int exp1 = 0; + int neg1; + s++; /* skip 'p' */ + neg1 = isneg(&s); /* signal */ + if (!isdigit(cast(unsigned char, *s))) + goto ret; /* must have at least one digit */ + while (isdigit(cast(unsigned char, *s))) /* read exponent */ + exp1 = exp1 * 10 + *(s++) - '0'; + if (neg1) exp1 = -exp1; + e += exp1; + } + *endptr = cast(char *, s); /* valid up to here */ + ret: + if (neg) r = -r; + return ldexp(r, e); +} + +#endif + + +int luaO_str2d (const char *s, size_t len, lua_Number *result) { char *endptr; - *result = lua_str2number(s, &endptr); - if (endptr == s) return 0; /* conversion failed */ - if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ - *result = cast_num(strtoul(s, &endptr, 16)); - if (*endptr == '\0') return 1; /* most common case */ + if (strpbrk(s, "nN")) /* reject 'inf' and 'nan' */ + return 0; + else if (strpbrk(s, "xX")) /* hexa? */ + *result = lua_strx2number(s, &endptr); + else + *result = lua_str2number(s, &endptr); + if (endptr == s) return 0; /* nothing recognized */ while (isspace(cast(unsigned char, *endptr))) endptr++; - if (*endptr != '\0') return 0; /* invalid trailing characters? */ - return 1; + return (endptr == s + len); /* OK if no trailing characters */ } diff --git a/src/lobject.h b/src/lobject.h index 4a1256e..32ec197 100644 --- a/src/lobject.h +++ b/src/lobject.h @@ -413,7 +413,7 @@ LUAI_FUNC int luaO_log2 (unsigned int x); LUAI_FUNC int luaO_int2fb (unsigned int x); LUAI_FUNC int luaO_fb2int (int x); LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2); -LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result); +LUAI_FUNC int luaO_str2d (const char *s, size_t len, lua_Number *result); LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp); LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); diff --git a/src/luaconf.h b/src/luaconf.h index 6423bfe..f5dd8d2 100644 --- a/src/luaconf.h +++ b/src/luaconf.h @@ -40,11 +40,13 @@ #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #define LUA_USE_READLINE /* needs some extra libraries */ +#define LUA_USE_STRTODHEX #endif #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX #define LUA_DL_DYLD /* does not need extra library */ +#define LUA_USE_STRTODHEX #endif @@ -520,6 +522,11 @@ @@ lua_number2str converts a number to a string. @@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. @@ lua_str2number converts a string to a number. +@@ lua_strx2number converts an hexadecimal numeric string to a number. +** In C99, 'strtod' does both conversions. C89, however, has no function +** to convert floating hexadecimal strings to numbers. For these +** systems, you can leave 'lua_strx2number' undefined and Lua will +** provide its own implementation. */ #define LUA_NUMBER_SCAN "%lf" #define LUA_NUMBER_FMT "%.14g" @@ -527,6 +534,10 @@ #define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ #define lua_str2number(s,p) strtod((s), (p)) +#if defined(LUA_USE_STRTODHEX) +#define lua_strx2number(s,p) strtod((s), (p)) +#endif + /* @@ The luai_num* macros define the primitive operations over numbers. diff --git a/src/lvm.c b/src/lvm.c index 771c1ac..e8ae977 100644 --- a/src/lvm.c +++ b/src/lvm.c @@ -41,7 +41,7 @@ const TValue *luaV_tonumber (lua_State *L, const TValue *obj, TValue *n) { if (ttisnumber(obj)) return obj; resolverope(L, obj); resolvesubstr(L, obj); - if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { + if (ttisstring(obj) && luaO_str2d(svalue(obj), tsvalue(obj)->len, &num)) { setnvalue(n, num); return n; }