PageRenderTime 140ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/jsonrpc/public/services/simplejson/decoder.py

http://pyjamas.googlecode.com/
Python | 273 lines | 261 code | 9 blank | 3 comment | 8 complexity | 6aac446b95d1be48994b494df061d4e8 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
  1. """
  2. Implementation of JSONDecoder
  3. """
  4. import re
  5. from simplejson.scanner import Scanner, pattern
  6. FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
  7. def _floatconstants():
  8. import struct
  9. import sys
  10. _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
  11. if sys.byteorder != 'big':
  12. _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
  13. nan, inf = struct.unpack('dd', _BYTES)
  14. return nan, inf, -inf
  15. NaN, PosInf, NegInf = _floatconstants()
  16. def linecol(doc, pos):
  17. lineno = doc.count('\n', 0, pos) + 1
  18. if lineno == 1:
  19. colno = pos
  20. else:
  21. colno = pos - doc.rindex('\n', 0, pos)
  22. return lineno, colno
  23. def errmsg(msg, doc, pos, end=None):
  24. lineno, colno = linecol(doc, pos)
  25. if end is None:
  26. return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
  27. endlineno, endcolno = linecol(doc, end)
  28. return '%s: line %d column %d - line %d column %d (char %d - %d)' % (
  29. msg, lineno, colno, endlineno, endcolno, pos, end)
  30. _CONSTANTS = {
  31. '-Infinity': NegInf,
  32. 'Infinity': PosInf,
  33. 'NaN': NaN,
  34. 'true': True,
  35. 'false': False,
  36. 'null': None,
  37. }
  38. def JSONConstant(match, context, c=_CONSTANTS):
  39. return c[match.group(0)], None
  40. pattern('(-?Infinity|NaN|true|false|null)')(JSONConstant)
  41. def JSONNumber(match, context):
  42. match = JSONNumber.regex.match(match.string, *match.span())
  43. integer, frac, exp = match.groups()
  44. if frac or exp:
  45. res = float(integer + (frac or '') + (exp or ''))
  46. else:
  47. res = int(integer)
  48. return res, None
  49. pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(JSONNumber)
  50. STRINGCHUNK = re.compile(r'(.*?)(["\\])', FLAGS)
  51. BACKSLASH = {
  52. '"': u'"', '\\': u'\\', '/': u'/',
  53. 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
  54. }
  55. DEFAULT_ENCODING = "utf-8"
  56. def scanstring(s, end, encoding=None, _b=BACKSLASH, _m=STRINGCHUNK.match):
  57. if encoding is None:
  58. encoding = DEFAULT_ENCODING
  59. chunks = []
  60. _append = chunks.append
  61. begin = end - 1
  62. while 1:
  63. chunk = _m(s, end)
  64. if chunk is None:
  65. raise ValueError(
  66. errmsg("Unterminated string starting at", s, begin))
  67. end = chunk.end()
  68. content, terminator = chunk.groups()
  69. if content:
  70. if not isinstance(content, unicode):
  71. content = unicode(content, encoding)
  72. _append(content)
  73. if terminator == '"':
  74. break
  75. try:
  76. esc = s[end]
  77. except IndexError:
  78. raise ValueError(
  79. errmsg("Unterminated string starting at", s, begin))
  80. if esc != 'u':
  81. try:
  82. m = _b[esc]
  83. except KeyError:
  84. raise ValueError(
  85. errmsg("Invalid \\escape: %r" % (esc,), s, end))
  86. end += 1
  87. else:
  88. esc = s[end + 1:end + 5]
  89. try:
  90. m = unichr(int(esc, 16))
  91. if len(esc) != 4 or not esc.isalnum():
  92. raise ValueError
  93. except ValueError:
  94. raise ValueError(errmsg("Invalid \\uXXXX escape", s, end))
  95. end += 5
  96. _append(m)
  97. return u''.join(chunks), end
  98. def JSONString(match, context):
  99. encoding = getattr(context, 'encoding', None)
  100. return scanstring(match.string, match.end(), encoding)
  101. pattern(r'"')(JSONString)
  102. WHITESPACE = re.compile(r'\s*', FLAGS)
  103. def JSONObject(match, context, _w=WHITESPACE.match):
  104. pairs = {}
  105. s = match.string
  106. end = _w(s, match.end()).end()
  107. nextchar = s[end:end + 1]
  108. # trivial empty object
  109. if nextchar == '}':
  110. return pairs, end + 1
  111. if nextchar != '"':
  112. raise ValueError(errmsg("Expecting property name", s, end))
  113. end += 1
  114. encoding = getattr(context, 'encoding', None)
  115. iterscan = JSONScanner.iterscan
  116. while True:
  117. key, end = scanstring(s, end, encoding)
  118. end = _w(s, end).end()
  119. if s[end:end + 1] != ':':
  120. raise ValueError(errmsg("Expecting : delimiter", s, end))
  121. end = _w(s, end + 1).end()
  122. try:
  123. value, end = iterscan(s, idx=end, context=context).next()
  124. except StopIteration:
  125. raise ValueError(errmsg("Expecting object", s, end))
  126. pairs[key] = value
  127. end = _w(s, end).end()
  128. nextchar = s[end:end + 1]
  129. end += 1
  130. if nextchar == '}':
  131. break
  132. if nextchar != ',':
  133. raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
  134. end = _w(s, end).end()
  135. nextchar = s[end:end + 1]
  136. end += 1
  137. if nextchar != '"':
  138. raise ValueError(errmsg("Expecting property name", s, end - 1))
  139. object_hook = getattr(context, 'object_hook', None)
  140. if object_hook is not None:
  141. pairs = object_hook(pairs)
  142. return pairs, end
  143. pattern(r'{')(JSONObject)
  144. def JSONArray(match, context, _w=WHITESPACE.match):
  145. values = []
  146. s = match.string
  147. end = _w(s, match.end()).end()
  148. # look-ahead for trivial empty array
  149. nextchar = s[end:end + 1]
  150. if nextchar == ']':
  151. return values, end + 1
  152. iterscan = JSONScanner.iterscan
  153. while True:
  154. try:
  155. value, end = iterscan(s, idx=end, context=context).next()
  156. except StopIteration:
  157. raise ValueError(errmsg("Expecting object", s, end))
  158. values.append(value)
  159. end = _w(s, end).end()
  160. nextchar = s[end:end + 1]
  161. end += 1
  162. if nextchar == ']':
  163. break
  164. if nextchar != ',':
  165. raise ValueError(errmsg("Expecting , delimiter", s, end))
  166. end = _w(s, end).end()
  167. return values, end
  168. pattern(r'\[')(JSONArray)
  169. ANYTHING = [
  170. JSONObject,
  171. JSONArray,
  172. JSONString,
  173. JSONConstant,
  174. JSONNumber,
  175. ]
  176. JSONScanner = Scanner(ANYTHING)
  177. class JSONDecoder(object):
  178. """
  179. Simple JSON <http://json.org> decoder
  180. Performs the following translations in decoding:
  181. +---------------+-------------------+
  182. | JSON | Python |
  183. +===============+===================+
  184. | object | dict |
  185. +---------------+-------------------+
  186. | array | list |
  187. +---------------+-------------------+
  188. | string | unicode |
  189. +---------------+-------------------+
  190. | number (int) | int, long |
  191. +---------------+-------------------+
  192. | number (real) | float |
  193. +---------------+-------------------+
  194. | true | True |
  195. +---------------+-------------------+
  196. | false | False |
  197. +---------------+-------------------+
  198. | null | None |
  199. +---------------+-------------------+
  200. It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
  201. their corresponding ``float`` values, which is outside the JSON spec.
  202. """
  203. _scanner = Scanner(ANYTHING)
  204. __all__ = ['__init__', 'decode', 'raw_decode']
  205. def __init__(self, encoding=None, object_hook=None):
  206. """
  207. ``encoding`` determines the encoding used to interpret any ``str``
  208. objects decoded by this instance (utf-8 by default). It has no
  209. effect when decoding ``unicode`` objects.
  210. Note that currently only encodings that are a superset of ASCII work,
  211. strings of other encodings should be passed in as ``unicode``.
  212. ``object_hook``, if specified, will be called with the result
  213. of every JSON object decoded and its return value will be used in
  214. place of the given ``dict``. This can be used to provide custom
  215. deserializations (e.g. to support JSON-RPC class hinting).
  216. """
  217. self.encoding = encoding
  218. self.object_hook = object_hook
  219. def decode(self, s, _w=WHITESPACE.match):
  220. """
  221. Return the Python representation of ``s`` (a ``str`` or ``unicode``
  222. instance containing a JSON document)
  223. """
  224. obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  225. end = _w(s, end).end()
  226. if end != len(s):
  227. raise ValueError(errmsg("Extra data", s, end, len(s)))
  228. return obj
  229. def raw_decode(self, s, **kw):
  230. """
  231. Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
  232. with a JSON document) and return a 2-tuple of the Python
  233. representation and the index in ``s`` where the document ended.
  234. This can be used to decode a JSON document from a string that may
  235. have extraneous data at the end.
  236. """
  237. kw.setdefault('context', self)
  238. try:
  239. obj, end = self._scanner.iterscan(s, **kw).next()
  240. except StopIteration:
  241. raise ValueError("No JSON object could be decoded")
  242. return obj, end
  243. __all__ = ['JSONDecoder']