view core/src/luan/impl/LuanParser.java @ 670:58ebfec6178b

all luan now compiles
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 12 Apr 2016 01:05:57 -0600
parents e320488819b6
children 82f130eba7b0
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 luan.Luan;
import luan.LuanState;
import luan.LuanTable;
import luan.modules.PackageLuan;


final class LuanParser {

	private interface Sym {
		public ExpString exp();
	}

	private int symCounter = 0;

	private class LocalSym implements Sym {
		final String name;
		final String javaName;
		String pointer = null;

		LocalSym(String name) {
			this.name = name;
			this.javaName = name + "_" + (++symCounter);
		}

		String declaration() {
			if( pointer==null )
				return "Object " + javaName + " = null;\n";
			else
				return "final Pointer " + javaName + " = " + pointer + ";\n";
		}

		String ref() {
			if( pointer==null )
				return javaName;
			else
				return javaName + ".o";
		}

		@Override public ExpString exp() {
			ExpString exp = new ExpString(true,false);
			exp.list.add( new Object() {
				@Override public String toString() {
					return ref();
				}
			} );
			return exp;
		}
	}

	private class UpSym implements Sym {
		final String name;
		final int i;
		final String value;

		UpSym(String name,int i,String value) {
			this.name = name;
			this.i = i;
			this.value = value;
		}

		String init() {
			return "upValues[" + i + "] = " + value + ";\n";
		}

		String ref() {
			return "upValues[" + i + "].o";
		}

		@Override public ExpString exp() {
			ExpString exp = new ExpString(true,false);
			exp.list.add( new Object() {
				@Override public String toString() {
					return ref();
				}
			} );
			return exp;
		}
	}

	private final class Frame {
		final Frame parent;
		final List<LocalSym> symbols = new ArrayList<LocalSym>();
		int loops = 0;
		boolean isVarArg = false;
		final List<UpSym> upValueSymbols = new ArrayList<UpSym>();

		Frame() {
			this.parent = null;
		}

		Frame(Frame parent) {
			this.parent = parent;
		}

		LocalSym addLocalSym(String name) {
			LocalSym sym = new LocalSym(name);
			symbols.add(sym);
			return sym;
		}

		UpSym addUpSym(String name,String value) {
			UpSym sym = new UpSym( name, upValueSymbols.size(), value );
			upValueSymbols.add(sym);
			return sym;
		}

		LocalSym getLocalSym(String name) {
			int i = symbols.size();
			while( --i >= 0 ) {
				LocalSym sym = symbols.get(i);
				if( sym.name.equals(name) )
					return sym;
			}
			return null;
		}

		UpSym getUpSym(String name) {
			for( UpSym upSym : upValueSymbols ) {
				if( upSym.name.equals(name) )
					return upSym;
			}
			for( Frame f=parent; f!=null; f=f.parent ) {
				LocalSym sym = f.getLocalSym(name);
				if( sym != null ) {
					sym.pointer = "new Pointer()";
					return addUpSym(name,sym.javaName);
				}
			}
			return null;
		}

		Sym getSym(String name) {
			Sym sym = getLocalSym(name);
			return sym != null ? sym : getUpSym(name);
		}

	}

	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);
		}
	}

//	final LuanSource source;
	private Frame frame;
	private final Parser parser;
	private final StmtString top = new StmtString();

	LuanParser(String sourceName,String sourceText) {
//		this.source = source;
		this.frame = new Frame();
		this.parser = new Parser(sourceName,sourceText);
	}

	void addVar(String name) {
		UpSym upSym = frame.addUpSym( "-ADDED-" ,"new Pointer()");
		if( name != null ) {
			LocalSym sym = frame.addLocalSym( name );
			sym.pointer = "upValues[" + upSym.i + "]";
			top.list.add( sym.declaration() );
		}
	}

	private int symbolsSize() {
		return frame.symbols.size();
	}

	private void addSymbol(String name,StmtString stmt) {
		final LocalSym sym = frame.addLocalSym(name);
		stmt.list.add( new Object() {
			@Override public String toString() {
				return sym.declaration();
			}
		} );
	}

	private void addSymbols(List<String> names,StmtString stmt) {
		for( String name : names ) {
			addSymbol(name,stmt);
		}
	}

	private Sym getSym(String name) {
		return frame.getSym(name);
	}

	private void popSymbols(int n) {
		List<LocalSym> symbols = frame.symbols;
		while( n-- > 0 ) {
			symbols.remove(symbols.size()-1);
		}
	}

	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 Class newFnClass(int start,StmtString stmt) {
		if( !stmt.hasReturn )
			stmt.list.add( "return LuanFunction.NOTHING;\n" );
		return toFnClass( stmt, frame.upValueSymbols );
	}

	private ExpString newFnExpStr(int start,StmtString stmt) {
		if( !stmt.hasReturn )
			stmt.list.add( "return LuanFunction.NOTHING;\n" );
		return toFnExpStr( stmt, frame.upValueSymbols );
	}

	Class Expression() throws ParseException {
		Spaces(In.NOTHING);
		int start = parser.begin();
		ExpString expr = ExprZ(In.NOTHING);
		if( expr != null && parser.endOfInput() ) {
			top.list.add( "return " );
			top.list.addAll( expr.list );
			top.list.add( ";\n" );
			top.hasReturn = true;
			return parser.success(newFnClass(start,top));
		}
		return parser.failure(null);
	}

	Class RequiredModule() throws ParseException {
		Spaces(In.NOTHING);
		int start = parser.begin();
		frame.isVarArg = true;
		top.list.add( "final Object[] varArgs = LuanImpl.varArgs(args,0);\n" );
		StmtString block = RequiredBlock();
		top.list.addAll( block.list );
		top.hasReturn = block.hasReturn;
		if( parser.endOfInput() )
			return parser.success(newFnClass(start,top));
		throw parser.exception();
	}

	private StmtString RequiredBlock() throws ParseException {
		StmtString stmts = new StmtString();
		int stackStart = symbolsSize();
		boolean isReturn = Stmt(stmts);
		while( !isReturn && StmtSep(stmts) ) {
			Spaces(In.NOTHING);
			isReturn = Stmt(stmts);
		}
		while( StmtSep(null) )
			Spaces(In.NOTHING);
		int stackEnd = symbolsSize();
		popSymbols( stackEnd - stackStart );
		stmts.hasReturn = isReturn;
		return stmts;
	}

	private boolean StmtSep(StmtString 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.list.addAll(stmt.list);
				return parser.success();
			}
		}
		return parser.failure();
	}

	private boolean EndOfLine() {
		return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' );
	}

	private boolean Stmt(StmtString stmts) throws ParseException {
		StmtString stmt;
		if( (stmt=ReturnStmt()) != null ) {
			stmts.list.addAll(stmt.list);
			return true;
		}
		if( (stmt=FunctionStmt()) != null
			|| (stmt=LocalStmt()) != 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.list.addAll(stmt.list);
		}
		return false;
	}

	private ExpString indexExpStr(ExpString exp1,ExpString exp2) {
		ExpString exp = new ExpString(true,false);
		exp.list.add( "luan.index(" );
		exp.list.addAll( exp1.expr().list );
		exp.list.add( "," );
		exp.list.addAll( exp2.expr().list );
		exp.list.add( ")" );
		return exp;
	}

	private ExpString callExpStr(ExpString fn,ExpString args) {
		ExpString exp = new ExpString(false,true);
		exp.list.add( "Luan.checkFunction(" );
		exp.list.addAll( fn.expr().list );
		exp.list.add( ").call(luan,Luan.array(" );
		exp.list.addAll( args.list );
		exp.list.add( "))" );
		return exp;
	}

	private StmtString TemplateStmt() throws ParseException {
		ExpString exprs = TemplateExpressions(In.NOTHING);
		if( exprs == null )
			return null;
		ExpString requireCall = new ExpString(true,false);
		requireCall.list.add( "PackageLuan.require(luan,\"luan:Io\")" );
		ExpString stdoutExp = indexExpStr( requireCall.expr(), constExpStr("stdout") );
		ExpString writeExp = indexExpStr( stdoutExp, constExpStr("write") );
		ExpString writeCall = callExpStr( writeExp, exprs );
		StmtString stmt = new StmtString();
		stmt.list.addAll( writeCall.list );
		stmt.list.add( ";\n" );
		return stmt;
	}

	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);
		StmtString stmt = new StmtString();
		stmt.list.add( "return " );
		if( exprs != null )
			stmt.list.addAll( exprs.list );
		else
			stmt.list.add( "LuanFunction.NOTHING" );
		stmt.list.add( ";\n" );
		return parser.success( stmt );
	}

	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);
		StmtString stmt = new StmtString();
		String name = RequiredName(In.NOTHING);
		addSymbol( name, stmt );
		ExpString fnDef = RequiredFunction(In.NOTHING);
		stmt.list.addAll( nameVar(name).set( fnDef ).list );
/*
		Settable s = new SetLocalVar(symbolsSize()-1);
		StmtString stmt = new StmtString();
		stmt.list.add( settableToString(s) );
		stmt.list.add( ".set(luan," );
		stmt.list.addAll( fnDef.list );
		stmt.list.add( ");\n" );
*/
		return parser.success( stmt );
	}

	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");
		StmtString stmt = new StmtString();
		stmt.list.add( "break;\n" );
		return parser.success( stmt );
	}

	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);

		String fnVar = "fn"+ ++forCounter;
		ExpString fnExp = new ExpString(false,false);
		fnExp.list.add( fnVar + ".call(luan)" );
		StmtString stmt = new StmtString();
		stmt.list.add( ""
			+"LuanFunction "+fnVar+" = Luan.checkFunction("
		);
		stmt.list.addAll( expr.list );
		stmt.list.add( ");\n" );
		stmt.list.add( "while(true) {\n" );
		List<Var> vars = new ArrayList<Var>();
		for( String name : names ) {
			addSymbol(name, stmt);
			vars.add(nameVar(name));
		}
		ExpString firstVar = vars.get(0).exp();
		stmt.list.addAll( makeSetStmt(vars,fnExp).list );
		stmt.list.add( "if( " );
		stmt.list.addAll( firstVar.list );
 		stmt.list.add( "==null )  break;\n" );
		StmtString loop = RequiredLoopBlock();
		RequiredKeyword("end",In.NOTHING);
		stmt.list.addAll( loop.list );
		stmt.list.add( "}\n" );
		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 StmtString LocalStmt() throws ParseException {
		parser.begin();
		if( !Keyword("local",In.NOTHING) )
			return parser.failure(null);
		List<String> names = NameList(In.NOTHING);
		if( names==null ) {
			if( Keyword("function",In.NOTHING) )
				return parser.failure(null);  // handled later
			throw parser.exception("Invalid local statement");
		}
		StmtString stmt = new StmtString();
		addSymbols(names,stmt);
		if( parser.match( '=' ) ) {
			Spaces(In.NOTHING);
			ExpString values = ExpStringList(In.NOTHING);
			if( values==null )
				throw parser.exception("Expressions expected");
			List<Var> vars = new ArrayList<Var>();
			for( String name : names ) {
				vars.add(nameVar(name));
			}
			stmt.list.addAll( makeSetStmt(vars,values).list );
		}
		return parser.success(stmt);
	}

	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);
		StmtString stmt = new StmtString();
		stmt.list.add( "while( Luan.checkBoolean(" );
		stmt.list.addAll( cnd.list );
		stmt.list.add( ") ) {\n" );
		stmt.list.addAll( loop.list );
		stmt.list.add( "}\n" );
		return parser.success( stmt );
	}

	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();
		StmtString stmt = new StmtString();
		stmt.list.add( "do {\n" );
		stmt.list.addAll( loop.list );
		stmt.list.add( "} while( !Luan.checkBoolean(" );
		stmt.list.addAll( cnd.list );
		stmt.list.add( ") );\n" );
		return parser.success( stmt );
	}

	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);
		StmtString stmt = new StmtString();
		ExpString cnd;
		StmtString block;
		cnd = RequiredExpr(In.NOTHING).expr();
		RequiredKeyword("then",In.NOTHING);
		block = RequiredBlock();
		stmt.list.add( "if( Luan.checkBoolean(" );
		stmt.list.addAll( cnd.list );
		stmt.list.add( ") ) {\n" );
		stmt.list.addAll( block.list );
		while( Keyword("elseif",In.NOTHING) ) {
			cnd = RequiredExpr(In.NOTHING).expr();
			RequiredKeyword("then",In.NOTHING);
			block = RequiredBlock();
			stmt.list.add( "} else if( Luan.checkBoolean(" );
			stmt.list.addAll( cnd.list );
			stmt.list.add( ") ) {\n" );
			stmt.list.addAll( block.list );
		}
		if( Keyword("else",In.NOTHING) ) {
			block = RequiredBlock();
			stmt.list.add( "} else {\n" );
			stmt.list.addAll( block.list );
		}
		RequiredKeyword("end",In.NOTHING);
		stmt.list.add( "}\n" );
		return parser.success( stmt );
	}

	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);
		return parser.success( makeSetStmt(vars,values) );
	}

	private StmtString makeSetStmt(List<Var> vars,ExpString values) throws ParseException {
		int n = vars.size();
		if( n == 1 )
			return vars.get(0).set(values);
		StmtString stmt = new StmtString();
		stmt.list.add( "t = " );
		stmt.list.addAll( values.list );
		stmt.list.add( ";\n" );
		ExpString t = new ExpString(values.isExpr,false);
		t.list.add( "t" );
		stmt.list.addAll( vars.get(0).set(t).list );
		for( int i=1; i<n; i++ ) {
			t.list.clear();
			t.list.add( "LuanImpl.pick(t,"+i+")" );
			stmt.list.addAll( vars.get(i).set(t).list );
		}
		return stmt;
	}
/*
	private static String varsToString(Settable[] vars) {
		StringBuilder sb = new StringBuilder();
		sb.append( "new Settable[]{" );
		for( Settable v : vars ) {
			sb.append( settableToString(v) ).append( ',' );
		}
		sb.append( "}" );
		return sb.toString();
	}
*/
	private StmtString ExpressionsStmt() throws ParseException {
		parser.begin();
		ExpString exp = ExprZ(In.NOTHING);
		if( exp != null && exp.isStmt ) {
			StmtString stmt = new StmtString();
			if( exp.isExpr ) {
				stmt.list.add( "LuanImpl.nop(" );
				stmt.list.addAll( exp.list );
				stmt.list.add( ")" );
			} else {
				stmt.list.addAll( exp.list );
			}
			stmt.list.add( ";\n" );
			return parser.success( stmt );
		}
		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();
			ExpString newExp = new ExpString(true,true);
			newExp.list.add( "(LuanImpl.cnd(t = " );
			newExp.list.addAll( exp.list );
			newExp.list.add( ") ? t : (" );
			newExp.list.addAll( exp2.list );
			newExp.list.add( "))" );
			exp = newExp;
		}
		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();
			ExpString newExp = new ExpString(true,true);
			newExp.list.add( "(LuanImpl.cnd(t = " );
			newExp.list.addAll( exp.list );
			newExp.list.add( ") ? (" );
			newExp.list.addAll( exp2.list );
			newExp.list.add( ") : t)" );
			exp = newExp;
		}
		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();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.eq(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} else if( parser.match("~=") ) {
				Spaces(in);
				exp = exp.expr();
				ExpString exp2 = required(ConcatExpr(in)).expr();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "!LuanImpl.eq(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} else if( parser.match("<=") ) {
				Spaces(in);
				exp = exp.expr();
				ExpString exp2 = required(ConcatExpr(in)).expr();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.le(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} else if( parser.match(">=") ) {
				Spaces(in);
				exp = exp.expr();
				ExpString exp2 = required(ConcatExpr(in)).expr();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.le(luan," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp.list );
				newExp.list.add( ")" );
				exp = newExp;
			} else if( parser.match("<") ) {
				Spaces(in);
				exp = exp.expr();
				ExpString exp2 = required(ConcatExpr(in)).expr();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.lt(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} else if( parser.match(">") ) {
				Spaces(in);
				exp = exp.expr();
				ExpString exp2 = required(ConcatExpr(in)).expr();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.lt(luan," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp.list );
				newExp.list.add( ")" );
				exp = newExp;
			} 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();
			ExpString newExp = new ExpString(true,false);
			newExp.list.add( "LuanImpl.concat(luan," );
			newExp.list.addAll( exp.list );
			newExp.list.add( "," );
			newExp.list.addAll( exp2.list );
			newExp.list.add( ")" );
			exp = newExp;
		}
		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();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.add(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} else if( Minus() ) {
				Spaces(in);
				exp = exp.expr();
				ExpString exp2 = required(TermExpr(in)).expr();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.sub(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} 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();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.mul(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} else if( parser.match('/') ) {
				Spaces(in);
				exp = exp.expr();
				ExpString exp2 = required(UnaryExpr(in)).expr();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.div(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} else if( Mod() ) {
				Spaces(in);
				exp = exp.expr();
				ExpString exp2 = required(UnaryExpr(in)).expr();
				ExpString newExp = new ExpString(true,false);
				newExp.list.add( "LuanImpl.mod(luan," );
				newExp.list.addAll( exp.list );
				newExp.list.add( "," );
				newExp.list.addAll( exp2.list );
				newExp.list.add( ")" );
				exp = newExp;
			} 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();
			ExpString newExp = new ExpString(true,false);
			newExp.list.add( "LuanImpl.len(luan," );
			newExp.list.addAll( exp.list );
			newExp.list.add( ")" );
			return parser.success(newExp);
		}
		if( Minus() ) {
			Spaces(in);
			ExpString exp = required(UnaryExpr(in)).expr();
			ExpString newExp = new ExpString(true,false);
			newExp.list.add( "LuanImpl.unm(luan," );
			newExp.list.addAll( exp.list );
			newExp.list.add( ")" );
			return parser.success(newExp);
		}
		if( Keyword("not",in) ) {
			Spaces(in);
			ExpString exp = required(UnaryExpr(in)).expr();
			ExpString newExp = new ExpString(true,false);
			newExp.list.add( "!Luan.checkBoolean(" );
			newExp.list.addAll( exp.list );
			newExp.list.add( ")" );
			return parser.success(newExp);
		}
		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));
			ExpString newExp = new ExpString(true,false);
			newExp.list.add( "LuanImpl.pow(luan," );
			newExp.list.addAll( exp1.expr().list );
			newExp.list.add( "," );
			newExp.list.addAll( exp2.expr().list );
			newExp.list.add( ")" );
			exp1 = newExp;
		}
		return parser.success(exp1);
	}

	private ExpString SingleExpr(In in) throws ParseException {
		parser.begin();
		ExpString 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 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);
		StmtString stmt = new StmtString();
		List<String> names = NameList(in);
		if( names != null ) {
			addSymbols(names,stmt);
			List<Var> vars = new ArrayList<Var>();
			for( String name : names ) {
				vars.add(nameVar(name));
			}
			ExpString args = new ExpString(false,false);
			args.list.add( "args" );
			stmt.list.addAll( makeSetStmt(vars,args).list );
			if( parser.match(',') ) {
				Spaces(inParens);
				if( !parser.match("...") )
					throw parser.exception();
				Spaces(inParens);
				frame.isVarArg = true;
				stmt.list.add( "final Object[] varArgs = LuanImpl.varArgs(args," + vars.size() + ");\n" );
			}
		} else if( parser.match("...") ) {
			Spaces(inParens);
			frame.isVarArg = true;
			stmt.list.add( "final Object[] varArgs = LuanImpl.varArgs(args,0);\n" );
		}
		RequiredMatch(')');
		Spaces(in);
		StmtString block = RequiredBlock();
		stmt.list.addAll( block.list );
		stmt.hasReturn = block.hasReturn;
		RequiredKeyword("end",in);
		ExpString fnDef = newFnExpStr(start,stmt);
		frame = frame.parent;
		return parser.success(fnDef);
	}

	private ExpString VarArgs(In in) throws ParseException {
		parser.begin();
		if( !frame.isVarArg || !parser.match("...") )
			return parser.failure(null);
		Spaces(in);
		ExpString exp = new ExpString(false,false);
		exp.list.add("varArgs");
		return parser.success(exp);
	}

	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);
		exp = new ExpString(true,false);
		exp.list.add( "LuanImpl.table(" );
		exp.list.addAll( expString(builder).list );
		exp.list.add( ")" );
		return parser.success( exp );
	}

	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();
			ExpString newExp = new ExpString(true,false);
			newExp.list.add( "new TableField(" );
			newExp.list.addAll( exp.list );
			newExp.list.add( "," );
			newExp.list.addAll( val.list );
			newExp.list.add( ")" );
			builder.add( newExp );
			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 ExpString env() {
		Sym sym = getSym("_ENV");
		if( sym != null )
			return sym.exp();
		return null;
	}

	private Var nameVar(final String name) {
		return new Var() {

			public ExpString exp() throws ParseException {
				Sym sym = getSym(name);
				if( sym != null )
					return sym.exp();
				ExpString envExpr = env();
				if( envExpr != null )
					return indexExpStr( 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 {
				Sym sym = getSym(name);
				if( sym != null ) {
					StmtString stmt = new StmtString();
					stmt.list.addAll( sym.exp().list );
					stmt.list.add( " = " );
					stmt.list.addAll( val.expr().list );
					stmt.list.add( ";\n" );
					return stmt;
				}
				ExpString envExpr = env();
				if( envExpr != null )
					return indexVar( envExpr, constExpStr(name) ).set(val);
				throw new RuntimeException();
			}
		};
	}

	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) {
				StmtString stmt = new StmtString();
				stmt.list.add( "LuanImpl.put(luan," );
				stmt.list.addAll( table.expr().list );
				stmt.list.add( "," );
				stmt.list.addAll( key.expr().list );
				stmt.list.add( "," );
				stmt.list.addAll( val.expr().list );
				stmt.list.add( ");\n" );
				return stmt;
			}
		};
	}

	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) ) {
			ExpString exp = new ExpString(true,false);
			exp.list.add( "null" );
			return parser.success(exp);
		}
		Boolean b = BooleanLiteral(in);
		if( b != null ) {
			ExpString exp = new ExpString(true,false);
			exp.list.add( b.toString() );
			return parser.success(exp);
		}
		Number n = NumberLiteral(in);
		if( n != null ) {
			String s = n.toString();
			if( n instanceof Long )
				s += "L";
			ExpString exp = new ExpString(true,false);
			exp.list.add( s );
			return parser.success(exp);
		}
		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")
		;
		ExpString exp = new ExpString(true,false);
		exp.list.add( "\""+s+"\"" );
		return exp;
	}

	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 List list = new ArrayList();
		final boolean isExpr;
		final boolean isStmt;
/*
		ExpString(Expressions exp) {
			if( exp==null )  throw new NullPointerException();
			int i = LuanImpl.addObj(exp);
			list.add( "((Expressions)LuanImpl.getObj(" + i + ")).eval(luan)" );
			isExpr = exp instanceof Expr;
			isStmt = exp instanceof StmtExp;
		}
*/
		ExpString(boolean isExpr,boolean isStmt) {
			this.isExpr = isExpr;
			this.isStmt = isStmt;
		}

		ExpString expr() {
			if( isExpr )
				return this;
			ExpString exp = new ExpString(true,isStmt);
			exp.list.add( "Luan.first(" );
			exp.list.addAll( list );
			exp.list.add( ")" );
			return exp;
		}
	}

	private ExpString expString(List<ExpString> list) {
		ExpString exp = new ExpString(false,false);
		switch(list.size()) {
		case 0:
			exp.list.add("LuanFunction.NOTHING");
			return exp;
		case 1:
			return list.get(0);
		default:
			int lastI = list.size() - 1;
			exp.list.add( "new Object[]{" );
			for( int i=0; i<lastI; i++ ) {
				exp.list.addAll( list.get(i).expr().list );
				exp.list.add( "," );
			}
			ExpString last = list.get(lastI);
			if( last.isExpr ) {
				exp.list.addAll( last.list );
				exp.list.add( "}" );
			} else {
				exp.list.add( "}" );
				exp.list.add( 0, "LuanImpl.concatArgs(" );
				exp.list.add( "," );
				exp.list.addAll( last.list );
				exp.list.add( ")" );
			}
			return exp;
		}
	}

	private static class StmtString {
		final List list = new ArrayList();
		boolean hasReturn = false;
	}

	private static String concat(List list) {
		StringBuilder sb = new StringBuilder();
		for( Object o : list ) {
			if( o instanceof List )  throw new RuntimeException();
			if( o instanceof ExpString )  throw new RuntimeException();
			if( o instanceof StmtString )  throw new RuntimeException();
			sb.append( o.toString() );
		}
		return sb.toString();
	}

	private Class toFnClass(StmtString stmt,List<UpSym> upValueSymbols) {
		String code = concat(stmt.list);
//System.out.println("code:\n"+code);

		String className = "EXP" + ++classCounter;
		String classCode = ""
			+"package luan.impl;\n"
			+"import luan.Luan;\n"
			+"import luan.LuanFunction;\n"
			+"import luan.LuanState;\n"
			+"import luan.LuanException;\n"
			+"import luan.modules.PackageLuan;\n"
			+"\n"
			+"public class " + className +" extends Closure {\n"
			+"	public "+className+"(LuanState luan) throws LuanException {\n"
			+"		super("+upValueSymbols.size()+");\n"
			+		init(upValueSymbols)
			+"	}\n"
			+"\n"
			+"	@Override public Object doCall(LuanState luan,Object[] args) throws LuanException {\n"
			+"		Object t;\n"
			+"		" + code
			+"	}\n"
			+"}\n"
		;
		try {
System.out.println(parser.sourceName);
System.out.println(classCode);
			return LuanJavaCompiler.compile("luan.impl."+className,parser.sourceName,classCode);
		} catch(ClassNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	private ExpString toFnExpStr(StmtString stmt,List<UpSym> upValueSymbols) {
		ExpString exp = new ExpString(true,false);
		exp.list.add( ""
			+"\n"
			+"new Closure("+upValueSymbols.size()+") {\n"
			+"{\n"
			+ init(upValueSymbols)
			+"}\n"
			+"	@Override public Object doCall(LuanState luan,Object[] args) throws LuanException {\n"
			+"		Object t;\n"
			+"		"
		);
		exp.list.addAll( stmt.list );
		exp.list.add( ""
			+"	}\n"
			+"}\n"
		);
		return exp;
	}

	private static String init(List<UpSym> upValueSymbols) {
		StringBuilder sb = new StringBuilder();
		for( UpSym upSym : upValueSymbols ) {
			sb.append( upSym.init() );
		}
		return sb.toString();
	}

/*
	private static String settableToString(Settable settable) {
		if( settable==null )  throw new NullPointerException();
		int i = LuanImpl.addObj(settable);
		return"((Settable)LuanImpl.getObj(" + i + "))";
	}
*/
}