Mercurial Hosting > luan
diff src/luan/interp/LuanParser.java @ 48:64ecb7a3aad7
rename Lua to Luan
git-svn-id: https://luan-java.googlecode.com/svn/trunk@49 21e917c8-12df-6dd8-5cb6-c86387c605b9
author | fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9> |
---|---|
date | Fri, 28 Dec 2012 03:29:12 +0000 |
parents | src/luan/interp/LuaParser.java@a443637829c1 |
children | 8ede219cd111 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/interp/LuanParser.java Fri Dec 28 03:29:12 2012 +0000 @@ -0,0 +1,1090 @@ +package luan.interp; + +import java.util.Set; +import java.util.HashSet; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.util.Scanner; +import org.parboiled.BaseParser; +import org.parboiled.Parboiled; +import org.parboiled.Rule; +import org.parboiled.parserunners.ReportingParseRunner; +import org.parboiled.support.ParsingResult; +import org.parboiled.support.Var; +import org.parboiled.support.StringVar; +import org.parboiled.support.StringBuilderVar; +import org.parboiled.support.ValueStack; +import org.parboiled.errors.ErrorUtils; +import luan.Luan; +import luan.LuanState; +import luan.LuanSource; + + +class LuanParser extends BaseParser<Object> { + + LuanSource source; + + LuanSource.Element se(int start) { + return new LuanSource.Element(source,start,currentIndex()); + } + + static final String _ENV = "_ENV"; + + static final class Frame { + final Frame parent; + final List<String> symbols = new ArrayList<String>(); + int stackSize = 0; + int loops = 0; + boolean isVarArg = false; + final List<String> upValueSymbols = new ArrayList<String>(); + final List<UpValue.Getter> upValueGetters = new ArrayList<UpValue.Getter>(); + + Frame() { + this.parent = null; + upValueSymbols.add(_ENV); + upValueGetters.add(UpValue.globalGetter); + } + + Frame(Frame parent) { + this.parent = parent; + } + + int stackIndex(String name) { + int i = symbols.size(); + while( --i >= 0 ) { + if( symbols.get(i).equals(name) ) + return i; + } + return -1; + } + + int upValueIndex(String name) { + int i = upValueSymbols.size(); + while( --i >= 0 ) { + if( upValueSymbols.get(i).equals(name) ) + return i; + } + if( parent==null ) + return -1; + i = parent.stackIndex(name); + if( i != -1 ) { + upValueGetters.add(new UpValue.StackGetter(i)); + } else { + i = parent.upValueIndex(name); + if( i == -1 ) + return -1; + upValueGetters.add(new UpValue.NestedGetter(i)); + } + upValueSymbols.add(name); + return upValueSymbols.size() - 1; + } + } + + static final UpValue.Getter[] NO_UP_VALUE_GETTERS = new UpValue.Getter[0]; + + int nEquals; + int parens = 0; + Frame frame = new Frame(); + + boolean nEquals(int n) { + nEquals = n; + return true; + } + + boolean incParens() { + parens++; + return true; + } + + boolean decParens() { + parens--; + return true; + } + + List<String> symbols() { + return frame.symbols; + } + + int symbolsSize() { + return frame.symbols.size(); + } + + boolean addSymbol(String name) { + frame.symbols.add(name); + if( frame.stackSize < symbolsSize() ) + frame.stackSize = symbolsSize(); + return true; + } + + boolean addSymbols(List<String> names) { + frame.symbols.addAll(names); + if( frame.stackSize < symbolsSize() ) + frame.stackSize = symbolsSize(); + return true; + } + + int stackIndex(String name) { + return frame.stackIndex(name); + } + + boolean popSymbols(int n) { + List<String> symbols = frame.symbols; + while( n-- > 0 ) { + symbols.remove(symbols.size()-1); + } + return true; + } + + int upValueIndex(String name) { + return frame.upValueIndex(name); + } + + boolean incLoops() { + frame.loops++; + return true; + } + + boolean decLoops() { + frame.loops--; + return true; + } + + Chunk newChunk(int start) { + return new Chunk( se(start), (Stmt)pop(), frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) ); + } + + Rule Target() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + Spaces(), + FirstOf( + Sequence( ExpList(), EOI ), + Sequence( + start.set(currentIndex()), + action( frame.isVarArg = true ), + Block(), + EOI, + push( newChunk(start.get()) ) + ) + ) + ); + } + + Rule Block() { + Var<List<Stmt>> stmts = new Var<List<Stmt>>(new ArrayList<Stmt>()); + Var<Integer> stackStart = new Var<Integer>(symbolsSize()); + return Sequence( + Optional( Stmt(stmts) ), + ZeroOrMore( + StmtSep(), + Optional( Stmt(stmts) ) + ), + push( newBlock(stmts.get(),stackStart.get()) ) + ); + } + + Stmt newBlock(List<Stmt> stmts,int stackStart) { + int stackEnd = symbolsSize(); + popSymbols( stackEnd - stackStart ); + if( stmts.isEmpty() ) + return Stmt.EMPTY; + if( stmts.size()==1 && stackStart==stackEnd ) + return stmts.get(0); + return new Block( stmts.toArray(new Stmt[0]), stackStart, stackEnd ); + } + + Rule StmtSep() { + return Sequence( + FirstOf( + ';', + Sequence( + Optional( "--", ZeroOrMore(NoneOf("\r\n")) ), + EndOfLine() + ) + ), + Spaces() + ); + } + + Rule EndOfLine() { + return FirstOf("\r\n", '\r', '\n'); + } + + Rule Stmt(Var<List<Stmt>> stmts) { + return FirstOf( + LocalStmt(stmts), + Sequence( + FirstOf( + ReturnStmt(), + FunctionStmt(), + LocalFunctionStmt(), + BreakStmt(), + GenericForStmt(), + NumericForStmt(), + TryStmt(), + DoStmt(), + WhileStmt(), + RepeatStmt(), + IfStmt(), + SetStmt(), + ExpressionsStmt() + ), + stmts.get().add( (Stmt)pop() ) + ) + ); + } + + Rule ReturnStmt() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + Keyword("return"), Expressions(), + push( new ReturnStmt( se(start.get()), (Expressions)pop() ) ) + ); + } + + Rule FunctionStmt() { + return Sequence( + Keyword("function"), FnName(), Function(), + push( new SetStmt( (Settable)pop(1), expr(pop()) ) ) + ); + } + + Rule FnName() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + push(null), // marker + Name(), + ZeroOrMore( + '.', Spaces(), + makeVarExp(start.get()), + NameExpr() + ), + makeSettableVar(start.get()) + ); + } + + Rule LocalFunctionStmt() { + return Sequence( + Keyword("local"), Keyword("function"), + Name(), + addSymbol( (String)pop() ), + Function(), + push( new SetStmt( new SetLocalVar(symbolsSize()-1), expr(pop()) ) ) + ); + } + + Rule BreakStmt() { + return Sequence( + Keyword("break"), + frame.loops > 0, + push( new BreakStmt() ) + ); + } + + Rule GenericForStmt() { + Var<Integer> start = new Var<Integer>(); + Var<Integer> stackStart = new Var<Integer>(symbolsSize()); + Var<List<String>> names = new Var<List<String>>(new ArrayList<String>()); + return Sequence( + start.set(currentIndex()), + Keyword("for"), NameList(names), Keyword("in"), Expr(), Keyword("do"), + addSymbols(names.get()), + LoopBlock(), Keyword("end"), + push( new GenericForStmt( se(start.get()), stackStart.get(), symbolsSize() - stackStart.get(), expr(pop(1)), (Stmt)pop() ) ), + popSymbols( symbolsSize() - stackStart.get() ) + ); + } + + Rule NumericForStmt() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + Keyword("for"), Name(), '=', Spaces(), Expr(), Keyword("to"), Expr(), + push( new ConstExpr(1) ), // default step + Optional( + Keyword("step"), + drop(), + Expr() + ), + addSymbol( (String)pop(3) ), // add "for" var to symbols + Keyword("do"), LoopBlock(), Keyword("end"), + push( new NumericForStmt( se(start.get()), symbolsSize()-1, expr(pop(3)), expr(pop(2)), expr(pop(1)), (Stmt)pop() ) ), + popSymbols(1) + ); + } + + Rule TryStmt() { + return Sequence( + Keyword("try"), Block(), + Keyword("catch"), Name(), addSymbol( (String)pop() ), + Keyword("do"), Block(), Keyword("end"), + push( new TryStmt( (Stmt)pop(1), symbolsSize()-1, (Stmt)pop() ) ), + popSymbols(1) + ); + } + + Rule DoStmt() { + return Sequence( + Keyword("do"), Block(), Keyword("end") + ); + } + + Rule LocalStmt(Var<List<Stmt>> stmts) { + Var<List<String>> names = new Var<List<String>>(new ArrayList<String>()); + return Sequence( + Keyword("local"), NameList(names), + Optional( + '=', Spaces(), ExpList(), + stmts.get().add( newSetLocalStmt(names.get().size()) ) + ), + addSymbols(names.get()) + ); + } + + Rule NameList(Var<List<String>> names) { + return Sequence( + Name(), + names.get().add( (String)pop() ), + ZeroOrMore( + ',', Spaces(), Name(), + names.get().add( (String)pop() ) + ) + ); + } + + SetStmt newSetLocalStmt(int nVars) { + Expressions values = (Expressions)pop(); + SetLocalVar[] vars = new SetLocalVar[nVars]; + int stackStart = symbolsSize(); + for( int i=0; i<vars.length; i++ ) { + vars[i] = new SetLocalVar(stackStart+i); + } + return new SetStmt( vars, values ); + } + + Rule WhileStmt() { + return Sequence( + Keyword("while"), Expr(), Keyword("do"), LoopBlock(), Keyword("end"), + push( new WhileStmt( expr(pop(1)), (Stmt)pop() ) ) + ); + } + + Rule RepeatStmt() { + return Sequence( + Keyword("repeat"), LoopBlock(), Keyword("until"), Expr(), + push( new RepeatStmt( (Stmt)pop(1), expr(pop()) ) ) + ); + } + + Rule LoopBlock() { + return Sequence( incLoops(), Block(), decLoops() ); + } + + Rule IfStmt() { + Var<Integer> n = new Var<Integer>(1); + return Sequence( + Keyword("if"), Expr(), Keyword("then"), Block(), + push(Stmt.EMPTY), + ZeroOrMore( + Keyword("elseif"), drop(), Expr(), Keyword("then"), Block(), + push(Stmt.EMPTY), + n.set(n.get()+1) + ), + Optional( + Keyword("else"), drop(), Block() + ), + Keyword("end"), + buildIfStmt(n.get()) + ); + } + + boolean buildIfStmt(int n) { + while( n-- > 0 ) { + Stmt elseStmt = (Stmt)pop(); + Stmt thenStmt = (Stmt)pop(); + Expr cnd = expr(pop()); + push( new IfStmt(cnd,thenStmt,elseStmt) ); + } + return true; + } + + Rule SetStmt() { + return Sequence( + VarList(), + '=', Spaces(), + ExpList(), + push( newSetStmt() ) + ); + } + + Rule ExpressionsStmt() { + return Sequence( + ExpList(), + push( new ExpressionsStmt((Expressions)pop()) ) + ); + } + + SetStmt newSetStmt() { + Expressions values = (Expressions)pop(); + @SuppressWarnings("unchecked") + List<Settable> vars = (List<Settable>)pop(); + return new SetStmt( vars.toArray(new Settable[0]), values ); + } + + Rule VarList() { + Var<List<Settable>> vars = new Var<List<Settable>>(new ArrayList<Settable>()); + return Sequence( + SettableVar(), + vars.get().add( (Settable)pop() ), + ZeroOrMore( + ',', Spaces(), SettableVar(), + vars.get().add( (Settable)pop() ) + ), + push(vars.get()) + ); + } + + Rule SettableVar() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + Var(), + makeSettableVar(start.get()) + ); + } + + boolean makeSettableVar(int start) { + Object obj2 = pop(); + if( obj2==null ) + return false; + Object obj1 = pop(); + if( obj1!=null ) { + Expr key = expr(obj2); + Expr table = expr(obj1); + return push( new SetTableEntry(se(start),table,key) ); + } + String name = (String)obj2; + int index = stackIndex(name); + if( index != -1 ) + return push( new SetLocalVar(index) ); + index = upValueIndex(name); + if( index != -1 ) + return push( new SetUpVar(index) ); + return push( new SetTableEntry( se(start), env(), new ConstExpr(name) ) ); + } + + Rule Expr() { + return FirstOf( + VarArgs(), + OrExpr() + ); + } + + Rule OrExpr() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + AndExpr(), + ZeroOrMore( "or", Spaces(), AndExpr(), push( new OrExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ) + ); + } + + Rule AndExpr() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + RelExpr(), + ZeroOrMore( "and", Spaces(), RelExpr(), push( new AndExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ) + ); + } + + Rule RelExpr() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + ConcatExpr(), + ZeroOrMore( + FirstOf( + Sequence( "==", Spaces(), ConcatExpr(), push( new EqExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ), + Sequence( "~=", Spaces(), ConcatExpr(), push( new NotExpr(se(start.get()),new EqExpr(se(start.get()),expr(pop(1)),expr(pop()))) ) ), + Sequence( "<=", Spaces(), ConcatExpr(), push( new LeExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ), + Sequence( ">=", Spaces(), ConcatExpr(), push( new LeExpr(se(start.get()),expr(pop()),expr(pop())) ) ), + Sequence( "<", Spaces(), ConcatExpr(), push( new LtExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ), + Sequence( ">", Spaces(), ConcatExpr(), push( new LtExpr(se(start.get()),expr(pop()),expr(pop())) ) ) + ) + ) + ); + } + + Rule ConcatExpr() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + SumExpr(), + Optional( "..", Spaces(), ConcatExpr(), push( new ConcatExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ) + ); + } + + Rule SumExpr() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + TermExpr(), + ZeroOrMore( + FirstOf( + Sequence( '+', Spaces(), TermExpr(), push( new AddExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ), + Sequence( '-', TestNot('-'), Spaces(), TermExpr(), push( new SubExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ) + ) + ) + ); + } + + Rule TermExpr() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + UnaryExpr(), + ZeroOrMore( + FirstOf( + Sequence( '*', Spaces(), UnaryExpr(), push( new MulExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ), + Sequence( '/', Spaces(), UnaryExpr(), push( new DivExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ), + Sequence( '%', Spaces(), UnaryExpr(), push( new ModExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ) + ) + ) + ); + } + + Rule UnaryExpr() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + FirstOf( + Sequence( '#', Spaces(), PowExpr(), push( new LenExpr(se(start.get()),expr(pop())) ) ), + Sequence( '-', TestNot('-'), Spaces(), PowExpr(), push( new UnmExpr(se(start.get()),expr(pop())) ) ), + Sequence( "not", Spaces(), PowExpr(), push( new NotExpr(se(start.get()),expr(pop())) ) ), + PowExpr() + ) + ); + } + + Rule PowExpr() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + SingleExpr(), + Optional( '^', Spaces(), PowExpr(), push( new PowExpr(se(start.get()),expr(pop(1)),expr(pop())) ) ) + ); + } + + Rule SingleExpr() { + return FirstOf( + FunctionExpr(), + TableExpr(), + VarExp(), + LiteralExpr() + ); + } + + Rule FunctionExpr() { + return Sequence( "function", Spaces(), Function() ); + } + + Rule Function() { + Var<Integer> start = new Var<Integer>(); + Var<List<String>> names = new Var<List<String>>(new ArrayList<String>()); + return Sequence( + start.set(currentIndex()), + '(', incParens(), Spaces(), + action( frame = new Frame(frame) ), + Optional( + FirstOf( + Sequence( + NameList(names), addSymbols(names.get()), + Optional( ',', Spaces(), VarArgName() ) + ), + VarArgName() + ) + ), + ')', decParens(), Spaces(), Block(), Keyword("end"), + push( newChunk(start.get()) ), + action( frame = frame.parent ) + ); + } + + Rule VarArgName() { + return Sequence( + "...", Spaces(), + action( frame.isVarArg = true ) + ); + } + + Rule VarArgs() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + "...", Spaces(), + frame.isVarArg, + push( new VarArgs(se(start.get())) ) + ); + } + + Rule TableExpr() { + Var<Integer> start = new Var<Integer>(); + Var<List<TableExpr.Field>> fields = new Var<List<TableExpr.Field>>(new ArrayList<TableExpr.Field>()); + Var<ExpList.Builder> builder = new Var<ExpList.Builder>(new ExpList.Builder()); + return Sequence( + start.set(currentIndex()), + '{', incParens(), Spaces(), + Optional( + Field(fields,builder), + ZeroOrMore( + FieldSep(), + Field(fields,builder) + ), + Optional( FieldSep() ) + ), + '}', decParens(), + Spaces(), + push( new TableExpr( se(start.get()), fields.get().toArray(new TableExpr.Field[0]), builder.get().build() ) ) + ); + } + + Rule FieldSep() { + return Sequence( AnyOf(",;"), Spaces() ); + } + + Rule Field(Var<List<TableExpr.Field>> fields,Var<ExpList.Builder> builder) { + return FirstOf( + Sequence( + FirstOf( SubExpr(), NameExpr() ), + '=', Spaces(), Expr(), + fields.get().add( new TableExpr.Field( expr(pop(1)), expr(pop()) ) ) + ), + Sequence( + Expr(), + addToExpList(builder.get()) + ) + ); + } + + static Expr expr(Object obj) { + if( obj instanceof Expressions ) + return new ExpressionsExpr((Expressions)obj); + return (Expr)obj; + } + + Rule VarExp() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + Var(), + makeVarExp(start.get()) + ); + } + + Rule Var() { + Var<Integer> start = new Var<Integer>(); + return Sequence( + start.set(currentIndex()), + FirstOf( + Sequence( + '(', incParens(), Spaces(), Expr(), ')', decParens(), Spaces(), + push(expr(pop())), + push(null) // marker + ), + Sequence( + push(null), // marker + Name() + ) + ), + ZeroOrMore( + makeVarExp(start.get()), + FirstOf( + SubExpr(), + Sequence( '.', Spaces(), NameExpr() ), + Sequence( + Args(start), + push(null) // marker + ) + ) + ) + ); + } + + Expr env() { + int index = stackIndex(_ENV); + if( index != -1 ) + return new GetLocalVar(null,index); + index = upValueIndex(_ENV); + if( index != -1 ) + return new GetUpVar(null,index); + throw new RuntimeException("_ENV not found"); + } + + boolean makeVarExp(int start) { + Object obj2 = pop(); + if( obj2==null ) + return true; + Object obj1 = pop(); + if( obj1 != null ) + return push( new IndexExpr( se(start), expr(obj1), expr(obj2) ) ); + String name = (String)obj2; + int index = stackIndex(name); + if( index != -1 ) + return push( new GetLocalVar(se(start),index) ); + index = upValueIndex(name); + if( index != -1 ) + return push( new GetUpVar(se(start),index) ); + return push( new IndexExpr( se(start), env(), new ConstExpr(name) ) ); + } + + // function should be on top of the stack + Rule Args(Var<Integer> start) { + return Sequence( + FirstOf( + Sequence( + '(', incParens(), Spaces(), Expressions(), ')', decParens(), Spaces() + ), + Sequence( + TableExpr(), + push( new ExpList.SingleExpList(expr(pop())) ) + ), + Sequence( + StringLiteral(), Spaces(), + push( new ExpList.SingleExpList(new ConstExpr(pop())) ) + ) + ), + push( new FnCall( se(start.get()), expr(pop(1)), (Expressions)pop() ) ) + ); + } + + Rule Expressions() { + return FirstOf( + ExpList(), + push( ExpList.emptyExpList ) + ); + } + + Rule ExpList() { + Var<ExpList.Builder> builder = new Var<ExpList.Builder>(new ExpList.Builder()); + return Sequence( + Expr(), + addToExpList(builder.get()), + ZeroOrMore( + ',', Spaces(), Expr(), + addToExpList(builder.get()) + ), + push( builder.get().build() ) + ); + } + + boolean addToExpList(ExpList.Builder bld) { + Object obj = pop(); + if( obj instanceof Expressions ) { + bld.add( (Expressions)obj ); + } else { + bld.add( (Expr)obj ); + } + return true; + } + + Rule SubExpr() { + return Sequence( '[', incParens(), Spaces(), Expr(), ']', decParens(), Spaces() ); + } + + Rule NameExpr() { + return Sequence( + Name(), + push( new ConstExpr((String)pop()) ) + ); + } + + Rule Name() { + return Sequence( + Sequence( + NameFirstChar(), + ZeroOrMore( NameChar() ) + ), + !keywords.contains(match()), + push(match()), + Spaces() + ); + } + + Rule NameChar() { + return FirstOf( NameFirstChar(), Digit() ); + } + + Rule NameFirstChar() { + return FirstOf( + CharRange('a', 'z'), + CharRange('A', 'Z'), + '_' + ); + } + + Rule Keyword(String keyword) { + return Sequence( + keyword, + TestNot( NameChar() ), + Spaces() + ); + } + + static final Set<String> keywords = new HashSet<String>(Arrays.asList( + "and", + "break", + "catch", + "do", + "else", + "elseif", + "end", + "false", + "for", + "function", + "goto", + "if", + "in", + "local", + "nil", + "not", + "or", + "repeat", + "return", + "step", + "then", + "to", + "true", + "try", + "until", + "while" + )); + + Rule LiteralExpr() { + return Sequence( + Literal(), Spaces(), + push(new ConstExpr(pop())) + ); + } + + Rule Literal() { + return FirstOf( + NilLiteral(), + BooleanLiteral(), + NumberLiteral(), + StringLiteral() + ); + } + + Rule NilLiteral() { + return Sequence( "nil", push(null) ); + } + + Rule BooleanLiteral() { + return FirstOf( + Sequence( "true", push(true) ), + Sequence( "false", push(false) ) + ); + } + + Rule NumberLiteral() { + return FirstOf( + Sequence( + IgnoreCase("0x"), + HexNumber() + ), + Sequence( + DecNumber(), + push(Double.valueOf(match())) + ) + ); + } + + Rule DecNumber() { + return FirstOf( + Sequence( + Int(), + Optional( '.', Optional(Int()) ), + Exponent() + ), + Sequence( '.', Int(), Exponent() ) + ); + } + + Rule Exponent() { + return Optional( + IgnoreCase('e'), + Optional(AnyOf("+-")), + Int() + ); + } + + Rule Int() { + return OneOrMore(Digit()); + } + + Rule Digit() { + return CharRange('0', '9'); + } + + Rule HexNumber() { + return FirstOf( + Sequence( + HexInt(), + push( (double)Long.parseLong(match(),16) ), + Optional( '.', Optional(HexDec()) ), + HexExponent() + ), + Sequence( push(0.0), '.', HexDec(), HexExponent() ) + ); + } + + Rule HexDec() { + return Sequence( + HexInt(), + push( (Double)pop() + (double)Long.parseLong(match(),16) / Math.pow(16,matchLength()) ) + ); + } + + Rule HexExponent() { + return Optional( + IgnoreCase('p'), + Sequence( + Optional(AnyOf("+-")), + HexInt() + ), + push( (Double)pop() * Math.pow(2,(double)Long.parseLong(match())) ) + ); + } + + Rule HexInt() { + return OneOrMore(Digit()); + } + + + Rule HexDigit() { + return FirstOf( + Digit(), + AnyOf("abcdefABCDEF") + ); + } + + Rule StringLiteral() { + return FirstOf( + QuotedString('"'), + QuotedString('\''), + LongString() + ); + } + + Rule LongString() { + return Sequence( + '[', + ZeroOrMore('='), + nEquals(matchLength()), + '[', + ZeroOrMore( + TestNot(LongBracketsEnd()), + ANY + ), + push( match() ), + LongBracketsEnd() + ); + } + + Rule QuotedString(char quote) { + StringBuilderVar buf = new StringBuilderVar(); + return Sequence( + quote, + ZeroOrMore( + FirstOf( + Sequence( + NoneOf("\\\n"+quote), + buf.append(matchedChar()) + ), + EscSeq(buf) + ) + ), + quote, + push( buf.getString() ) + ); + } + + Rule EscSeq(StringBuilderVar buf) { + return Sequence( + '\\', + FirstOf( + Sequence( 'a', buf.append('\u0007') ), + Sequence( 'b', buf.append('\b') ), + Sequence( 'f', buf.append('\f') ), + Sequence( 'n', buf.append('\n') ), + Sequence( 'r', buf.append('\r') ), + Sequence( 't', buf.append('\t') ), + Sequence( 'v', buf.append('\u000b') ), + Sequence( '\\', buf.append('\\') ), + Sequence( '"', buf.append('"') ), + Sequence( '\'', buf.append('\'') ), + Sequence( + 'x', + Sequence( HexDigit(), HexDigit() ), + buf.append( (char)Integer.parseInt(match(),16) ) + ), + Sequence( + Sequence( + Digit(), + Optional( + Digit(), + Optional( + Digit() + ) + ) + ), + buf.append( (char)Integer.parseInt(match()) ) + ) + ) + ); + } + + Rule Spaces() { + return ZeroOrMore( + FirstOf( + AnyOf(" \t"), + Comment(), + Sequence( '\\', EndOfLine() ), + Sequence( AnyOf("\r\n"), parens > 0 ) + ) + ); + } + + Rule Comment() { + return Sequence( + "--[", + ZeroOrMore('='), + nEquals(matchLength()), + '[', + ZeroOrMore( + TestNot(LongBracketsEnd()), + ANY + ), + LongBracketsEnd() + ); + } + + Rule LongBracketsEnd() { + return Sequence( ']', ZeroOrMore('='), nEquals==matchLength(), ']' ); + } + + static boolean action(Object obj) { + return true; + } + + // for debugging + boolean print(Object o) { + System.out.println(o); + return true; + } + +}