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