import of github code used for the hackathon
[github-barcamp-201407] / ijson-1.1 / ijson / backends / python.py
1 '''
2 Pure-python parsing backend.
3 '''
4 from __future__ import unicode_literals
5 from decimal import Decimal
6 import re
7 from codecs import unicode_escape_decode
8
9 from ijson import common
10 from ijson.compat import chr
11
12
13 BUFSIZE = 16 * 1024
14 NONWS = re.compile(r'\S')
15 LEXTERM = re.compile(r'[^a-z0-9\.+-]')
16
17
18 class UnexpectedSymbol(common.JSONError):
19     def __init__(self, symbol, reader):
20         super(UnexpectedSymbol, self).__init__('Unexpected symbol "%s" at %d' % (symbol[0], reader.pos - len(symbol)))
21
22 class Lexer(object):
23     '''
24     JSON lexer. Supports iterator interface.
25     '''
26     def __init__(self, f):
27         self.f = f
28
29     def __iter__(self):
30         self.buffer = ''
31         self.pos = 0
32         return self
33
34     def __next__(self):
35         while True:
36             match = NONWS.search(self.buffer, self.pos)
37             if match:
38                 self.pos = match.start()
39                 char = self.buffer[self.pos]
40                 if 'a' <= char <= 'z' or '0' <= char <= '9' or char == '-':
41                     return self.lexem()
42                 elif char == '"':
43                     return self.stringlexem()
44                 else:
45                     self.pos += 1
46                     return char
47             self.buffer = self.f.read(BUFSIZE).decode('utf-8')
48             self.pos = 0
49             if not len(self.buffer):
50                 raise StopIteration
51     next = __next__
52
53     def lexem(self):
54         current = self.pos
55         while True:
56             match = LEXTERM.search(self.buffer, current)
57             if match:
58                 current = match.start()
59                 break
60             else:
61                 current = len(self.buffer)
62                 self.buffer += self.f.read(BUFSIZE).decode('utf-8')
63                 if len(self.buffer) == current:
64                     break
65         result = self.buffer[self.pos:current]
66         self.pos = current
67         if self.pos > BUFSIZE:
68             self.buffer = self.buffer[self.pos:]
69             self.pos = 0
70         return result
71
72     def stringlexem(self):
73         start = self.pos + 1
74         while True:
75             try:
76                 end = self.buffer.index('"', start)
77                 escpos = end - 1
78                 while self.buffer[escpos] == '\\':
79                     escpos -= 1
80                 if (end - escpos) % 2 == 0:
81                     start = end + 1
82                 else:
83                     result = self.buffer[self.pos:end + 1]
84                     self.pos = end + 1
85                     return result
86             except ValueError:
87                 old_len = len(self.buffer)
88                 self.buffer += self.f.read(BUFSIZE).decode('utf-8')
89                 if len(self.buffer) == old_len:
90                     raise common.IncompleteJSONError()
91
92 def unescape(s):
93     start = 0
94     while start < len(s):
95         pos = s.find('\\', start)
96         if pos == -1:
97             yield s[start:]
98             break
99         yield s[start:pos]
100         pos += 1
101         esc = s[pos]
102         if esc == 'b':
103             yield '\b'
104         elif esc == 'f':
105             yield '\f'
106         elif esc == 'n':
107             yield '\n'
108         elif esc == 'r':
109             yield '\r'
110         elif esc == 't':
111             yield '\t'
112         elif esc == 'u':
113             yield chr(int(s[pos + 1:pos + 5], 16))
114             pos += 4
115         else:
116             yield esc
117         start = pos + 1
118
119 def parse_value(lexer, symbol=None):
120     try:
121         if symbol is None:
122             symbol = next(lexer)
123         if symbol == 'null':
124             yield ('null', None)
125         elif symbol == 'true':
126             yield ('boolean', True)
127         elif symbol == 'false':
128             yield ('boolean', False)
129         elif symbol == '[':
130             for event in parse_array(lexer):
131                 yield event
132         elif symbol == '{':
133             for event in parse_object(lexer):
134                 yield event
135         elif symbol[0] == '"':
136             yield ('string', ''.join(unescape(symbol[1:-1])))
137         else:
138             try:
139                 number = Decimal(symbol) if '.' in symbol else int(symbol)
140                 yield ('number', number)
141             except ValueError:
142                 raise UnexpectedSymbol(symbol, lexer)
143     except StopIteration:
144         raise common.IncompleteJSONError()
145
146 def parse_array(lexer):
147     yield ('start_array', None)
148     symbol = next(lexer)
149     if symbol != ']':
150         while True:
151             for event in parse_value(lexer, symbol):
152                 yield event
153             symbol = next(lexer)
154             if symbol == ']':
155                 break
156             if symbol != ',':
157                 raise UnexpectedSymbol(symbol, lexer)
158             symbol = next(lexer)
159     yield ('end_array', None)
160
161 def parse_object(lexer):
162     yield ('start_map', None)
163     symbol = next(lexer)
164     if symbol != '}':
165         while True:
166             if symbol[0] != '"':
167                 raise UnexpectedSymbol(symbol, lexer)
168             yield ('map_key', symbol[1:-1])
169             symbol = next(lexer)
170             if symbol != ':':
171                 raise UnexpectedSymbol(symbol, lexer)
172             for event in parse_value(lexer):
173                 yield event
174             symbol = next(lexer)
175             if symbol == '}':
176                 break
177             if symbol != ',':
178                 raise UnexpectedSymbol(symbol, lexer)
179             symbol = next(lexer)
180     yield ('end_map', None)
181
182 def basic_parse(file):
183     '''
184     Iterator yielding unprefixed events.
185
186     Parameters:
187
188     - file: a readable file-like object with JSON input
189     '''
190     lexer = iter(Lexer(file))
191     for value in parse_value(lexer):
192         yield value
193     try:
194         next(lexer)
195     except StopIteration:
196         pass
197     else:
198         raise common.JSONError('Additional data')
199
200 def parse(file):
201     '''
202     Backend-specific wrapper for ijson.common.parse.
203     '''
204     return common.parse(basic_parse(file))
205
206 def items(file, prefix):
207     '''
208     Backend-specific wrapper for ijson.common.items.
209     '''
210     return common.items(parse(file), prefix)

Benjamin Mako Hill || Want to submit a patch?