|
1 json_parse = (function () { |
|
2 |
|
3 // This is a function that can parse a JSON text, producing a JavaScript |
|
4 // data structure. It is a simple, recursive descent parser. It does not use |
|
5 // eval or regular expressions, so it can be used as a model for implementing |
|
6 // a JSON parser in other languages. |
|
7 |
|
8 // We are defining the function inside of another function to avoid creating |
|
9 // global variables. |
|
10 |
|
11 var at, // The index of the current character |
|
12 ch, // The current character |
|
13 escapee = { |
|
14 '"': '"', |
|
15 '\\': '\\', |
|
16 '/': '/', |
|
17 b: '\b', |
|
18 f: '\f', |
|
19 n: '\n', |
|
20 r: '\r', |
|
21 t: '\t' |
|
22 }, |
|
23 text, |
|
24 |
|
25 error = function (m) { |
|
26 |
|
27 // Call error when something is wrong. |
|
28 |
|
29 throw { |
|
30 name: 'SyntaxError', |
|
31 message: m, |
|
32 at: at, |
|
33 text: text |
|
34 }; |
|
35 }, |
|
36 |
|
37 next = function (c) { |
|
38 |
|
39 // If a c parameter is provided, verify that it matches the current character. |
|
40 |
|
41 if (c && c !== ch) { |
|
42 error("Expected '" + c + "' instead of '" + ch + "'"); |
|
43 } |
|
44 |
|
45 // Get the next character. When there are no more characters, |
|
46 // return the empty string. |
|
47 |
|
48 ch = text.charAt(at); |
|
49 at += 1; |
|
50 return ch; |
|
51 }, |
|
52 |
|
53 number = function () { |
|
54 |
|
55 // Parse a number value. |
|
56 |
|
57 var number, |
|
58 string = ''; |
|
59 |
|
60 if (ch === '-') { |
|
61 string = '-'; |
|
62 next('-'); |
|
63 } |
|
64 while (ch >= '0' && ch <= '9') { |
|
65 string += ch; |
|
66 next(); |
|
67 } |
|
68 if (ch === '.') { |
|
69 string += '.'; |
|
70 while (next() && ch >= '0' && ch <= '9') { |
|
71 string += ch; |
|
72 } |
|
73 } |
|
74 if (ch === 'e' || ch === 'E') { |
|
75 string += ch; |
|
76 next(); |
|
77 if (ch === '-' || ch === '+') { |
|
78 string += ch; |
|
79 next(); |
|
80 } |
|
81 while (ch >= '0' && ch <= '9') { |
|
82 string += ch; |
|
83 next(); |
|
84 } |
|
85 } |
|
86 number = +string; |
|
87 if (isNaN(number)) { |
|
88 error("Bad number"); |
|
89 } else { |
|
90 return number; |
|
91 } |
|
92 }, |
|
93 |
|
94 string = function () { |
|
95 |
|
96 // Parse a string value. |
|
97 |
|
98 var hex, |
|
99 i, |
|
100 string = '', |
|
101 uffff; |
|
102 |
|
103 // When parsing for string values, we must look for " and \ characters. |
|
104 |
|
105 if (ch === '"') { |
|
106 while (next()) { |
|
107 if (ch === '"') { |
|
108 next(); |
|
109 return string; |
|
110 } else if (ch === '\\') { |
|
111 next(); |
|
112 if (ch === 'u') { |
|
113 uffff = 0; |
|
114 for (i = 0; i < 4; i += 1) { |
|
115 hex = parseInt(next(), 16); |
|
116 if (!isFinite(hex)) { |
|
117 break; |
|
118 } |
|
119 uffff = uffff * 16 + hex; |
|
120 } |
|
121 string += String.fromCharCode(uffff); |
|
122 } else if (typeof escapee[ch] === 'string') { |
|
123 string += escapee[ch]; |
|
124 } else { |
|
125 break; |
|
126 } |
|
127 } else { |
|
128 string += ch; |
|
129 } |
|
130 } |
|
131 } |
|
132 error("Bad string"); |
|
133 }, |
|
134 |
|
135 white = function () { |
|
136 |
|
137 // Skip whitespace. |
|
138 |
|
139 while (ch && ch <= ' ') { |
|
140 next(); |
|
141 } |
|
142 }, |
|
143 |
|
144 word = function () { |
|
145 |
|
146 // true, false, or null. |
|
147 |
|
148 switch (ch) { |
|
149 case 't': |
|
150 next('t'); |
|
151 next('r'); |
|
152 next('u'); |
|
153 next('e'); |
|
154 return true; |
|
155 case 'f': |
|
156 next('f'); |
|
157 next('a'); |
|
158 next('l'); |
|
159 next('s'); |
|
160 next('e'); |
|
161 return false; |
|
162 case 'n': |
|
163 next('n'); |
|
164 next('u'); |
|
165 next('l'); |
|
166 next('l'); |
|
167 return null; |
|
168 } |
|
169 error("Unexpected '" + ch + "'"); |
|
170 }, |
|
171 |
|
172 value, // Place holder for the value function. |
|
173 |
|
174 array = function () { |
|
175 |
|
176 // Parse an array value. |
|
177 |
|
178 var array = []; |
|
179 |
|
180 if (ch === '[') { |
|
181 next('['); |
|
182 white(); |
|
183 if (ch === ']') { |
|
184 next(']'); |
|
185 return array; // empty array |
|
186 } |
|
187 while (ch) { |
|
188 array.push(value()); |
|
189 white(); |
|
190 if (ch === ']') { |
|
191 next(']'); |
|
192 return array; |
|
193 } |
|
194 next(','); |
|
195 white(); |
|
196 } |
|
197 } |
|
198 error("Bad array"); |
|
199 }, |
|
200 |
|
201 object = function () { |
|
202 |
|
203 // Parse an object value. |
|
204 |
|
205 var key, |
|
206 object = {}; |
|
207 |
|
208 if (ch === '{') { |
|
209 next('{'); |
|
210 white(); |
|
211 if (ch === '}') { |
|
212 next('}'); |
|
213 return object; // empty object |
|
214 } |
|
215 while (ch) { |
|
216 key = string(); |
|
217 white(); |
|
218 next(':'); |
|
219 if (Object.hasOwnProperty.call(object, key)) { |
|
220 error('Duplicate key "' + key + '"'); |
|
221 } |
|
222 object[key] = value(); |
|
223 white(); |
|
224 if (ch === '}') { |
|
225 next('}'); |
|
226 return object; |
|
227 } |
|
228 next(','); |
|
229 white(); |
|
230 } |
|
231 } |
|
232 error("Bad object"); |
|
233 }; |
|
234 |
|
235 value = function () { |
|
236 |
|
237 // Parse a JSON value. It could be an object, an array, a string, a number, |
|
238 // or a word. |
|
239 |
|
240 white(); |
|
241 switch (ch) { |
|
242 case '{': |
|
243 return object(); |
|
244 case '[': |
|
245 return array(); |
|
246 case '"': |
|
247 return string(); |
|
248 case '-': |
|
249 return number(); |
|
250 default: |
|
251 return ch >= '0' && ch <= '9' ? number() : word(); |
|
252 } |
|
253 }; |
|
254 |
|
255 // Return the json_parse function. It will have access to all of the above |
|
256 // functions and variables. |
|
257 |
|
258 return function (source, reviver) { |
|
259 var result; |
|
260 |
|
261 text = source; |
|
262 at = 0; |
|
263 ch = ' '; |
|
264 result = value(); |
|
265 white(); |
|
266 if (ch) { |
|
267 error("Syntax error"); |
|
268 } |
|
269 |
|
270 // If there is a reviver function, we recursively walk the new structure, |
|
271 // passing each name/value pair to the reviver function for possible |
|
272 // transformation, starting with a temporary root object that holds the result |
|
273 // in an empty key. If there is not a reviver function, we simply return the |
|
274 // result. |
|
275 |
|
276 return typeof reviver === 'function' ? (function walk(holder, key) { |
|
277 var k, v, value = holder[key]; |
|
278 if (value && typeof value === 'object') { |
|
279 for (k in value) { |
|
280 if (Object.hasOwnProperty.call(value, k)) { |
|
281 v = walk(value, k); |
|
282 if (v !== undefined) { |
|
283 value[k] = v; |
|
284 } else { |
|
285 delete value[k]; |
|
286 } |
|
287 } |
|
288 } |
|
289 } |
|
290 return reviver.call(holder, key, value); |
|
291 }({'': result}, '')) : result; |
|
292 }; |
|
293 }()); |