Mercurial Hosting > luan
view core/src/luan/impl/LuanParser.java @ 653:538b0ae08faa
compile IfStmt and BreakStmt
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 05 Apr 2016 13:01:04 -0600 |
parents | 067d9470184d |
children | ea7dbd2dfa65 |
line wrap: on
line source
package luan.impl; 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 luan.Luan; import luan.LuanState; import luan.LuanTable; import luan.modules.PackageLuan; final class LuanParser { private 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(LuanTable java) { this.parent = null; upValueSymbols.add(JAVA); upValueGetters.add(new UpValue.ValueGetter(java)); } Frame(Frame parent) { this.parent = parent; if( upValueIndex(JAVA) != 0 ) throw new RuntimeException(); } 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; } void addUpValueGetter(String name,UpValue.Getter upValueGetter) { upValueSymbols.add(name); upValueGetters.add(upValueGetter); } } private static class In { static final In NOTHING = new In(false,false); final boolean parens; final boolean template; private In(boolean parens,boolean template) { this.parens = parens; this.template = template; } In parens() { return parens ? this : new In(true,false); } In template() { return template ? this : new In(false,true); } } private static final String JAVA = "-JAVA-"; // inaccessible from Luan private static final String _ENV = "_ENV"; private static final UpValue.Getter[] NO_UP_VALUE_GETTERS = new UpValue.Getter[0]; // final LuanSource source; private Frame frame; private final Parser parser; LuanParser(String sourceName,String sourceText,LuanTable env) { // this.source = source; this.frame = new Frame( env!=null ? env : new LuanTable() ); this.parser = new Parser(sourceName,sourceText); if( env != null ) addVar(_ENV,env); } void addVar(String name,Object value) { frame.addUpValueGetter(name,new UpValue.ValueGetter(value)); } private List<String> symbols() { return frame.symbols; } private int symbolsSize() { return frame.symbols.size(); } private void addSymbol(String name) { frame.symbols.add(name); if( frame.stackSize < symbolsSize() ) frame.stackSize = symbolsSize(); } private void addSymbols(List<String> names) { frame.symbols.addAll(names); if( frame.stackSize < symbolsSize() ) frame.stackSize = symbolsSize(); } private int stackIndex(String name) { return frame.stackIndex(name); } private void popSymbols(int n) { List<String> symbols = frame.symbols; while( n-- > 0 ) { symbols.remove(symbols.size()-1); } } private int upValueIndex(String name) { return frame.upValueIndex(name); } private void incLoops() { frame.loops++; } private void decLoops() { frame.loops--; } private <T> T required(T t) throws ParseException { if( t==null ) throw parser.exception(); return t; } private <T> T required(T t,String msg) throws ParseException { if( t==null ) throw parser.exception(msg); return t; } private static Expr expr(Expressions exprs) { if( exprs==null ) return null; if( exprs instanceof Expr ) return (Expr)exprs; return new ExpressionsExpr(exprs); } private FnDef newFnDef(int start,Stmt stmt) { return new FnDef( stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) ); } FnDef Expression() throws ParseException { Spaces(In.NOTHING); int start = parser.begin(); Expressions expr = exp(ExprZ(In.NOTHING)); if( expr != null && parser.endOfInput() ) { Stmt stmt = new ReturnStmt( expr ); return parser.success(newFnDef(start,stmt)); } return parser.failure(null); } FnDef RequiredModule() throws ParseException { Spaces(In.NOTHING); int start = parser.begin(); frame.isVarArg = true; Stmt stmt = stmt(RequiredBlock()); if( parser.endOfInput() ) return parser.success(newFnDef(start,stmt)); throw parser.exception(); } private StmtString RequiredBlock() throws ParseException { StringBuilder stmts = new StringBuilder(); int stackStart = symbolsSize(); Stmt(stmts); while( StmtSep(stmts) ) { Spaces(In.NOTHING); Stmt(stmts); } int stackEnd = symbolsSize(); popSymbols( stackEnd - stackStart ); if( stmts.length() == 0 ) return EMPTY_STMT; String code = stmts.toString(); if( stackStart < stackEnd ) code = "" +"try {\n" + code +"} finally {\n" +" $luan.stackClear("+stackStart+","+stackEnd+");\n" +"}\n" ; return new StmtString(code); } private boolean StmtSep(StringBuilder stmts) throws ParseException { parser.begin(); if( parser.match( ';' ) ) return parser.success(); if( EndOfLine() ) return parser.success(); parser.rollback(); StmtString stmt = stmtStr(TemplateStmt()); if( stmt != null ) { stmts.append(stmt.code); return parser.success(); } return parser.failure(); } private boolean EndOfLine() { return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' ); } private void Stmt(StringBuilder stmts) throws ParseException { if( LocalStmt(stmts) ) return; StmtString stmt; if( (stmt=stmtStr(ReturnStmt())) != null || (stmt=stmtStr(FunctionStmt())) != null || (stmt=stmtStr(LocalFunctionStmt())) != null || (stmt=BreakStmt()) != null || (stmt=ForStmt()) != null || (stmt=DoStmt()) != null || (stmt=WhileStmt()) != null || (stmt=RepeatStmt()) != null || (stmt=IfStmt()) != null || (stmt=SetStmt()) != null || (stmt=ExpressionsStmt()) != null ) { stmts.append(stmt.code); } } private Stmt TemplateStmt() throws ParseException { parser.currentIndex(); Expressions exprs = TemplateExpressions(In.NOTHING); if( exprs == null ) return null; FnCall requireCall = new FnCall( new ConstExpr(PackageLuan.requireFn), new ConstExpr("luan:Io") ); Expr stdoutExp = new IndexExpr( expr(requireCall), new ConstExpr("stdout") ); Expr writeExp = new IndexExpr( stdoutExp, new ConstExpr("write") ); FnCall writeCall = new FnCall( writeExp, exprs ); return new ExpressionsStmt(writeCall); } private Expressions TemplateExpressions(In in) throws ParseException { if( in.template ) return null; int start = parser.begin(); if( !parser.match( "%>" ) ) return parser.failure(null); EndOfLine(); In inTemplate = in.template(); List<Expressions> builder = new ArrayList<Expressions>(); while(true) { if( parser.match( "<%=" ) ) { Spaces(inTemplate); builder.add( exp(RequiredExpr(inTemplate)) ); RequiredMatch( "%>" ); } else if( parser.match( "<%" ) ) { Spaces(inTemplate); return parser.success(ExpList.build(builder)); } else { int i = parser.currentIndex(); do { if( parser.match( "%>" ) ) throw parser.exception("'%>' unexpected"); if( !parser.anyChar() ) throw parser.exception("Unclosed template expression"); } while( !parser.test( "<%" ) ); String match = parser.textFrom(i); builder.add( new ConstExpr(match) ); } } } private Stmt ReturnStmt() throws ParseException { parser.begin(); if( !Keyword("return",In.NOTHING) ) return parser.failure(null); Expressions exprs = ExpList(In.NOTHING); if( exprs==null ) exprs = ExpList.emptyExpList; return parser.success( new ReturnStmt(exprs) ); } private Stmt FunctionStmt() throws ParseException { parser.begin(); if( !Keyword("function",In.NOTHING) ) return parser.failure(null); parser.currentIndex(); Var var = nameVar(RequiredName(In.NOTHING)); while( parser.match( '.' ) ) { Spaces(In.NOTHING); Expr exp = NameExpr(In.NOTHING); if( exp==null ) return parser.failure(null); var = indexVar( expr(var.expr()), exp ); } Settable fnName = var.settable(); FnDef fnDef = RequiredFunction(In.NOTHING); return parser.success( new SetStmt(fnName,fnDef) ); } private Stmt LocalFunctionStmt() throws ParseException { parser.begin(); if( !(Keyword("local",In.NOTHING) && Keyword("function",In.NOTHING)) ) return parser.failure(null); String name = RequiredName(In.NOTHING); addSymbol( name ); FnDef fnDef = RequiredFunction(In.NOTHING); return parser.success( new SetStmt( new SetLocalVar(symbolsSize()-1), fnDef ) ); } private StmtString BreakStmt() throws ParseException { parser.begin(); if( !Keyword("break",In.NOTHING) ) return parser.failure(null); if( frame.loops <= 0 ) throw parser.exception("'break' outside of loop"); return parser.success( new StmtString("break;\n") ); } int forCounter = 0; private StmtString ForStmt() throws ParseException { parser.begin(); int stackStart = symbolsSize(); if( !Keyword("for",In.NOTHING) ) return parser.failure(null); List<String> names = RequiredNameList(In.NOTHING); if( !Keyword("in",In.NOTHING) ) return parser.failure(null); ExpString expr = RequiredExpr(In.NOTHING).expr(); RequiredKeyword("do",In.NOTHING); SetLocalVar[] vars = new SetLocalVar[names.size()]; for( int i=0; i<vars.length; i++ ) { vars[i] = new SetLocalVar(stackStart+i); } String varsStr = varsToString(vars); ExpString firstVar = new ExpString(new GetLocalVar(stackStart)); addSymbols(names); StmtString loop = RequiredLoopBlock(); RequiredKeyword("end",In.NOTHING); String fnVar = "$fn"+ ++forCounter; String code = "" +"try {\n" +"LuanFunction "+fnVar+" = $Luan.checkFunction(" + expr.code + ");\n" +"while(true) {\n" +"for( int i="+stackStart+"; i<"+symbolsSize()+"; i++ ) {\n" +"$luan.stackSet(i,null);\n" +"}\n" +"$Luan.set($luan," + varsStr + ", "+fnVar+".call($luan) );\n" +"if( " + firstVar.code + "==null ) break;\n" + loop.code +"}" +"} finally {\n" +"$luan.stackClear("+stackStart+","+symbolsSize()+");\n" +"}\n" ; StmtString stmt = new StmtString(code); popSymbols( symbolsSize() - stackStart ); return parser.success(stmt); } private StmtString DoStmt() throws ParseException { parser.begin(); if( !Keyword("do",In.NOTHING) ) return parser.failure(null); StmtString stmt = RequiredBlock(); RequiredKeyword("end",In.NOTHING); return parser.success(stmt); } private boolean LocalStmt(StringBuilder stmts) throws ParseException { parser.begin(); if( !Keyword("local",In.NOTHING) ) return parser.failure(); List<String> names = NameList(In.NOTHING); if( names==null ) { if( Keyword("function",In.NOTHING) ) return parser.failure(); // handled later throw parser.exception("Invalid local statement"); } if( parser.match( '=' ) ) { Spaces(In.NOTHING); ExpString values = ExpStringList(In.NOTHING); if( values==null ) throw parser.exception("Expressions expected"); SetLocalVar[] vars = new SetLocalVar[names.size()]; int stackStart = symbolsSize(); for( int i=0; i<vars.length; i++ ) { vars[i] = new SetLocalVar(stackStart+i); } String varsStr = varsToString(vars); String code = "$Luan.set($luan," + varsStr + "," + values.code + ");\n"; stmts.append(code); } addSymbols(names); return parser.success(); } private List<String> RequiredNameList(In in) throws ParseException { parser.begin(); List<String> names = NameList(in); if( names==null ) throw parser.exception("Name expected"); return parser.success(names); } private List<String> NameList(In in) throws ParseException { String name = Name(in); if( name==null ) return null; List<String> names = new ArrayList<String>(); names.add(name); while( (name=anotherName(in)) != null ) { names.add(name); } return names; } private String anotherName(In in) throws ParseException { parser.begin(); if( !parser.match( ',' ) ) return parser.failure(null); Spaces(in); String name = Name(in); if( name==null ) return parser.failure(null); return parser.success(name); } private StmtString WhileStmt() throws ParseException { parser.begin(); if( !Keyword("while",In.NOTHING) ) return parser.failure(null); ExpString cnd = RequiredExpr(In.NOTHING).expr(); RequiredKeyword("do",In.NOTHING); StmtString loop = RequiredLoopBlock(); RequiredKeyword("end",In.NOTHING); String code = "" +"while( $Luan.checkBoolean(" + cnd.code + ") ) {\n" + loop.code +"}\n" ; return parser.success( new StmtString(code) ); } private StmtString RepeatStmt() throws ParseException { parser.begin(); if( !Keyword("repeat",In.NOTHING) ) return parser.failure(null); StmtString loop =RequiredLoopBlock(); RequiredKeyword("until",In.NOTHING); ExpString cnd = RequiredExpr(In.NOTHING).expr(); String code = "" +"do {\n" + loop.code +"} while( !$Luan.checkBoolean(" + cnd.code + ") );\n" ; return parser.success( new StmtString(code) ); } private StmtString RequiredLoopBlock() throws ParseException { incLoops(); StmtString stmt = RequiredBlock(); decLoops(); return stmt; } private StmtString IfStmt() throws ParseException { parser.begin(); if( !Keyword("if",In.NOTHING) ) return parser.failure(null); StringBuilder sb = new StringBuilder(); ExpString cnd; StmtString block; cnd = RequiredExpr(In.NOTHING).expr(); RequiredKeyword("then",In.NOTHING); block = RequiredBlock(); sb.append( "if( $Luan.checkBoolean(" ).append( cnd.code ).append( ") ) {\n" ).append( block.code ); while( Keyword("elseif",In.NOTHING) ) { cnd = RequiredExpr(In.NOTHING).expr(); RequiredKeyword("then",In.NOTHING); block = RequiredBlock(); sb.append( "} else if( $Luan.checkBoolean(" ).append( cnd.code ).append( ") ) {\n" ).append( block.code ); } if( Keyword("else",In.NOTHING) ) { block = RequiredBlock(); sb.append( "} else {\n" ).append( block.code ); } RequiredKeyword("end",In.NOTHING); sb.append( "}\n" ); return parser.success( new StmtString(sb.toString()) ); } private StmtString SetStmt() throws ParseException { parser.begin(); List<Settable> vars = new ArrayList<Settable>(); Settable s = SettableVar(); if( s == null ) return parser.failure(null); vars.add(s); while( parser.match( ',' ) ) { Spaces(In.NOTHING); s = SettableVar(); if( s == null ) return parser.failure(null); vars.add(s); } if( !parser.match( '=' ) ) return parser.failure(null); Spaces(In.NOTHING); ExpString values = ExpStringList(In.NOTHING); if( values==null ) // throw parser.exception("Expressions expected"); return parser.failure(null); String varsStr = varsToString(vars.toArray(new Settable[0])); String code = "$Luan.set($luan," + varsStr + "," + values.code + "); "; return parser.success( new StmtString(code) ); } private static String varsToString(Settable[] vars) { StringBuilder sb = new StringBuilder(); sb.append( "new Settable[]{" ); for( Settable v : vars ) { sb.append( new SettableString(v).code ).append( ',' ); } sb.append( "}" ); return sb.toString(); } private StmtString ExpressionsStmt() throws ParseException { parser.begin(); ExpString exp = ExprZ(In.NOTHING); if( exp != null && exp.isStmt ) { String code = exp.code; if( exp.isExpr ) code = "$Luan.nop(" + code + ")"; code += ";\n"; return parser.success( new StmtString(code) ); } return parser.failure(null); } private Settable SettableVar() throws ParseException { int start = parser.begin(); Var var = VarZ(In.NOTHING); if( var==null ) return parser.failure(null); return parser.success( var.settable() ); } private ExpString RequiredExpr(In in) throws ParseException { parser.begin(); return parser.success(required(ExprZ(in),"Bad expression")); } private ExpString ExprZ(In in) throws ParseException { return OrExpr(in); } private ExpString OrExpr(In in) throws ParseException { parser.begin(); ExpString exp = AndExpr(in); if( exp==null ) return parser.failure(null); while( Keyword("or",in) ) { exp = exp.expr(); ExpString exp2 = required(RelExpr(in)).expr(); String code = "($Luan.cnd($cnd = " + exp.code + ") ? $cnd : (" + exp2.code + "))"; exp = new ExpString(code,true,true); } return parser.success(exp); } private ExpString AndExpr(In in) throws ParseException { parser.begin(); ExpString exp = RelExpr(in); if( exp==null ) return parser.failure(null); while( Keyword("and",in) ) { exp = exp.expr(); ExpString exp2 = required(RelExpr(in)).expr(); String code = "($Luan.cnd($cnd = " + exp.code + ") ? (" + exp2.code + ") : $cnd)"; exp = new ExpString(code,true,true); } return parser.success(exp); } private ExpString RelExpr(In in) throws ParseException { parser.begin(); ExpString exp = ConcatExpr(in); if( exp==null ) return parser.failure(null); while(true) { if( parser.match("==") ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(ConcatExpr(in)).expr(); String code = "$Luan.eq($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else if( parser.match("~=") ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(ConcatExpr(in)).expr(); String code = "!$Luan.eq($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else if( parser.match("<=") ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(ConcatExpr(in)).expr(); String code = "$Luan.le($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else if( parser.match(">=") ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(ConcatExpr(in)).expr(); String code = "$Luan.le($luan," + exp2.code + "," + exp.code + ")"; exp = new ExpString(code,true,false); } else if( parser.match("<") ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(ConcatExpr(in)).expr(); String code = "$Luan.lt($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else if( parser.match(">") ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(ConcatExpr(in)).expr(); String code = "$Luan.lt($luan," + exp2.code + "," + exp.code + ")"; exp = new ExpString(code,true,false); } else break; } return parser.success(exp); } private ExpString ConcatExpr(In in) throws ParseException { parser.begin(); ExpString exp = SumExpr(in); if( exp==null ) return parser.failure(null); if( parser.match("..") ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(ConcatExpr(in)).expr(); String code = "$Luan.concat($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } return parser.success(exp); } private ExpString SumExpr(In in) throws ParseException { parser.begin(); ExpString exp = TermExpr(in); if( exp==null ) return parser.failure(null); while(true) { if( parser.match('+') ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(TermExpr(in)).expr(); String code = "$Luan.add($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else if( Minus() ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(TermExpr(in)).expr(); String code = "$Luan.sub($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else break; } return parser.success(exp); } private boolean Minus() { parser.begin(); return parser.match('-') && !parser.match('-') ? parser.success() : parser.failure(); } private ExpString TermExpr(In in) throws ParseException { parser.begin(); ExpString exp = UnaryExpr(in); if( exp==null ) return parser.failure(null); while(true) { if( parser.match('*') ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(UnaryExpr(in)).expr(); String code = "$Luan.mul($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else if( parser.match('/') ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(UnaryExpr(in)).expr(); String code = "$Luan.div($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else if( Mod() ) { Spaces(in); exp = exp.expr(); ExpString exp2 = required(UnaryExpr(in)).expr(); String code = "$Luan.mod($luan," + exp.code + "," + exp2.code + ")"; exp = new ExpString(code,true,false); } else break; } return parser.success(exp); } private boolean Mod() { parser.begin(); return parser.match('%') && !parser.match('>') ? parser.success() : parser.failure(); } private ExpString UnaryExpr(In in) throws ParseException { parser.begin(); if( parser.match('#') ) { Spaces(in); ExpString exp = required(UnaryExpr(in)).expr(); String code = "$Luan.len($luan," + exp.code + ")"; exp = new ExpString(code,true,false); return parser.success(exp); } if( Minus() ) { Spaces(in); ExpString exp = required(UnaryExpr(in)).expr(); String code = "$Luan.unm($luan," + exp.code + ")"; exp = new ExpString(code,true,false); return parser.success(exp); } if( Keyword("not",in) ) { Spaces(in); ExpString exp = required(UnaryExpr(in)).expr(); String code = "$Luan.not(" + exp.code + ")"; exp = new ExpString(code,true,false); return parser.success(exp); } ExpString exp = PowExpr(in); if( exp==null ) return parser.failure(null); return parser.success(exp); } private ExpString PowExpr(In in) throws ParseException { parser.begin(); Expressions exp = SingleExpr(in); if( exp==null ) return parser.failure(null); ExpString exp1 = new ExpString(exp); if( parser.match('^') ) { Spaces(in); ExpString exp2 = required(PowExpr(in)); String code = "$Luan.pow($luan," + exp1.code + "," + exp2.code + ")"; exp1 = new ExpString(code,true,false); } return parser.success(exp1); } private Expressions SingleExpr(In in) throws ParseException { parser.begin(); Expressions exp; exp = FunctionExpr(in); if( exp != null ) return parser.success(exp); exp = VarExp(in); if( exp != null ) return parser.success(exp); exp = VarArgs(in); if( exp != null ) return parser.success(exp); return parser.failure(null); } private Expr FunctionExpr(In in) throws ParseException { if( !Keyword("function",in) ) return null; return RequiredFunction(in); } private FnDef RequiredFunction(In in) throws ParseException { int start = parser.begin(); RequiredMatch('('); In inParens = in.parens(); Spaces(inParens); frame = new Frame(frame); List<String> names = NameList(in); if( names != null ) { addSymbols(names); if( parser.match(',') ) { Spaces(inParens); if( !parser.match("...") ) throw parser.exception(); Spaces(inParens); frame.isVarArg = true; } } else if( parser.match("...") ) { Spaces(inParens); frame.isVarArg = true; } RequiredMatch(')'); Spaces(in); Stmt block = stmt(RequiredBlock()); RequiredKeyword("end",in); FnDef fnDef = newFnDef(start,block); frame = frame.parent; return parser.success(fnDef); } private VarArgs VarArgs(In in) throws ParseException { parser.begin(); if( !frame.isVarArg || !parser.match("...") ) return parser.failure(null); Spaces(in); return parser.success( new VarArgs() ); } private Expr TableExpr(In in) throws ParseException { parser.begin(); if( !parser.match('{') ) return parser.failure(null); Spaces(In.NOTHING); List<TableExpr.Field> fields = new ArrayList<TableExpr.Field>(); List<Expressions> builder = new ArrayList<Expressions>(); Field(fields,builder); while( FieldSep() ) { Spaces(In.NOTHING); Field(fields,builder); } Expressions exp = TemplateExpressions(In.NOTHING); if( exp != null ) builder.add(exp); if( !parser.match('}') ) throw parser.exception("Expected table element or '}'"); Spaces(in); return parser.success( new TableExpr( fields.toArray(new TableExpr.Field[0]), ExpList.build(builder) ) ); } private boolean FieldSep() throws ParseException { return parser.anyOf(",;") || EndOfLine(); } private boolean Field(List<TableExpr.Field> fields,List<Expressions> builder) throws ParseException { parser.begin(); Expr exp = SubExpr(In.NOTHING); if( exp==null ) exp = NameExpr(In.NOTHING); if( exp!=null && parser.match('=') ) { Spaces(In.NOTHING); fields.add( new TableExpr.Field( exp, required(expr(exp(ExprZ(In.NOTHING)))) ) ); return parser.success(); } parser.rollback(); Expressions exprs = exp(ExprZ(In.NOTHING)); if( exprs != null ) { builder.add(exprs); return parser.success(); } return parser.failure(); } private Expressions VarExp(In in) throws ParseException { Var var = VarZ(in); return var==null ? null : var.expr(); } private Var VarZ(In in) throws ParseException { parser.begin(); Var var = VarStart(in); if( var==null ) return parser.failure(null); Var var2; while( (var2=Var2(in,var.expr())) != null ) { var = var2; } return parser.success(var); } private Var VarStart(In in) throws ParseException { if( parser.match('(') ) { In inParens = in.parens(); Spaces(inParens); Expr exp = expr(exp(RequiredExpr(inParens))); RequiredMatch(')'); Spaces(in); return exprVar(exp); } String name = Name(in); if( name != null ) return nameVar(name); Expressions exp; exp = TableExpr(in); if( exp != null ) return exprVar(exp); exp = Literal(in); if( exp != null ) return exprVar(exp); return null; } private Var Var2(In in,Expressions exp1) throws ParseException { parser.begin(); Expr exp2 = SubExpr(in); if( exp2 != null ) return parser.success(indexVar(expr(exp1),exp2)); if( parser.match('.') ) { Spaces(in); exp2 = NameExpr(in); if( exp2!=null ) return parser.success(indexVar(expr(exp1),exp2)); return parser.failure(null); } FnCall fnCall = Args( in, expr(exp1), new ArrayList<ExpString>() ); if( fnCall != null ) return parser.success(exprVar(fnCall)); return parser.failure(null); } private interface Var { public Expressions expr() throws ParseException; public Settable settable() throws ParseException; } private Expr env() { int index = stackIndex(_ENV); if( index != -1 ) return new GetLocalVar(index); index = upValueIndex(_ENV); if( index != -1 ) return new GetUpVar(index); return null; } private Var nameVar(final String name) { return new Var() { public Expr expr() throws ParseException { int index = stackIndex(name); if( index != -1 ) return new GetLocalVar(index); index = upValueIndex(name); if( index != -1 ) return new GetUpVar(index); Expr envExpr = env(); if( envExpr != null ) return new IndexExpr( envExpr, new ConstExpr(name) ); parser.failure(null); throw parser.exception("name '"+name+"' not defined"); } public Settable settable() throws ParseException { int index = stackIndex(name); if( index != -1 ) return new SetLocalVar(index); index = upValueIndex(name); if( index != -1 ) return new SetUpVar(index); Expr envExpr = env(); if( envExpr != null ) return new SetTableEntry( envExpr, new ConstExpr(name) ); parser.failure(null); throw parser.exception("name '"+name+"' not defined"); } }; } private Var exprVar(final Expressions expr) { return new Var() { public Expressions expr() { return expr; } public Settable settable() { return null; } }; } private Var indexVar(final Expr table,final Expr key) { return new Var() { public Expr expr() { return new IndexExpr( table, key ); } public Settable settable() { return new SetTableEntry(table,key); } }; } private FnCall Args(In in,Expr fn,List<ExpString> builder) throws ParseException { parser.begin(); return args(in,builder) ? parser.success( new FnCall( fn, ExpList.build(expList(builder)) ) ) : parser.failure((FnCall)null); } private boolean args(In in,List<ExpString> builder) throws ParseException { parser.begin(); if( parser.match('(') ) { In inParens = in.parens(); Spaces(inParens); ExpList(inParens,builder); // optional if( !parser.match(')') ) throw parser.exception("Expression or ')' expected"); Spaces(in); return parser.success(); } Expr exp = TableExpr(in); if( exp != null ) { builder.add(new ExpString(exp)); return parser.success(); } String s = StringLiteral(in); if( s != null ) { builder.add( new ExpString(new ConstExpr(s)) ); return parser.success(); } /* Expressions exps = TemplateExpressions(in); if( exps != null ) { builder.add(exps); return parser.success(); } */ return parser.failure(); } private Expressions ExpList(In in) throws ParseException { List<ExpString> builder = new ArrayList<ExpString>(); return ExpList(in,builder) ? ExpList.build(expList(builder)) : null; } private ExpString ExpStringList(In in) throws ParseException { List<ExpString> builder = new ArrayList<ExpString>(); return ExpList(in,builder) ? exp(builder) : null; } private boolean ExpList(In in,List<ExpString> builder) throws ParseException { parser.begin(); Expressions exp = TemplateExpressions(in); if( exp != null ) { builder.add(new ExpString(exp)); return parser.success(); } ExpString es = ExprZ(in); if( es==null ) return parser.failure(); builder.add(es); while( parser.match(',') ) { Spaces(in); exp = TemplateExpressions(in); if( exp != null ) { builder.add(new ExpString(exp)); return parser.success(); } builder.add( RequiredExpr(in) ); } return parser.success(); } private Expr SubExpr(In in) throws ParseException { parser.begin(); if( !parser.match('[') || parser.test("[") || parser.test("=") ) return parser.failure(null); Spaces(In.NOTHING); Expr exp = expr(exp(RequiredExpr(In.NOTHING))); RequiredMatch(']'); Spaces(in); return parser.success(exp); } private Expr NameExpr(In in) throws ParseException { parser.begin(); String name = Name(in); if( name==null ) return parser.failure(null); return parser.success(new ConstExpr(name)); } private String RequiredName(In in) throws ParseException { parser.begin(); String name = Name(in); if( name==null ) throw parser.exception("Name expected"); return parser.success(name); } private String Name(In in) throws ParseException { int start = parser.begin(); if( !NameFirstChar() ) return parser.failure(null); while( NameChar() ); String match = parser.textFrom(start); if( keywords.contains(match) ) return parser.failure(null); Spaces(in); return parser.success(match); } private boolean NameChar() { return NameFirstChar() || Digit(); } private boolean NameFirstChar() { return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_'); } private void RequiredMatch(char c) throws ParseException { if( !parser.match(c) ) throw parser.exception("'"+c+"' expected"); } private void RequiredMatch(String s) throws ParseException { if( !parser.match(s) ) throw parser.exception("'"+s+"' expected"); } private void RequiredKeyword(String keyword,In in) throws ParseException { if( !Keyword(keyword,in) ) throw parser.exception("'"+keyword+"' expected"); } private boolean Keyword(String keyword,In in) throws ParseException { parser.begin(); if( !parser.match(keyword) || NameChar() ) return parser.failure(); Spaces(in); return parser.success(); } private static final Set<String> keywords = new HashSet<String>(Arrays.asList( "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" )); private Expr Literal(In in) throws ParseException { parser.begin(); if( NilLiteral(in) ) return parser.success(new ConstExpr(null)); Boolean b = BooleanLiteral(in); if( b != null ) return parser.success(new ConstExpr(b)); Number n = NumberLiteral(in); if( n != null ) return parser.success(new ConstExpr(n)); String s = StringLiteral(in); if( s != null ) return parser.success(new ConstExpr(s)); return parser.failure(null); } private boolean NilLiteral(In in) throws ParseException { return Keyword("nil",in); } private Boolean BooleanLiteral(In in) throws ParseException { if( Keyword("true",in) ) return true; if( Keyword("false",in) ) return false; return null; } private Number NumberLiteral(In in) throws ParseException { parser.begin(); Number n; if( parser.matchIgnoreCase("0x") ) { n = HexNumber(); } else { n = DecNumber(); } if( n==null || NameChar() ) return parser.failure(null); Spaces(in); return parser.success(n); } private Number DecNumber() { int start = parser.begin(); boolean isInt = true; if( Int() ) { if( parser.match('.') ) { isInt = false; Int(); // optional } } else if( parser.match('.') && Int() ) { // ok isInt = false; } else return parser.failure(null); if( Exponent() ) // optional isInt = false; String s = parser.textFrom(start); if( isInt ) { try { return parser.success(Integer.valueOf(s)); } catch(NumberFormatException e) {} try { return parser.success(Long.valueOf(s)); } catch(NumberFormatException e) {} } return parser.success(Double.valueOf(s)); } private boolean Exponent() { parser.begin(); if( !parser.matchIgnoreCase("e") ) return parser.failure(); parser.anyOf("+-"); // optional if( !Int() ) return parser.failure(); return parser.success(); } private boolean Int() { if( !Digit() ) return false; while( Digit() ); return true; } private boolean Digit() { return parser.inCharRange('0', '9'); } private Number HexNumber() { int start = parser.begin(); long nLong = 0; double n; if( HexInt() ) { nLong = Long.parseLong(parser.textFrom(start),16); n = (double)nLong; if( parser.match('.') ) { start = parser.currentIndex(); if( HexInt() ) { String dec = parser.textFrom(start); n += (double)Long.parseLong(dec,16) / Math.pow(16,dec.length()); } } } else if( parser.match('.') && HexInt() ) { String dec = parser.textFrom(start+1); n = (double)Long.parseLong(dec,16) / Math.pow(16,dec.length()); } else { return parser.failure(null); } if( parser.matchIgnoreCase("p") ) { parser.anyOf("+-"); // optional start = parser.currentIndex(); if( !HexInt() ) return parser.failure(null); n *= Math.pow(2,(double)Long.parseLong(parser.textFrom(start))); } if( nLong == n ) { int nInt = (int)nLong; if( nInt == nLong ) return parser.success(Integer.valueOf(nInt)); return parser.success(Long.valueOf(nLong)); } return parser.success(Double.valueOf(n)); } private boolean HexInt() { if( !HexDigit() ) return false; while( HexDigit() ); return true; } private boolean HexDigit() { return Digit() || parser.anyOf("abcdefABCDEF"); } private String StringLiteral(In in) throws ParseException { String s; if( (s=QuotedString('"'))==null && (s=QuotedString('\''))==null && (s=LongString())==null ) return null; Spaces(in); return s; } private String LongString() throws ParseException { parser.begin(); if( !parser.match('[') ) return parser.failure(null); int start = parser.currentIndex(); while( parser.match('=') ); int nEquals = parser.currentIndex() - start; if( !parser.match('[') ) return parser.failure(null); EndOfLine(); start = parser.currentIndex(); while( !LongBracketsEnd(nEquals) ) { if( !parser.anyChar() ) throw parser.exception("Unclosed long string"); } String s = parser.text.substring( start, parser.currentIndex() - nEquals - 2 ); return parser.success(s); } private String QuotedString(char quote) throws ParseException { parser.begin(); if( !parser.match(quote) ) return parser.failure(null); StringBuilder buf = new StringBuilder(); while( !parser.match(quote) ) { Character c = EscSeq(); if( c != null ) { buf.append(c); } else { if( parser.test('\r') || parser.test('\n') || !parser.anyChar() ) throw parser.exception("Unclosed string"); buf.append(parser.lastChar()); } } return parser.success(buf.toString()); } private Character EscSeq() { parser.begin(); if( !parser.match('\\') ) return parser.failure(null); if( parser.match('a') ) return parser.success('\u0007'); if( parser.match('b') ) return parser.success('\b'); if( parser.match('f') ) return parser.success('\f'); if( parser.match('n') ) return parser.success('\n'); if( parser.match('r') ) return parser.success('\r'); if( parser.match('t') ) return parser.success('\t'); if( parser.match('v') ) return parser.success('\u000b'); if( parser.match('\\') ) return parser.success('\\'); if( parser.match('"') ) return parser.success('"'); if( parser.match('\'') ) return parser.success('\''); int start = parser.currentIndex(); if( parser.match('x') && HexDigit() && HexDigit() ) return parser.success((char)Integer.parseInt(parser.textFrom(start+1),16)); if( Digit() ) { if( Digit() ) Digit(); // optional return parser.success((char)Integer.parseInt(parser.textFrom(start))); } if( EndOfLine() ) return parser.success('\n'); return parser.failure(null); } private void Spaces(In in) throws ParseException { while( parser.anyOf(" \t") || Comment() || ContinueOnNextLine() || in.parens && EndOfLine() ); } private boolean ContinueOnNextLine() { parser.begin(); return parser.match('\\') && EndOfLine() ? parser.success() : parser.failure(); } private boolean Comment() throws ParseException { if( LongComment() ) return true; if( parser.match("--") ) { while( parser.noneOf("\r\n") ); return true; } return false; } private boolean LongComment() throws ParseException { parser.begin(); if( !parser.match("--[") ) return parser.failure(); int start = parser.currentIndex(); while( parser.match('=') ); int nEquals = parser.currentIndex() - start; if( !parser.match('[') ) return parser.failure(); while( !LongBracketsEnd(nEquals) ) { if( !parser.anyChar() ) throw parser.exception("Unclosed comment"); } return parser.success(); } private boolean LongBracketsEnd(int nEquals) { parser.begin(); if( !parser.match(']') ) return parser.failure(); while( nEquals-- > 0 ) { if( !parser.match('=') ) return parser.failure(); } if( !parser.match(']') ) return parser.failure(); return parser.success(); } private static int classCounter = 0; private class ExpString { final String code; final boolean isExpr; final boolean isStmt; ExpString(Expressions exp) { if( exp==null ) throw new NullPointerException(); int i = $Luan.addExpressions(exp); code = "$Luan.getExpressions(" + i + ").eval($luan)"; isExpr = exp instanceof Expr; isStmt = exp instanceof StmtExp; } ExpString(String code,boolean isExpr,boolean isStmt) { this.code = code; this.isExpr = isExpr; this.isStmt = isStmt; } ExpString expr() { return isExpr ? this : new ExpString( "$Luan.first(" + code + ")", true, isStmt ); } Expressions toExpressions() { String superClass = isStmt ? "StmtExp" : "Expressions"; String className = "EXP" + ++classCounter; String classCode = "" +"package luan.impl;\n" +"import luan.LuanException;\n" +"\n" +"public class " + className +" implements " + superClass + " {\n" +" @Override public Object eval(LuanStateImpl $luan) throws LuanException {\n" +" Object $cnd;\n" +" return " + code + ";\n" +" }\n" +"}\n" ; //System.out.println(code); try { Class cls = LuanJavaCompiler.compile("luan.impl."+className,code,classCode); return (Expressions)cls.newInstance(); } catch(ClassNotFoundException e) { throw new RuntimeException(e); } catch(InstantiationException e) { throw new RuntimeException(e); } catch(IllegalAccessException e) { throw new RuntimeException(e); } } } private static Expressions exp(ExpString expStr) { return expStr==null ? null : expStr.toExpressions(); } private ExpString exp(List<ExpString> list) { switch(list.size()) { case 0: return new ExpString("LuanFunction.NOTHING",false,false); case 1: return list.get(0); default: int lastI = list.size() - 1; StringBuilder sb = new StringBuilder(); sb.append( "new Object[]{" ); for( int i=0; i<lastI; i++ ) { sb.append( list.get(i).expr().code ).append( ',' ); } ExpString last = list.get(lastI); if( last.isExpr ) { sb.append( last.code ).append( '}' ); return new ExpString(sb.toString(),false,false); } else { sb.append( '}' ); String s = "$Luan.concatArgs(" + sb + "," + last.code + ")"; return new ExpString(s,false,false); } } } private static List<Expressions> expList(List<ExpString> esList) { List<Expressions> list = new ArrayList<Expressions>(); for( ExpString es : esList ) { list.add(exp(es)); } return list; } private static class StmtString { final String code; StmtString(Stmt stmt) { if( stmt==null ) throw new NullPointerException(); int i = $Luan.addStmt(stmt); code = "$Luan.getStmt(" + i + ").eval($luan);\n"; } StmtString(String code) { this.code = code; if( code.contains("LuanParser") ) throw new RuntimeException("\n"+code); } Stmt toStmt() { String className = "EXP" + ++classCounter; String classCode = "" +"package luan.impl;\n" +"import luan.LuanFunction;\n" +"import luan.LuanException;\n" +"\n" +"public class " + className +" implements Stmt {\n" +" @Override public void eval(LuanStateImpl $luan) throws LuanException {\n" +" Object $cnd;\n" +" " + code +" }\n" +"}\n" ; //System.out.println(code); try { Class cls = LuanJavaCompiler.compile("luan.impl."+className,"code",classCode); return (Stmt)cls.newInstance(); } catch(ClassNotFoundException e) { throw new RuntimeException(e); } catch(InstantiationException e) { throw new RuntimeException(e); } catch(IllegalAccessException e) { throw new RuntimeException(e); } } } private static StmtString EMPTY_STMT = new StmtString(""); private static Stmt stmt(StmtString stmtStr) { return stmtStr==null ? null : stmtStr.toStmt(); } private static StmtString stmtStr(Stmt stmt) { return stmt==null ? null : new StmtString(stmt); } private static class SettableString { final String code; SettableString(Settable settable) { if( settable==null ) throw new NullPointerException(); int i = $Luan.addSettable(settable); code = "$Luan.getSettable(" + i + ")"; } SettableString(String code) { this.code = code; } } }