comparison src/goodjava/json/JsonParser.java @ 1402:27efb1fcbcb5

move luan.lib to goodjava
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 17 Sep 2019 01:35:01 -0400
parents src/luan/lib/json/JsonParser.java@6c8ceca4a10b
children
comparison
equal deleted inserted replaced
1401:ef1620aa99cb 1402:27efb1fcbcb5
1 package goodjava.json;
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.Collections;
8 import goodjava.parser.Parser;
9 import goodjava.parser.ParseException;
10
11
12 public final class JsonParser {
13
14 public static Object parse(String text) throws ParseException {
15 return new JsonParser(text).parse();
16 }
17
18 private final Parser parser;
19
20 private JsonParser(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 List a = array();
51 if( a != null )
52 return a;
53 Map 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 '\'': // not in spec
75 case '\\':
76 case '/':
77 sb.append(c);
78 continue;
79 case 'b':
80 sb.append('\b');
81 continue;
82 case 'f':
83 sb.append('\f');
84 continue;
85 case 'n':
86 sb.append('\n');
87 continue;
88 case 'r':
89 sb.append('\r');
90 continue;
91 case 't':
92 sb.append('\t');
93 continue;
94 case 'u':
95 int n = 0;
96 for( int i=0; i<4; i++ ) {
97 int d;
98 if( parser.inCharRange('0','9') ) {
99 d = parser.lastChar() - '0';
100 } else if( parser.inCharRange('a','f') ) {
101 d = parser.lastChar() - 'a' + 10;
102 } else if( parser.inCharRange('A','F') ) {
103 d = parser.lastChar() - 'A' + 10;
104 } else {
105 throw exception("invalid hex digit");
106 }
107 n = 16*n + d;
108 }
109 sb.append((char)n);
110 continue;
111 }
112 }
113 throw exception("invalid escape char");
114 default:
115 sb.append(c);
116 }
117 }
118 parser.failure();
119 throw exception("unclosed string");
120 }
121
122 private Number number() {
123 int start = parser.begin();
124 boolean isFloat = false;
125 parser.match('-');
126 if( !parser.match('0') ) {
127 if( !parser.inCharRange('1','9') )
128 return parser.failure(null);
129 while( parser.inCharRange('0','9') );
130 }
131 if( parser.match('.') ) {
132 if( !parser.inCharRange('0','9') )
133 return parser.failure(null);
134 while( parser.inCharRange('0','9') );
135 isFloat = true;
136 }
137 if( parser.anyOf("eE") ) {
138 parser.anyOf("+-");
139 if( !parser.inCharRange('0','9') )
140 return parser.failure(null);
141 while( parser.inCharRange('0','9') );
142 isFloat = true;
143 }
144 String s = parser.textFrom(start);
145 Number n;
146 if(isFloat)
147 n = Double.valueOf(s);
148 else
149 n = Long.valueOf(s);
150 return parser.success(n);
151 }
152
153 private List array() throws ParseException {
154 parser.begin();
155 if( !parser.match('[') )
156 return parser.failure(null);
157 spaces();
158 if( parser.match(']') )
159 return parser.success(Collections.emptyList());
160 List list = new ArrayList();
161 list.add( value() );
162 spaces();
163 while( parser.match(',') ) {
164 spaces();
165 list.add( value() );
166 spaces();
167 }
168 if( parser.match(']') )
169 return parser.success(list);
170 if( parser.endOfInput() ) {
171 parser.failure();
172 throw exception("unclosed array");
173 }
174 throw exception("unexpected text in array");
175 }
176
177 private Map object() throws ParseException {
178 parser.begin();
179 if( !parser.match('{') )
180 return parser.failure(null);
181 spaces();
182 if( parser.match('}') )
183 return parser.success(Collections.emptyMap());
184 Map map = new LinkedHashMap();
185 addEntry(map);
186 while( parser.match(',') ) {
187 spaces();
188 addEntry(map);
189 }
190 if( parser.match('}') )
191 return parser.success(map);
192 if( parser.endOfInput() ) {
193 parser.failure();
194 throw exception("unclosed object");
195 }
196 throw exception("unexpected text in object");
197 }
198
199 private void addEntry(Map map) throws ParseException {
200 String key = string();
201 if( key==null )
202 throw exception("invalid object key");
203 spaces();
204 if( !parser.match(':') )
205 throw exception("':' expected");
206 spaces();
207 Object value = value();
208 spaces();
209 map.put(key,value);
210 }
211
212 private void spaces() {
213 while( parser.anyOf(" \t\r\n") );
214 }
215
216 }