Created
March 14, 2013 16:24
-
-
Save homm/5162786 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import print_function | |
import re | |
ws_match = re.compile(r'[ \t\n\r]*').match | |
number_match = re.compile( | |
r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?[ \t\n\r]*', | |
(re.VERBOSE | re.MULTILINE | re.DOTALL)).match | |
nan = float('nan') | |
inf = float('inf') | |
def decode(s, pos=0): | |
def parse_any(pos): | |
c = s[pos] | |
if c == '"': | |
return parse_string(pos) | |
if c == '{': | |
return parse_dict(ws_match(s, pos + 1).end()) | |
if c == '[': | |
return parse_list(ws_match(s, pos + 1).end()) | |
if c in 'ntfNI-': | |
# constants | |
if s.startswith('null', pos): | |
return None, ws_match(s, pos + 4).end() | |
if s.startswith('true', pos): | |
return True, ws_match(s, pos + 4).end() | |
if s.startswith('false', pos): | |
return False, ws_match(s, pos + 5).end() | |
# numbers, but constants | |
if s.startswith('NaN', pos): | |
return nan, ws_match(s, pos + 5).end() | |
if s.startswith('Infinity', pos): | |
return inf, ws_match(s, pos + 8).end() | |
if s.startswith('-Infinity', pos): | |
return -inf, ws_match(s, pos + 9).end() | |
num = number_match(s, pos) | |
if num is not None: | |
integer, frac, exp = num.groups() | |
if frac or exp: | |
res = float(integer + (frac or '') + (exp or '')) | |
else: | |
res = int(integer) | |
return res, num.end() | |
raise ValueError('Unknown token.') | |
def parse_dict(pos): | |
if s[pos] == '}': | |
return {}, pos + 1 | |
d = {} | |
while True: | |
if s[pos] != '"': | |
raise ValueError('Objects keys should be strings.') | |
key, pos = parse_string(pos) | |
if s[pos] != ':': | |
raise ValueError('Not colon.') | |
value, pos = parse_any(ws_match(s, pos + 1).end()) | |
d[key] = value | |
if s[pos] != ',': | |
break | |
pos = ws_match(s, pos + 1).end() | |
if s[pos] != '}': | |
raise ValueError('Object not closed.') | |
return d, ws_match(s, pos + 1).end() | |
def parse_list(pos): | |
if s[pos] == ']': | |
return [], pos + 1 | |
l = [] | |
_append = l.append | |
while True: | |
value, pos = parse_any(pos) | |
l.append(value) | |
if s[pos] != ',': | |
break | |
pos = ws_match(s, pos + 1).end() | |
if s[pos] != ']': | |
raise ValueError('Array not closed.') | |
return l, ws_match(s, pos + 1).end() | |
def parse_string(pos): | |
first = pos + 1 | |
while True: | |
pos = s.index('"', pos + 1) | |
if s[pos - 1] != '\\': | |
break | |
return s[first:pos], ws_match(s, pos + 1).end() | |
return parse_any(ws_match(s, pos).end())[0] | |
if __name__ == '__main__': | |
test = """ | |
{ | |
"json\\\"on\\\" on" : "yes" , | |
"om-nom-nom":null,"empty obj":{}, | |
"arr": [null, true , false, [], null, true , false, []], | |
"numbers = ": [2, 3,5,66e4], | |
"longobj": { | |
"key1": "testdata", | |
"key2": "testdata", | |
"key3": "testdata", | |
"key4": "testdata", | |
"key5": "testdata", | |
"key6": "testdata", | |
"key7": "testdata", | |
"key8": "testdata", | |
"key9": "testdata", | |
"key10": "testdata", | |
"key11": "testdata", | |
"key12": "testdata", | |
"key13": "testdata", | |
"key14": "testdata" | |
} | |
} | |
""" | |
from timeit import repeat | |
number = 1000 | |
# test = open("go/src/pkg/encoding/json/testdata/code.json").read() | |
print() | |
print('>>> pyjson') | |
print(decode(test)) | |
print(repeat(lambda: decode(test), number=number, repeat=3)) | |
from json import loads | |
print() | |
print('>>> json') | |
print(loads(test)) | |
print(repeat(lambda: loads(test), number=number, repeat=3)) | |
try: | |
from demjson import decode | |
except ImportError: | |
pass | |
else: | |
print() | |
print('>>> demjson') | |
print(decode(test)) | |
print(repeat(lambda: decode(test), number=number, repeat=3)) | |
try: | |
from ujson import loads | |
except ImportError: | |
pass | |
else: | |
print() | |
print('>>> ujson') | |
print(loads(test)) | |
print(repeat(lambda: loads(test), number=number, repeat=3)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment