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