]> git.mxchange.org Git - simgear.git/commitdiff
Nasal: fix parsing octal/hex numbers in strings as well as during lexing.
authorThomas Geymayer <tomgey@gmail.com>
Thu, 26 Jun 2014 07:44:36 +0000 (09:44 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Thu, 26 Jun 2014 12:21:18 +0000 (14:21 +0200)
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
simgear/nasal/cppbind/nasal_num_test.cxx [new file with mode: 0644]
simgear/nasal/lex.c
simgear/nasal/string.c

index 8e8057f82002cf9b0f97c50130b9b656a538e0c1..4cb1c5aee2d89edf206e8b2aa05dd96e89085fad 100644 (file)
@@ -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 (file)
index 0000000..1746591
--- /dev/null
@@ -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>);
+}
index 92777ff9db5b5f0c604a99b097a332831e037674..dbeed7b8f40341364f4d648a3093298e2da4df24 100644 (file)
@@ -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] == '.') {
index ce8b5ac252721919a91293196b8ba29b277d1604..a252f602c41befdadf70aad1248b26a433502de1 100644 (file)
@@ -132,15 +132,25 @@ void naStr_gcclean(struct naStr* str)
 // remaining 67%.
 ////////////////////////////////////////////////////////////////////////
 
-// Reads an unsigned decimal out of the scalar starting at i, stores
+// 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
-// decimal numbers are allowed, and are returned as zero.
-static int readdec(unsigned char* s, int len, int i, double* v)
+// 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;
     }