1
10
11 import com.sekati.core.CoreObject;
12
25 class com.sekati.data.JSON extends CoreObject {
26
31 private static var BACKSLASH:String = "u005C";
32 private static var _instance:JSON;
33 private var at:Number;
34 private var ch:String;
35 private var text:String;
36 private var outer:Boolean;
37
40 private function JSON() {
41 super( );
42 at = 0;
43 ch = " ";
44 }
45
49 public static function getInstance():JSON {
50 if (!_instance) {
51 _instance = new JSON( );
52 }
53 return _instance;
54 }
55
59 public static function get $():JSON {
60 return JSON.getInstance( );
61 }
62
67 static function stringify(arg:Object):String {
68
69 var c:Object, i:Number, l:Number, s:String = "", v:Object;
70
71 switch (typeof arg) {
72 case "object":
73 if (arg) {
74 if (arg instanceof Array) {
75 for (i = 0; i < arg.length ; ++i) {
76 v = stringify( arg[i] );
77 if (s) {
78 s += ",";
79 }
80 s += String( v );
81 }
82 return "[" + s + "]";
83 } else if (typeof arg.toString != "undefined") {
84 for (i in arg) {
85 v = arg[i];
86 if (typeof v != "undefined" && typeof v != "function") {
87 v = stringify( v );
88 if (s) {
89 s += ",";
90 }
91 s += i + ":" + v;
92 }
93 }
94 return "{" + s + "}";
95 }
96 }
97 return "null";
98 case "number":
99 return isFinite( arg ) ? String( arg ) : "null";
100 case "string":
101 l = arg.length;
102 s = "\"";
103 for (i = 0; i < l ; i += 1) {
104 c = arg.charAt( i );
105 if (c >= " ") {
106 var cond1:Boolean = (c == BACKSLASH);
107 if (cond1 || c == "\"") {
108 s += BACKSLASH;
109 }
110 s += String( c );
111 } else {
112 switch (c) {
113 case BACKSLASH + "b":
114 s += BACKSLASH + "b";
115
116 break;
117 case BACKSLASH + "f":
118
119 s += BACKSLASH + "f";
120 break;
121 case BACKSLASH + "n":
122
123 s += BACKSLASH + "n";
124 break;
125 case BACKSLASH + "r":
126
127 s += BACKSLASH + "r";
128 break;
129 case BACKSLASH + "t":
130
131 s += BACKSLASH + "t";
132 break;
133 default:
134 c = c.charCodeAt( );
135
136 s += BACKSLASH + "u00" + Math.floor( Number( c ) / 16 ).toString( 16 ) + (Number( c ) % 16).toString( 16 );
137 }
138 }
139 }
140 return s + "\"";
141 case "boolean":
142 return String( arg );
143 default:
144 return "null";
145 }
146 }
147
152 public static function parse(text:String):Object {
153
154 var _instance:JSON = getInstance( );
155 _instance.at = 0;
156 _instance.ch = " ";
157 _instance.text = text;
158
159 return _instance.value( );
160 }
161 private function error(m:Object):Void {
162 }
163 private function next():String {
164 ch = text.charAt( at );
165 at += 1;
166 return ch;
167 }
168 private function white():Void {
169 while (ch) {
170 if (ch <= " ") {
171 this.next( );
172 } else if (ch == "/") {
173 switch (this.next( )) {
174 case "/" :
175 while (this.next( ) && ch != BACKSLASH + "n" && ch != "r") {
176 }
177 break;
178 case "*" :
179 this.next( );
180 while (true) {
181 if (ch) {
182 if (ch == "*") {
183 if (this.next( ) == "/") {
184 next( );
185 break;
186 }
187 } else {
188 this.next( );
189 }
190 } else {
191 error( "Unterminated comment" );
192 }
193 }
194 break;
195 default :
196 this.error( "Syntax error" );
197 }
198 } else {
199 break;
200 }
201 }
202 }
203 private function str():String {
204 var i:Number, s:String = "", t:Number, u:Number;
205 outer = false;
206
207 if (ch == "\"" || ch == "\"") {
208 var outerChar:String = ch;
209 while (this.next( )) {
210 if (ch == outerChar) {
211 this.next( );
212 return s;
213 } else if (ch == BACKSLASH) {
214 switch (this.next( )) {
215 case "b" :
216 s += "b";
217 break;
218 case "f" :
219 s += "f";
220 break;
221 case "n" :
222 s += BACKSLASH + "n";
223 break;
224 case "r" :
225 s += "r";
226 break;
227 case "t" :
228 s += BACKSLASH + "t";
229 break;
230 case "u" :
231 u = 0;
232 for (i = 0; i < 4 ; i += 1) {
233 t = parseInt( this.next( ), 16 );
234 if (!isFinite( t )) {
235 outer = true;
236 break;
237 }
238 u = u * 16 + t;
239 }
240 if (outer) {
241 outer = false;
242 break;
243 }
244 s += String.fromCharCode( u );
245 break;
246 default :
247 s += ch;
248 }
249 } else {
250 s += ch;
251 }
252 }
253 }
254 this.error( "Bad string" );
255 }
256 private function key():String {
257 var s:String = ch;
258 outer = false;
259
260 var semiColon:Number = text.indexOf( ":", at );
261
262
263 var quoteIndex:Number = text.indexOf( "\"", at );
264 var squoteIndex:Number = text.indexOf( "\"", at );
265 if ((quoteIndex <= semiColon && quoteIndex > -1) || (squoteIndex <= semiColon && squoteIndex > -1)) {
266 s = str( );
267 white( );
268 trace( ch );
269 if (ch == ":") {
270 return s;
271 } else {
272 this.error( "Bad key" );
273 }
274 }
275
276 while (this.next( )) {
277 if (ch == ":") {
278 return s;
279 }
280 if (ch <= " ") {
281
282 } else {
283 s += ch;
284 }
285 }
286 this.error( "Bad key" );
287 }
288 private function arr():Array {
289 var a:Array = [];
290
291 if (ch == "[") {
292 this.next( );
293 this.white( );
294 if (ch == "]") {
295 this.next( );
296 return a;
297 }
298 while (ch) {
299 if (ch == "]") {
300 this.next( );
301 return a;
302 }
303 a.push( this.value( ) );
304 this.white( );
305 if (ch == "]") {
306 this.next( );
307 return a;
308 } else if (ch != ",") {
309 break;
310 }
311 this.next( );
312 this.white( );
313 }
314 }
315 this.error( "Bad array" );
316 }
317 private function obj():Object {
318 var k:String, o:Object = {};
319
320 if (ch == "{") {
321 this.next( );
322 this.white( );
323 if (ch == "}") {
324 this.next( );
325 return o;
326 }
327 while (ch) {
328 if (ch == "}") {
329 this.next( );
330 return o;
331 }
332 k = this.key( );
333 if (ch != ":") {
334 break;
335 }
336 this.next( );
337 o[k] = this.value( );
338 this.white( );
339 if (ch == "}") {
340 this.next( );
341 return o;
342 } else if (ch != ",") {
343 break;
344 }
345 this.next( );
346 this.white( );
347 }
348 }
349 this.error( "Bad object" );
350 }
351 private function num():Number {
352 var n:String = "", v:Number;
353
354 if (ch == "-") {
355 n = "-";
356 this.next( );
357 }
358 while ((ch >= "0" && ch <= "9") || ch == "x" || (ch >= "a" && ch <= "f") || (ch >= "A" && ch <= "F")) {
359 n += ch;
360 this.next( );
361 }
362 if (ch == ".") {
363 n += ".";
364 this.next( );
365 while (ch >= "0" && ch <= "9") {
366 n += ch;
367 this.next( );
368 }
369 }
370 if (ch == "e" || ch == "E") {
371 n += ch;
372 this.next( );
373 if (ch == "-" || ch == "+") {
374 n += ch;
375 this.next( );
376 }
377 while (ch >= "0" && ch <= "9") {
378 n += ch;
379 this.next( );
380 }
381 }
382 v = Number( n );
383 if (!isFinite( v )) {
384 this.error( "Bad number" );
385 }
386 return v;
387 }
388 private function word():Boolean {
389 switch (ch) {
390 case "t" :
391 if (this.next( ) == "r" && this.next( ) == "u" && this.next( ) == "e") {
392 this.next( );
393 return true;
394 }
395 break;
396 case "f" :
397 if (this.next( ) == "a" && this.next( ) == "l" && this.next( ) == "s" && this.next( ) == "e") {
398 this.next( );
399 return false;
400 }
401 break;
402 case "n" :
403 if (this.next( ) == "u" && this.next( ) == "l" && this.next( ) == "l") {
404 this.next( );
405 return null;
406 }
407 break;
408 }
409 this.error( "Syntax error" );
410 }
411 private function value():Object {
412 this.white( );
413 switch (ch) {
414 case "{" :
415 return this.obj( );
416 case "[" :
417 return this.arr( );
418 case "\"" :
419 case "\"" :
420 return this.str( );
421 case "-" :
422 return this.num( );
423 default :
424 return ch >= "0" && ch <= "9" ? this.num( ) : this.word( );
425 }
426 }
427 }