Mercurial > wikked
comparison static/js/jsonjs/json_parse_state.js @ 60:8250c977bc50
Moved static files to the root directory.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Tue, 05 Feb 2013 14:49:34 -0800 |
parents | wikked/static/js/jsonjs/json_parse_state.js@c946f4facfa2 |
children |
comparison
equal
deleted
inserted
replaced
59:59ecc742ab8e | 60:8250c977bc50 |
---|---|
1 /* | |
2 json_parse_state.js | |
3 2012-06-01 | |
4 | |
5 Public Domain. | |
6 | |
7 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. | |
8 | |
9 This file creates a json_parse function. | |
10 | |
11 json_parse(text, reviver) | |
12 This method parses a JSON text to produce an object or array. | |
13 It can throw a SyntaxError exception. | |
14 | |
15 The optional reviver parameter is a function that can filter and | |
16 transform the results. It receives each of the keys and values, | |
17 and its return value is used instead of the original value. | |
18 If it returns what it received, then the structure is not modified. | |
19 If it returns undefined then the member is deleted. | |
20 | |
21 Example: | |
22 | |
23 // Parse the text. Values that look like ISO date strings will | |
24 // be converted to Date objects. | |
25 | |
26 myData = json_parse(text, function (key, value) { | |
27 var a; | |
28 if (typeof value === 'string') { | |
29 a = | |
30 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); | |
31 if (a) { | |
32 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], | |
33 +a[5], +a[6])); | |
34 } | |
35 } | |
36 return value; | |
37 }); | |
38 | |
39 This is a reference implementation. You are free to copy, modify, or | |
40 redistribute. | |
41 | |
42 This code should be minified before deployment. | |
43 See http://javascript.crockford.com/jsmin.html | |
44 | |
45 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO | |
46 NOT CONTROL. | |
47 */ | |
48 | |
49 /*jslint regexp: false*/ | |
50 | |
51 /*members "", "\"", ",", "\/", ":", "[", "\\", "]", acomma, avalue, b, | |
52 call, colon, container, exec, f, false, firstavalue, firstokey, | |
53 fromCharCode, go, hasOwnProperty, key, length, n, null, ocomma, okey, | |
54 ovalue, pop, prototype, push, r, replace, slice, state, t, test, true, | |
55 value, "{", "}" | |
56 */ | |
57 | |
58 var json_parse = (function () { | |
59 "use strict"; | |
60 | |
61 // This function creates a JSON parse function that uses a state machine rather | |
62 // than the dangerous eval function to parse a JSON text. | |
63 | |
64 var state, // The state of the parser, one of | |
65 // 'go' The starting state | |
66 // 'ok' The final, accepting state | |
67 // 'firstokey' Ready for the first key of the object or | |
68 // the closing of an empty object | |
69 // 'okey' Ready for the next key of the object | |
70 // 'colon' Ready for the colon | |
71 // 'ovalue' Ready for the value half of a key/value pair | |
72 // 'ocomma' Ready for a comma or closing } | |
73 // 'firstavalue' Ready for the first value of an array or | |
74 // an empty array | |
75 // 'avalue' Ready for the next value of an array | |
76 // 'acomma' Ready for a comma or closing ] | |
77 stack, // The stack, for controlling nesting. | |
78 container, // The current container object or array | |
79 key, // The current key | |
80 value, // The current value | |
81 escapes = { // Escapement translation table | |
82 '\\': '\\', | |
83 '"': '"', | |
84 '/': '/', | |
85 't': '\t', | |
86 'n': '\n', | |
87 'r': '\r', | |
88 'f': '\f', | |
89 'b': '\b' | |
90 }, | |
91 string = { // The actions for string tokens | |
92 go: function () { | |
93 state = 'ok'; | |
94 }, | |
95 firstokey: function () { | |
96 key = value; | |
97 state = 'colon'; | |
98 }, | |
99 okey: function () { | |
100 key = value; | |
101 state = 'colon'; | |
102 }, | |
103 ovalue: function () { | |
104 state = 'ocomma'; | |
105 }, | |
106 firstavalue: function () { | |
107 state = 'acomma'; | |
108 }, | |
109 avalue: function () { | |
110 state = 'acomma'; | |
111 } | |
112 }, | |
113 number = { // The actions for number tokens | |
114 go: function () { | |
115 state = 'ok'; | |
116 }, | |
117 ovalue: function () { | |
118 state = 'ocomma'; | |
119 }, | |
120 firstavalue: function () { | |
121 state = 'acomma'; | |
122 }, | |
123 avalue: function () { | |
124 state = 'acomma'; | |
125 } | |
126 }, | |
127 action = { | |
128 | |
129 // The action table describes the behavior of the machine. It contains an | |
130 // object for each token. Each object contains a method that is called when | |
131 // a token is matched in a state. An object will lack a method for illegal | |
132 // states. | |
133 | |
134 '{': { | |
135 go: function () { | |
136 stack.push({state: 'ok'}); | |
137 container = {}; | |
138 state = 'firstokey'; | |
139 }, | |
140 ovalue: function () { | |
141 stack.push({container: container, state: 'ocomma', key: key}); | |
142 container = {}; | |
143 state = 'firstokey'; | |
144 }, | |
145 firstavalue: function () { | |
146 stack.push({container: container, state: 'acomma'}); | |
147 container = {}; | |
148 state = 'firstokey'; | |
149 }, | |
150 avalue: function () { | |
151 stack.push({container: container, state: 'acomma'}); | |
152 container = {}; | |
153 state = 'firstokey'; | |
154 } | |
155 }, | |
156 '}': { | |
157 firstokey: function () { | |
158 var pop = stack.pop(); | |
159 value = container; | |
160 container = pop.container; | |
161 key = pop.key; | |
162 state = pop.state; | |
163 }, | |
164 ocomma: function () { | |
165 var pop = stack.pop(); | |
166 container[key] = value; | |
167 value = container; | |
168 container = pop.container; | |
169 key = pop.key; | |
170 state = pop.state; | |
171 } | |
172 }, | |
173 '[': { | |
174 go: function () { | |
175 stack.push({state: 'ok'}); | |
176 container = []; | |
177 state = 'firstavalue'; | |
178 }, | |
179 ovalue: function () { | |
180 stack.push({container: container, state: 'ocomma', key: key}); | |
181 container = []; | |
182 state = 'firstavalue'; | |
183 }, | |
184 firstavalue: function () { | |
185 stack.push({container: container, state: 'acomma'}); | |
186 container = []; | |
187 state = 'firstavalue'; | |
188 }, | |
189 avalue: function () { | |
190 stack.push({container: container, state: 'acomma'}); | |
191 container = []; | |
192 state = 'firstavalue'; | |
193 } | |
194 }, | |
195 ']': { | |
196 firstavalue: function () { | |
197 var pop = stack.pop(); | |
198 value = container; | |
199 container = pop.container; | |
200 key = pop.key; | |
201 state = pop.state; | |
202 }, | |
203 acomma: function () { | |
204 var pop = stack.pop(); | |
205 container.push(value); | |
206 value = container; | |
207 container = pop.container; | |
208 key = pop.key; | |
209 state = pop.state; | |
210 } | |
211 }, | |
212 ':': { | |
213 colon: function () { | |
214 if (Object.hasOwnProperty.call(container, key)) { | |
215 throw new SyntaxError('Duplicate key "' + key + '"'); | |
216 } | |
217 state = 'ovalue'; | |
218 } | |
219 }, | |
220 ',': { | |
221 ocomma: function () { | |
222 container[key] = value; | |
223 state = 'okey'; | |
224 }, | |
225 acomma: function () { | |
226 container.push(value); | |
227 state = 'avalue'; | |
228 } | |
229 }, | |
230 'true': { | |
231 go: function () { | |
232 value = true; | |
233 state = 'ok'; | |
234 }, | |
235 ovalue: function () { | |
236 value = true; | |
237 state = 'ocomma'; | |
238 }, | |
239 firstavalue: function () { | |
240 value = true; | |
241 state = 'acomma'; | |
242 }, | |
243 avalue: function () { | |
244 value = true; | |
245 state = 'acomma'; | |
246 } | |
247 }, | |
248 'false': { | |
249 go: function () { | |
250 value = false; | |
251 state = 'ok'; | |
252 }, | |
253 ovalue: function () { | |
254 value = false; | |
255 state = 'ocomma'; | |
256 }, | |
257 firstavalue: function () { | |
258 value = false; | |
259 state = 'acomma'; | |
260 }, | |
261 avalue: function () { | |
262 value = false; | |
263 state = 'acomma'; | |
264 } | |
265 }, | |
266 'null': { | |
267 go: function () { | |
268 value = null; | |
269 state = 'ok'; | |
270 }, | |
271 ovalue: function () { | |
272 value = null; | |
273 state = 'ocomma'; | |
274 }, | |
275 firstavalue: function () { | |
276 value = null; | |
277 state = 'acomma'; | |
278 }, | |
279 avalue: function () { | |
280 value = null; | |
281 state = 'acomma'; | |
282 } | |
283 } | |
284 }; | |
285 | |
286 function debackslashify(text) { | |
287 | |
288 // Remove and replace any backslash escapement. | |
289 | |
290 return text.replace(/\\(?:u(.{4})|([^u]))/g, function (a, b, c) { | |
291 return b ? String.fromCharCode(parseInt(b, 16)) : escapes[c]; | |
292 }); | |
293 } | |
294 | |
295 return function (source, reviver) { | |
296 | |
297 // A regular expression is used to extract tokens from the JSON text. | |
298 // The extraction process is cautious. | |
299 | |
300 var r, // The result of the exec method. | |
301 tx = /^[\x20\t\n\r]*(?:([,:\[\]{}]|true|false|null)|(-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)|"((?:[^\r\n\t\\\"]|\\(?:["\\\/trnfb]|u[0-9a-fA-F]{4}))*)")/; | |
302 | |
303 // Set the starting state. | |
304 | |
305 state = 'go'; | |
306 | |
307 // The stack records the container, key, and state for each object or array | |
308 // that contains another object or array while processing nested structures. | |
309 | |
310 stack = []; | |
311 | |
312 // If any error occurs, we will catch it and ultimately throw a syntax error. | |
313 | |
314 try { | |
315 | |
316 // For each token... | |
317 | |
318 for (;;) { | |
319 r = tx.exec(source); | |
320 if (!r) { | |
321 break; | |
322 } | |
323 | |
324 // r is the result array from matching the tokenizing regular expression. | |
325 // r[0] contains everything that matched, including any initial whitespace. | |
326 // r[1] contains any punctuation that was matched, or true, false, or null. | |
327 // r[2] contains a matched number, still in string form. | |
328 // r[3] contains a matched string, without quotes but with escapement. | |
329 | |
330 if (r[1]) { | |
331 | |
332 // Token: Execute the action for this state and token. | |
333 | |
334 action[r[1]][state](); | |
335 | |
336 } else if (r[2]) { | |
337 | |
338 // Number token: Convert the number string into a number value and execute | |
339 // the action for this state and number. | |
340 | |
341 value = +r[2]; | |
342 number[state](); | |
343 } else { | |
344 | |
345 // String token: Replace the escapement sequences and execute the action for | |
346 // this state and string. | |
347 | |
348 value = debackslashify(r[3]); | |
349 string[state](); | |
350 } | |
351 | |
352 // Remove the token from the string. The loop will continue as long as there | |
353 // are tokens. This is a slow process, but it allows the use of ^ matching, | |
354 // which assures that no illegal tokens slip through. | |
355 | |
356 source = source.slice(r[0].length); | |
357 } | |
358 | |
359 // If we find a state/token combination that is illegal, then the action will | |
360 // cause an error. We handle the error by simply changing the state. | |
361 | |
362 } catch (e) { | |
363 state = e; | |
364 } | |
365 | |
366 // The parsing is finished. If we are not in the final 'ok' state, or if the | |
367 // remaining source contains anything except whitespace, then we did not have | |
368 //a well-formed JSON text. | |
369 | |
370 if (state !== 'ok' || /[^\x20\t\n\r]/.test(source)) { | |
371 throw state instanceof SyntaxError ? state : new SyntaxError('JSON'); | |
372 } | |
373 | |
374 // If there is a reviver function, we recursively walk the new structure, | |
375 // passing each name/value pair to the reviver function for possible | |
376 // transformation, starting with a temporary root object that holds the current | |
377 // value in an empty key. If there is not a reviver function, we simply return | |
378 // that value. | |
379 | |
380 return typeof reviver === 'function' ? (function walk(holder, key) { | |
381 var k, v, value = holder[key]; | |
382 if (value && typeof value === 'object') { | |
383 for (k in value) { | |
384 if (Object.prototype.hasOwnProperty.call(value, k)) { | |
385 v = walk(value, k); | |
386 if (v !== undefined) { | |
387 value[k] = v; | |
388 } else { | |
389 delete value[k]; | |
390 } | |
391 } | |
392 } | |
393 } | |
394 return reviver.call(holder, key, value); | |
395 }({'': value}, '')) : value; | |
396 }; | |
397 }()); |