Mercurial Hosting > luan
comparison src/luan/modules/parsers/Json.java @ 775:1a68fc55a80c
simplify dir structure
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 26 Aug 2016 14:36:40 -0600 |
parents | core/src/luan/modules/parsers/Json.java@ae612dfc57cb |
children | 88b5b81cad4a |
comparison
equal
deleted
inserted
replaced
774:3e30cf310e56 | 775:1a68fc55a80c |
---|---|
1 package luan.modules.parsers; | |
2 | |
3 import java.util.List; | |
4 import java.util.ArrayList; | |
5 import java.util.Map; | |
6 import java.util.LinkedHashMap; | |
7 import java.util.Iterator; | |
8 import luan.LuanTable; | |
9 import luan.LuanException; | |
10 | |
11 | |
12 public final class Json { | |
13 | |
14 public static Object parse(String text) throws ParseException { | |
15 return new Json(text).parse(); | |
16 } | |
17 | |
18 private final Parser parser; | |
19 | |
20 private Json(String text) { | |
21 this.parser = new Parser(text); | |
22 } | |
23 | |
24 private ParseException exception(String msg) { | |
25 return new ParseException(parser,msg); | |
26 } | |
27 | |
28 private Object parse() throws ParseException { | |
29 spaces(); | |
30 Object value = value(); | |
31 spaces(); | |
32 if( !parser.endOfInput() ) | |
33 throw exception("unexpected text"); | |
34 return value; | |
35 } | |
36 | |
37 private Object value() throws ParseException { | |
38 if( parser.match("null") ) | |
39 return null; | |
40 if( parser.match("true") ) | |
41 return Boolean.TRUE; | |
42 if( parser.match("false") ) | |
43 return Boolean.FALSE; | |
44 String s = string(); | |
45 if( s != null ) | |
46 return s; | |
47 Number n = number(); | |
48 if( n != null ) | |
49 return n; | |
50 LuanTable a = array(); | |
51 if( a != null ) | |
52 return a; | |
53 LuanTable o = object(); | |
54 if( o != null ) | |
55 return o; | |
56 throw exception("invalid value"); | |
57 } | |
58 | |
59 private String string() throws ParseException { | |
60 parser.begin(); | |
61 if( !parser.match('"') ) | |
62 return parser.failure(null); | |
63 StringBuilder sb = new StringBuilder(); | |
64 while( parser.anyChar() ) { | |
65 char c = parser.lastChar(); | |
66 switch(c) { | |
67 case '"': | |
68 return parser.success(sb.toString()); | |
69 case '\\': | |
70 if( parser.anyChar() ) { | |
71 c = parser.lastChar(); | |
72 switch(c) { | |
73 case '"': | |
74 case '\\': | |
75 case '/': | |
76 sb.append(c); | |
77 continue; | |
78 case 'b': | |
79 sb.append('\b'); | |
80 continue; | |
81 case 'f': | |
82 sb.append('\f'); | |
83 continue; | |
84 case 'n': | |
85 sb.append('\n'); | |
86 continue; | |
87 case 'r': | |
88 sb.append('\r'); | |
89 continue; | |
90 case 't': | |
91 sb.append('\t'); | |
92 continue; | |
93 case 'u': | |
94 int n = 0; | |
95 for( int i=0; i<4; i++ ) { | |
96 int d; | |
97 if( parser.inCharRange('0','9') ) { | |
98 d = parser.lastChar() - '0'; | |
99 } else if( parser.inCharRange('a','f') ) { | |
100 d = parser.lastChar() - 'a' + 10; | |
101 } else if( parser.inCharRange('A','F') ) { | |
102 d = parser.lastChar() - 'A' + 10; | |
103 } else { | |
104 throw exception("invalid hex digit"); | |
105 } | |
106 n = 16*n + d; | |
107 } | |
108 sb.append((char)n); | |
109 continue; | |
110 } | |
111 } | |
112 throw exception("invalid escape char"); | |
113 default: | |
114 sb.append(c); | |
115 } | |
116 } | |
117 parser.failure(); | |
118 throw exception("unclosed string"); | |
119 } | |
120 | |
121 private Number number() { | |
122 int start = parser.begin(); | |
123 boolean isFloat = false; | |
124 parser.match('-'); | |
125 if( !parser.match('0') ) { | |
126 if( !parser.inCharRange('1','9') ) | |
127 return parser.failure(null); | |
128 while( parser.inCharRange('0','9') ); | |
129 } | |
130 if( parser.match('.') ) { | |
131 if( !parser.inCharRange('0','9') ) | |
132 return parser.failure(null); | |
133 while( parser.inCharRange('0','9') ); | |
134 isFloat = true; | |
135 } | |
136 if( parser.anyOf("eE") ) { | |
137 parser.anyOf("+-"); | |
138 if( !parser.inCharRange('0','9') ) | |
139 return parser.failure(null); | |
140 while( parser.inCharRange('0','9') ); | |
141 isFloat = true; | |
142 } | |
143 String s = parser.textFrom(start); | |
144 Number n; | |
145 if(isFloat) | |
146 n = Double.valueOf(s); | |
147 else | |
148 n = Long.valueOf(s); | |
149 return parser.success(n); | |
150 } | |
151 | |
152 private LuanTable array() throws ParseException { | |
153 parser.begin(); | |
154 if( !parser.match('[') ) | |
155 return parser.failure(null); | |
156 spaces(); | |
157 if( parser.match(']') ) | |
158 return parser.success(new LuanTable()); | |
159 List list = new ArrayList(); | |
160 list.add( value() ); | |
161 spaces(); | |
162 while( parser.match(',') ) { | |
163 spaces(); | |
164 list.add( value() ); | |
165 spaces(); | |
166 } | |
167 if( parser.match(']') ) | |
168 return parser.success(new LuanTable(list)); | |
169 if( parser.endOfInput() ) { | |
170 parser.failure(); | |
171 throw exception("unclosed array"); | |
172 } | |
173 throw exception("unexpected text in array"); | |
174 } | |
175 | |
176 private LuanTable object() throws ParseException { | |
177 parser.begin(); | |
178 if( !parser.match('{') ) | |
179 return parser.failure(null); | |
180 spaces(); | |
181 if( parser.match('}') ) | |
182 return parser.success(new LuanTable()); | |
183 Map map = new LinkedHashMap(); | |
184 addEntry(map); | |
185 while( parser.match(',') ) { | |
186 spaces(); | |
187 addEntry(map); | |
188 } | |
189 if( parser.match('}') ) | |
190 return parser.success(new LuanTable(map)); | |
191 if( parser.endOfInput() ) { | |
192 parser.failure(); | |
193 throw exception("unclosed object"); | |
194 } | |
195 throw exception("unexpected text in object"); | |
196 } | |
197 | |
198 private void addEntry(Map map) throws ParseException { | |
199 String key = string(); | |
200 if( key==null ) | |
201 throw exception("invalid object key"); | |
202 spaces(); | |
203 if( !parser.match(':') ) | |
204 throw exception("':' expected"); | |
205 spaces(); | |
206 Object value = value(); | |
207 spaces(); | |
208 map.put(key,value); | |
209 } | |
210 | |
211 private void spaces() { | |
212 while( parser.anyOf(" \t\r\n") ); | |
213 } | |
214 | |
215 | |
216 | |
217 | |
218 | |
219 | |
220 | |
221 | |
222 | |
223 public static String toString(Object obj) throws LuanException { | |
224 StringBuilder sb = new StringBuilder(); | |
225 toString(obj,sb); | |
226 return sb.toString(); | |
227 } | |
228 | |
229 private static void toString(Object obj,StringBuilder sb) throws LuanException { | |
230 if( obj == null || obj instanceof Boolean || obj instanceof Number ) { | |
231 sb.append(obj); | |
232 return; | |
233 } | |
234 if( obj instanceof String ) { | |
235 toString((String)obj,sb); | |
236 return; | |
237 } | |
238 if( obj instanceof LuanTable ) { | |
239 toString((LuanTable)obj,sb); | |
240 return; | |
241 } | |
242 throw new LuanException("can't handle type "+obj.getClass().getName()); | |
243 } | |
244 | |
245 private static void toString(final String s,StringBuilder sb) { | |
246 sb.append('"'); | |
247 for( int i=0; i<s.length(); i++ ) { | |
248 char c = s.charAt(i); | |
249 switch(c) { | |
250 case '"': | |
251 sb.append("\\\""); | |
252 break; | |
253 case '\\': | |
254 sb.append("\\\\"); | |
255 break; | |
256 case '\b': | |
257 sb.append("\\b"); | |
258 break; | |
259 case '\f': | |
260 sb.append("\\f"); | |
261 break; | |
262 case '\n': | |
263 sb.append("\\n"); | |
264 break; | |
265 case '\r': | |
266 sb.append("\\r"); | |
267 break; | |
268 case '\t': | |
269 sb.append("\\t"); | |
270 break; | |
271 default: | |
272 sb.append(c); | |
273 } | |
274 } | |
275 sb.append('"'); | |
276 } | |
277 | |
278 private static void toString(LuanTable t,StringBuilder sb) throws LuanException { | |
279 if( t.isList() ) { | |
280 final List list = t.asList(); | |
281 if( list.isEmpty() ) { | |
282 sb.append("{}"); | |
283 return; | |
284 } | |
285 sb.append('['); | |
286 toString(list.get(0),sb); | |
287 for( int i=1; i<list.size(); i++ ) { | |
288 sb.append(','); | |
289 toString(list.get(i),sb); | |
290 } | |
291 sb.append(']'); | |
292 return; | |
293 } | |
294 sb.append('{'); | |
295 Iterator<Map.Entry<Object,Object>> i = t.rawIterator(); | |
296 toString(i.next(),sb); | |
297 while( i.hasNext() ) { | |
298 sb.append(','); | |
299 toString(i.next(),sb); | |
300 } | |
301 sb.append('}'); | |
302 } | |
303 | |
304 private static void toString(Map.Entry<Object,Object> entry,StringBuilder sb) throws LuanException { | |
305 Object key = entry.getKey(); | |
306 if( !(key instanceof String) ) | |
307 throw new LuanException("table keys must be strings"); | |
308 toString((String)key,sb); | |
309 sb.append(':'); | |
310 toString(entry.getValue(),sb); | |
311 } | |
312 | |
313 } |