Skip to content

Instantly share code, notes, and snippets.

@homm
Created March 14, 2013 16:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save homm/5162786 to your computer and use it in GitHub Desktop.
Save homm/5162786 to your computer and use it in GitHub Desktop.
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