Author: Antonio Cuni <[email protected]>
Branch: fastjson
Changeset: r64792:535479329816
Date: 2013-06-05 14:14 +0200
http://bitbucket.org/pypy/pypy/changeset/535479329816/

Log:    add support for parsing float values

diff --git a/pypy/module/_fastjson/interp_decoder.py 
b/pypy/module/_fastjson/interp_decoder.py
--- a/pypy/module/_fastjson/interp_decoder.py
+++ b/pypy/module/_fastjson/interp_decoder.py
@@ -7,6 +7,15 @@
 def is_whitespace(ch):
     return ch == ' ' or ch == '\t' or ch == '\r' or ch == '\n'
 
+# precomputing negative powers of 10 is MUCH faster than using e.g. math.pow
+# at runtime
+NEG_POW_10 = [10**-i for i in range(16)]
+def neg_pow_10(x, exp):
+    if exp >= len(NEG_POW_10):
+        return 0.0
+    return x * NEG_POW_10[exp]
+
+
 TYPE_UNKNOWN = 0
 TYPE_STRING = 1
 
@@ -23,6 +32,12 @@
     def peek(self):
         return self.s[self.i]
 
+    def peek_maybe(self):
+        if self.eof():
+            return '\0'
+        else:
+            return self.peek()
+
     def next(self):
         ch = self.peek()
         self.i += 1
@@ -56,30 +71,69 @@
             self.next()
             return self.decode_string()
         elif ch.isdigit() or ch == '-':
-            return self.decode_numeric(ch)
+            return self.decode_numeric()
         elif ch == '{':
             self.next()
             return self.decode_object()
         else:
-            assert False, 'Unkown char: %s' % ch
+            self._raise("No JSON object could be decoded: unexpected '%s' at 
char %d",
+                        ch, self.i)
 
-    def decode_numeric(self, ch):
-        intval = 0
+    def decode_numeric(self):
+        intval = self.parse_integer()
+        #
+        is_float = False
+        exp = 0
+        frcval = 0.0
+        frccount = 0
+        #
+        # check for the optional fractional part
+        ch = self.peek_maybe()
+        if ch == '.':
+            is_float = True
+            self.next()
+            frcval, frccount = self.parse_digits()
+            frcval = neg_pow_10(frcval, frccount)
+            ch = self.peek_maybe()
+        # check for the optional exponent part
+        if ch == 'E' or ch == 'e':
+            is_float = True
+            self.next()
+            exp = self.parse_integer()
+        #
+        if is_float:
+            # build the float
+            floatval = intval + frcval
+            floatval = floatval * 10**exp
+            return self.space.wrap(floatval)
+        else:
+            return self.space.wrap(intval)
+
+    def parse_integer(self):
+        "Parse a decimal number with an optional minus sign"
         sign = 1
-        if ch == '-':
+        if self.peek_maybe() == '-':
             sign = -1
             self.next()
+        intval, _ = self.parse_digits()
+        return sign * intval
 
+    def parse_digits(self):
+        "Parse a sequence of digits as a decimal number. No sign allowed"
+        intval = 0
+        count = 0
         while not self.eof():
             ch = self.peek()
             if ch.isdigit():
                 intval = intval*10 + ord(ch)-ord('0')
+                count += 1
                 self.next()
             else:
                 break
-        #
-        intval = intval*sign
-        return self.space.wrap(intval)
+        if count == 0:
+            self._raise("Expected digit at char %d", self.i)
+        return intval, count
+        
 
     def decode_object(self):
         start = self.i
diff --git a/pypy/module/_fastjson/test/test__fastjson.py 
b/pypy/module/_fastjson/test/test__fastjson.py
--- a/pypy/module/_fastjson/test/test__fastjson.py
+++ b/pypy/module/_fastjson/test/test__fastjson.py
@@ -71,9 +71,31 @@
 
     def test_decode_numeric(self):
         import _fastjson
-        assert _fastjson.loads('42') == 42
-        assert _fastjson.loads('-42') == -42
-        raises(ValueError, "_fastjson.loads('42 abc')")
+        def check(s, val):
+            res = _fastjson.loads(s)
+            assert type(res) is type(val)
+            assert res == val
+        #
+        check('42', 42)
+        check('-42', -42)
+        check('42.123', 42.123)
+        check('42E0', 42.0)
+        check('42E3', 42000.0)
+        check('42E-1', 4.2)
+        check('42.123E3', 42123.0)
+
+    def test_decode_numeric_invalid(self):
+        import _fastjson
+        def error(s):
+            raises(ValueError, _fastjson.loads, s)
+        #
+        error('  42   abc')
+        error('.123')
+        error('12.')
+        error('12.-3')
+        error('12E')
+        error('12E-')
+
 
     def test_decode_object(self):
         import _fastjson
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to