Nasal: fix parsing octal/hex numbers in strings as well as during lexing.
Parse the same number formats (octal, dec, hex) in literals and tokens. Was previously quite a mess, and is still not the best solution, as lexing and string parsing uses different implementations, although they are meant to do exactly the same conversions.
This commit is contained in:
parent
3bbb272ad5
commit
49bcf49db1
@ -41,3 +41,8 @@ add_boost_test(cppbind_ghost
|
||||
SOURCES cppbind_test_ghost.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
|
||||
add_boost_test(nasal_num
|
||||
SOURCES nasal_num_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
92
simgear/nasal/cppbind/nasal_num_test.cxx
Normal file
92
simgear/nasal/cppbind/nasal_num_test.cxx
Normal file
@ -0,0 +1,92 @@
|
||||
#define BOOST_TEST_MODULE cppbind
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "NasalCallContext.hxx"
|
||||
|
||||
class TestContext:
|
||||
public nasal::CallContext
|
||||
{
|
||||
public:
|
||||
TestContext():
|
||||
CallContext(naNewContext(), 0, 0)
|
||||
{}
|
||||
|
||||
~TestContext()
|
||||
{
|
||||
naFreeContext(c);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T from_str(const std::string& str)
|
||||
{
|
||||
return from_nasal<T>(to_nasal(str));
|
||||
}
|
||||
|
||||
naRef exec(const std::string& code_str, nasal::Me me)
|
||||
{
|
||||
int err_line = -1;
|
||||
naRef code = naParseCode( c, to_nasal("<TextContext::exec>"), 0,
|
||||
(char*)code_str.c_str(), code_str.length(),
|
||||
&err_line );
|
||||
if( !naIsCode(code) )
|
||||
throw std::runtime_error("Failed to parse code: " + code_str);
|
||||
|
||||
return naCallMethod(code, me, 0, 0, naNil());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T exec(const std::string& code)
|
||||
{
|
||||
return from_nasal<T>(exec(code, naNil()));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T convert(const std::string& str)
|
||||
{
|
||||
return from_nasal<T>(to_nasal(str));
|
||||
}
|
||||
};
|
||||
|
||||
static void runNumTests( double (TestContext::*test_double)(const std::string&),
|
||||
int (TestContext::*test_int)(const std::string&) )
|
||||
{
|
||||
TestContext c;
|
||||
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("0.5"), 0.5, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_double)(".6"), 0.6, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("-.7"), -0.7, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("-0.8"), -0.8, 1e-5);
|
||||
BOOST_CHECK_SMALL((c.*test_double)("0.0"), 1e-5);
|
||||
BOOST_CHECK_SMALL((c.*test_double)("-.0"), 1e-5);
|
||||
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("1.23e4"), 1.23e4, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("1.23e-4"), 1.23e-4, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("-1.23e4"), -1.23e4, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("-1.23e-4"), -1.23e-4, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("1e-4"), 1e-4, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("-1e-4"), -1e-4, 1e-5);
|
||||
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("123"), 123);
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("-958"), -958);
|
||||
|
||||
BOOST_CHECK_CLOSE((c.*test_int)("-1e7"), -1e7, 1e-5);
|
||||
BOOST_CHECK_CLOSE((c.*test_int)("2E07"), 2e07, 1e-5);
|
||||
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("0755"), 0755);
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("0055"), 055);
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("-0155"), -0155);
|
||||
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("0x755"), 0x755);
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("0x055"), 0x55);
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("-0x155"), -0x155);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( parse_num )
|
||||
{
|
||||
runNumTests(&TestContext::convert<double>, &TestContext::convert<int>);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( lex_num )
|
||||
{
|
||||
runNumTests(&TestContext::exec<double>, &TestContext::exec<int>);
|
||||
}
|
@ -252,12 +252,12 @@ static int lexStringLiteral(struct Parser* p, int index, char q)
|
||||
return i+1;
|
||||
}
|
||||
|
||||
static int lexHexLiteral(struct Parser* p, int index)
|
||||
static int lexIntLiteral(struct Parser* p, int index, int base)
|
||||
{
|
||||
int nib, i = index;
|
||||
double d = 0;
|
||||
while(i < p->len && (nib = hex(p->buf[i])) >= 0) {
|
||||
d = d*16 + nib;
|
||||
while(i < p->len && (nib = hex(p->buf[i])) >= 0 && nib < base) {
|
||||
d = d * base + nib;
|
||||
i++;
|
||||
}
|
||||
newToken(p, index, TOK_LITERAL, 0, 0, d);
|
||||
@ -273,8 +273,12 @@ static int lexNumLiteral(struct Parser* p, int index)
|
||||
unsigned char* buf = (unsigned char*)p->buf;
|
||||
double d;
|
||||
|
||||
if(buf[i] == '0' && i+2<len && buf[i+1] == 'x' && ISHEX(buf[i+2]))
|
||||
return lexHexLiteral(p, index+2);
|
||||
if(buf[i] == '0') {
|
||||
if(i+2<len && buf[i+1] == 'x' && ISHEX(buf[i+2]))
|
||||
return lexIntLiteral(p, index+2, 16);
|
||||
if(i+1<len && ISNUM(buf[i+1]) )
|
||||
return lexIntLiteral(p, index+1, 8);
|
||||
}
|
||||
|
||||
while(i<len && ISNUM(buf[i])) i++;
|
||||
if(i<len && buf[i] == '.') {
|
||||
|
@ -132,15 +132,25 @@ void naStr_gcclean(struct naStr* str)
|
||||
// remaining 67%.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Reads an unsigned decimal out of the scalar starting at i, stores
|
||||
// it in v, and returns the next index to start at. Zero-length
|
||||
// decimal numbers are allowed, and are returned as zero.
|
||||
static int readdec(unsigned char* s, int len, int i, double* v)
|
||||
// TODO unify with number conversion in lex.c
|
||||
static int hex(char c)
|
||||
{
|
||||
if(c >= '0' && c <= '9') return c - '0';
|
||||
if(c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
if(c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reads an unsigned integer out of the scalar starting at i, stores
|
||||
// it in v, and returns the next index to start at. Zero-length
|
||||
// integer numbers are allowed, and are returned as zero.
|
||||
static int readint(unsigned char* s, int len, int i, double* v, int base)
|
||||
{
|
||||
int val;
|
||||
*v = 0;
|
||||
if(i >= len) return len;
|
||||
while(i < len && s[i] >= '0' && s[i] <= '9') {
|
||||
*v= (*v) * 10 + (s[i] - '0');
|
||||
while(i < len && (val = hex(s[i])) >= 0 && val < base) {
|
||||
*v= (*v) * base + val;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
@ -151,12 +161,16 @@ static int readdec(unsigned char* s, int len, int i, double* v)
|
||||
// decimal numbers are allowed, and are returned as zero.
|
||||
static int readsigned(unsigned char* s, int len, int i, double* v)
|
||||
{
|
||||
int i0 = i, i2;
|
||||
int i0 = i, i2, base = 10;
|
||||
double sgn=1, val;
|
||||
if(i >= len) { *v = 0; return len; }
|
||||
if(s[i] == '+') { i++; }
|
||||
else if(s[i] == '-') { i++; sgn = -1; }
|
||||
i2 = readdec(s, len, i, &val);
|
||||
if(s[i] == '0') {
|
||||
i++; base = 8;
|
||||
if( i < len && s[i] == 'x' ) { i++; base = 16; }
|
||||
}
|
||||
i2 = readint(s, len, i, &val, base);
|
||||
if(i0 == i && i2 == i) {
|
||||
*v = 0;
|
||||
return i0; // don't successfully parse bare "+" or "-"
|
||||
@ -201,7 +215,7 @@ static int tonum(unsigned char* s, int len, double* result)
|
||||
// Read the fractional part, if any
|
||||
if(i < len && s[i] == '.') {
|
||||
i++;
|
||||
fraclen = readdec(s, len, i, &frac) - i;
|
||||
fraclen = readint(s, len, i, &frac, 10) - i;
|
||||
i += fraclen;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user