From 49bcf49db17a1cae1490ddeda377159314cff75c Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Thu, 26 Jun 2014 09:44:36 +0200 Subject: [PATCH] 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. --- simgear/nasal/cppbind/CMakeLists.txt | 5 ++ simgear/nasal/cppbind/nasal_num_test.cxx | 92 ++++++++++++++++++++++++ simgear/nasal/lex.c | 14 ++-- simgear/nasal/string.c | 32 ++++++--- 4 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 simgear/nasal/cppbind/nasal_num_test.cxx diff --git a/simgear/nasal/cppbind/CMakeLists.txt b/simgear/nasal/cppbind/CMakeLists.txt index 8e8057f8..4cb1c5ae 100644 --- a/simgear/nasal/cppbind/CMakeLists.txt +++ b/simgear/nasal/cppbind/CMakeLists.txt @@ -40,4 +40,9 @@ endif(ENABLE_TESTS) 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} ) \ No newline at end of file diff --git a/simgear/nasal/cppbind/nasal_num_test.cxx b/simgear/nasal/cppbind/nasal_num_test.cxx new file mode 100644 index 00000000..1746591f --- /dev/null +++ b/simgear/nasal/cppbind/nasal_num_test.cxx @@ -0,0 +1,92 @@ +#define BOOST_TEST_MODULE cppbind +#include + +#include "NasalCallContext.hxx" + +class TestContext: + public nasal::CallContext +{ + public: + TestContext(): + CallContext(naNewContext(), 0, 0) + {} + + ~TestContext() + { + naFreeContext(c); + } + + template + T from_str(const std::string& str) + { + return from_nasal(to_nasal(str)); + } + + naRef exec(const std::string& code_str, nasal::Me me) + { + int err_line = -1; + naRef code = naParseCode( c, to_nasal(""), 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 + T exec(const std::string& code) + { + return from_nasal(exec(code, naNil())); + } + + template + T convert(const std::string& str) + { + return from_nasal(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, &TestContext::convert); +} + +BOOST_AUTO_TEST_CASE( lex_num ) +{ + runNumTests(&TestContext::exec, &TestContext::exec); +} diff --git a/simgear/nasal/lex.c b/simgear/nasal/lex.c index 92777ff9..dbeed7b8 100644 --- a/simgear/nasal/lex.c +++ b/simgear/nasal/lex.c @@ -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= '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; }