Mercurial Hosting > luan
annotate src/luan/modules/parsers/LuanParser.java @ 1574:f118ead273a1
LuanParser.longString
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sun, 20 Dec 2020 13:17:27 -0700 |
parents | b89212fd04b5 |
children | c922446f53aa |
rev | line source |
---|---|
1447 | 1 package luan.modules.parsers; |
2 | |
3 import goodjava.parser.Parser; | |
4 import goodjava.parser.ParseException; | |
5 import luan.Luan; | |
6 import luan.LuanTable; | |
7 import luan.LuanException; | |
8 | |
9 | |
10 public final class LuanParser { | |
11 | |
1562 | 12 public static Object parse(String text) throws ParseException { |
13 return new LuanParser(text).parse(); | |
1447 | 14 } |
15 | |
16 private static final Object NULL = new Object(); | |
17 private final Parser parser; | |
18 | |
1562 | 19 private LuanParser(String text) { |
1447 | 20 this.parser = new Parser(text); |
21 } | |
22 | |
23 private ParseException exception(String msg) { | |
24 return new ParseException(parser,msg); | |
25 } | |
26 | |
27 private Object parse() throws ParseException { | |
28 do { spaces(); } while( endOfLine() ); | |
29 Object value = requiredValue(); | |
30 do { spaces(); } while( endOfLine() ); | |
31 if( !parser.endOfInput() ) | |
32 throw exception("unexpected text"); | |
33 return value; | |
34 } | |
35 | |
36 private Object requiredValue() throws ParseException { | |
37 Object value = value(); | |
38 if( value == null ) | |
39 throw exception("invalid value"); | |
40 if( value == NULL ) | |
41 return null; | |
42 return value; | |
43 } | |
44 | |
45 private Object value() throws ParseException { | |
46 if( parser.match("nil") ) | |
47 return NULL; | |
48 if( parser.match("true") ) | |
49 return Boolean.TRUE; | |
50 if( parser.match("false") ) | |
51 return Boolean.FALSE; | |
52 String s = string(); | |
53 if( s != null ) | |
54 return s; | |
55 Number n = number(); | |
56 if( n != null ) | |
57 return n; | |
58 LuanTable tbl = table(); | |
59 if( tbl != null ) | |
60 return tbl; | |
61 return null; | |
62 } | |
63 | |
64 private String string() throws ParseException { | |
1574
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
65 String s; |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
66 s = quotedString(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
67 if( s != null ) |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
68 return s; |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
69 s = longString(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
70 if( s != null ) |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
71 return s; |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
72 return null; |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
73 } |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
74 |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
75 private String quotedString() throws ParseException { |
1447 | 76 parser.begin(); |
77 if( !parser.match('"') ) | |
78 return parser.failure(null); | |
79 StringBuilder sb = new StringBuilder(); | |
80 while( parser.anyChar() ) { | |
81 char c = parser.lastChar(); | |
82 switch(c) { | |
83 case '"': | |
84 return parser.success(sb.toString()); | |
85 case '\\': | |
86 if( parser.anyChar() ) { | |
87 c = parser.lastChar(); | |
88 switch(c) { | |
89 case '"': | |
90 case '\'': | |
91 case '\\': | |
92 sb.append(c); | |
93 continue; | |
94 case 'b': | |
95 sb.append('\b'); | |
96 continue; | |
97 case 'f': | |
98 sb.append('\f'); | |
99 continue; | |
100 case 'n': | |
101 sb.append('\n'); | |
102 continue; | |
103 case 'r': | |
104 sb.append('\r'); | |
105 continue; | |
106 case 't': | |
107 sb.append('\t'); | |
108 continue; | |
109 case 'u': | |
110 int n = 0; | |
111 for( int i=0; i<4; i++ ) { | |
112 int d; | |
113 if( parser.inCharRange('0','9') ) { | |
114 d = parser.lastChar() - '0'; | |
115 } else if( parser.inCharRange('a','f') ) { | |
116 d = parser.lastChar() - 'a' + 10; | |
117 } else if( parser.inCharRange('A','F') ) { | |
118 d = parser.lastChar() - 'A' + 10; | |
119 } else { | |
120 throw exception("invalid hex digit"); | |
121 } | |
122 n = 16*n + d; | |
123 } | |
124 sb.append((char)n); | |
125 continue; | |
126 } | |
127 } | |
128 throw exception("invalid escape char"); | |
129 default: | |
130 sb.append(c); | |
131 } | |
132 } | |
133 parser.failure(); | |
134 throw exception("unclosed string"); | |
135 } | |
136 | |
1574
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
137 private String longString() throws ParseException { |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
138 parser.begin(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
139 if( !parser.match('[') ) |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
140 return parser.failure(null); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
141 int start = parser.currentIndex(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
142 while( parser.match('=') ); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
143 int nEquals = parser.currentIndex() - start; |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
144 if( !parser.match('[') ) |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
145 return parser.failure(null); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
146 endOfLine(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
147 start = parser.currentIndex(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
148 while( !longBracketsEnd(nEquals) ) { |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
149 if( !(endOfLine() || parser.anyChar()) ) |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
150 throw exception("Unclosed long string"); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
151 } |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
152 String s = parser.text.substring( start, parser.currentIndex() - nEquals - 2 ); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
153 return parser.success(s); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
154 } |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
155 |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
156 private boolean longBracketsEnd(int nEquals) { |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
157 parser.begin(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
158 if( !parser.match(']') ) |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
159 return parser.failure(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
160 while( nEquals-- > 0 ) { |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
161 if( !parser.match('=') ) |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
162 return parser.failure(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
163 } |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
164 if( !parser.match(']') ) |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
165 return parser.failure(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
166 return parser.success(); |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
167 } |
f118ead273a1
LuanParser.longString
Franklin Schmidt <fschmidt@gmail.com>
parents:
1562
diff
changeset
|
168 |
1447 | 169 private Number number() { |
170 parser.begin(); | |
171 if( parser.match("double") ) { | |
172 Number n = inParens(); | |
173 if( n==null ) | |
174 return parser.failure(null); | |
175 n = Luan.asDouble(n); | |
176 if( n==null ) | |
177 return parser.failure(null); | |
178 return n; | |
179 } else if( parser.match("float") ) { | |
180 Number n = inParens(); | |
181 if( n==null ) | |
182 return parser.failure(null); | |
183 n = Luan.asFloat(n); | |
184 if( n==null ) | |
185 return parser.failure(null); | |
186 return n; | |
187 } else if( parser.match("integer") ) { | |
188 Number n = inParens(); | |
189 if( n==null ) | |
190 return parser.failure(null); | |
191 n = Luan.asInteger(n); | |
192 if( n==null ) | |
193 return parser.failure(null); | |
194 return n; | |
195 } else if( parser.match("long") ) { | |
196 Number n = inParens(); | |
197 if( n==null ) | |
198 return parser.failure(null); | |
199 n = Luan.asLong(n); | |
200 if( n==null ) | |
201 return parser.failure(null); | |
202 return n; | |
203 } else { | |
204 Number n = untypedNumber(); | |
205 if( n != null ) | |
206 return parser.success(n); | |
207 else | |
208 return parser.failure(null); | |
209 } | |
210 } | |
211 | |
212 private Number inParens() { | |
213 spaces(); | |
214 if( !parser.match('(') ) | |
215 return null; | |
216 spaces(); | |
217 Number n = untypedNumber(); | |
218 if( n==null ) | |
219 return null; | |
220 spaces(); | |
221 if( !parser.match(')') ) | |
222 return null; | |
223 return n; | |
224 } | |
225 | |
226 private Number untypedNumber() { | |
227 int start = parser.begin(); | |
228 boolean isFloat = false; | |
229 parser.match('-'); | |
230 if( !parser.match('0') ) { | |
231 if( !parser.inCharRange('1','9') ) | |
232 return parser.failure(null); | |
233 while( parser.inCharRange('0','9') ); | |
234 } | |
235 if( parser.match('.') ) { | |
236 if( !parser.inCharRange('0','9') ) | |
237 return parser.failure(null); | |
238 while( parser.inCharRange('0','9') ); | |
239 isFloat = true; | |
240 } | |
241 if( parser.anyOf("eE") ) { | |
242 parser.anyOf("+-"); | |
243 if( !parser.inCharRange('0','9') ) | |
244 return parser.failure(null); | |
245 while( parser.inCharRange('0','9') ); | |
246 isFloat = true; | |
247 } | |
248 String s = parser.textFrom(start); | |
249 Number n; | |
250 if(isFloat) | |
251 n = Double.valueOf(s); | |
252 else | |
253 n = Long.valueOf(s); | |
254 return parser.success(n); | |
255 } | |
256 | |
257 private LuanTable table() throws ParseException { | |
258 parser.begin(); | |
259 if( !parser.match('{') ) | |
260 return parser.failure(null); | |
1562 | 261 LuanTable tbl = new LuanTable(); |
1447 | 262 do { |
263 spaces(); | |
264 Object obj = value(); | |
265 if( obj != null ) { | |
266 if( obj != NULL ) | |
267 tbl.rawAdd(obj); | |
268 spaces(); | |
269 continue; | |
270 } | |
271 Object key = key(); | |
272 if( key != null ) { | |
273 spaces(); | |
274 requiredMatch('='); | |
275 spaces(); | |
276 Object value = requiredValue(); | |
277 spaces(); | |
278 try { | |
279 tbl.rawPut(key,value); | |
280 } catch(LuanException e) { | |
281 throw new RuntimeException(e); | |
282 } | |
283 } | |
284 } while( fieldSep() ); | |
285 requiredMatch('}'); | |
286 return parser.success(tbl); | |
287 } | |
288 | |
289 private Object key() throws ParseException { | |
290 if( parser.match('[') ) { | |
291 spaces(); | |
292 Object key = requiredValue(); | |
293 spaces(); | |
294 requiredMatch(']'); | |
295 return key; | |
296 } | |
297 int start = parser.currentIndex(); | |
298 if( nameFirstChar() ) { | |
299 while( nameChar() ); | |
300 return parser.textFrom(start); | |
301 } | |
302 return null; | |
303 } | |
304 | |
305 private boolean nameChar() { | |
306 return nameFirstChar() || parser.inCharRange('0','9'); | |
307 } | |
308 | |
309 private boolean nameFirstChar() { | |
310 return parser.inCharRange('a','z') || parser.inCharRange('A','Z') || parser.match('_'); | |
311 } | |
312 | |
313 private boolean fieldSep() throws ParseException { | |
314 return parser.anyOf(",;") || endOfLine(); | |
315 } | |
316 | |
317 private boolean endOfLine() { | |
318 return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' ); | |
319 } | |
320 | |
321 private void requiredMatch(char c) throws ParseException { | |
322 if( !parser.match(c) ) | |
323 throw exception("'"+c+"' expected"); | |
324 } | |
325 | |
326 private void spaces() { | |
327 while( parser.anyOf(" \t") ); | |
328 } | |
329 | |
330 } |