720
|
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 luan.LuanTable;
|
|
8
|
|
9
|
|
10 public final class Json {
|
|
11
|
|
12 public static Object parse(String text) throws ParseException {
|
|
13 return new Json(text).parse();
|
|
14 }
|
|
15
|
|
16 private final Parser parser;
|
|
17
|
|
18 private Json(String text) {
|
|
19 this.parser = new Parser(text);
|
|
20 }
|
|
21
|
|
22 private ParseException exception(String msg) {
|
|
23 return new ParseException(parser,msg);
|
|
24 }
|
|
25
|
|
26 private Object parse() throws ParseException {
|
|
27 spaces();
|
|
28 Object value = value();
|
|
29 spaces();
|
|
30 if( !parser.endOfInput() )
|
|
31 throw exception("unexpected text");
|
|
32 return value;
|
|
33 }
|
|
34
|
|
35 private Object value() throws ParseException {
|
|
36 if( parser.match("null") )
|
|
37 return null;
|
|
38 if( parser.match("true") )
|
|
39 return Boolean.TRUE;
|
|
40 if( parser.match("false") )
|
|
41 return Boolean.FALSE;
|
|
42 String s = string();
|
|
43 if( s != null )
|
|
44 return s;
|
|
45 Number n = number();
|
|
46 if( n != null )
|
|
47 return n;
|
|
48 LuanTable a = array();
|
|
49 if( a != null )
|
|
50 return a;
|
|
51 LuanTable o = object();
|
|
52 if( o != null )
|
|
53 return o;
|
|
54 throw exception("invalid value");
|
|
55 }
|
|
56
|
|
57 private String string() throws ParseException {
|
|
58 parser.begin();
|
|
59 if( !parser.match('"') )
|
|
60 return parser.failure(null);
|
|
61 StringBuilder sb = new StringBuilder();
|
|
62 while( parser.anyChar() ) {
|
|
63 char c = parser.lastChar();
|
|
64 switch(c) {
|
|
65 case '"':
|
|
66 return parser.success(sb.toString());
|
|
67 case '\\':
|
|
68 if( parser.anyChar() ) {
|
|
69 c = parser.lastChar();
|
|
70 switch(c) {
|
|
71 case '"':
|
|
72 case '\\':
|
|
73 case '/':
|
|
74 sb.append(c);
|
|
75 continue;
|
|
76 case 'b':
|
|
77 sb.append('\b');
|
|
78 continue;
|
|
79 case 'f':
|
|
80 sb.append('\f');
|
|
81 continue;
|
|
82 case 'n':
|
|
83 sb.append('\n');
|
|
84 continue;
|
|
85 case 'r':
|
|
86 sb.append('\r');
|
|
87 continue;
|
|
88 case 't':
|
|
89 sb.append('\t');
|
|
90 continue;
|
|
91 case 'u':
|
|
92 int n = 0;
|
|
93 for( int i=0; i<4; i++ ) {
|
|
94 int d;
|
|
95 if( parser.inCharRange('0','9') ) {
|
|
96 d = parser.lastChar() - '0';
|
|
97 } else if( parser.inCharRange('a','f') ) {
|
|
98 d = parser.lastChar() - 'a' + 10;
|
|
99 } else if( parser.inCharRange('A','F') ) {
|
|
100 d = parser.lastChar() - 'A' + 10;
|
|
101 } else {
|
|
102 throw exception("invalid hex digit");
|
|
103 }
|
|
104 n = 16*n + d;
|
|
105 }
|
|
106 sb.append((char)n);
|
|
107 continue;
|
|
108 }
|
|
109 }
|
|
110 throw exception("invalid escape char");
|
|
111 default:
|
|
112 sb.append(c);
|
|
113 }
|
|
114 }
|
|
115 parser.failure();
|
|
116 throw exception("unclosed string");
|
|
117 }
|
|
118
|
|
119 private Number number() {
|
|
120 int start = parser.begin();
|
|
121 parser.match('-');
|
|
122 if( !parser.match('0') ) {
|
|
123 if( !parser.inCharRange('1','9') )
|
|
124 return parser.failure(null);
|
|
125 while( parser.inCharRange('0','9') );
|
|
126 }
|
|
127 if( parser.match('.') ) {
|
|
128 if( !parser.inCharRange('0','9') )
|
|
129 return parser.failure(null);
|
|
130 while( parser.inCharRange('0','9') );
|
|
131 }
|
|
132 if( parser.anyOf("eE") ) {
|
|
133 parser.anyOf("+-");
|
|
134 if( !parser.inCharRange('0','9') )
|
|
135 return parser.failure(null);
|
|
136 while( parser.inCharRange('0','9') );
|
|
137 }
|
|
138 String s = parser.textFrom(start);
|
|
139 return parser.success(Double.valueOf(s));
|
|
140 }
|
|
141
|
|
142 private LuanTable array() throws ParseException {
|
|
143 parser.begin();
|
|
144 if( !parser.match('[') )
|
|
145 return parser.failure(null);
|
|
146 spaces();
|
|
147 if( parser.match(']') )
|
|
148 return parser.success(new LuanTable());
|
|
149 List list = new ArrayList();
|
|
150 list.add( value() );
|
|
151 spaces();
|
|
152 while( parser.match(',') ) {
|
|
153 spaces();
|
|
154 list.add( value() );
|
|
155 spaces();
|
|
156 }
|
|
157 if( parser.match(']') )
|
|
158 return parser.success(new LuanTable(list));
|
|
159 if( parser.endOfInput() ) {
|
|
160 parser.failure();
|
|
161 throw exception("unclosed array");
|
|
162 }
|
|
163 throw exception("unexpected text in array");
|
|
164 }
|
|
165
|
|
166 private LuanTable object() throws ParseException {
|
|
167 parser.begin();
|
|
168 if( !parser.match('{') )
|
|
169 return parser.failure(null);
|
|
170 spaces();
|
|
171 if( parser.match('}') )
|
|
172 return parser.success(new LuanTable());
|
|
173 Map map = new LinkedHashMap();
|
|
174 addEntry(map);
|
|
175 while( parser.match(',') ) {
|
|
176 spaces();
|
|
177 addEntry(map);
|
|
178 }
|
|
179 if( parser.match('}') )
|
|
180 return parser.success(new LuanTable(map));
|
|
181 if( parser.endOfInput() ) {
|
|
182 parser.failure();
|
|
183 throw exception("unclosed object");
|
|
184 }
|
|
185 throw exception("unexpected text in object");
|
|
186 }
|
|
187
|
|
188 private void addEntry(Map map) throws ParseException {
|
|
189 String key = string();
|
|
190 if( key==null )
|
|
191 throw exception("invalid object key");
|
|
192 spaces();
|
|
193 if( !parser.match(':') )
|
|
194 throw exception("':' expected");
|
|
195 spaces();
|
|
196 Object value = value();
|
|
197 spaces();
|
|
198 map.put(key,value);
|
|
199 }
|
|
200
|
|
201 private void spaces() {
|
|
202 while( parser.anyOf(" \t\r\n") );
|
|
203 }
|
|
204 }
|