Mercurial Hosting > luan
view core/src/luan/impl/LuanParser.java @ 665:41f8fdbc3a0a
compile modules
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 07 Apr 2016 17:06:22 -0600 |
parents | 71f8f5075df8 |
children | 2f449ccf54d2 |
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,StmtString stmt) { if( !stmt.hasReturn ) stmt = new StmtString( stmt.code + "return LuanFunction.NOTHING;\n" ); return toFnDef( stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) ); } private ExpString newFnExpStr(int start,StmtString stmt) { if( !stmt.hasReturn ) stmt = new StmtString( stmt.code + "return LuanFunction.NOTHING;\n" ); return toFnExpStr( stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) ); } FnDef Expression() throws ParseException { Spaces(In.NOTHING); int start = parser.begin(); ExpString expr = ExprZ(In.NOTHING); if( expr != null && parser.endOfInput() ) { String code = "return " + expr.code + ";\n"; StmtString stmt = new StmtString(code); stmt.hasReturn = true; return parser.success(newFnDef(start,stmt)); } return parser.failure(null); } FnDef RequiredModule() throws ParseException { Spaces(In.NOTHING); int start = parser.begin(); frame.isVarArg = true; StmtString 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(); boolean isReturn = Stmt(stmts); while( !isReturn && StmtSep(stmts) ) { Spaces(In.NOTHING); isReturn = Stmt(stmts); } StmtSep(null); Spaces(In.NOTHING); 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" ; StmtString stmt = new StmtString(code); stmt.hasReturn = isReturn; return stmt; } private boolean StmtSep(StringBuilder stmts) throws ParseException { parser.begin(); if( parser.match( ';' ) ) return parser.success(); if( EndOfLine() ) return parser.success(); if( stmts != null ) { // parser.rollback(); StmtString stmt = 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 boolean Stmt(StringBuilder stmts) throws ParseException { if( LocalStmt(stmts) ) return false; StmtString stmt; if( (stmt=ReturnStmt()) != null ) { stmts.append(stmt.code); return true; } if( (stmt=FunctionStmt()) != null || (stmt=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); } return false; } private ExpString indexExpStr(ExpString exp1,ExpString exp2) { String code = "luan.index(" + exp1.expr().code + "," + exp2.expr().code + ")"; return new ExpString(code,true,false); } private ExpString callExpStr(ExpString fn,ExpString args) { String code = "Luan.checkFunction(" + fn.expr().code + ").call(luan,Luan.array(" + args.code + "))"; return new ExpString(code,false,true); } private StmtString TemplateStmt() throws ParseException { ExpString exprs = TemplateExpressions(In.NOTHING); if( exprs == null ) return null; String code = "PackageLuan.require(luan,\"luan:Io\")"; ExpString requireCall = new ExpString(code,true,false); ExpString stdoutExp = indexExpStr( requireCall.expr(), constExpStr("stdout") ); ExpString writeExp = indexExpStr( stdoutExp, constExpStr("write") ); ExpString writeCall = callExpStr( writeExp, exprs ); return new StmtString( writeCall.code + ";\n" ); } private ExpString 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<ExpString> builder = new ArrayList<ExpString>(); while(true) { if( parser.match( "<%=" ) ) { Spaces(inTemplate); builder.add( RequiredExpr(inTemplate) ); RequiredMatch( "%>" ); } else if( parser.match( "<%" ) ) { Spaces(inTemplate); return parser.success(expString(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( constExpStr(match) ); } } } private StmtString ReturnStmt() throws ParseException { parser.begin(); if( !Keyword("return",In.NOTHING) ) return parser.failure(null); ExpString exprs = ExpStringList(In.NOTHING); String code = "return " + (exprs!=null ? exprs.code : "LuanFunction.NOTHING") + ";\n"; return parser.success( new StmtString(code) ); } private StmtString 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); ExpString exp = NameExpr(In.NOTHING); if( exp==null ) return parser.failure(null); var = indexVar( var.exp(), exp ); } ExpString fnDef = RequiredFunction(In.NOTHING); return parser.success( var.set(fnDef) ); } private StmtString 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 ); ExpString fnDef = RequiredFunction(In.NOTHING); Settable s = new SetLocalVar(symbolsSize()-1); String code = new SettableString(s).code + ".set(luan," + fnDef.code + ");\n"; return parser.success( new StmtString(code) ); } 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" +"LuanImpl.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 = "LuanImpl.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<Var> vars = new ArrayList<Var>(); Var v = SettableVar(); if( v == null ) return parser.failure(null); vars.add(v); while( parser.match( ',' ) ) { Spaces(In.NOTHING); v = SettableVar(); if( v == null ) return parser.failure(null); vars.add(v); } 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); int n = vars.size(); if( n == 1 ) return parser.success( vars.get(0).set(values) ); StringBuilder sb = new StringBuilder(); sb.append( "t = " + values.code + ";\n" ); ExpString t = new ExpString("t",true,false); sb.append( vars.get(0).set(new ExpString("t",true,false)).code ); for( int i=1; i<n; i++ ) { sb.append( vars.get(i).set(new ExpString("LuanImpl.pick(t,"+i+")",true,false)).code ); } return parser.success( new StmtString(sb.toString()) ); } 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 = "LuanImpl.nop(" + code + ")"; code += ";\n"; return parser.success( new StmtString(code) ); } return parser.failure(null); } private Var SettableVar() throws ParseException { int start = parser.begin(); Var var = VarZ(In.NOTHING); if( var==null || !var.isSettable() ) return parser.failure(null); return parser.success( var ); } 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 = "(LuanImpl.cnd(t = " + exp.code + ") ? t : (" + 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 = "(LuanImpl.cnd(t = " + exp.code + ") ? (" + exp2.code + ") : t)"; 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 = "LuanImpl.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 = "!LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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 = "LuanImpl.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.checkBoolean(" + 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(); ExpString exp1 = SingleExpr(in); if( exp1==null ) return parser.failure(null); if( parser.match('^') ) { Spaces(in); ExpString exp2 = required(PowExpr(in)); String code = "LuanImpl.pow(luan," + exp1.code + "," + exp2.code + ")"; exp1 = new ExpString(code,true,false); } return parser.success(exp1); } private ExpString SingleExpr(In in) throws ParseException { parser.begin(); ExpString es = FunctionExpr(in); if( es != null ) return parser.success(es); es = VarExp(in); if( es != null ) return parser.success(es); Expressions exp = VarArgs(in); if( exp != null ) return parser.success(new ExpString(exp)); return parser.failure(null); } private ExpString FunctionExpr(In in) throws ParseException { if( !Keyword("function",in) ) return null; return RequiredFunction(in); } private ExpString 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); StmtString block = RequiredBlock(); RequiredKeyword("end",in); ExpString fnDef = newFnExpStr(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 ExpString TableExpr(In in) throws ParseException { parser.begin(); if( !parser.match('{') ) return parser.failure(null); Spaces(In.NOTHING); List<ExpString> builder = new ArrayList<ExpString>(); Field(builder); while( FieldSep() ) { Spaces(In.NOTHING); Field(builder); } ExpString exp = TemplateExpressions(In.NOTHING); if( exp != null ) builder.add(exp); if( !parser.match('}') ) throw parser.exception("Expected table element or '}'"); Spaces(in); String code = "LuanImpl.table(" + expString(builder).code + ")"; return parser.success( new ExpString(code,true,false) ); } private boolean FieldSep() throws ParseException { return parser.anyOf(",;") || EndOfLine(); } private boolean Field(List<ExpString> builder) throws ParseException { parser.begin(); ExpString exp = SubExpr(In.NOTHING); if( exp==null ) exp = NameExpr(In.NOTHING); if( exp!=null && parser.match('=') ) { Spaces(In.NOTHING); ExpString val = RequiredExpr(In.NOTHING).expr(); String code = "new TableField(" + exp.code + "," + val.code + ")"; builder.add( new ExpString(code,true,false) ); return parser.success(); } parser.rollback(); ExpString exprs = ExprZ(In.NOTHING); if( exprs != null ) { builder.add(exprs); return parser.success(); } return parser.failure(); } private ExpString VarExp(In in) throws ParseException { Var var = VarZ(in); return var==null ? null : var.exp(); } 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.exp())) != null ) { var = var2; } return parser.success(var); } private Var VarStart(In in) throws ParseException { if( parser.match('(') ) { In inParens = in.parens(); Spaces(inParens); ExpString exp = RequiredExpr(inParens).expr(); RequiredMatch(')'); Spaces(in); return exprVar(exp); } String name = Name(in); if( name != null ) return nameVar(name); ExpString 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,ExpString exp1) throws ParseException { parser.begin(); ExpString exp2 = SubExpr(in); if( exp2 != null ) return parser.success(indexVar(exp1,exp2)); if( parser.match('.') ) { Spaces(in); exp2 = NameExpr(in); if( exp2!=null ) return parser.success(indexVar(exp1,exp2)); return parser.failure(null); } ExpString fnCall = Args( in, exp1, new ArrayList<ExpString>() ); if( fnCall != null ) return parser.success(exprVar(fnCall)); return parser.failure(null); } private interface Var { public ExpString exp() throws ParseException; // public Settable settable() throws ParseException; public boolean isSettable(); public StmtString set(ExpString val) 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 ExpString exp() throws ParseException { int index = stackIndex(name); if( index != -1 ) return new ExpString(new GetLocalVar(index)); index = upValueIndex(name); if( index != -1 ) return new ExpString(new GetUpVar(index)); Expr envExpr = env(); if( envExpr != null ) return indexExpStr( new ExpString(envExpr), constExpStr(name) ); parser.failure(null); throw parser.exception("name '"+name+"' not defined"); } public boolean isSettable() { return true; } private 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"); } public StmtString set(ExpString val) throws ParseException { String code = new SettableString(settable()).code + ".set(luan," + val.expr().code + ");\n"; return new StmtString(code); } }; } private Var exprVar(final ExpString expr) { return new Var() { public ExpString exp() { return expr; } public boolean isSettable() { return false; } public StmtString set(ExpString val) { throw new RuntimeException(); } }; } private Var indexVar(final ExpString table,final ExpString key) { return new Var() { public ExpString exp() { return indexExpStr( table, key ); } public boolean isSettable() { return true; } /* public Settable settable() { return new SetTableEntry(expr(LuanParser.exp(table)),expr(LuanParser.exp(key))); } */ public StmtString set(ExpString val) { String code = "LuanImpl.put(luan," + table.expr().code + "," + key.expr().code + "," + val.expr().code + ");\n"; return new StmtString(code); } }; } private ExpString Args(In in,ExpString fn,List<ExpString> builder) throws ParseException { parser.begin(); return args(in,builder) ? parser.success( callExpStr( fn, expString(builder) ) ) : parser.failure((ExpString)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(); } ExpString exp = TableExpr(in); if( exp != null ) { builder.add(exp); return parser.success(); } String s = StringLiteral(in); if( s != null ) { builder.add( constExpStr(s) ); return parser.success(); } /* Expressions exps = TemplateExpressions(in); if( exps != null ) { builder.add(exps); return parser.success(); } */ return parser.failure(); } private ExpString ExpStringList(In in) throws ParseException { List<ExpString> builder = new ArrayList<ExpString>(); return ExpList(in,builder) ? expString(builder) : null; } private boolean ExpList(In in,List<ExpString> builder) throws ParseException { parser.begin(); ExpString exp = TemplateExpressions(in); if( exp != null ) { builder.add(exp); return parser.success(); } exp = ExprZ(in); if( exp==null ) return parser.failure(); builder.add(exp); while( parser.match(',') ) { Spaces(in); exp = TemplateExpressions(in); if( exp != null ) { builder.add(exp); return parser.success(); } builder.add( RequiredExpr(in) ); } return parser.success(); } private ExpString SubExpr(In in) throws ParseException { parser.begin(); if( !parser.match('[') || parser.test("[") || parser.test("=") ) return parser.failure(null); Spaces(In.NOTHING); ExpString exp = RequiredExpr(In.NOTHING).expr(); RequiredMatch(']'); Spaces(in); return parser.success(exp); } private ExpString NameExpr(In in) throws ParseException { parser.begin(); String name = Name(in); if( name==null ) return parser.failure(null); return parser.success(constExpStr(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 ExpString Literal(In in) throws ParseException { parser.begin(); if( NilLiteral(in) ) return parser.success(new ExpString("null",true,false)); Boolean b = BooleanLiteral(in); if( b != null ) return parser.success(new ExpString(b.toString(),true,false)); Number n = NumberLiteral(in); if( n != null ) { String s = n.toString(); if( n instanceof Long ) s += "L"; return parser.success(new ExpString(s,true,false)); } String s = StringLiteral(in); if( s != null ) return parser.success(constExpStr(s)); return parser.failure(null); } private ExpString constExpStr(String s) { s = s .replace("\\","\\\\") .replace("\"","\\\"") .replace("\n","\\n") .replace("\r","\\r") .replace("\t","\\t") .replace("\b","\\b") ; return new ExpString( "\""+s+"\"", true,false); } 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 = LuanImpl.addObj(exp); code = "((Expressions)LuanImpl.getObj(" + 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 ); } } private ExpString expString(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 = "LuanImpl.concatArgs(" + sb + "," + last.code + ")"; return new ExpString(s,false,false); } } } private static class StmtString { final String code; boolean hasReturn = false; StmtString(String code) { this.code = code; if( code.contains("LuanParser") ) throw new RuntimeException("\n"+code); } } private static StmtString EMPTY_STMT = new StmtString(""); private static FnDef toFnDef(StmtString stmt,int stackSize,int numArgs,boolean isVarArg,UpValue.Getter[] upValueGetters) { int i = LuanImpl.addObj(upValueGetters); String className = "EXP" + ++classCounter; String classCode = "" +"package luan.impl;\n" +"import luan.Luan;\n" +"import luan.LuanFunction;\n" +"import luan.LuanException;\n" +"import luan.modules.PackageLuan;\n" +"\n" +"public class " + className +" extends FnDef {\n" +" public "+className+"() {\n" +" super("+stackSize+","+numArgs+","+isVarArg+",(UpValue.Getter[])LuanImpl.getObj("+i+"));\n" +" }\n" +"\n" +" @Override public Object run(LuanStateImpl luan) throws LuanException {\n" +" Object t;\n" +" " + stmt.code +" }\n" +"}\n" ; try { //System.out.println(classCode); Class cls = LuanJavaCompiler.compile("luan.impl."+className,"code",classCode); return (FnDef)cls.newInstance(); } catch(ClassNotFoundException e) { throw new RuntimeException(e); } catch(InstantiationException e) { throw new RuntimeException(e); } catch(IllegalAccessException e) { throw new RuntimeException(e); } } private ExpString toFnExpStr(StmtString stmt,int stackSize,int numArgs,boolean isVarArg,UpValue.Getter[] upValueGetters) { int i = LuanImpl.addObj(upValueGetters); String classCode = "" +"\n" +"new FnDef("+stackSize+","+numArgs+","+isVarArg+",(UpValue.Getter[])LuanImpl.getObj("+i+")) {\n" +" @Override public Object run(LuanStateImpl luan) throws LuanException {\n" +" Object t;\n" +" " + stmt.code +" }\n" +"}.eval(luan)\n" ; return new ExpString(classCode,true,false); } private static class SettableString { final String code; SettableString(Settable settable) { if( settable==null ) throw new NullPointerException(); int i = LuanImpl.addObj(settable); code = "((Settable)LuanImpl.getObj(" + i + "))"; } SettableString(String code) { this.code = code; } } }