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 }