Mercurial Hosting > luan
comparison core/src/luan/impl/LuanParser.java @ 171:3dcb0f9bee82
add core component
git-svn-id: https://luan-java.googlecode.com/svn/trunk@172 21e917c8-12df-6dd8-5cb6-c86387c605b9
author | fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9> |
---|---|
date | Sun, 22 Jun 2014 05:41:22 +0000 |
parents | src/luan/impl/LuanParser.java@4eaee12f6c65 |
children | bdbd4740121f |
comparison
equal
deleted
inserted
replaced
170:7c792a328a83 | 171:3dcb0f9bee82 |
---|---|
1 package luan.impl; | |
2 | |
3 import java.util.Set; | |
4 import java.util.HashSet; | |
5 import java.util.Arrays; | |
6 import java.util.List; | |
7 import java.util.ArrayList; | |
8 import java.util.Scanner; | |
9 import luan.Luan; | |
10 import luan.LuanState; | |
11 import luan.LuanSource; | |
12 | |
13 | |
14 final class LuanParser { | |
15 | |
16 private static final class Frame { | |
17 final Frame parent; | |
18 final List<String> symbols = new ArrayList<String>(); | |
19 int stackSize = 0; | |
20 int loops = 0; | |
21 boolean isVarArg = false; | |
22 final List<String> upValueSymbols = new ArrayList<String>(); | |
23 final List<UpValue.Getter> upValueGetters = new ArrayList<UpValue.Getter>(); | |
24 | |
25 Frame(UpValue.Getter envGetter) { | |
26 this.parent = null; | |
27 upValueSymbols.add(_ENV); | |
28 upValueGetters.add(envGetter); | |
29 } | |
30 | |
31 Frame(Frame parent) { | |
32 this.parent = parent; | |
33 if( upValueIndex(_ENV) != 0 ) | |
34 throw new RuntimeException(); | |
35 } | |
36 | |
37 int stackIndex(String name) { | |
38 int i = symbols.size(); | |
39 while( --i >= 0 ) { | |
40 if( symbols.get(i).equals(name) ) | |
41 return i; | |
42 } | |
43 return -1; | |
44 } | |
45 | |
46 int upValueIndex(String name) { | |
47 int i = upValueSymbols.size(); | |
48 while( --i >= 0 ) { | |
49 if( upValueSymbols.get(i).equals(name) ) | |
50 return i; | |
51 } | |
52 if( parent==null ) | |
53 return -1; | |
54 i = parent.stackIndex(name); | |
55 if( i != -1 ) { | |
56 upValueGetters.add(new UpValue.StackGetter(i)); | |
57 } else { | |
58 i = parent.upValueIndex(name); | |
59 if( i == -1 ) | |
60 return -1; | |
61 upValueGetters.add(new UpValue.NestedGetter(i)); | |
62 } | |
63 upValueSymbols.add(name); | |
64 return upValueSymbols.size() - 1; | |
65 } | |
66 | |
67 void addUpValueGetter(String name,UpValue.Getter upValueGetter) { | |
68 upValueSymbols.add(name); | |
69 upValueGetters.add(upValueGetter); | |
70 } | |
71 } | |
72 | |
73 private static class In { | |
74 static final In NOTHING = new In(false,false); | |
75 | |
76 final boolean parens; | |
77 final boolean template; | |
78 | |
79 private In(boolean parens,boolean template) { | |
80 this.parens = parens; | |
81 this.template = template; | |
82 } | |
83 | |
84 In parens() { | |
85 return parens ? this : new In(true,template); | |
86 } | |
87 | |
88 In template() { | |
89 return template ? this : new In(parens,true); | |
90 } | |
91 } | |
92 | |
93 private static final String _ENV = "_ENV"; | |
94 private static final UpValue.Getter[] NO_UP_VALUE_GETTERS = new UpValue.Getter[0]; | |
95 | |
96 final LuanSource source; | |
97 private Frame frame; | |
98 private final Parser parser; | |
99 private final boolean interactive; | |
100 | |
101 LuanParser(LuanSource source,UpValue.Getter envGetter) { | |
102 this.source = source; | |
103 this.frame = new Frame(envGetter); | |
104 this.parser = new Parser(source); | |
105 this.interactive = envGetter instanceof UpValue.ValueGetter; | |
106 } | |
107 | |
108 void addVar(String name,Object value) { | |
109 frame.addUpValueGetter(name,new UpValue.ValueGetter(value)); | |
110 } | |
111 | |
112 private LuanSource.Element se(int start) { | |
113 return new LuanSource.Element(source,start,parser.currentIndex()); | |
114 } | |
115 | |
116 private List<String> symbols() { | |
117 return frame.symbols; | |
118 } | |
119 | |
120 private int symbolsSize() { | |
121 return frame.symbols.size(); | |
122 } | |
123 | |
124 private void addSymbol(String name) { | |
125 frame.symbols.add(name); | |
126 if( frame.stackSize < symbolsSize() ) | |
127 frame.stackSize = symbolsSize(); | |
128 } | |
129 | |
130 private void addSymbols(List<String> names) { | |
131 frame.symbols.addAll(names); | |
132 if( frame.stackSize < symbolsSize() ) | |
133 frame.stackSize = symbolsSize(); | |
134 } | |
135 | |
136 private int stackIndex(String name) { | |
137 return frame.stackIndex(name); | |
138 } | |
139 | |
140 private void popSymbols(int n) { | |
141 List<String> symbols = frame.symbols; | |
142 while( n-- > 0 ) { | |
143 symbols.remove(symbols.size()-1); | |
144 } | |
145 } | |
146 | |
147 private int upValueIndex(String name) { | |
148 return frame.upValueIndex(name); | |
149 } | |
150 | |
151 private void incLoops() { | |
152 frame.loops++; | |
153 } | |
154 | |
155 private void decLoops() { | |
156 frame.loops--; | |
157 } | |
158 | |
159 private <T> T required(T t) throws ParseException { | |
160 if( t==null ) | |
161 throw parser.exception(); | |
162 return t; | |
163 } | |
164 | |
165 private <T> T required(T t,String msg) throws ParseException { | |
166 if( t==null ) | |
167 throw parser.exception(msg); | |
168 return t; | |
169 } | |
170 | |
171 private static Expr expr(Expressions exprs) { | |
172 if( exprs instanceof Expr ) | |
173 return (Expr)exprs; | |
174 return new ExpressionsExpr(exprs); | |
175 } | |
176 | |
177 private FnDef newFnDef(int start,Stmt stmt) { | |
178 return new FnDef( se(start), stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) ); | |
179 } | |
180 | |
181 FnDef Expression() throws ParseException { | |
182 Spaces(In.NOTHING); | |
183 int start = parser.begin(); | |
184 Expressions expr = Expr(In.NOTHING); | |
185 if( expr != null && parser.endOfInput() ) { | |
186 Stmt stmt = new ReturnStmt( se(start), expr ); | |
187 return parser.success(newFnDef(start,stmt)); | |
188 } | |
189 return parser.failure(null); | |
190 } | |
191 | |
192 FnDef RequiredModule() throws ParseException { | |
193 Spaces(In.NOTHING); | |
194 int start = parser.begin(); | |
195 frame.isVarArg = true; | |
196 Stmt stmt = RequiredBlock(); | |
197 if( parser.endOfInput() ) | |
198 return parser.success(newFnDef(start,stmt)); | |
199 throw parser.exception(); | |
200 } | |
201 | |
202 private Stmt RequiredBlock() throws ParseException { | |
203 List<Stmt> stmts = new ArrayList<Stmt>(); | |
204 int stackStart = symbolsSize(); | |
205 Stmt(stmts); | |
206 while( StmtSep(stmts) ) { | |
207 Spaces(In.NOTHING); | |
208 Stmt(stmts); | |
209 } | |
210 int stackEnd = symbolsSize(); | |
211 popSymbols( stackEnd - stackStart ); | |
212 if( stmts.isEmpty() ) | |
213 return Stmt.EMPTY; | |
214 if( stmts.size()==1 && stackStart==stackEnd ) | |
215 return stmts.get(0); | |
216 return new Block( stmts.toArray(new Stmt[0]), stackStart, stackEnd ); | |
217 } | |
218 | |
219 private boolean StmtSep(List<Stmt> stmts) throws ParseException { | |
220 parser.begin(); | |
221 if( parser.match( ';' ) ) | |
222 return parser.success(); | |
223 if( parser.match( "--" ) ) { | |
224 while( parser.noneOf("\r\n") ); | |
225 } | |
226 if( EndOfLine() ) | |
227 return parser.success(); | |
228 parser.rollback(); | |
229 Stmt stmt = TemplateStmt(); | |
230 if( stmt != null ) { | |
231 stmts.add(stmt); | |
232 return parser.success(); | |
233 } | |
234 return parser.failure(); | |
235 } | |
236 | |
237 private boolean EndOfLine() { | |
238 return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' ); | |
239 } | |
240 | |
241 private void Stmt(List<Stmt> stmts) throws ParseException { | |
242 if( LocalStmt(stmts) ) | |
243 return; | |
244 Stmt stmt; | |
245 if( (stmt=ReturnStmt()) != null | |
246 || (stmt=FunctionStmt()) != null | |
247 || (stmt=LocalFunctionStmt()) != null | |
248 || (stmt=ImportStmt()) != null | |
249 || (stmt=BreakStmt()) != null | |
250 || (stmt=ForStmt()) != null | |
251 || (stmt=TryStmt()) != null | |
252 || (stmt=DoStmt()) != null | |
253 || (stmt=WhileStmt()) != null | |
254 || (stmt=FunctionStmt()) != null | |
255 || (stmt=RepeatStmt()) != null | |
256 || (stmt=IfStmt()) != null | |
257 || (stmt=SetStmt()) != null | |
258 || (stmt=ExpressionsStmt()) != null | |
259 ) { | |
260 stmts.add(stmt); | |
261 } | |
262 } | |
263 | |
264 private Stmt TemplateStmt() throws ParseException { | |
265 int start = parser.currentIndex(); | |
266 Expressions exp = TemplateExpressions(In.NOTHING); | |
267 if( exp == null ) | |
268 return null; | |
269 Expr fnExp = (Expr)nameVar(start,"Io").expr(); | |
270 fnExp = new IndexExpr( se(start), fnExp, new ConstExpr("stdout") ); | |
271 fnExp = new IndexExpr( se(start), fnExp, new ConstExpr("write") ); | |
272 FnCall fnCall = new FnCall( se(start), fnExp, exp ); | |
273 return new ExpressionsStmt(fnCall); | |
274 } | |
275 | |
276 private Expressions TemplateExpressions(In in) throws ParseException { | |
277 if( in.template ) | |
278 return null; | |
279 int start = parser.begin(); | |
280 if( !parser.match( "%>" ) ) | |
281 return parser.failure(null); | |
282 EndOfLine(); | |
283 In inTemplate = in.template(); | |
284 List<Expressions> builder = new ArrayList<Expressions>(); | |
285 while(true) { | |
286 if( parser.match( "<%=" ) ) { | |
287 Spaces(inTemplate); | |
288 builder.add( RequiredExpr(inTemplate) ); | |
289 RequiredMatch( "%>" ); | |
290 } else if( parser.match( "<%" ) ) { | |
291 Spaces(inTemplate); | |
292 return parser.success(ExpList.build(builder)); | |
293 } else { | |
294 int i = parser.currentIndex(); | |
295 do { | |
296 if( parser.match( "%>" ) ) | |
297 throw parser.exception("'%>' unexpected"); | |
298 if( !parser.anyChar() ) | |
299 throw parser.exception("Unclosed template expression"); | |
300 } while( !parser.test( "<%" ) ); | |
301 String match = parser.textFrom(i); | |
302 builder.add( new ConstExpr(match) ); | |
303 } | |
304 } | |
305 } | |
306 | |
307 private Stmt ReturnStmt() throws ParseException { | |
308 int start = parser.begin(); | |
309 if( !Keyword("return",In.NOTHING) ) | |
310 return parser.failure(null); | |
311 Expressions exprs = ExpList(In.NOTHING); | |
312 if( exprs==null ) | |
313 exprs = ExpList.emptyExpList; | |
314 return parser.success( new ReturnStmt(se(start),exprs) ); | |
315 } | |
316 | |
317 private Stmt FunctionStmt() throws ParseException { | |
318 parser.begin(); | |
319 if( !Keyword("function",In.NOTHING) ) | |
320 return parser.failure(null); | |
321 | |
322 int start = parser.currentIndex(); | |
323 Var var = nameVar(start,RequiredName(In.NOTHING)); | |
324 while( parser.match( '.' ) ) { | |
325 Spaces(In.NOTHING); | |
326 var = indexVar( start, expr(var.expr()), NameExpr(In.NOTHING) ); | |
327 } | |
328 Settable fnName = var.settable(); | |
329 | |
330 FnDef fnDef = RequiredFunction(In.NOTHING); | |
331 return parser.success( new SetStmt(fnName,fnDef) ); | |
332 } | |
333 | |
334 private Stmt LocalFunctionStmt() throws ParseException { | |
335 parser.begin(); | |
336 if( !(Keyword("local",In.NOTHING) && Keyword("function",In.NOTHING)) ) | |
337 return parser.failure(null); | |
338 String name = RequiredName(In.NOTHING); | |
339 addSymbol( name ); | |
340 FnDef fnDef = RequiredFunction(In.NOTHING); | |
341 return parser.success( new SetStmt( new SetLocalVar(symbolsSize()-1), fnDef ) ); | |
342 } | |
343 | |
344 private Stmt ImportStmt() throws ParseException { | |
345 int start = parser.begin(); | |
346 if( !Keyword("import",In.NOTHING) ) | |
347 return parser.failure(null); | |
348 Expr importExpr = (Expr)nameVar(start,"require").expr(); | |
349 String modName = StringLiteral(In.NOTHING); | |
350 if( modName==null ) | |
351 return parser.failure(null); | |
352 String varName = modName.substring(modName.lastIndexOf('.')+1); | |
353 LuanSource.Element se = se(start); | |
354 FnCall require = new FnCall( se, importExpr, new ConstExpr(modName) ); | |
355 Settable settable; | |
356 if( interactive ) { | |
357 settable = nameVar(se,varName).settable(); | |
358 } else { | |
359 addSymbol( varName ); | |
360 settable = new SetLocalVar(symbolsSize()-1); | |
361 } | |
362 return parser.success( new SetStmt( settable, expr(require) ) ); | |
363 } | |
364 | |
365 private Stmt BreakStmt() throws ParseException { | |
366 parser.begin(); | |
367 if( !Keyword("break",In.NOTHING) ) | |
368 return parser.failure(null); | |
369 if( frame.loops <= 0 ) | |
370 throw parser.exception("'break' outside of loop"); | |
371 return parser.success( new BreakStmt() ); | |
372 } | |
373 | |
374 private Stmt ForStmt() throws ParseException { | |
375 int start = parser.begin(); | |
376 int stackStart = symbolsSize(); | |
377 if( !Keyword("for",In.NOTHING) ) | |
378 return parser.failure(null); | |
379 List<String> names = RequiredNameList(In.NOTHING); | |
380 if( !Keyword("in",In.NOTHING) ) | |
381 return parser.failure(null); | |
382 Expr expr = expr(RequiredExpr(In.NOTHING)); | |
383 RequiredKeyword("do",In.NOTHING); | |
384 addSymbols(names); | |
385 Stmt loop = RequiredLoopBlock(); | |
386 RequiredKeyword("end",In.NOTHING); | |
387 Stmt stmt = new ForStmt( se(start), stackStart, symbolsSize() - stackStart, expr, loop ); | |
388 popSymbols( symbolsSize() - stackStart ); | |
389 return parser.success(stmt); | |
390 } | |
391 | |
392 private Stmt TryStmt() throws ParseException { | |
393 parser.begin(); | |
394 if( !Keyword("try",In.NOTHING) ) | |
395 return parser.failure(null); | |
396 Stmt tryBlock = RequiredBlock(); | |
397 RequiredKeyword("catch",In.NOTHING); | |
398 String name = RequiredName(In.NOTHING); | |
399 addSymbol(name); | |
400 RequiredKeyword("do",In.NOTHING); | |
401 Stmt catchBlock = RequiredBlock(); | |
402 RequiredKeyword("end",In.NOTHING); | |
403 Stmt stmt = new TryStmt( tryBlock, symbolsSize()-1, catchBlock ); | |
404 popSymbols(1); | |
405 return parser.success(stmt); | |
406 } | |
407 | |
408 private Stmt DoStmt() throws ParseException { | |
409 parser.begin(); | |
410 if( !Keyword("do",In.NOTHING) ) | |
411 return parser.failure(null); | |
412 Stmt stmt = RequiredBlock(); | |
413 RequiredKeyword("end",In.NOTHING); | |
414 return parser.success(stmt); | |
415 } | |
416 | |
417 private boolean LocalStmt(List<Stmt> stmts) throws ParseException { | |
418 parser.begin(); | |
419 if( !Keyword("local",In.NOTHING) ) | |
420 return parser.failure(); | |
421 List<String> names = NameList(In.NOTHING); | |
422 if( names==null ) | |
423 return parser.failure(); | |
424 if( parser.match( '=' ) ) { | |
425 Spaces(In.NOTHING); | |
426 Expressions values = ExpList(In.NOTHING); | |
427 if( values==null ) | |
428 throw parser.exception("Expressions expected"); | |
429 SetLocalVar[] vars = new SetLocalVar[names.size()]; | |
430 int stackStart = symbolsSize(); | |
431 for( int i=0; i<vars.length; i++ ) { | |
432 vars[i] = new SetLocalVar(stackStart+i); | |
433 } | |
434 stmts.add( new SetStmt( vars, values ) ); | |
435 } | |
436 addSymbols(names); | |
437 return parser.success(); | |
438 } | |
439 | |
440 private List<String> RequiredNameList(In in) throws ParseException { | |
441 parser.begin(); | |
442 List<String> names = NameList(in); | |
443 if( names==null ) | |
444 throw parser.exception("Name expected"); | |
445 return parser.success(names); | |
446 } | |
447 | |
448 private List<String> NameList(In in) throws ParseException { | |
449 String name = Name(in); | |
450 if( name==null ) | |
451 return null; | |
452 List<String> names = new ArrayList<String>(); | |
453 names.add(name); | |
454 while( (name=anotherName(in)) != null ) { | |
455 names.add(name); | |
456 } | |
457 return names; | |
458 } | |
459 | |
460 private String anotherName(In in) throws ParseException { | |
461 parser.begin(); | |
462 if( !parser.match( ',' ) ) | |
463 return parser.failure(null); | |
464 Spaces(in); | |
465 String name = Name(in); | |
466 if( name==null ) | |
467 return parser.failure(null); | |
468 return parser.success(name); | |
469 } | |
470 | |
471 private Stmt WhileStmt() throws ParseException { | |
472 int start = parser.begin(); | |
473 if( !Keyword("while",In.NOTHING) ) | |
474 return parser.failure(null); | |
475 Expr cnd = expr(RequiredExpr(In.NOTHING)); | |
476 RequiredKeyword("do",In.NOTHING); | |
477 Stmt loop = RequiredLoopBlock(); | |
478 RequiredKeyword("end",In.NOTHING); | |
479 return parser.success( new WhileStmt(se(start),cnd,loop) ); | |
480 } | |
481 | |
482 private Stmt RepeatStmt() throws ParseException { | |
483 int start = parser.begin(); | |
484 if( !Keyword("repeat",In.NOTHING) ) | |
485 return parser.failure(null); | |
486 Stmt loop = RequiredLoopBlock(); | |
487 RequiredKeyword("until",In.NOTHING); | |
488 Expr cnd = expr(RequiredExpr(In.NOTHING)); | |
489 return parser.success( new RepeatStmt(se(start),loop,cnd) ); | |
490 } | |
491 | |
492 private Stmt RequiredLoopBlock() throws ParseException { | |
493 incLoops(); | |
494 Stmt stmt = RequiredBlock(); | |
495 decLoops(); | |
496 return stmt; | |
497 } | |
498 | |
499 private Stmt IfStmt() throws ParseException { | |
500 parser.begin(); | |
501 if( !Keyword("if",In.NOTHING) ) | |
502 return parser.failure(null); | |
503 return parser.success( IfStmt2() ); | |
504 } | |
505 | |
506 private Stmt IfStmt2() throws ParseException { | |
507 int start = parser.currentIndex(); | |
508 Expr cnd = expr(RequiredExpr(In.NOTHING)); | |
509 RequiredKeyword("then",In.NOTHING); | |
510 Stmt thenBlock = RequiredBlock(); | |
511 Stmt elseBlock; | |
512 if( Keyword("elseif",In.NOTHING) ) { | |
513 elseBlock = IfStmt2(); | |
514 } else { | |
515 elseBlock = Keyword("else",In.NOTHING) ? RequiredBlock() : Stmt.EMPTY; | |
516 RequiredKeyword("end",In.NOTHING); | |
517 } | |
518 return new IfStmt(se(start),cnd,thenBlock,elseBlock); | |
519 } | |
520 | |
521 private Stmt SetStmt() throws ParseException { | |
522 parser.begin(); | |
523 List<Settable> vars = new ArrayList<Settable>(); | |
524 Settable s = SettableVar(); | |
525 if( s == null ) | |
526 return parser.failure(null); | |
527 vars.add(s); | |
528 while( parser.match( ',' ) ) { | |
529 Spaces(In.NOTHING); | |
530 s = SettableVar(); | |
531 if( s == null ) | |
532 return parser.failure(null); | |
533 vars.add(s); | |
534 } | |
535 if( !parser.match( '=' ) ) | |
536 return parser.failure(null); | |
537 Spaces(In.NOTHING); | |
538 Expressions values = ExpList(In.NOTHING); | |
539 if( values==null ) | |
540 throw parser.exception("Expressions expected"); | |
541 return parser.success( new SetStmt( vars.toArray(new Settable[0]), values ) ); | |
542 } | |
543 | |
544 private Stmt ExpressionsStmt() throws ParseException { | |
545 parser.begin(); | |
546 Expressions exp = Expr(In.NOTHING); | |
547 if( exp instanceof FnCall || exp instanceof AndExpr || exp instanceof OrExpr ) | |
548 return parser.success( new ExpressionsStmt(exp) ); | |
549 return parser.failure(null); | |
550 } | |
551 | |
552 private Settable SettableVar() throws ParseException { | |
553 int start = parser.begin(); | |
554 Var var = VarZ(In.NOTHING); | |
555 if( var==null ) | |
556 return parser.failure(null); | |
557 return parser.success( var.settable() ); | |
558 } | |
559 | |
560 private Expressions RequiredExpr(In in) throws ParseException { | |
561 parser.begin(); | |
562 return parser.success(required(Expr(in),"Bad expression")); | |
563 } | |
564 | |
565 private Expressions Expr(In in) throws ParseException { | |
566 parser.begin(); | |
567 Expressions exp; | |
568 return (exp = VarArgs(in)) != null | |
569 || (exp = OrExpr(in)) != null | |
570 ? parser.success(exp) | |
571 : parser.failure((Expressions)null) | |
572 ; | |
573 } | |
574 | |
575 private Expressions OrExpr(In in) throws ParseException { | |
576 int start = parser.begin(); | |
577 Expressions exp = AndExpr(in); | |
578 if( exp==null ) | |
579 return parser.failure(null); | |
580 while( Keyword("or",in) ) { | |
581 exp = new OrExpr( se(start), expr(exp), required(expr(AndExpr(in))) ); | |
582 } | |
583 return parser.success(exp); | |
584 } | |
585 | |
586 private Expressions AndExpr(In in) throws ParseException { | |
587 int start = parser.begin(); | |
588 Expressions exp = RelExpr(in); | |
589 if( exp==null ) | |
590 return parser.failure(null); | |
591 while( Keyword("and",in) ) { | |
592 exp = new AndExpr( se(start), expr(exp), required(expr(RelExpr(in))) ); | |
593 } | |
594 return parser.success(exp); | |
595 } | |
596 | |
597 private Expressions RelExpr(In in) throws ParseException { | |
598 int start = parser.begin(); | |
599 Expressions exp = ConcatExpr(in); | |
600 if( exp==null ) | |
601 return parser.failure(null); | |
602 while(true) { | |
603 if( parser.match("==") ) { | |
604 Spaces(in); | |
605 exp = new EqExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) ); | |
606 } else if( parser.match("~=") ) { | |
607 Spaces(in); | |
608 exp = new NotExpr( se(start), new EqExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) ) ); | |
609 } else if( parser.match("<=") ) { | |
610 Spaces(in); | |
611 exp = new LeExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) ); | |
612 } else if( parser.match(">=") ) { | |
613 Spaces(in); | |
614 exp = new LeExpr( se(start), required(expr(ConcatExpr(in))), expr(exp) ); | |
615 } else if( parser.match("<") ) { | |
616 Spaces(in); | |
617 exp = new LtExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) ); | |
618 } else if( parser.match(">") ) { | |
619 Spaces(in); | |
620 exp = new LtExpr( se(start), required(expr(ConcatExpr(in))), expr(exp) ); | |
621 } else | |
622 break; | |
623 } | |
624 return parser.success(exp); | |
625 } | |
626 | |
627 private Expressions ConcatExpr(In in) throws ParseException { | |
628 int start = parser.begin(); | |
629 Expressions exp = SumExpr(in); | |
630 if( exp==null ) | |
631 return parser.failure(null); | |
632 if( parser.match("..") ) { | |
633 Spaces(in); | |
634 exp = new ConcatExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) ); | |
635 } | |
636 return parser.success(exp); | |
637 } | |
638 | |
639 private Expressions SumExpr(In in) throws ParseException { | |
640 int start = parser.begin(); | |
641 Expressions exp = TermExpr(in); | |
642 if( exp==null ) | |
643 return parser.failure(null); | |
644 while(true) { | |
645 if( parser.match('+') ) { | |
646 Spaces(in); | |
647 exp = new AddExpr( se(start), expr(exp), required(expr(TermExpr(in))) ); | |
648 } else if( Minus() ) { | |
649 Spaces(in); | |
650 exp = new SubExpr( se(start), expr(exp), required(expr(TermExpr(in))) ); | |
651 } else | |
652 break; | |
653 } | |
654 return parser.success(exp); | |
655 } | |
656 | |
657 private boolean Minus() { | |
658 parser.begin(); | |
659 return parser.match('-') && !parser.match('-') ? parser.success() : parser.failure(); | |
660 } | |
661 | |
662 private Expressions TermExpr(In in) throws ParseException { | |
663 int start = parser.begin(); | |
664 Expressions exp = UnaryExpr(in); | |
665 if( exp==null ) | |
666 return parser.failure(null); | |
667 while(true) { | |
668 if( parser.match('*') ) { | |
669 Spaces(in); | |
670 exp = new MulExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) ); | |
671 } else if( parser.match('/') ) { | |
672 Spaces(in); | |
673 exp = new DivExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) ); | |
674 } else if( Mod() ) { | |
675 Spaces(in); | |
676 exp = new ModExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) ); | |
677 } else | |
678 break; | |
679 } | |
680 return parser.success(exp); | |
681 } | |
682 | |
683 private boolean Mod() { | |
684 parser.begin(); | |
685 return parser.match('%') && !parser.match('>') ? parser.success() : parser.failure(); | |
686 } | |
687 | |
688 private Expressions UnaryExpr(In in) throws ParseException { | |
689 int start = parser.begin(); | |
690 if( parser.match('#') ) { | |
691 Spaces(in); | |
692 return parser.success( new LenExpr( se(start), required(expr(UnaryExpr(in))) ) ); | |
693 } | |
694 if( Minus() ) { | |
695 Spaces(in); | |
696 return parser.success( new UnmExpr( se(start), required(expr(UnaryExpr(in))) ) ); | |
697 } | |
698 if( Keyword("not",in) ) { | |
699 Spaces(in); | |
700 return parser.success( new NotExpr( se(start), required(expr(UnaryExpr(in))) ) ); | |
701 } | |
702 Expressions exp = PowExpr(in); | |
703 if( exp==null ) | |
704 return parser.failure(null); | |
705 return parser.success(exp); | |
706 } | |
707 | |
708 private Expressions PowExpr(In in) throws ParseException { | |
709 int start = parser.begin(); | |
710 Expressions exp = SingleExpr(in); | |
711 if( exp==null ) | |
712 return parser.failure(null); | |
713 if( parser.match('^') ) { | |
714 Spaces(in); | |
715 exp = new ConcatExpr( se(start), expr(exp), required(expr(PowExpr(in))) ); | |
716 } | |
717 return parser.success(exp); | |
718 } | |
719 | |
720 private Expressions SingleExpr(In in) throws ParseException { | |
721 parser.begin(); | |
722 Expressions exp; | |
723 exp = FunctionExpr(in); | |
724 if( exp != null ) | |
725 return parser.success(exp); | |
726 exp = TableExpr(in); | |
727 if( exp != null ) | |
728 return parser.success(exp); | |
729 exp = VarExp(in); | |
730 if( exp != null ) | |
731 return parser.success(exp); | |
732 exp = Literal(in); | |
733 if( exp != null ) | |
734 return parser.success(exp); | |
735 return parser.failure(null); | |
736 } | |
737 | |
738 private Expr FunctionExpr(In in) throws ParseException { | |
739 if( !Keyword("function",in) ) | |
740 return null; | |
741 return RequiredFunction(in); | |
742 } | |
743 | |
744 private FnDef RequiredFunction(In in) throws ParseException { | |
745 int start = parser.begin(); | |
746 RequiredMatch('('); | |
747 In inParens = in.parens(); | |
748 Spaces(inParens); | |
749 frame = new Frame(frame); | |
750 List<String> names = NameList(in); | |
751 if( names != null ) { | |
752 addSymbols(names); | |
753 if( parser.match(',') ) { | |
754 Spaces(inParens); | |
755 if( !parser.match("...") ) | |
756 throw parser.exception(); | |
757 frame.isVarArg = true; | |
758 } | |
759 } else if( parser.match("...") ) { | |
760 Spaces(inParens); | |
761 frame.isVarArg = true; | |
762 } | |
763 RequiredMatch(')'); | |
764 Spaces(in); | |
765 Stmt block = RequiredBlock(); | |
766 RequiredKeyword("end",in); | |
767 FnDef fnDef = newFnDef(start,block); | |
768 frame = frame.parent; | |
769 return parser.success(fnDef); | |
770 } | |
771 | |
772 private VarArgs VarArgs(In in) throws ParseException { | |
773 int start = parser.begin(); | |
774 if( !frame.isVarArg || !parser.match("...") ) | |
775 return parser.failure(null); | |
776 Spaces(in); | |
777 return parser.success( new VarArgs(se(start)) ); | |
778 } | |
779 | |
780 private Expr TableExpr(In in) throws ParseException { | |
781 int start = parser.begin(); | |
782 if( !parser.match('{') ) | |
783 return parser.failure(null); | |
784 In inParens = in.parens(); | |
785 Spaces(inParens); | |
786 List<TableExpr.Field> fields = new ArrayList<TableExpr.Field>(); | |
787 List<Expressions> builder = new ArrayList<Expressions>(); | |
788 while( Field(fields,builder,in) && FieldSep(inParens) ); | |
789 Spaces(inParens); | |
790 if( !parser.match('}') ) | |
791 throw parser.exception("Expected table element or '}'"); | |
792 return parser.success( new TableExpr( se(start), fields.toArray(new TableExpr.Field[0]), ExpList.build(builder) ) ); | |
793 } | |
794 | |
795 private boolean FieldSep(In in) throws ParseException { | |
796 if( !parser.anyOf(",;") ) | |
797 return false; | |
798 Spaces(in); | |
799 return true; | |
800 } | |
801 | |
802 private boolean Field(List<TableExpr.Field> fields,List<Expressions> builder,In in) throws ParseException { | |
803 parser.begin(); | |
804 Expr exp = SubExpr(in); | |
805 if( exp==null ) | |
806 exp = NameExpr(in); | |
807 if( exp!=null && parser.match('=') ) { | |
808 Spaces(in); | |
809 fields.add( new TableExpr.Field( exp, required(expr(Expr(in))) ) ); | |
810 return parser.success(); | |
811 } | |
812 parser.rollback(); | |
813 Expressions exprs = Expr(in); | |
814 if( exprs != null ) { | |
815 builder.add(exprs); | |
816 return parser.success(); | |
817 } | |
818 return parser.failure(); | |
819 } | |
820 | |
821 private Expressions VarExp(In in) throws ParseException { | |
822 Var var = VarZ(in); | |
823 return var==null ? null : var.expr(); | |
824 } | |
825 | |
826 private Var VarZ(In in) throws ParseException { | |
827 int start = parser.begin(); | |
828 Var var = VarStart(in); | |
829 if( var==null ) | |
830 return parser.failure(null); | |
831 Var var2; | |
832 while( (var2=Var2(in,start,var.expr())) != null ) { | |
833 var = var2; | |
834 } | |
835 return parser.success(var); | |
836 } | |
837 | |
838 private Var Var2(In in,int start,Expressions exp1) throws ParseException { | |
839 parser.begin(); | |
840 Var var = VarExt(in,start,exp1); | |
841 if( var != null ) | |
842 return parser.success(var); | |
843 if( parser.match("->") ) { | |
844 Spaces(in); | |
845 List<Expressions> builder = new ArrayList<Expressions>(); | |
846 builder.add(exp1); | |
847 Expr exp2 = expr(RequiredVarExpB(in)); | |
848 FnCall fnCall = required(Args( in, start, exp2, builder )); | |
849 return parser.success(exprVar(fnCall)); | |
850 } | |
851 FnCall fnCall = Args( in, start, expr(exp1), new ArrayList<Expressions>() ); | |
852 if( fnCall != null ) | |
853 return parser.success(exprVar(fnCall)); | |
854 return parser.failure(null); | |
855 } | |
856 | |
857 private Expressions RequiredVarExpB(In in) throws ParseException { | |
858 int start = parser.begin(); | |
859 Var var = required(VarStart(in)); | |
860 Var var2; | |
861 while( (var2=VarExt(in,start,var.expr())) != null ) { | |
862 var = var2; | |
863 } | |
864 return parser.success(var.expr()); | |
865 } | |
866 | |
867 private Var VarExt(In in,int start,Expressions exp1) throws ParseException { | |
868 parser.begin(); | |
869 Expr exp2 = SubExpr(in); | |
870 if( exp2 != null ) | |
871 return parser.success(indexVar(start,expr(exp1),exp2)); | |
872 if( parser.match('.') ) { | |
873 Spaces(in); | |
874 exp2 = NameExpr(in); | |
875 if( exp2!=null ) | |
876 return parser.success(indexVar(start,expr(exp1),exp2)); | |
877 } | |
878 return parser.failure(null); | |
879 } | |
880 | |
881 private Var VarStart(In in) throws ParseException { | |
882 int start = parser.begin(); | |
883 if( parser.match('(') ) { | |
884 In inParens = in.parens(); | |
885 Spaces(inParens); | |
886 Expr exp = expr(RequiredExpr(inParens)); | |
887 RequiredMatch(')'); | |
888 Spaces(in); | |
889 return parser.success(exprVar(exp)); | |
890 } | |
891 String name = Name(in); | |
892 if( name != null ) | |
893 return parser.success(nameVar(start,name)); | |
894 return parser.failure(null); | |
895 } | |
896 | |
897 private Expr env() { | |
898 int index = stackIndex(_ENV); | |
899 if( index != -1 ) | |
900 return new GetLocalVar(null,index); | |
901 index = upValueIndex(_ENV); | |
902 if( index != -1 ) | |
903 return new GetUpVar(null,index); | |
904 throw new RuntimeException("_ENV not found"); | |
905 } | |
906 | |
907 private interface Var { | |
908 public Expressions expr(); | |
909 public Settable settable(); | |
910 } | |
911 | |
912 private Var nameVar(final int start,final String name) { | |
913 return nameVar(se(start),name); | |
914 } | |
915 | |
916 private Var nameVar(final LuanSource.Element se,final String name) { | |
917 return new Var() { | |
918 | |
919 public Expr expr() { | |
920 int index = stackIndex(name); | |
921 if( index != -1 ) | |
922 return new GetLocalVar(se,index); | |
923 index = upValueIndex(name); | |
924 if( index != -1 ) | |
925 return new GetUpVar(se,index); | |
926 return new IndexExpr( se, env(), new ConstExpr(name) ); | |
927 } | |
928 | |
929 public Settable settable() { | |
930 int index = stackIndex(name); | |
931 if( index != -1 ) | |
932 return new SetLocalVar(index); | |
933 index = upValueIndex(name); | |
934 if( index != -1 ) | |
935 return new SetUpVar(index); | |
936 return new SetTableEntry( se, env(), new ConstExpr(name) ); | |
937 } | |
938 }; | |
939 } | |
940 | |
941 private Var exprVar(final Expressions expr) { | |
942 return new Var() { | |
943 | |
944 public Expressions expr() { | |
945 return expr; | |
946 } | |
947 | |
948 public Settable settable() { | |
949 return null; | |
950 } | |
951 }; | |
952 } | |
953 | |
954 private Var indexVar(final int start,final Expr table,final Expr key) { | |
955 return new Var() { | |
956 | |
957 public Expr expr() { | |
958 return new IndexExpr( se(start), table, key ); | |
959 } | |
960 | |
961 public Settable settable() { | |
962 return new SetTableEntry(se(start),table,key); | |
963 } | |
964 }; | |
965 } | |
966 | |
967 private FnCall Args(In in,int start,Expr fn,List<Expressions> builder) throws ParseException { | |
968 parser.begin(); | |
969 return args(in,builder) | |
970 ? parser.success( new FnCall( se(start), fn, ExpList.build(builder) ) ) | |
971 : parser.failure((FnCall)null); | |
972 } | |
973 | |
974 private boolean args(In in,List<Expressions> builder) throws ParseException { | |
975 if( parser.match('(') ) { | |
976 In inParens = in.parens(); | |
977 Spaces(inParens); | |
978 ExpList(inParens,builder); // optional | |
979 if( !parser.match(')') ) | |
980 throw parser.exception("Expression or ')' expected"); | |
981 Spaces(in); | |
982 return true; | |
983 } | |
984 Expr exp = TableExpr(in); | |
985 if( exp != null ) { | |
986 builder.add(exp); | |
987 return true; | |
988 } | |
989 String s = StringLiteral(in); | |
990 if( s != null ) { | |
991 builder.add( new ConstExpr(s) ); | |
992 return true; | |
993 } | |
994 Expressions exps = TemplateExpressions(in); | |
995 if( exps != null ) { | |
996 builder.add(exps); | |
997 return true; | |
998 } | |
999 return false; | |
1000 } | |
1001 | |
1002 private Expressions ExpList(In in) throws ParseException { | |
1003 List<Expressions> builder = new ArrayList<Expressions>(); | |
1004 return ExpList(in,builder) ? ExpList.build(builder) : null; | |
1005 } | |
1006 | |
1007 private boolean ExpList(In in,List<Expressions> builder) throws ParseException { | |
1008 parser.begin(); | |
1009 Expressions exp = TemplateExpressions(in); | |
1010 if( exp != null ) { | |
1011 builder.add(exp); | |
1012 return parser.success(); | |
1013 } | |
1014 exp = Expr(in); | |
1015 if( exp==null ) | |
1016 return parser.failure(); | |
1017 builder.add(exp); | |
1018 while( parser.match(',') ) { | |
1019 Spaces(in); | |
1020 exp = TemplateExpressions(in); | |
1021 if( exp != null ) { | |
1022 builder.add(exp); | |
1023 return parser.success(); | |
1024 } | |
1025 builder.add( RequiredExpr(in) ); | |
1026 } | |
1027 return parser.success(); | |
1028 } | |
1029 | |
1030 private Expr SubExpr(In in) throws ParseException { | |
1031 parser.begin(); | |
1032 if( !parser.match('[') ) | |
1033 return parser.failure(null); | |
1034 In inParens = in.parens(); | |
1035 Spaces(inParens); | |
1036 Expr exp = expr(RequiredExpr(inParens)); | |
1037 RequiredMatch(']'); | |
1038 Spaces(in); | |
1039 return parser.success(exp); | |
1040 } | |
1041 | |
1042 private Expr NameExpr(In in) throws ParseException { | |
1043 String name = Name(in); | |
1044 return name==null ? null : new ConstExpr(name); | |
1045 } | |
1046 | |
1047 private String RequiredName(In in) throws ParseException { | |
1048 parser.begin(); | |
1049 String name = Name(in); | |
1050 if( name==null ) | |
1051 throw parser.exception("Name expected"); | |
1052 return parser.success(name); | |
1053 } | |
1054 | |
1055 private String Name(In in) throws ParseException { | |
1056 int start = parser.begin(); | |
1057 if( !NameFirstChar() ) | |
1058 return parser.failure(null); | |
1059 while( NameChar() ); | |
1060 String match = parser.textFrom(start); | |
1061 if( keywords.contains(match) ) | |
1062 return parser.failure(null); | |
1063 Spaces(in); | |
1064 return parser.success(match); | |
1065 } | |
1066 | |
1067 private boolean NameChar() { | |
1068 return NameFirstChar() || Digit(); | |
1069 } | |
1070 | |
1071 private boolean NameFirstChar() { | |
1072 return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_'); | |
1073 } | |
1074 | |
1075 private void RequiredMatch(char c) throws ParseException { | |
1076 if( !parser.match(c) ) | |
1077 throw parser.exception("'"+c+"' expected"); | |
1078 } | |
1079 | |
1080 private void RequiredMatch(String s) throws ParseException { | |
1081 if( !parser.match(s) ) | |
1082 throw parser.exception("'"+s+"' expected"); | |
1083 } | |
1084 | |
1085 private void RequiredKeyword(String keyword,In in) throws ParseException { | |
1086 if( !Keyword(keyword,in) ) | |
1087 throw parser.exception("'"+keyword+"' expected"); | |
1088 } | |
1089 | |
1090 private boolean Keyword(String keyword,In in) throws ParseException { | |
1091 parser.begin(); | |
1092 if( !parser.match(keyword) || NameChar() ) | |
1093 return parser.failure(); | |
1094 Spaces(in); | |
1095 return parser.success(); | |
1096 } | |
1097 | |
1098 private static final Set<String> keywords = new HashSet<String>(Arrays.asList( | |
1099 "and", | |
1100 "break", | |
1101 "catch", | |
1102 "do", | |
1103 "else", | |
1104 "elseif", | |
1105 "end", | |
1106 "false", | |
1107 "for", | |
1108 "function", | |
1109 "goto", | |
1110 "if", | |
1111 "import", | |
1112 "in", | |
1113 "local", | |
1114 "nil", | |
1115 "not", | |
1116 "or", | |
1117 "repeat", | |
1118 "return", | |
1119 "then", | |
1120 "true", | |
1121 "try", | |
1122 "until", | |
1123 "while" | |
1124 )); | |
1125 | |
1126 private Expr Literal(In in) throws ParseException { | |
1127 if( NilLiteral(in) ) | |
1128 return new ConstExpr(null); | |
1129 Boolean b = BooleanLiteral(in); | |
1130 if( b != null ) | |
1131 return new ConstExpr(b); | |
1132 Number n = NumberLiteral(in); | |
1133 if( n != null ) | |
1134 return new ConstExpr(n); | |
1135 String s = StringLiteral(in); | |
1136 if( s != null ) | |
1137 return new ConstExpr(s); | |
1138 return null; | |
1139 } | |
1140 | |
1141 private boolean NilLiteral(In in) throws ParseException { | |
1142 return Keyword("nil",in); | |
1143 } | |
1144 | |
1145 private Boolean BooleanLiteral(In in) throws ParseException { | |
1146 if( Keyword("true",in) ) | |
1147 return true; | |
1148 if( Keyword("false",in) ) | |
1149 return false; | |
1150 return null; | |
1151 } | |
1152 | |
1153 private Number NumberLiteral(In in) throws ParseException { | |
1154 parser.begin(); | |
1155 Number n; | |
1156 if( parser.matchIgnoreCase("0x") ) { | |
1157 n = HexNumber(); | |
1158 } else { | |
1159 n = DecNumber(); | |
1160 } | |
1161 if( n==null || NameChar() ) | |
1162 return parser.failure(null); | |
1163 Spaces(in); | |
1164 return parser.success(n); | |
1165 } | |
1166 | |
1167 private Number DecNumber() { | |
1168 int start = parser.begin(); | |
1169 if( Int() ) { | |
1170 if( parser.match('.') ) | |
1171 Int(); // optional | |
1172 } else if( parser.match('.') && Int() ) { | |
1173 // ok | |
1174 } else | |
1175 return parser.failure(null); | |
1176 Exponent(); // optional | |
1177 return parser.success(Double.valueOf(parser.textFrom(start))); | |
1178 } | |
1179 | |
1180 private boolean Exponent() { | |
1181 parser.begin(); | |
1182 if( !parser.matchIgnoreCase("e") ) | |
1183 return parser.failure(); | |
1184 parser.anyOf("+-"); // optional | |
1185 if( !Int() ) | |
1186 return parser.failure(); | |
1187 return parser.success(); | |
1188 } | |
1189 | |
1190 private boolean Int() { | |
1191 if( !Digit() ) | |
1192 return false; | |
1193 while( Digit() ); | |
1194 return true; | |
1195 } | |
1196 | |
1197 private boolean Digit() { | |
1198 return parser.inCharRange('0', '9'); | |
1199 } | |
1200 | |
1201 private Number HexNumber() { | |
1202 int start = parser.begin(); | |
1203 double n; | |
1204 if( HexInt() ) { | |
1205 n = (double)Long.parseLong(parser.textFrom(start),16); | |
1206 if( parser.match('.') ) { | |
1207 start = parser.currentIndex(); | |
1208 if( HexInt() ) { | |
1209 String dec = parser.textFrom(start); | |
1210 n += (double)Long.parseLong(dec,16) / Math.pow(16,dec.length()); | |
1211 } | |
1212 } | |
1213 } else if( parser.match('.') && HexInt() ) { | |
1214 String dec = parser.textFrom(start+1); | |
1215 n = (double)Long.parseLong(dec,16) / Math.pow(16,dec.length()); | |
1216 } else { | |
1217 return parser.failure(null); | |
1218 } | |
1219 if( parser.matchIgnoreCase("p") ) { | |
1220 parser.anyOf("+-"); // optional | |
1221 start = parser.currentIndex(); | |
1222 if( !HexInt() ) | |
1223 return parser.failure(null); | |
1224 n *= Math.pow(2,(double)Long.parseLong(parser.textFrom(start))); | |
1225 } | |
1226 return parser.success(Double.valueOf(n)); | |
1227 } | |
1228 | |
1229 private boolean HexInt() { | |
1230 if( !HexDigit() ) | |
1231 return false; | |
1232 while( HexDigit() ); | |
1233 return true; | |
1234 } | |
1235 | |
1236 | |
1237 private boolean HexDigit() { | |
1238 return Digit() || parser.anyOf("abcdefABCDEF"); | |
1239 } | |
1240 | |
1241 private String StringLiteral(In in) throws ParseException { | |
1242 String s; | |
1243 if( (s=QuotedString('"'))==null | |
1244 && (s=QuotedString('\''))==null | |
1245 && (s=LongString())==null | |
1246 ) | |
1247 return null; | |
1248 Spaces(in); | |
1249 return s; | |
1250 } | |
1251 | |
1252 private String LongString() throws ParseException { | |
1253 parser.begin(); | |
1254 if( !parser.match('[') ) | |
1255 return parser.failure(null); | |
1256 int start = parser.currentIndex(); | |
1257 while( parser.match('=') ); | |
1258 int nEquals = parser.currentIndex() - start; | |
1259 if( !parser.match('[') ) | |
1260 return parser.failure(null); | |
1261 EndOfLine(); | |
1262 start = parser.currentIndex(); | |
1263 while( !LongBracketsEnd(nEquals) ) { | |
1264 if( !parser.anyChar() ) | |
1265 throw parser.exception("Unclosed long string"); | |
1266 } | |
1267 String s = parser.text.substring( start, parser.currentIndex() - nEquals - 2 ); | |
1268 return parser.success(s); | |
1269 } | |
1270 | |
1271 private String QuotedString(char quote) throws ParseException { | |
1272 parser.begin(); | |
1273 if( !parser.match(quote) ) | |
1274 return parser.failure(null); | |
1275 StringBuilder buf = new StringBuilder(); | |
1276 while( !parser.match(quote) ) { | |
1277 Character c = EscSeq(); | |
1278 if( c != null ) { | |
1279 buf.append(c); | |
1280 } else { | |
1281 if( !parser.anyChar() ) | |
1282 throw parser.exception("Unclosed string"); | |
1283 buf.append(parser.lastChar()); | |
1284 } | |
1285 } | |
1286 return parser.success(buf.toString()); | |
1287 } | |
1288 | |
1289 private Character EscSeq() { | |
1290 parser.begin(); | |
1291 if( !parser.match('\\') ) | |
1292 return parser.failure(null); | |
1293 if( parser.match('a') ) return parser.success('\u0007'); | |
1294 if( parser.match('b') ) return parser.success('\b'); | |
1295 if( parser.match('f') ) return parser.success('\f'); | |
1296 if( parser.match('n') ) return parser.success('\n'); | |
1297 if( parser.match('r') ) return parser.success('\r'); | |
1298 if( parser.match('t') ) return parser.success('\t'); | |
1299 if( parser.match('v') ) return parser.success('\u000b'); | |
1300 if( parser.match('\\') ) return parser.success('\\'); | |
1301 if( parser.match('"') ) return parser.success('"'); | |
1302 if( parser.match('\'') ) return parser.success('\''); | |
1303 int start = parser.currentIndex(); | |
1304 if( parser.match('x') && HexDigit() && HexDigit() ) | |
1305 return parser.success((char)Integer.parseInt(parser.textFrom(start+1),16)); | |
1306 if( Digit() ) { | |
1307 if( Digit() ) Digit(); // optional | |
1308 return parser.success((char)Integer.parseInt(parser.textFrom(start))); | |
1309 } | |
1310 return parser.failure(null); | |
1311 } | |
1312 | |
1313 private void Spaces(In in) throws ParseException { | |
1314 while( parser.anyOf(" \t") || Comment() || ContinueOnNextLine() || in.parens && NewLine() ); | |
1315 } | |
1316 | |
1317 private boolean ContinueOnNextLine() { | |
1318 parser.begin(); | |
1319 return parser.match('\\') && EndOfLine() ? parser.success() : parser.failure(); | |
1320 } | |
1321 | |
1322 private boolean NewLine() { | |
1323 if( !EndOfLine() ) | |
1324 return false; | |
1325 if( parser.match("--") ) { | |
1326 while( parser.noneOf("\r\n") ); | |
1327 } | |
1328 return true; | |
1329 } | |
1330 | |
1331 private boolean Comment() throws ParseException { | |
1332 parser.begin(); | |
1333 if( !parser.match("--[") ) | |
1334 return parser.failure(); | |
1335 int start = parser.currentIndex(); | |
1336 while( parser.match('=') ); | |
1337 int nEquals = parser.currentIndex() - start; | |
1338 if( !parser.match('[') ) | |
1339 return parser.failure(); | |
1340 while( !LongBracketsEnd(nEquals) ) { | |
1341 if( !parser.anyChar() ) | |
1342 throw parser.exception("Unclosed comment"); | |
1343 } | |
1344 return parser.success(); | |
1345 } | |
1346 | |
1347 private boolean LongBracketsEnd(int nEquals) { | |
1348 parser.begin(); | |
1349 if( !parser.match(']') ) | |
1350 return parser.failure(); | |
1351 while( nEquals-- > 0 ) { | |
1352 if( !parser.match('=') ) | |
1353 return parser.failure(); | |
1354 } | |
1355 if( !parser.match(']') ) | |
1356 return parser.failure(); | |
1357 return parser.success(); | |
1358 } | |
1359 | |
1360 } |