changeset 166:4eaee12f6c65

move luan/interp to impl git-svn-id: https://luan-java.googlecode.com/svn/trunk@167 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Sun, 22 Jun 2014 04:17:38 +0000
parents 94bbc4cbc106
children 4c0131c2b650
files src/luan/LuanState.java src/luan/impl/AddExpr.java src/luan/impl/AndExpr.java src/luan/impl/BinaryOpExpr.java src/luan/impl/Block.java src/luan/impl/BreakException.java src/luan/impl/BreakStmt.java src/luan/impl/Closure.java src/luan/impl/Code.java src/luan/impl/CodeImpl.java src/luan/impl/ConcatExpr.java src/luan/impl/ConstExpr.java src/luan/impl/DivExpr.java src/luan/impl/EqExpr.java src/luan/impl/ExpList.java src/luan/impl/Expr.java src/luan/impl/Expressions.java src/luan/impl/ExpressionsExpr.java src/luan/impl/ExpressionsStmt.java src/luan/impl/FnCall.java src/luan/impl/FnDef.java src/luan/impl/ForStmt.java src/luan/impl/GetLocalVar.java src/luan/impl/GetUpVar.java src/luan/impl/IfStmt.java src/luan/impl/IndexExpr.java src/luan/impl/LeExpr.java src/luan/impl/LenExpr.java src/luan/impl/LtExpr.java src/luan/impl/LuanCompiler.java src/luan/impl/LuanParser.java src/luan/impl/LuanStateImpl.java src/luan/impl/ModExpr.java src/luan/impl/MulExpr.java src/luan/impl/NotExpr.java src/luan/impl/OrExpr.java src/luan/impl/ParseException.java src/luan/impl/Parser.java src/luan/impl/PowExpr.java src/luan/impl/RepeatStmt.java src/luan/impl/ReturnException.java src/luan/impl/ReturnStmt.java src/luan/impl/SetLocalVar.java src/luan/impl/SetStmt.java src/luan/impl/SetTableEntry.java src/luan/impl/SetUpVar.java src/luan/impl/Settable.java src/luan/impl/Stmt.java src/luan/impl/SubExpr.java src/luan/impl/TableExpr.java src/luan/impl/TryStmt.java src/luan/impl/UnaryOpExpr.java src/luan/impl/UnmExpr.java src/luan/impl/UpValue.java src/luan/impl/VarArgs.java src/luan/impl/WhileStmt.java src/luan/interp/AddExpr.java src/luan/interp/AndExpr.java src/luan/interp/BinaryOpExpr.java src/luan/interp/Block.java src/luan/interp/BreakException.java src/luan/interp/BreakStmt.java src/luan/interp/Closure.java src/luan/interp/Code.java src/luan/interp/CodeImpl.java src/luan/interp/ConcatExpr.java src/luan/interp/ConstExpr.java src/luan/interp/DivExpr.java src/luan/interp/EqExpr.java src/luan/interp/ExpList.java src/luan/interp/Expr.java src/luan/interp/Expressions.java src/luan/interp/ExpressionsExpr.java src/luan/interp/ExpressionsStmt.java src/luan/interp/FnCall.java src/luan/interp/FnDef.java src/luan/interp/ForStmt.java src/luan/interp/GetLocalVar.java src/luan/interp/GetUpVar.java src/luan/interp/IfStmt.java src/luan/interp/IndexExpr.java src/luan/interp/LeExpr.java src/luan/interp/LenExpr.java src/luan/interp/LtExpr.java src/luan/interp/LuanCompiler.java src/luan/interp/LuanParser.java src/luan/interp/LuanStateImpl.java src/luan/interp/ModExpr.java src/luan/interp/MulExpr.java src/luan/interp/NotExpr.java src/luan/interp/OrExpr.java src/luan/interp/ParseException.java src/luan/interp/Parser.java src/luan/interp/PowExpr.java src/luan/interp/RepeatStmt.java src/luan/interp/ReturnException.java src/luan/interp/ReturnStmt.java src/luan/interp/SetLocalVar.java src/luan/interp/SetStmt.java src/luan/interp/SetTableEntry.java src/luan/interp/SetUpVar.java src/luan/interp/Settable.java src/luan/interp/Stmt.java src/luan/interp/SubExpr.java src/luan/interp/TableExpr.java src/luan/interp/TryStmt.java src/luan/interp/UnaryOpExpr.java src/luan/interp/UnmExpr.java src/luan/interp/UpValue.java src/luan/interp/VarArgs.java src/luan/interp/WhileStmt.java src/luan/lib/BasicLib.java
diffstat 112 files changed, 3162 insertions(+), 3162 deletions(-) [+]
line wrap: on
line diff
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/LuanState.java
--- a/src/luan/LuanState.java	Sun Jun 22 04:10:59 2014 +0000
+++ b/src/luan/LuanState.java	Sun Jun 22 04:17:38 2014 +0000
@@ -6,7 +6,7 @@
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.LinkedHashMap;
-import luan.interp.LuanCompiler;
+import luan.impl.LuanCompiler;
 import luan.lib.BasicLib;
 import luan.lib.PackageLib;
 import luan.lib.JavaLib;
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/AddExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/AddExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,23 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class AddExpr extends BinaryOpExpr {
+
+	AddExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		Number n1 = Luan.toNumber(o1);
+		Number n2 = Luan.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return n1.doubleValue() + n2.doubleValue();
+		return arithmetic(luan,"__add",o1,o2);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/AndExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/AndExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,18 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class AndExpr extends BinaryOpExpr {
+
+	AndExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object v1 = op1.eval(luan);
+		return !Luan.toBoolean(v1) ? v1 : op2.eval(luan);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/BinaryOpExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/BinaryOpExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,24 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+abstract class BinaryOpExpr extends CodeImpl implements Expr {
+	final Expr op1;
+	final Expr op2;
+
+	BinaryOpExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se);
+		this.op1 = op1;
+		this.op2 = op2;
+	}
+
+	Object arithmetic(LuanStateImpl luan,String op,Object o1,Object o2) throws LuanException {
+		return luan.bit(se()).arithmetic("__mod",o1,o2);
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/Block.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/Block.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,29 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+final class Block implements Stmt {
+	final Stmt[] stmts;
+	private final int stackStart;
+	private final int stackEnd;
+
+	Block(Stmt[] stmts,int stackStart,int stackEnd) {
+		if( stmts.length==0 )
+			throw new RuntimeException("empty block");
+		this.stmts = stmts;
+		this.stackStart = stackStart;
+		this.stackEnd = stackEnd;
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		try {
+			for( Stmt stmt : stmts ) {
+				stmt.eval(luan);
+			}
+		} finally {
+			luan.stackClear(stackStart,stackEnd);
+		}
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/BreakException.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/BreakException.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,4 @@
+package luan.impl;
+
+
+final class BreakException extends RuntimeException {}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/BreakStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/BreakStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,9 @@
+package luan.impl;
+
+
+final class BreakStmt implements Stmt {
+
+	@Override public void eval(LuanStateImpl luan) {
+		throw new BreakException();
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/Closure.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/Closure.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,79 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanFunction;
+import luan.LuanState;
+import luan.LuanElement;
+import luan.LuanException;
+import luan.DeepCloner;
+import luan.DeepCloneable;
+
+
+final class Closure extends LuanFunction implements DeepCloneable<Closure> {
+	private final FnDef fnDef;
+	private UpValue[] upValues;
+
+	Closure(LuanStateImpl luan,FnDef fnDef) throws LuanException {
+		this.fnDef = fnDef;
+		UpValue.Getter[] upValueGetters = fnDef.upValueGetters;
+		upValues = new UpValue[upValueGetters.length];
+		for( int i=0; i<upValues.length; i++ ) {
+			upValues[i] = upValueGetters[i].get(luan);
+		}
+	}
+
+	private Closure(Closure c) {
+		this.fnDef = c.fnDef;
+	}
+
+	@Override public Closure shallowClone() {
+		return new Closure(this);
+	}
+
+	@Override public void deepenClone(Closure clone,DeepCloner cloner) {
+		clone.upValues = cloner.deepClone(upValues);
+	}
+
+	UpValue[] upValues() {
+		return upValues;
+	}
+
+	@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+		return call(this,(LuanStateImpl)luan,args);
+	}
+
+	private static Object call(Closure closure,LuanStateImpl luan,Object[] args) throws LuanException {
+		while(true) {
+			FnDef fnDef = closure.fnDef;
+			Object[] varArgs = null;
+			if( fnDef.isVarArg ) {
+				if( args.length > fnDef.numArgs ) {
+					varArgs = new Object[ args.length - fnDef.numArgs ];
+					for( int i=0; i<varArgs.length; i++ ) {
+						varArgs[i] = args[fnDef.numArgs+i];
+					}
+				} else {
+					varArgs = LuanFunction.NOTHING;
+				}
+			}
+			Object[] stack = luan.newFrame(closure,fnDef.stackSize,varArgs);
+			final int n = Math.min(args.length,fnDef.numArgs);
+			for( int i=0; i<n; i++ ) {
+				stack[i] = args[i];
+			}
+			Object returnValues;
+			try {
+				fnDef.block.eval(luan);
+			} catch(ReturnException e) {
+			} finally {
+				returnValues = luan.returnValues;
+				closure = luan.tailFn;
+				luan.popFrame();
+			}
+			if( closure == null )
+				return returnValues;
+			args = Luan.array(returnValues);
+		}
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/Code.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/Code.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,8 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+interface Code {
+	public LuanSource.Element se();
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/CodeImpl.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/CodeImpl.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,16 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+class CodeImpl implements Code {
+	final LuanSource.Element se;
+
+	CodeImpl(LuanSource.Element se) {
+		this.se = se;
+	}
+
+	@Override public final LuanSource.Element se() {
+		return se;
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ConcatExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ConcatExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,31 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class ConcatExpr extends BinaryOpExpr {
+
+	ConcatExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		String s1 = luan.bit(op1.se()).toString(o1);
+		String s2 = luan.bit(op2.se()).toString(o2);
+/*
+		if( s1 != null && s2 != null )
+			return s1 + s2;
+		LuanFunction fn = luan.getBinHandler(se,"__concat",o1,o2);
+		if( fn != null )
+			return Luan.first(luan.call(fn,se,"__concat",o1,o2));
+		String type = s1==null ? Luan.type(o1) : Luan.type(o2);
+		throw new LuanException( luan, se, "attempt to concatenate a " + type + " value" );
+*/
+		return s1 + s2;
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ConstExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ConstExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,25 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+final class ConstExpr extends CodeImpl implements Expr {
+	private final Object obj;
+
+	ConstExpr(Object obj) {
+		this(null,obj);
+	}
+
+	ConstExpr(LuanSource.Element se,Object obj) {
+		super(se);
+		this.obj = obj;
+	}
+
+	@Override public Object eval(LuanStateImpl luan) {
+		return obj;
+	}
+
+	@Override public String toString() {
+		return "(ConstExpr "+obj+")";
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/DivExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/DivExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,23 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class DivExpr extends BinaryOpExpr {
+
+	DivExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		Number n1 = Luan.toNumber(o1);
+		Number n2 = Luan.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return n1.doubleValue() / n2.doubleValue();
+		return arithmetic(luan,"__div",o1,o2);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/EqExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/EqExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,40 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanFunction;
+import luan.LuanTable;
+import luan.LuanException;
+import luan.LuanSource;
+import luan.LuanBit;
+
+
+final class EqExpr extends BinaryOpExpr {
+
+	EqExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		if( o1 == o2 || o1 != null && o1.equals(o2) )
+			return true;
+		if( o1 instanceof Number && o2 instanceof Number ) {
+			Number n1 = (Number)o1;
+			Number n2 = (Number)o2;
+			return n1.doubleValue() == n2.doubleValue();
+		}
+		if( o1==null || o2==null || !o1.getClass().equals(o2.getClass()) )
+			return false;
+		LuanTable mt1 = luan.getMetatable(o1);
+		LuanTable mt2 = luan.getMetatable(o2);
+		if( mt1==null || mt2==null )
+			return false;
+		Object f = mt1.get("__eq");
+		if( f == null || !f.equals(mt2.get("__eq")) )
+			return null;
+		LuanBit bit = luan.bit(se);
+		LuanFunction fn = bit.checkFunction(f);
+		return Luan.toBoolean( Luan.first(bit.call(fn,"__eq",new Object[]{o1,o2})) );
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ExpList.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ExpList.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,96 @@
+package luan.impl;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import luan.LuanException;
+import luan.LuanSource;
+import luan.LuanFunction;
+import luan.Luan;
+
+
+final class ExpList {
+
+	static final Expressions emptyExpList = new Expressions() {
+
+		@Override public Object[] eval(LuanStateImpl luan) {
+			return LuanFunction.NOTHING;
+		}
+
+		@Override public LuanSource.Element se() {
+			return null;
+		}
+	};
+
+	static Expr[] toArray(List<Expressions> list) {
+		Expr[] a = new Expr[list.size()];
+		for( int i=0; i<a.length; i++ ) {
+			Expressions exprs = list.get(i);
+			if( exprs instanceof Expr ) {
+				a[i] = (Expr)exprs;
+			} else {
+				a[i] = new ExpressionsExpr(exprs);
+			}
+		}
+		return a;
+	}
+
+	static Expressions build(List<Expressions> list) {
+		switch(list.size()) {
+		case 0:
+			return emptyExpList;
+		case 1:
+			return list.get(0);
+		default:
+			if( list.get(list.size()-1) instanceof Expr ) {
+				return new ExprList1( toArray(list) );
+			} else {
+				Expressions last = list.remove(list.size()-1);
+				return new ExprList2( toArray(list), last );
+			}
+		}
+	}
+
+	private static class ExprList1 implements Expressions {
+		private final Expr[] exprs;
+
+		private ExprList1(Expr[] exprs) {
+			this.exprs = exprs;
+		}
+	
+		@Override public Object eval(LuanStateImpl luan) throws LuanException {
+			Object[] a = new Object[exprs.length];
+			for( int i=0; i<exprs.length; i++ ) {
+				a[i] = exprs[i].eval(luan);
+			}
+			return a;
+		}
+	
+		@Override public LuanSource.Element se() {
+			return new LuanSource.Element(exprs[0].se().source,exprs[0].se().start,exprs[exprs.length-1].se().end);
+		}
+	}
+
+	private static class ExprList2 implements Expressions {
+		private final Expr[] exprs;
+		private final Expressions last;
+	
+		private ExprList2(Expr[] exprs,Expressions last) {
+			this.exprs = exprs;
+			this.last = last;
+		}
+	
+		@Override public Object eval(LuanStateImpl luan) throws LuanException {
+			List<Object> list = new ArrayList<Object>();
+			for( Expr expr : exprs ) {
+				list.add( expr.eval(luan) );
+			}
+			list.addAll( Arrays.asList(Luan.array( last.eval(luan) )) );
+			return list.toArray();
+		}
+	
+		@Override public LuanSource.Element se() {
+			return new LuanSource.Element(exprs[0].se().source,exprs[0].se().start,last.se().end);
+		}
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/Expr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/Expr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,6 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+interface Expr extends Expressions {}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/Expressions.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/Expressions.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,8 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+interface Expressions extends Code {
+	public Object eval(LuanStateImpl luan) throws LuanException;
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ExpressionsExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ExpressionsExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,24 @@
+package luan.impl;
+
+import java.util.List;
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class ExpressionsExpr implements Expr {
+	private final Expressions expressions;
+
+	ExpressionsExpr(Expressions expressions) {
+		this.expressions = expressions;
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		return Luan.first( expressions.eval(luan) );
+	}
+
+	public LuanSource.Element se() {
+		return expressions.se();
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ExpressionsStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ExpressionsStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,17 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+final class ExpressionsStmt implements Stmt {
+	private final Expressions expressions;
+
+	ExpressionsStmt(Expressions expressions) {
+		this.expressions = expressions;
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		expressions.eval(luan);
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/FnCall.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/FnCall.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,39 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class FnCall extends CodeImpl implements Expressions {
+	final Expr fnExpr;
+	final Expressions args;
+	final String fnName;
+
+	FnCall(LuanSource.Element se,Expr fnExpr,Expressions args) {
+		super(se);
+		this.fnExpr = fnExpr;
+		this.args = args;
+		this.fnName = fnExpr.se().text();
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		return call( luan, fnExpr.eval(luan) );
+	}
+
+	private Object call(LuanStateImpl luan,Object o) throws LuanException {
+		if( o instanceof LuanFunction ) {
+			LuanFunction fn = (LuanFunction)o;
+			return luan.bit(se).call( fn, fnName, Luan.array(args.eval(luan)) );
+		}
+		Object h = luan.getHandler("__call",o);
+		if( h != null )
+			return call(luan,h);
+		throw luan.bit(fnExpr.se()).exception( "attempt to call '"+fnExpr.se().text()+"' (a " + Luan.type(o) + " value)" );
+	}
+
+	@Override public String toString() {
+		return "(FnCall "+fnName+" "+fnExpr+" "+args+")";
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/FnDef.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/FnDef.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,42 @@
+package luan.impl;
+
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class FnDef extends CodeImpl implements Expr {
+	final Stmt block;
+	final int stackSize;
+	final int numArgs;
+	final boolean isVarArg;
+	final UpValue.Getter[] upValueGetters;
+
+	FnDef(LuanSource.Element se,Stmt block,int stackSize,int numArgs,boolean isVarArg,UpValue.Getter[] upValueGetters) {
+		super(se);
+		this.block = block;
+		this.stackSize = stackSize;
+		this.numArgs = numArgs;
+		this.isVarArg = isVarArg;
+		this.upValueGetters = upValueGetters;
+		fixReturns(block);
+	}
+
+	private static void fixReturns(Stmt stmt) {
+		if( stmt instanceof ReturnStmt ) {
+			ReturnStmt rs = (ReturnStmt)stmt;
+			rs.throwReturnException = false;
+		} else if( stmt instanceof Block ) {
+			Block b = (Block)stmt;
+			fixReturns( b.stmts[b.stmts.length-1] );
+		} else if( stmt instanceof IfStmt ) {
+			IfStmt is = (IfStmt)stmt;
+			fixReturns( is.thenStmt );
+			fixReturns( is.elseStmt );
+		}
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		return new Closure(luan,this);
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ForStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ForStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,54 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanFunction;
+import luan.LuanSource;
+import luan.LuanBit;
+
+
+final class ForStmt extends CodeImpl implements Stmt {
+	private final int iVars;
+	private final int nVars;
+	private final Expr iterExpr;
+	private final Stmt block;
+
+	ForStmt(LuanSource.Element se,int iVars,int nVars,Expr iterExpr,Stmt block) {
+		super(se);
+		this.iVars = iVars;
+		this.nVars = nVars;
+		this.iterExpr = iterExpr;
+		this.block = block;
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		LuanFunction iter = luan.bit(se).checkFunction( iterExpr.eval(luan) );
+		LuanBit bit = luan.bit(iterExpr.se());
+		String name = iterExpr.se().text();
+		try {
+			while(true) {
+				Object vals = bit.call(iter,name,LuanFunction.NOTHING);
+				if( vals==null )
+					break;
+				if( vals instanceof Object[] ) {
+					Object[] a = (Object[])vals;
+					if( a.length==0 )
+						break;
+					for( int i=0; i<nVars; i++ ) {
+						luan.stackSet( iVars+i, i < a.length ? a[i] : null );
+					}
+				} else {
+					luan.stackSet( iVars, vals );
+					for( int i=1; i<nVars; i++ ) {
+						luan.stackSet( iVars+i, null );
+					}
+				}
+				block.eval(luan);
+			}
+		} catch(BreakException e) {
+		} finally {
+			luan.stackClear(iVars,iVars+nVars);
+		}
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/GetLocalVar.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/GetLocalVar.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,17 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+final class GetLocalVar extends CodeImpl implements Expr {
+	private final int index;
+
+	GetLocalVar(LuanSource.Element se,int index) {
+		super(se);
+		this.index = index;
+	}
+
+	@Override public Object eval(LuanStateImpl luan) {
+		return luan.stackGet(index);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/GetUpVar.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/GetUpVar.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,17 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+final class GetUpVar extends CodeImpl implements Expr {
+	private final int index;
+
+	GetUpVar(LuanSource.Element se,int index) {
+		super(se);
+		this.index = index;
+	}
+
+	@Override public Object eval(LuanStateImpl luan) {
+		return luan.closure().upValues()[index].get();
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/IfStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/IfStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,27 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class IfStmt extends CodeImpl implements Stmt {
+	private final Expr cnd;
+	final Stmt thenStmt;
+	final Stmt elseStmt;
+
+	IfStmt(LuanSource.Element se,Expr cnd,Stmt thenStmt,Stmt elseStmt) {
+		super(se);
+		this.cnd = cnd;
+		this.thenStmt = thenStmt;
+		this.elseStmt = elseStmt;
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		if( luan.bit(se).checkBoolean( cnd.eval(luan) ) ) {
+			thenStmt.eval(luan);
+		} else {
+			elseStmt.eval(luan);
+		}
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/IndexExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/IndexExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,41 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanSource;
+
+
+final class IndexExpr extends BinaryOpExpr {
+
+	IndexExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		return index(luan,op1.eval(luan),op2.eval(luan));
+	}
+
+	private Object index(LuanStateImpl luan,Object t,Object key) throws LuanException {
+		Object h;
+		if( t instanceof LuanTable ) {
+			LuanTable tbl = (LuanTable)t;
+			Object value = tbl.get(key);
+			if( value != null )
+				return value;
+			h = luan.getHandler("__index",t);
+			if( h==null )
+				return null;
+		} else {
+			h = luan.getHandler("__index",t);
+			if( h==null )
+				throw luan.bit(op1.se()).exception( "attempt to index '"+op1.se().text()+"' (a " + Luan.type(t) + " value)" );
+		}
+		if( h instanceof LuanFunction ) {
+			LuanFunction fn = (LuanFunction)h;
+			return Luan.first(luan.bit(se).call(fn,"__index",new Object[]{t,key}));
+		}
+		return index(luan,h,key);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/LeExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/LeExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,38 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.LuanSource;
+import luan.LuanBit;
+
+
+final class LeExpr extends BinaryOpExpr {
+
+	LeExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		if( o1 instanceof Number && o2 instanceof Number ) {
+			Number n1 = (Number)o1;
+			Number n2 = (Number)o2;
+			return n1.doubleValue() <= n2.doubleValue();
+		}
+		if( o1 instanceof String && o2 instanceof String ) {
+			String s1 = (String)o1;
+			String s2 = (String)o2;
+			return s1.compareTo(s2) <= 0;
+		}
+		LuanBit bit = luan.bit(se);
+		LuanFunction fn = bit.getBinHandler("__le",o1,o2);
+		if( fn != null )
+			return Luan.toBoolean( Luan.first(bit.call(fn,"__le",new Object[]{o1,o2})) );
+		fn = bit.getBinHandler("__lt",o1,o2);
+		if( fn != null )
+			return !Luan.toBoolean( Luan.first(bit.call(fn,"__lt",new Object[]{o2,o1})) );
+		throw bit.exception( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) );
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/LenExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/LenExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,37 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.LuanSource;
+import luan.LuanBit;
+
+
+final class LenExpr extends UnaryOpExpr {
+
+	LenExpr(LuanSource.Element se,Expr op) {
+		super(se,op);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o = op.eval(luan);
+		if( o instanceof String ) {
+			String s = (String)o;
+			return s.length();
+		}
+		if( o instanceof byte[] ) {
+			byte[] a = (byte[])o;
+			return a.length;
+		}
+		LuanBit bit = luan.bit(se);
+		LuanFunction fn = bit.getHandlerFunction("__len",o);
+		if( fn != null )
+			return Luan.first(bit.call(fn,"__len",new Object[]{o}));
+		if( o instanceof LuanTable ) {
+			LuanTable t = (LuanTable)o;
+			return t.length();
+		}
+		throw bit.exception( "attempt to get length of a " + Luan.type(o) + " value" );
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/LtExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/LtExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,20 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class LtExpr extends BinaryOpExpr {
+
+	LtExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		return luan.bit(se).isLessThan(o1,o2);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/LuanCompiler.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/LuanCompiler.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,55 @@
+package luan.impl;
+
+import luan.LuanFunction;
+import luan.LuanState;
+import luan.LuanException;
+import luan.LuanSource;
+import luan.LuanElement;
+import luan.LuanTable;
+import java.util.Map;
+
+
+public final class LuanCompiler {
+	private LuanCompiler() {}  // never
+
+	public static LuanFunction compile(LuanState luan,LuanSource src,LuanTable env,boolean allowExpr) throws LuanException {
+		UpValue.Getter envGetter = env!=null ? new UpValue.ValueGetter(env) : new UpValue.EnvGetter();
+		LuanParser parser = new LuanParser(src,envGetter);
+		for( Map.Entry<Object,Object> entry : luan.global() ) {
+			Object key = entry.getKey();
+			if( key instanceof String )
+				parser.addVar( (String)key, entry.getValue() );
+		}
+		FnDef fnDef = parse(luan,parser,allowExpr);
+		if( env != null )
+			return new Closure((LuanStateImpl)luan,fnDef);
+		final Closure c = new Closure((LuanStateImpl)luan,fnDef);
+		return new LuanFunction() {
+			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+				Object rtn = c.call(luan,args);
+				if( rtn instanceof Object[] && ((Object[])rtn).length==0 )
+					rtn = c.upValues()[0].get();
+				return rtn;
+			}
+		};
+	}
+
+	private static FnDef parse(LuanState luan,LuanParser parser,boolean allowExpr) throws LuanException {
+		try {
+			if( allowExpr ) {
+				FnDef fnDef = parser.Expression();
+				if( fnDef != null )
+					return fnDef;
+			}
+			return parser.RequiredModule();
+		} catch(ParseException e) {
+//e.printStackTrace();
+			LuanElement le = new LuanSource.CompilerElement(parser.source);
+			throw luan.bit(le).exception( e.getFancyMessage() );
+		}
+	}
+
+	public static LuanState newLuanState() {
+		return new LuanStateImpl();
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/LuanParser.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/LuanParser.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,1360 @@
+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.LuanSource;
+
+
+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(UpValue.Getter envGetter) {
+			this.parent = null;
+			upValueSymbols.add(_ENV);
+			upValueGetters.add(envGetter);
+		}
+
+		Frame(Frame parent) {
+			this.parent = parent;
+			if( upValueIndex(_ENV) != 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,template);
+		}
+
+		In template() {
+			return template ? this : new In(parens,true);
+		}
+	}
+
+	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;
+	private final boolean interactive;
+
+	LuanParser(LuanSource source,UpValue.Getter envGetter) {
+		this.source = source;
+		this.frame = new Frame(envGetter);
+		this.parser = new Parser(source);
+		this.interactive = envGetter instanceof UpValue.ValueGetter;
+	}
+
+	void addVar(String name,Object value) {
+		frame.addUpValueGetter(name,new UpValue.ValueGetter(value));
+	}
+
+	private LuanSource.Element se(int start) {
+		return new LuanSource.Element(source,start,parser.currentIndex());
+	}
+
+	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 instanceof Expr )
+			return (Expr)exprs; 
+		return new ExpressionsExpr(exprs);
+	}
+
+	private FnDef newFnDef(int start,Stmt stmt) {
+		return new FnDef( se(start), stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) );
+	}
+
+	FnDef Expression() throws ParseException {
+		Spaces(In.NOTHING);
+		int start = parser.begin();
+		Expressions expr = Expr(In.NOTHING);
+		if( expr != null && parser.endOfInput() ) {
+			Stmt stmt = new ReturnStmt( se(start), expr );
+			return parser.success(newFnDef(start,stmt));
+		}
+		return parser.failure(null);
+	}
+
+	FnDef RequiredModule() throws ParseException {
+		Spaces(In.NOTHING);
+		int start = parser.begin();
+		frame.isVarArg = true;
+		Stmt stmt = RequiredBlock();
+		if( parser.endOfInput() )
+			return parser.success(newFnDef(start,stmt));
+		throw parser.exception();
+	}
+
+	private Stmt RequiredBlock() throws ParseException {
+		List<Stmt> stmts = new ArrayList<Stmt>();
+		int stackStart = symbolsSize();
+		Stmt(stmts);
+		while( StmtSep(stmts) ) {
+			Spaces(In.NOTHING);
+			Stmt(stmts);
+		}
+		int stackEnd = symbolsSize();
+		popSymbols( stackEnd - stackStart );
+		if( stmts.isEmpty() )
+			return Stmt.EMPTY;
+		if( stmts.size()==1 && stackStart==stackEnd )
+			return stmts.get(0);
+		return new Block( stmts.toArray(new Stmt[0]), stackStart, stackEnd );
+	}
+
+	private boolean StmtSep(List<Stmt> stmts) throws ParseException {
+		parser.begin();
+		if( parser.match( ';' ) )
+			return parser.success();
+		if( parser.match( "--" ) ) {
+			while( parser.noneOf("\r\n") );
+		}
+		if( EndOfLine() )
+			return parser.success();
+		parser.rollback();
+		Stmt stmt = TemplateStmt();
+		if( stmt != null ) {
+			stmts.add(stmt);
+			return parser.success();
+		}
+		return parser.failure();
+	}
+
+	private boolean EndOfLine() {
+		return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' );
+	}
+
+	private void Stmt(List<Stmt> stmts) throws ParseException {
+		if( LocalStmt(stmts) )
+			return;
+		Stmt stmt;
+		if( (stmt=ReturnStmt()) != null
+			|| (stmt=FunctionStmt()) != null
+			|| (stmt=LocalFunctionStmt()) != null
+			|| (stmt=ImportStmt()) != null
+			|| (stmt=BreakStmt()) != null
+			|| (stmt=ForStmt()) != null
+			|| (stmt=TryStmt()) != null
+			|| (stmt=DoStmt()) != null
+			|| (stmt=WhileStmt()) != null
+			|| (stmt=FunctionStmt()) != null
+			|| (stmt=RepeatStmt()) != null
+			|| (stmt=IfStmt()) != null
+			|| (stmt=SetStmt()) != null
+			|| (stmt=ExpressionsStmt()) != null
+		) {
+			stmts.add(stmt);
+		}
+	}
+
+	private Stmt TemplateStmt() throws ParseException {
+		int start = parser.currentIndex();
+		Expressions exp = TemplateExpressions(In.NOTHING);
+		if( exp == null )
+			return null;
+		Expr fnExp = (Expr)nameVar(start,"Io").expr();
+		fnExp = new IndexExpr( se(start), fnExp, new ConstExpr("stdout") );
+		fnExp = new IndexExpr( se(start), fnExp, new ConstExpr("write") );
+		FnCall fnCall = new FnCall( se(start), fnExp, exp );
+		return new ExpressionsStmt(fnCall);
+	}
+
+	private Expressions TemplateExpressions(In in) throws ParseException {
+		if( in.template )
+			return null;
+		int start = parser.begin();
+		if( !parser.match( "%>" ) )
+			return parser.failure(null);
+		EndOfLine();
+		In inTemplate = in.template();
+		List<Expressions> builder = new ArrayList<Expressions>();
+		while(true) {
+			if( parser.match( "<%=" ) ) {
+				Spaces(inTemplate);
+				builder.add( RequiredExpr(inTemplate) );
+				RequiredMatch( "%>" );
+			} else if( parser.match( "<%" ) ) {
+				Spaces(inTemplate);
+				return parser.success(ExpList.build(builder));
+			} else {
+				int i = parser.currentIndex();
+				do {
+					if( parser.match( "%>" ) )
+						throw parser.exception("'%>' unexpected");
+					if( !parser.anyChar() )
+						throw parser.exception("Unclosed template expression");
+				} while( !parser.test( "<%" ) );
+				String match = parser.textFrom(i);
+				builder.add( new ConstExpr(match) );
+			}
+		}
+	}
+
+	private Stmt ReturnStmt() throws ParseException {
+		int start = parser.begin();
+		if( !Keyword("return",In.NOTHING) )
+			return parser.failure(null);
+		Expressions exprs = ExpList(In.NOTHING);
+		if( exprs==null )
+			exprs = ExpList.emptyExpList;
+		return parser.success( new ReturnStmt(se(start),exprs) );
+	}
+
+	private Stmt FunctionStmt() throws ParseException {
+		parser.begin();
+		if( !Keyword("function",In.NOTHING) )
+			return parser.failure(null);
+
+		int start = parser.currentIndex();
+		Var var = nameVar(start,RequiredName(In.NOTHING));
+		while( parser.match( '.' ) ) {
+			Spaces(In.NOTHING);
+			var = indexVar( start, expr(var.expr()), NameExpr(In.NOTHING) );
+		}
+		Settable fnName = var.settable();
+
+		FnDef fnDef = RequiredFunction(In.NOTHING);
+		return parser.success( new SetStmt(fnName,fnDef) );
+	}
+
+	private Stmt LocalFunctionStmt() throws ParseException {
+		parser.begin();
+		if( !(Keyword("local",In.NOTHING) && Keyword("function",In.NOTHING)) )
+			return parser.failure(null);
+		String name = RequiredName(In.NOTHING);
+		addSymbol( name );
+		FnDef fnDef = RequiredFunction(In.NOTHING);
+		return parser.success( new SetStmt( new SetLocalVar(symbolsSize()-1), fnDef ) );
+	}
+
+	private Stmt ImportStmt() throws ParseException {
+		int start = parser.begin();
+		if( !Keyword("import",In.NOTHING) )
+			return parser.failure(null);
+		Expr importExpr = (Expr)nameVar(start,"require").expr();
+		String modName = StringLiteral(In.NOTHING);
+		if( modName==null )
+			return parser.failure(null);
+		String varName = modName.substring(modName.lastIndexOf('.')+1);
+		LuanSource.Element se = se(start);
+		FnCall require = new FnCall( se, importExpr, new ConstExpr(modName) );
+		Settable settable;
+		if( interactive ) {
+			settable = nameVar(se,varName).settable();
+		} else {
+			addSymbol( varName );
+			settable = new SetLocalVar(symbolsSize()-1);
+		}
+		return parser.success( new SetStmt( settable, expr(require) ) );
+	}
+
+	private Stmt 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 BreakStmt() );
+	}
+
+	private Stmt ForStmt() throws ParseException {
+		int start = 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);
+		Expr expr = expr(RequiredExpr(In.NOTHING));
+		RequiredKeyword("do",In.NOTHING);
+		addSymbols(names);
+		Stmt loop = RequiredLoopBlock();
+		RequiredKeyword("end",In.NOTHING);
+		Stmt stmt = new ForStmt( se(start), stackStart, symbolsSize() - stackStart, expr, loop );
+		popSymbols( symbolsSize() - stackStart );
+		return parser.success(stmt);
+	}
+
+	private Stmt TryStmt() throws ParseException {
+		parser.begin();
+		if( !Keyword("try",In.NOTHING) )
+			return parser.failure(null);
+		Stmt tryBlock = RequiredBlock();
+		RequiredKeyword("catch",In.NOTHING);
+		String name = RequiredName(In.NOTHING);
+		addSymbol(name);
+		RequiredKeyword("do",In.NOTHING);
+		Stmt catchBlock = RequiredBlock();
+		RequiredKeyword("end",In.NOTHING);
+		Stmt stmt = new TryStmt( tryBlock, symbolsSize()-1, catchBlock );
+		popSymbols(1);
+		return parser.success(stmt);
+	}
+
+	private Stmt DoStmt() throws ParseException {
+		parser.begin();
+		if( !Keyword("do",In.NOTHING) )
+			return parser.failure(null);
+		Stmt stmt = RequiredBlock();
+		RequiredKeyword("end",In.NOTHING);
+		return parser.success(stmt);
+	}
+
+	private boolean LocalStmt(List<Stmt> stmts) throws ParseException {
+		parser.begin();
+		if( !Keyword("local",In.NOTHING) )
+			return parser.failure();
+		List<String> names = NameList(In.NOTHING);
+		if( names==null )
+			return parser.failure();
+		if( parser.match( '=' ) ) {
+			Spaces(In.NOTHING);
+			Expressions values = ExpList(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);
+			}
+			stmts.add( new SetStmt( vars, values ) );
+		}
+		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 Stmt WhileStmt() throws ParseException {
+		int start = parser.begin();
+		if( !Keyword("while",In.NOTHING) )
+			return parser.failure(null);
+		Expr cnd = expr(RequiredExpr(In.NOTHING));
+		RequiredKeyword("do",In.NOTHING);
+		Stmt loop = RequiredLoopBlock();
+		RequiredKeyword("end",In.NOTHING);
+		return parser.success( new WhileStmt(se(start),cnd,loop) );
+	}
+
+	private Stmt RepeatStmt() throws ParseException {
+		int start = parser.begin();
+		if( !Keyword("repeat",In.NOTHING) )
+			return parser.failure(null);
+		Stmt loop = RequiredLoopBlock();
+		RequiredKeyword("until",In.NOTHING);
+		Expr cnd = expr(RequiredExpr(In.NOTHING));
+		return parser.success( new RepeatStmt(se(start),loop,cnd) );
+	}
+
+	private Stmt RequiredLoopBlock() throws ParseException {
+		incLoops();
+		Stmt stmt = RequiredBlock();
+		decLoops();
+		return stmt;
+	}
+
+	private Stmt IfStmt() throws ParseException {
+		parser.begin();
+		if( !Keyword("if",In.NOTHING) )
+			return parser.failure(null);
+		return parser.success( IfStmt2() );
+	}
+
+	private Stmt IfStmt2() throws ParseException {
+		int start = parser.currentIndex();
+		Expr cnd = expr(RequiredExpr(In.NOTHING));
+		RequiredKeyword("then",In.NOTHING);
+		Stmt thenBlock = RequiredBlock();
+		Stmt elseBlock;
+		if( Keyword("elseif",In.NOTHING) ) {
+			elseBlock = IfStmt2();
+		} else {
+			elseBlock = Keyword("else",In.NOTHING) ? RequiredBlock() : Stmt.EMPTY;
+			RequiredKeyword("end",In.NOTHING);
+		}
+		return new IfStmt(se(start),cnd,thenBlock,elseBlock);
+	}
+
+	private Stmt SetStmt() throws ParseException {
+		parser.begin();
+		List<Settable> vars = new ArrayList<Settable>();
+		Settable s = SettableVar();
+		if( s == null )
+			return parser.failure(null);
+		vars.add(s);
+		while( parser.match( ',' ) ) {
+			Spaces(In.NOTHING);
+			s = SettableVar();
+			if( s == null )
+				return parser.failure(null);
+			vars.add(s);
+		}
+		if( !parser.match( '=' ) )
+			return parser.failure(null);
+		Spaces(In.NOTHING);
+		Expressions values = ExpList(In.NOTHING);
+		if( values==null )
+			throw parser.exception("Expressions expected");
+		return parser.success( new SetStmt( vars.toArray(new Settable[0]), values ) );
+	}
+
+	private Stmt ExpressionsStmt() throws ParseException {
+		parser.begin();
+		Expressions exp = Expr(In.NOTHING);
+		if( exp instanceof FnCall || exp instanceof AndExpr || exp instanceof OrExpr )
+			return parser.success( new ExpressionsStmt(exp) );
+		return parser.failure(null);
+	}
+
+	private Settable SettableVar() throws ParseException {
+		int start = parser.begin();
+		Var var = VarZ(In.NOTHING);
+		if( var==null )
+			return parser.failure(null);
+		return parser.success( var.settable() );
+	}
+
+	private Expressions RequiredExpr(In in) throws ParseException {
+		parser.begin();
+		return parser.success(required(Expr(in),"Bad expression"));
+	}
+
+	private Expressions Expr(In in) throws ParseException {
+		parser.begin();
+		Expressions exp;
+		return (exp = VarArgs(in)) != null
+			|| (exp = OrExpr(in)) != null
+			? parser.success(exp)
+			: parser.failure((Expressions)null)
+		;
+	}
+
+	private Expressions OrExpr(In in) throws ParseException {
+		int start = parser.begin();
+		Expressions exp = AndExpr(in);
+		if( exp==null )
+			return parser.failure(null);
+		while( Keyword("or",in) ) {
+			exp = new OrExpr( se(start), expr(exp), required(expr(AndExpr(in))) );
+		}
+		return parser.success(exp);
+	}
+
+	private Expressions AndExpr(In in) throws ParseException {
+		int start = parser.begin();
+		Expressions exp = RelExpr(in);
+		if( exp==null )
+			return parser.failure(null);
+		while( Keyword("and",in) ) {
+			exp = new AndExpr( se(start), expr(exp), required(expr(RelExpr(in))) );
+		}
+		return parser.success(exp);
+	}
+
+	private Expressions RelExpr(In in) throws ParseException {
+		int start = parser.begin();
+		Expressions exp = ConcatExpr(in);
+		if( exp==null )
+			return parser.failure(null);
+		while(true) {
+			if( parser.match("==") ) {
+				Spaces(in);
+				exp = new EqExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) );
+			} else if( parser.match("~=") ) {
+				Spaces(in);
+				exp = new NotExpr( se(start), new EqExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) ) );
+			} else if( parser.match("<=") ) {
+				Spaces(in);
+				exp = new LeExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) );
+			} else if( parser.match(">=") ) {
+				Spaces(in);
+				exp = new LeExpr( se(start), required(expr(ConcatExpr(in))), expr(exp) );
+			} else if( parser.match("<") ) {
+				Spaces(in);
+				exp = new LtExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) );
+			} else if( parser.match(">") ) {
+				Spaces(in);
+				exp = new LtExpr( se(start), required(expr(ConcatExpr(in))), expr(exp) );
+			} else
+				break;
+		}
+		return parser.success(exp);
+	}
+
+	private Expressions ConcatExpr(In in) throws ParseException {
+		int start = parser.begin();
+		Expressions exp = SumExpr(in);
+		if( exp==null )
+			return parser.failure(null);
+		if( parser.match("..") ) {
+			Spaces(in);
+			exp = new ConcatExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) );
+		}
+		return parser.success(exp);
+	}
+
+	private Expressions SumExpr(In in) throws ParseException {
+		int start = parser.begin();
+		Expressions exp = TermExpr(in);
+		if( exp==null )
+			return parser.failure(null);
+		while(true) {
+			if( parser.match('+') ) {
+				Spaces(in);
+				exp = new AddExpr( se(start), expr(exp), required(expr(TermExpr(in))) );
+			} else if( Minus() ) {
+				Spaces(in);
+				exp = new SubExpr( se(start), expr(exp), required(expr(TermExpr(in))) );
+			} else
+				break;
+		}
+		return parser.success(exp);
+	}
+
+	private boolean Minus() {
+		parser.begin();
+		return parser.match('-') && !parser.match('-') ? parser.success() : parser.failure();
+	}
+
+	private Expressions TermExpr(In in) throws ParseException {
+		int start = parser.begin();
+		Expressions exp = UnaryExpr(in);
+		if( exp==null )
+			return parser.failure(null);
+		while(true) {
+			if( parser.match('*') ) {
+				Spaces(in);
+				exp = new MulExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) );
+			} else if( parser.match('/') ) {
+				Spaces(in);
+				exp = new DivExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) );
+			} else if( Mod() ) {
+				Spaces(in);
+				exp = new ModExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) );
+			} else
+				break;
+		}
+		return parser.success(exp);
+	}
+
+	private boolean Mod() {
+		parser.begin();
+		return parser.match('%') && !parser.match('>') ? parser.success() : parser.failure();
+	}
+
+	private Expressions UnaryExpr(In in) throws ParseException {
+		int start = parser.begin();
+		if( parser.match('#') ) {
+			Spaces(in);
+			return parser.success( new LenExpr( se(start), required(expr(UnaryExpr(in))) ) );
+		}
+		if( Minus() ) {
+			Spaces(in);
+			return parser.success( new UnmExpr( se(start), required(expr(UnaryExpr(in))) ) );
+		}
+		if( Keyword("not",in) ) {
+			Spaces(in);
+			return parser.success( new NotExpr( se(start), required(expr(UnaryExpr(in))) ) );
+		}
+		Expressions exp = PowExpr(in);
+		if( exp==null )
+			return parser.failure(null);
+		return parser.success(exp);
+	}
+
+	private Expressions PowExpr(In in) throws ParseException {
+		int start = parser.begin();
+		Expressions exp = SingleExpr(in);
+		if( exp==null )
+			return parser.failure(null);
+		if( parser.match('^') ) {
+			Spaces(in);
+			exp = new ConcatExpr( se(start), expr(exp), required(expr(PowExpr(in))) );
+		}
+		return parser.success(exp);
+	}
+
+	private Expressions SingleExpr(In in) throws ParseException {
+		parser.begin();
+		Expressions exp;
+		exp = FunctionExpr(in);
+		if( exp != null )
+			return parser.success(exp);
+		exp = TableExpr(in);
+		if( exp != null )
+			return parser.success(exp);
+		exp = VarExp(in);
+		if( exp != null )
+			return parser.success(exp);
+		exp = Literal(in);
+		if( exp != null )
+			return parser.success(exp);
+		return parser.failure(null);
+	}
+
+	private Expr FunctionExpr(In in) throws ParseException {
+		if( !Keyword("function",in) )
+			return null;
+		return RequiredFunction(in);
+	}
+
+	private FnDef RequiredFunction(In in) throws ParseException {
+		int start = parser.begin();
+		RequiredMatch('(');
+		In inParens = in.parens();
+		Spaces(inParens);
+		frame = new Frame(frame);
+		List<String> names = NameList(in);
+		if( names != null ) {
+			addSymbols(names);
+			if( parser.match(',') ) {
+				Spaces(inParens);
+				if( !parser.match("...") )
+					throw parser.exception();
+				frame.isVarArg = true;
+			}
+		} else if( parser.match("...") ) {
+			Spaces(inParens);
+			frame.isVarArg = true;
+		}
+		RequiredMatch(')');
+		Spaces(in);
+		Stmt block = RequiredBlock();
+		RequiredKeyword("end",in);
+		FnDef fnDef = newFnDef(start,block);
+		frame = frame.parent;
+		return parser.success(fnDef);
+	}
+
+	private VarArgs VarArgs(In in) throws ParseException {
+		int start = parser.begin();
+		if( !frame.isVarArg || !parser.match("...") )
+			return parser.failure(null);
+		Spaces(in);
+		return parser.success( new VarArgs(se(start)) );
+	}
+
+	private Expr TableExpr(In in) throws ParseException {
+		int start = parser.begin();
+		if( !parser.match('{') )
+			return parser.failure(null);
+		In inParens = in.parens();
+		Spaces(inParens);
+		List<TableExpr.Field> fields = new ArrayList<TableExpr.Field>();
+		List<Expressions> builder = new ArrayList<Expressions>();
+		while( Field(fields,builder,in) && FieldSep(inParens) );
+		Spaces(inParens);
+		if( !parser.match('}') )
+			throw parser.exception("Expected table element or '}'");
+		return parser.success( new TableExpr( se(start), fields.toArray(new TableExpr.Field[0]), ExpList.build(builder) ) );
+	}
+
+	private boolean FieldSep(In in) throws ParseException {
+		if( !parser.anyOf(",;") )
+			return false;
+		Spaces(in);
+		return true;
+	}
+
+	private boolean Field(List<TableExpr.Field> fields,List<Expressions> builder,In in) throws ParseException {
+		parser.begin();
+		Expr exp = SubExpr(in);
+		if( exp==null )
+			exp = NameExpr(in);
+		if( exp!=null && parser.match('=') ) {
+			Spaces(in);
+			fields.add( new TableExpr.Field( exp, required(expr(Expr(in))) ) );
+			return parser.success();
+		}
+		parser.rollback();
+		Expressions exprs = Expr(in);
+		if( exprs != null ) {
+			builder.add(exprs);
+			return parser.success();
+		}
+		return parser.failure();
+	}
+
+	private Expressions VarExp(In in) throws ParseException {
+		Var var = VarZ(in);
+		return var==null ? null : var.expr();
+	}
+
+	private Var VarZ(In in) throws ParseException {
+		int start = parser.begin();
+		Var var = VarStart(in);
+		if( var==null )
+			return parser.failure(null);
+		Var var2;
+		while( (var2=Var2(in,start,var.expr())) != null ) {
+			var = var2;
+		}
+		return parser.success(var);
+	}
+
+	private Var Var2(In in,int start,Expressions exp1) throws ParseException {
+		parser.begin();
+		Var var = VarExt(in,start,exp1);
+		if( var != null )
+			return parser.success(var);
+		if( parser.match("->") ) {
+			Spaces(in);
+			List<Expressions> builder = new ArrayList<Expressions>();
+			builder.add(exp1);
+			Expr exp2 = expr(RequiredVarExpB(in));
+			FnCall fnCall = required(Args( in, start, exp2, builder ));
+			return parser.success(exprVar(fnCall));
+		}
+		FnCall fnCall = Args( in, start, expr(exp1), new ArrayList<Expressions>() );
+		if( fnCall != null )
+			return parser.success(exprVar(fnCall));
+		return parser.failure(null);
+	}
+
+	private Expressions RequiredVarExpB(In in) throws ParseException {
+		int start = parser.begin();
+		Var var = required(VarStart(in));
+		Var var2;
+		while( (var2=VarExt(in,start,var.expr())) != null ) {
+			var = var2;
+		}
+		return parser.success(var.expr());
+	}
+
+	private Var VarExt(In in,int start,Expressions exp1) throws ParseException {
+		parser.begin();
+		Expr exp2 = SubExpr(in);
+		if( exp2 != null )
+			return parser.success(indexVar(start,expr(exp1),exp2));
+		if( parser.match('.') ) {
+			Spaces(in);
+			exp2 = NameExpr(in);
+			if( exp2!=null )
+				return parser.success(indexVar(start,expr(exp1),exp2));
+		}
+		return parser.failure(null);
+	}
+
+	private Var VarStart(In in) throws ParseException {
+		int start = parser.begin();
+		if( parser.match('(') ) {
+			In inParens = in.parens();
+			Spaces(inParens);
+			Expr exp = expr(RequiredExpr(inParens));
+			RequiredMatch(')');
+			Spaces(in);
+			return parser.success(exprVar(exp));
+		}
+		String name = Name(in);
+		if( name != null )
+			return parser.success(nameVar(start,name));
+		return parser.failure(null);
+	}
+
+	private Expr env() {
+		int index = stackIndex(_ENV);
+		if( index != -1 )
+			return new GetLocalVar(null,index);
+		index = upValueIndex(_ENV);
+		if( index != -1 )
+			return new GetUpVar(null,index);
+		throw new RuntimeException("_ENV not found");
+	}
+
+	private interface Var {
+		public Expressions expr();
+		public Settable settable();
+	}
+
+	private Var nameVar(final int start,final String name) {
+		return nameVar(se(start),name);
+	}
+
+	private Var nameVar(final LuanSource.Element se,final String name) {
+		return new Var() {
+
+			public Expr expr() {
+				int index = stackIndex(name);
+				if( index != -1 )
+					return new GetLocalVar(se,index);
+				index = upValueIndex(name);
+				if( index != -1 )
+					return new GetUpVar(se,index);
+				return new IndexExpr( se, env(), new ConstExpr(name) );
+			}
+
+			public Settable settable() {
+				int index = stackIndex(name);
+				if( index != -1 )
+					return new SetLocalVar(index);
+				index = upValueIndex(name);
+				if( index != -1 )
+					return new SetUpVar(index);
+				return new SetTableEntry( se, env(), new ConstExpr(name) );
+			}
+		};
+	}
+
+	private Var exprVar(final Expressions expr) {
+		return new Var() {
+
+			public Expressions expr() {
+				return expr;
+			}
+
+			public Settable settable() {
+				return null;
+			}
+		};
+	}
+
+	private Var indexVar(final int start,final Expr table,final Expr key) {
+		return new Var() {
+
+			public Expr expr() {
+				return new IndexExpr( se(start), table, key );
+			}
+
+			public Settable settable() {
+				return new SetTableEntry(se(start),table,key);
+			}
+		};
+	}
+
+	private FnCall Args(In in,int start,Expr fn,List<Expressions> builder) throws ParseException {
+		parser.begin();
+		return args(in,builder)
+			? parser.success( new FnCall( se(start), fn, ExpList.build(builder) ) )
+			: parser.failure((FnCall)null);
+	}
+
+	private boolean args(In in,List<Expressions> builder) throws ParseException {
+		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 true;
+		}
+		Expr exp = TableExpr(in);
+		if( exp != null ) {
+			builder.add(exp);
+			return true;
+		}
+		String s = StringLiteral(in);
+		if( s != null ) {
+			builder.add( new ConstExpr(s) );
+			return true;
+		}
+		Expressions exps = TemplateExpressions(in);
+		if( exps != null ) {
+			builder.add(exps);
+			return true;
+		}
+		return false;
+	}
+
+	private Expressions ExpList(In in) throws ParseException {
+		List<Expressions> builder = new ArrayList<Expressions>();
+		return ExpList(in,builder) ? ExpList.build(builder) : null;
+	}
+
+	private boolean ExpList(In in,List<Expressions> builder) throws ParseException {
+		parser.begin();
+		Expressions exp = TemplateExpressions(in);
+		if( exp != null ) {
+			builder.add(exp);
+			return parser.success();
+		}
+		exp = Expr(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 Expr SubExpr(In in) throws ParseException {
+		parser.begin();
+		if( !parser.match('[') )
+			return parser.failure(null);
+		In inParens = in.parens();
+		Spaces(inParens);
+		Expr exp = expr(RequiredExpr(inParens));
+		RequiredMatch(']');
+		Spaces(in);
+		return parser.success(exp);
+	}
+
+	private Expr NameExpr(In in) throws ParseException {
+		String name = Name(in);
+		return name==null ? null : new ConstExpr(name);
+	}
+
+	private String RequiredName(In in) throws ParseException {
+		parser.begin();
+		String name = Name(in);
+		if( name==null )
+			throw parser.exception("Name expected");
+		return parser.success(name);
+	}
+
+	private String Name(In in) throws ParseException {
+		int start = parser.begin();
+		if( !NameFirstChar() )
+			return parser.failure(null);
+		while( NameChar() );
+		String match = parser.textFrom(start);
+		if( keywords.contains(match) )
+			return parser.failure(null);
+		Spaces(in);
+		return parser.success(match);
+	}
+
+	private boolean NameChar() {
+		return NameFirstChar() || Digit();
+	}
+
+	private boolean NameFirstChar() {
+		return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_');
+	}
+
+	private void RequiredMatch(char c) throws ParseException {
+		if( !parser.match(c) )
+			throw parser.exception("'"+c+"' expected");
+	}
+
+	private void RequiredMatch(String s) throws ParseException {
+		if( !parser.match(s) )
+			throw parser.exception("'"+s+"' expected");
+	}
+
+	private void RequiredKeyword(String keyword,In in) throws ParseException {
+		if( !Keyword(keyword,in) )
+			throw parser.exception("'"+keyword+"' expected");
+	}
+
+	private boolean Keyword(String keyword,In in) throws ParseException {
+		parser.begin();
+		if( !parser.match(keyword) || NameChar() )
+			return parser.failure();
+		Spaces(in);
+		return parser.success();
+	}
+
+	private static final Set<String> keywords = new HashSet<String>(Arrays.asList(
+		"and",
+		"break",
+		"catch",
+		"do",
+		"else",
+		"elseif",
+		"end",
+		"false",
+		"for",
+		"function",
+		"goto",
+		"if",
+		"import",
+		"in",
+		"local",
+		"nil",
+		"not",
+		"or",
+		"repeat",
+		"return",
+		"then",
+		"true",
+		"try",
+		"until",
+		"while"
+	));
+
+	private Expr Literal(In in) throws ParseException {
+		if( NilLiteral(in) )
+			return new ConstExpr(null);
+		Boolean b = BooleanLiteral(in);
+		if( b != null )
+			return new ConstExpr(b);
+		Number n = NumberLiteral(in);
+		if( n != null )
+			return new ConstExpr(n);
+		String s = StringLiteral(in);
+		if( s != null )
+			return new ConstExpr(s);
+		return null;
+	}
+
+	private boolean NilLiteral(In in) throws ParseException {
+		return Keyword("nil",in);
+	}
+
+	private Boolean BooleanLiteral(In in) throws ParseException {
+		if( Keyword("true",in) )
+			return true;
+		if( Keyword("false",in) )
+			return false;
+		return null;
+	}
+
+	private Number NumberLiteral(In in) throws ParseException {
+		parser.begin();
+		Number n;
+		if( parser.matchIgnoreCase("0x") ) {
+			n = HexNumber();
+		} else {
+			n = DecNumber();
+		}
+		if( n==null || NameChar() )
+			return parser.failure(null);
+		Spaces(in);
+		return parser.success(n);
+	}
+
+	private Number DecNumber() {
+		int start = parser.begin();
+		if( Int() ) {
+			if( parser.match('.') )
+				Int();  // optional
+		} else if( parser.match('.') && Int() ) {
+			// ok
+		} else
+			return parser.failure(null);
+		Exponent();  // optional
+		return parser.success(Double.valueOf(parser.textFrom(start)));
+	}
+
+	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();
+		double n;
+		if( HexInt() ) {
+			n = (double)Long.parseLong(parser.textFrom(start),16);
+			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)));
+		}
+		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.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)));
+		}
+		return parser.failure(null);
+	}
+
+	private void Spaces(In in) throws ParseException {
+		while( parser.anyOf(" \t") || Comment() || ContinueOnNextLine() || in.parens && NewLine() );
+	}
+
+	private boolean ContinueOnNextLine() {
+		parser.begin();
+		return parser.match('\\') &&  EndOfLine() ? parser.success() : parser.failure();
+	}
+
+	private boolean NewLine() {
+		if( !EndOfLine() )
+			return false;
+		if( parser.match("--") ) {
+			while( parser.noneOf("\r\n") );
+		}
+		return true;
+	}
+
+	private boolean Comment() 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();
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/LuanStateImpl.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/LuanStateImpl.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,137 @@
+package luan.impl;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.MetatableGetter;
+import luan.LuanException;
+import luan.LuanElement;
+import luan.DeepCloner;
+
+
+final class LuanStateImpl extends LuanState {
+
+	private static class Frame {
+		final Frame previousFrame;
+		final Closure closure;
+		final Object[] stack;
+		final Object[] varArgs;
+		UpValue[] downValues = null;
+
+		Frame( Frame previousFrame, Closure closure, int stackSize, Object[] varArgs) {
+			this.previousFrame = previousFrame;
+			this.closure = closure;
+			this.stack = new Object[stackSize];
+			this.varArgs = varArgs;
+		}
+
+		void stackClear(int start,int end) {
+			if( downValues != null ) {
+				for( int i=start; i<end; i++ ) {
+					UpValue downValue = downValues[i];
+					if( downValue != null ) {
+						downValue.close();
+						downValues[i] = null;
+					}
+				}
+			}
+			for( int i=start; i<end; i++ ) {
+				stack[i] = null;
+			}
+		}
+
+		UpValue getUpValue(int index) {
+			if( downValues==null )
+				downValues = new UpValue[stack.length];
+			if( downValues[index] == null )
+				downValues[index] = new UpValue(stack,index);
+			return downValues[index];
+		}
+	}
+
+	private Frame frame = null;
+	Object returnValues;
+	Closure tailFn;
+	Map<UpValue.EnvGetter,UpValue> envs = new HashMap<UpValue.EnvGetter,UpValue>();
+
+	LuanStateImpl() {}
+
+	private LuanStateImpl(LuanStateImpl luan) {
+		super(luan);
+	}
+
+	@Override public LuanState shallowClone() {
+//		if( frame != null )
+//			throw new IllegalStateException("frame isn't null");
+		return new LuanStateImpl(this);
+	}
+
+	@Override public void deepenClone(LuanState clone,DeepCloner cloner) {
+		super.deepenClone(clone,cloner);
+		LuanStateImpl cloneImpl = (LuanStateImpl)clone;
+		cloneImpl.envs = new HashMap<UpValue.EnvGetter,UpValue>();
+		for( Map.Entry<UpValue.EnvGetter,UpValue> entry : envs.entrySet() ) {
+			cloneImpl.envs.put( entry.getKey(), cloner.deepClone(entry.getValue()) );
+		}
+	}
+
+	// returns stack
+	Object[] newFrame(Closure closure, int stackSize, Object[] varArgs) {
+		returnValues = LuanFunction.NOTHING;
+		tailFn = null;
+		frame = new Frame(frame,closure,stackSize,varArgs);
+		return frame.stack;
+	}
+
+	void popFrame() {
+		returnValues = LuanFunction.NOTHING;
+		tailFn = null;
+		frame = frame.previousFrame;
+	}
+
+	Object stackGet(int index) {
+		return frame.stack[index];
+	}
+
+	void stackSet(int index,Object value) {
+		frame.stack[index] = value;
+	}
+
+	void stackClear(int start,int end) {
+		frame.stackClear(start,end);
+	}
+
+	Object[] varArgs() {
+		return frame.varArgs;
+	}
+
+	Closure closure() {
+		return frame.closure;
+	}
+
+	UpValue getUpValue(int index) {
+		return frame.getUpValue(index);
+	}
+
+	UpValue getUpValue(UpValue.EnvGetter getter) throws LuanException {
+		UpValue uv = envs.get(getter);
+		if( uv == null ) {
+			LuanTable env = new LuanTable();
+			uv = new UpValue(env);
+			envs.put(getter,uv);
+		}
+		return uv;
+	}
+
+	@Override public LuanTable currentEnvironment() {
+		if( frame==null )
+			return null;
+		return (LuanTable)frame.closure.upValues()[0].get();
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ModExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ModExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,23 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class ModExpr extends BinaryOpExpr {
+
+	ModExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		Number n1 = Luan.toNumber(o1);
+		Number n2 = Luan.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return n1.doubleValue() % n2.doubleValue();
+		return arithmetic(luan,"__mod",o1,o2);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/MulExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/MulExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,23 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class MulExpr extends BinaryOpExpr {
+
+	MulExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		Number n1 = Luan.toNumber(o1);
+		Number n2 = Luan.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return n1.doubleValue() * n2.doubleValue();
+		return arithmetic(luan,"__mul",o1,o2);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/NotExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/NotExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,17 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class NotExpr extends UnaryOpExpr {
+
+	NotExpr(LuanSource.Element se,Expr op) {
+		super(se,op);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		return !Luan.toBoolean(op.eval(luan));
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/OrExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/OrExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,18 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class OrExpr extends BinaryOpExpr {
+
+	OrExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object v1 = op1.eval(luan);
+		return Luan.toBoolean(v1) ? v1 : op2.eval(luan);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ParseException.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ParseException.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,55 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+public final class ParseException extends Exception {
+	public final LuanSource src;
+	public final int iCurrent;
+	public final int iHigh;
+
+	ParseException(String msg,LuanSource src,int iCurrent,int iHigh) {
+		super(msg);
+		this.src = src;
+		this.iCurrent = iCurrent;
+		this.iHigh = iHigh;
+//System.out.println("iCurrent = "+iCurrent);
+//System.out.println("iHigh = "+iHigh);
+	}
+
+	private class Location {
+		final int line;
+		final int pos;
+
+		Location(int index) {
+			int line = 0;
+			int i = -1;
+			while(true) {
+				int j = src.text.indexOf('\n',i+1);
+				if( j == -1 || j >= index )
+					break;
+				i = j;
+				line++;
+			}
+			this.line = line;
+			this.pos = index - i - 1;
+		}
+	}
+
+	private String[] lines() {
+		return src.text.split("\n",-1);
+	}
+
+	public String getFancyMessage() {
+		Location loc = new Location(iCurrent);
+		String line = lines()[loc.line];
+		String msg = getMessage() +  " (line " + (loc.line+1) + ", pos " + (loc.pos+1) + ") in " + src.name + "\n";
+		StringBuilder sb = new StringBuilder(msg);
+		sb.append( line + "\n" );
+		for( int i=0; i<loc.pos; i++ ) {
+			sb.append( line.charAt(i)=='\t' ? '\t' : ' ' );
+		}
+		sb.append("^\n");
+		return sb.toString();
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/Parser.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/Parser.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,152 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+final class Parser {
+	private final LuanSource src;
+	public final String text;
+	private final int len;
+	private int[] stack = new int[256];
+	private int frame = 0;
+	private int iHigh;
+
+	public Parser(LuanSource src) {
+		this.src = src;
+		this.text = src.text;
+		this.len = text.length();
+	}
+
+	private int i() {
+		return stack[frame];
+	}
+
+	private void i(int i) {
+		stack[frame] += i;
+		if( iHigh < stack[frame] )
+			iHigh = stack[frame];
+	}
+
+	public int begin() {
+		frame++;
+		if( frame == stack.length ) {
+			int[] a = new int[2*frame];
+			System.arraycopy(stack,0,a,0,frame);
+			stack = a;
+		}
+		stack[frame] = stack[frame-1];
+		return i();
+	}
+
+	public void rollback() {
+		stack[frame] = stack[frame-1];
+	}
+
+	public <T> T success(T t) {
+		success();
+		return t;
+	}
+
+	public boolean success() {
+		frame--;
+		stack[frame] = stack[frame+1];
+		return true;
+	}
+
+	public <T> T failure(T t) {
+		failure();
+		return t;
+	}
+
+	public boolean failure() {
+		frame--;
+		return false;
+	}
+
+	public ParseException exception(String msg) {
+		return new ParseException(msg,src,i(),iHigh);
+	}
+
+	public ParseException exception() {
+		return exception("Invalid input");
+	}
+
+	public int currentIndex() {
+		return i();
+	}
+
+	public char lastChar() {
+		return text.charAt(i()-1);
+	}
+
+	public char currentChar() {
+		return text.charAt(i());
+	}
+
+	public boolean endOfInput() {
+		return i() >= len;
+	}
+
+	public boolean match(char c) {
+		if( endOfInput() || text.charAt(i()) != c )
+			return false;
+		i(1);
+		return true;
+	}
+
+	public boolean match(String s) {
+		int n = s.length();
+		if( !text.regionMatches(i(),s,0,n) )
+			return false;
+		i(n);
+		return true;
+	}
+
+	public boolean matchIgnoreCase(String s) {
+		int n = s.length();
+		if( !text.regionMatches(true,i(),s,0,n) )
+			return false;
+		i(n);
+		return true;
+	}
+
+	public boolean anyOf(String s) {
+		if( endOfInput() || s.indexOf(text.charAt(i())) == -1 )
+			return false;
+		i(1);
+		return true;
+	}
+
+	public boolean noneOf(String s) {
+		if( endOfInput() || s.indexOf(text.charAt(i())) != -1 )
+			return false;
+		i(1);
+		return true;
+	}
+
+	public boolean inCharRange(char cLow, char cHigh) {
+		if( endOfInput() )
+			return false;
+		char c = text.charAt(i());
+		if( !(cLow <= c && c <= cHigh) )
+			return false;
+		i(1);
+		return true;
+	}
+
+	public boolean anyChar() {
+		if( endOfInput() )
+			return false;
+		i(1);
+		return true;
+	}
+
+	public boolean test(String s) {
+		return text.regionMatches(i(),s,0,s.length());
+	}
+
+	public String textFrom(int start) {
+		return text.substring(start,i());
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/PowExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/PowExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,23 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class PowExpr extends BinaryOpExpr {
+
+	PowExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		Number n1 = Luan.toNumber(o1);
+		Number n2 = Luan.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return Math.pow( n1.doubleValue(), n2.doubleValue() );
+		return arithmetic(luan,"__pow",o1,o2);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/RepeatStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/RepeatStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,25 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class RepeatStmt extends CodeImpl implements Stmt {
+	private final Stmt doStmt;
+	private final Expr cnd;
+
+	RepeatStmt(LuanSource.Element se,Stmt doStmt,Expr cnd) {
+		super(se);
+		this.doStmt = doStmt;
+		this.cnd = cnd;
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		try {
+			do {
+				doStmt.eval(luan);
+			} while( !luan.bit(se).checkBoolean( cnd.eval(luan) ) );
+		} catch(BreakException e) {}
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ReturnException.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ReturnException.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,4 @@
+package luan.impl;
+
+
+final class ReturnException extends RuntimeException {}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/ReturnStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/ReturnStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,39 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanFunction;
+import luan.LuanSource;
+
+
+final class ReturnStmt extends CodeImpl implements Stmt {
+	private final Expressions expressions;
+	private final Expr tailFnExpr;
+	boolean throwReturnException = true;
+
+	ReturnStmt(LuanSource.Element se,Expressions expressions) {
+		super(se);
+		if( expressions instanceof FnCall ) {  // tail call
+			FnCall fnCall = (FnCall)expressions;
+			this.expressions = fnCall.args;
+			this.tailFnExpr = fnCall.fnExpr;
+		} else {
+			this.expressions = expressions;
+			this.tailFnExpr = null;
+		}
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		luan.returnValues = expressions.eval(luan);
+		if( tailFnExpr != null ) {
+			LuanFunction tailFn = luan.bit(se).checkFunction( tailFnExpr.eval(luan) );
+			if( tailFn instanceof Closure ) {
+				luan.tailFn = (Closure)tailFn;
+			} else {
+				luan.returnValues =  luan.bit(tailFnExpr.se()).call(tailFn,tailFnExpr.se().text(),Luan.array(luan.returnValues));
+			}
+		}
+		if( throwReturnException )
+			throw new ReturnException();
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/SetLocalVar.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/SetLocalVar.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,14 @@
+package luan.impl;
+
+
+final class SetLocalVar implements Settable {
+	private final int index;
+
+	SetLocalVar(int index) {
+		this.index = index;
+	}
+
+	@Override public void set(LuanStateImpl luan,Object value) {
+		luan.stackSet( index, value );
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/SetStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/SetStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,36 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+
+
+final class SetStmt implements Stmt {
+	private final Settable[] vars;
+	private final Expressions expressions;
+
+	SetStmt(Settable var,Expr expr) {
+		this( new Settable[]{var}, expr );
+	}
+
+	SetStmt(Settable[] vars,Expressions expressions) {
+		this.vars = vars;
+		this.expressions = expressions;
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		final Object obj = expressions.eval(luan);
+		if( obj instanceof Object[] ) {
+			Object[] vals = (Object[])obj;
+			for( int i=0; i<vars.length; i++ ) {
+				Object val = i < vals.length ? vals[i] : null;
+				vars[i].set(luan,val);
+			}
+		} else {
+			vars[0].set(luan,obj);
+			for( int i=1; i<vars.length; i++ ) {
+				vars[i].set(luan,null);
+			}
+		}
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/SetTableEntry.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/SetTableEntry.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,48 @@
+package luan.impl;
+
+import luan.LuanException;
+import luan.LuanTable;
+import luan.Luan;
+import luan.LuanFunction;
+import luan.LuanSource;
+
+
+final class SetTableEntry extends CodeImpl implements Settable {
+	private final Expr tableExpr;
+	private final Expr keyExpr;
+
+	SetTableEntry(LuanSource.Element se,Expr tableExpr,Expr keyExpr) {
+		super(se);
+		this.tableExpr = tableExpr;
+		this.keyExpr = keyExpr;
+	}
+
+	@Override public void set(LuanStateImpl luan,Object value) throws LuanException {
+		newindex( luan, tableExpr.eval(luan), keyExpr.eval(luan), value );
+	}
+
+	private void newindex(LuanStateImpl luan,Object t,Object key,Object value) throws LuanException {
+		Object h;
+		if( t instanceof LuanTable ) {
+			LuanTable table = (LuanTable)t;
+			Object old = table.put(key,value);
+			if( old != null )
+				return;
+			h = luan.getHandler("__newindex",t);
+			if( h==null )
+				return;
+			table.put(key,old);
+		} else {
+			h = luan.getHandler("__newindex",t);
+			if( h==null )
+				throw luan.bit(se).exception( "attempt to index a " + Luan.type(t) + " value" );
+		}
+		if( h instanceof LuanFunction ) {
+			LuanFunction fn = (LuanFunction)h;
+			luan.bit(se).call(fn,"__newindex",new Object[]{t,key,value});
+			return;
+		}
+		newindex(luan,h,key,value);
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/SetUpVar.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/SetUpVar.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,14 @@
+package luan.impl;
+
+
+final class SetUpVar implements Settable {
+	private final int index;
+
+	SetUpVar(int index) {
+		this.index = index;
+	}
+
+	@Override public void set(LuanStateImpl luan,Object value) {
+		luan.closure().upValues()[index].set(value);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/Settable.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/Settable.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,8 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+interface Settable {
+	public void set(LuanStateImpl luan,Object value) throws LuanException;
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/Stmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/Stmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,12 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+interface Stmt {
+	public void eval(LuanStateImpl luan) throws LuanException;
+
+	static final Stmt EMPTY = new Stmt() {
+		@Override public void eval(LuanStateImpl luan) {}
+	};
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/SubExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/SubExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,23 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class SubExpr extends BinaryOpExpr {
+
+	SubExpr(LuanSource.Element se,Expr op1,Expr op2) {
+		super(se,op1,op2);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o1 = op1.eval(luan);
+		Object o2 = op2.eval(luan);
+		Number n1 = Luan.toNumber(o1);
+		Number n2 = Luan.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return n1.doubleValue() - n2.doubleValue();
+		return arithmetic(luan,"__sub",o1,o2);
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/TableExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/TableExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,45 @@
+package luan.impl;
+
+import luan.LuanException;
+import luan.LuanTable;
+import luan.LuanSource;
+
+
+final class TableExpr extends CodeImpl implements Expr {
+
+	static class Field {
+		final Expr key;
+		final Expr value;
+
+		Field(Expr key,Expr value) {
+			this.key = key;
+			this.value = value;
+		}
+	}
+
+	private final Field[] fields;
+	private final Expressions expressions;
+
+	TableExpr(LuanSource.Element se,Field[] fields,Expressions expressions) {
+		super(se);
+		this.fields = fields;
+		this.expressions = expressions;
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		LuanTable table = new LuanTable();
+		for( Field field : fields ) {
+			table.put( field.key.eval(luan), field.value.eval(luan) );
+		}
+		Object obj = expressions.eval(luan);
+		if( obj instanceof Object[] ) {
+			Object[] a = (Object[])obj;
+			for( int i=0; i<a.length; i++ ) {
+				table.put( i+1, a[i] );
+			}
+		} else {
+			table.put( 1, obj );
+		}
+		return table;
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/TryStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/TryStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,30 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+
+
+final class TryStmt implements Stmt {
+	private final Stmt tryBlock;
+	private final int iExceptionVar;
+	private final Stmt catchBlock;
+
+	TryStmt(Stmt tryBlock,int iExceptionVar,Stmt catchBlock) {
+		this.tryBlock = tryBlock;
+		this.iExceptionVar = iExceptionVar;
+		this.catchBlock = catchBlock;
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		try {
+			tryBlock.eval(luan);
+		} catch(LuanException e) {
+			try {
+				luan.stackSet( iExceptionVar, e );
+				catchBlock.eval(luan);
+			} finally {
+				luan.stackClear(iExceptionVar,iExceptionVar+1);
+			}
+		}
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/UnaryOpExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/UnaryOpExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,13 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+abstract class UnaryOpExpr extends CodeImpl implements Expr {
+	final Expr op;
+
+	UnaryOpExpr(LuanSource.Element se,Expr op) {
+		super(se);
+		this.op = op;
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/UnmExpr.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/UnmExpr.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,29 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.LuanSource;
+import luan.LuanBit;
+
+
+// unary minus
+final class UnmExpr extends UnaryOpExpr {
+
+	UnmExpr(LuanSource.Element se,Expr op) {
+		super(se,op);
+	}
+
+	@Override public Object eval(LuanStateImpl luan) throws LuanException {
+		Object o = op.eval(luan);
+		Number n = Luan.toNumber(o);
+		if( n != null )
+			return -n.doubleValue();
+		LuanBit bit = luan.bit(se);
+		LuanFunction fn = bit.getHandlerFunction("__unm",o);
+		if( fn != null ) {
+			return Luan.first(bit.call(fn,"__unm",new Object[]{o}));
+		}
+		throw bit.exception("attempt to perform arithmetic on a "+Luan.type(o)+" value");
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/UpValue.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/UpValue.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,105 @@
+package luan.impl;
+
+import luan.DeepCloner;
+import luan.DeepCloneable;
+import luan.LuanException;
+
+
+final class UpValue implements DeepCloneable<UpValue> {
+	private Object[] stack;
+	private int index;
+	private boolean isClosed = false;
+	private Object value;
+
+	UpValue(Object[] stack,int index) {
+		this.stack = stack;
+		this.index = index;
+	}
+
+	UpValue(Object value) {
+		this.value = value;
+		this.isClosed = true;
+	}
+
+	private UpValue() {}
+
+	@Override public UpValue shallowClone() {
+		return new UpValue();
+	}
+
+	@Override public void deepenClone(UpValue clone,DeepCloner cloner) {
+		clone.isClosed = isClosed;
+		if( isClosed ) {
+			clone.value = cloner.get(value);
+		} else {
+			clone.stack = cloner.deepClone(stack);
+			clone.index = index;
+		}
+	}
+
+	Object get() {
+		return isClosed ? value : stack[index];
+	}
+
+	void set(Object value) {
+		if( isClosed ) {
+			this.value = value;
+		} else {
+			stack[index] = value;
+		}
+	}
+
+	void close() {
+		value = stack[index];
+		isClosed = true;
+		stack = null;
+	}
+
+	static interface Getter {
+		public UpValue get(LuanStateImpl luan) throws LuanException;
+	}
+
+	static final class StackGetter implements Getter {
+		private final int index;
+
+		StackGetter(int index) {
+			this.index = index;
+		}
+
+		public UpValue get(LuanStateImpl luan) {
+			return luan.getUpValue(index);
+		}
+	}
+
+	static final class NestedGetter implements Getter {
+		private final int index;
+
+		NestedGetter(int index) {
+			this.index = index;
+		}
+
+		public UpValue get(LuanStateImpl luan) {
+			return luan.closure().upValues()[index];
+		}
+	}
+
+	static final class EnvGetter implements Getter {
+
+		public UpValue get(LuanStateImpl luan) throws LuanException {
+			return luan.getUpValue(this);
+		}
+	}
+
+	static final class ValueGetter implements Getter {
+		private final UpValue upValue;
+
+		ValueGetter(Object value) {
+			this.upValue = new UpValue(value);
+		}
+
+		public UpValue get(LuanStateImpl luan) {
+			return upValue;
+		}
+	}
+
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/VarArgs.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/VarArgs.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,15 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+final class VarArgs extends CodeImpl implements Expressions {
+
+	VarArgs(LuanSource.Element se) {
+		super(se);
+	}
+
+	@Override public Object[] eval(LuanStateImpl luan) {
+		return luan.varArgs();
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/impl/WhileStmt.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/impl/WhileStmt.java	Sun Jun 22 04:17:38 2014 +0000
@@ -0,0 +1,25 @@
+package luan.impl;
+
+import luan.Luan;
+import luan.LuanException;
+import luan.LuanSource;
+
+
+final class WhileStmt extends CodeImpl implements Stmt {
+	private final Expr cnd;
+	private final Stmt doStmt;
+
+	WhileStmt(LuanSource.Element se,Expr cnd,Stmt doStmt) {
+		super(se);
+		this.cnd = cnd;
+		this.doStmt = doStmt;
+	}
+
+	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		try {
+			while( luan.bit(se).checkBoolean( cnd.eval(luan) ) ) {
+				doStmt.eval(luan);
+			}
+		} catch(BreakException e) {}
+	}
+}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/AddExpr.java
--- a/src/luan/interp/AddExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class AddExpr extends BinaryOpExpr {
-
-	AddExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		Number n1 = Luan.toNumber(o1);
-		Number n2 = Luan.toNumber(o2);
-		if( n1 != null && n2 != null )
-			return n1.doubleValue() + n2.doubleValue();
-		return arithmetic(luan,"__add",o1,o2);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/AndExpr.java
--- a/src/luan/interp/AndExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class AndExpr extends BinaryOpExpr {
-
-	AndExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object v1 = op1.eval(luan);
-		return !Luan.toBoolean(v1) ? v1 : op2.eval(luan);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/BinaryOpExpr.java
--- a/src/luan/interp/BinaryOpExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-abstract class BinaryOpExpr extends CodeImpl implements Expr {
-	final Expr op1;
-	final Expr op2;
-
-	BinaryOpExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se);
-		this.op1 = op1;
-		this.op2 = op2;
-	}
-
-	Object arithmetic(LuanStateImpl luan,String op,Object o1,Object o2) throws LuanException {
-		return luan.bit(se()).arithmetic("__mod",o1,o2);
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/Block.java
--- a/src/luan/interp/Block.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-
-
-final class Block implements Stmt {
-	final Stmt[] stmts;
-	private final int stackStart;
-	private final int stackEnd;
-
-	Block(Stmt[] stmts,int stackStart,int stackEnd) {
-		if( stmts.length==0 )
-			throw new RuntimeException("empty block");
-		this.stmts = stmts;
-		this.stackStart = stackStart;
-		this.stackEnd = stackEnd;
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		try {
-			for( Stmt stmt : stmts ) {
-				stmt.eval(luan);
-			}
-		} finally {
-			luan.stackClear(stackStart,stackEnd);
-		}
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/BreakException.java
--- a/src/luan/interp/BreakException.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-package luan.interp;
-
-
-final class BreakException extends RuntimeException {}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/BreakStmt.java
--- a/src/luan/interp/BreakStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-package luan.interp;
-
-
-final class BreakStmt implements Stmt {
-
-	@Override public void eval(LuanStateImpl luan) {
-		throw new BreakException();
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/Closure.java
--- a/src/luan/interp/Closure.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanFunction;
-import luan.LuanState;
-import luan.LuanElement;
-import luan.LuanException;
-import luan.DeepCloner;
-import luan.DeepCloneable;
-
-
-final class Closure extends LuanFunction implements DeepCloneable<Closure> {
-	private final FnDef fnDef;
-	private UpValue[] upValues;
-
-	Closure(LuanStateImpl luan,FnDef fnDef) throws LuanException {
-		this.fnDef = fnDef;
-		UpValue.Getter[] upValueGetters = fnDef.upValueGetters;
-		upValues = new UpValue[upValueGetters.length];
-		for( int i=0; i<upValues.length; i++ ) {
-			upValues[i] = upValueGetters[i].get(luan);
-		}
-	}
-
-	private Closure(Closure c) {
-		this.fnDef = c.fnDef;
-	}
-
-	@Override public Closure shallowClone() {
-		return new Closure(this);
-	}
-
-	@Override public void deepenClone(Closure clone,DeepCloner cloner) {
-		clone.upValues = cloner.deepClone(upValues);
-	}
-
-	UpValue[] upValues() {
-		return upValues;
-	}
-
-	@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-		return call(this,(LuanStateImpl)luan,args);
-	}
-
-	private static Object call(Closure closure,LuanStateImpl luan,Object[] args) throws LuanException {
-		while(true) {
-			FnDef fnDef = closure.fnDef;
-			Object[] varArgs = null;
-			if( fnDef.isVarArg ) {
-				if( args.length > fnDef.numArgs ) {
-					varArgs = new Object[ args.length - fnDef.numArgs ];
-					for( int i=0; i<varArgs.length; i++ ) {
-						varArgs[i] = args[fnDef.numArgs+i];
-					}
-				} else {
-					varArgs = LuanFunction.NOTHING;
-				}
-			}
-			Object[] stack = luan.newFrame(closure,fnDef.stackSize,varArgs);
-			final int n = Math.min(args.length,fnDef.numArgs);
-			for( int i=0; i<n; i++ ) {
-				stack[i] = args[i];
-			}
-			Object returnValues;
-			try {
-				fnDef.block.eval(luan);
-			} catch(ReturnException e) {
-			} finally {
-				returnValues = luan.returnValues;
-				closure = luan.tailFn;
-				luan.popFrame();
-			}
-			if( closure == null )
-				return returnValues;
-			args = Luan.array(returnValues);
-		}
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/Code.java
--- a/src/luan/interp/Code.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-interface Code {
-	public LuanSource.Element se();
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/CodeImpl.java
--- a/src/luan/interp/CodeImpl.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-class CodeImpl implements Code {
-	final LuanSource.Element se;
-
-	CodeImpl(LuanSource.Element se) {
-		this.se = se;
-	}
-
-	@Override public final LuanSource.Element se() {
-		return se;
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ConcatExpr.java
--- a/src/luan/interp/ConcatExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanFunction;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class ConcatExpr extends BinaryOpExpr {
-
-	ConcatExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		String s1 = luan.bit(op1.se()).toString(o1);
-		String s2 = luan.bit(op2.se()).toString(o2);
-/*
-		if( s1 != null && s2 != null )
-			return s1 + s2;
-		LuanFunction fn = luan.getBinHandler(se,"__concat",o1,o2);
-		if( fn != null )
-			return Luan.first(luan.call(fn,se,"__concat",o1,o2));
-		String type = s1==null ? Luan.type(o1) : Luan.type(o2);
-		throw new LuanException( luan, se, "attempt to concatenate a " + type + " value" );
-*/
-		return s1 + s2;
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ConstExpr.java
--- a/src/luan/interp/ConstExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-final class ConstExpr extends CodeImpl implements Expr {
-	private final Object obj;
-
-	ConstExpr(Object obj) {
-		this(null,obj);
-	}
-
-	ConstExpr(LuanSource.Element se,Object obj) {
-		super(se);
-		this.obj = obj;
-	}
-
-	@Override public Object eval(LuanStateImpl luan) {
-		return obj;
-	}
-
-	@Override public String toString() {
-		return "(ConstExpr "+obj+")";
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/DivExpr.java
--- a/src/luan/interp/DivExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class DivExpr extends BinaryOpExpr {
-
-	DivExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		Number n1 = Luan.toNumber(o1);
-		Number n2 = Luan.toNumber(o2);
-		if( n1 != null && n2 != null )
-			return n1.doubleValue() / n2.doubleValue();
-		return arithmetic(luan,"__div",o1,o2);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/EqExpr.java
--- a/src/luan/interp/EqExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanFunction;
-import luan.LuanTable;
-import luan.LuanException;
-import luan.LuanSource;
-import luan.LuanBit;
-
-
-final class EqExpr extends BinaryOpExpr {
-
-	EqExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		if( o1 == o2 || o1 != null && o1.equals(o2) )
-			return true;
-		if( o1 instanceof Number && o2 instanceof Number ) {
-			Number n1 = (Number)o1;
-			Number n2 = (Number)o2;
-			return n1.doubleValue() == n2.doubleValue();
-		}
-		if( o1==null || o2==null || !o1.getClass().equals(o2.getClass()) )
-			return false;
-		LuanTable mt1 = luan.getMetatable(o1);
-		LuanTable mt2 = luan.getMetatable(o2);
-		if( mt1==null || mt2==null )
-			return false;
-		Object f = mt1.get("__eq");
-		if( f == null || !f.equals(mt2.get("__eq")) )
-			return null;
-		LuanBit bit = luan.bit(se);
-		LuanFunction fn = bit.checkFunction(f);
-		return Luan.toBoolean( Luan.first(bit.call(fn,"__eq",new Object[]{o1,o2})) );
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ExpList.java
--- a/src/luan/interp/ExpList.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-package luan.interp;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Arrays;
-import luan.LuanException;
-import luan.LuanSource;
-import luan.LuanFunction;
-import luan.Luan;
-
-
-final class ExpList {
-
-	static final Expressions emptyExpList = new Expressions() {
-
-		@Override public Object[] eval(LuanStateImpl luan) {
-			return LuanFunction.NOTHING;
-		}
-
-		@Override public LuanSource.Element se() {
-			return null;
-		}
-	};
-
-	static Expr[] toArray(List<Expressions> list) {
-		Expr[] a = new Expr[list.size()];
-		for( int i=0; i<a.length; i++ ) {
-			Expressions exprs = list.get(i);
-			if( exprs instanceof Expr ) {
-				a[i] = (Expr)exprs;
-			} else {
-				a[i] = new ExpressionsExpr(exprs);
-			}
-		}
-		return a;
-	}
-
-	static Expressions build(List<Expressions> list) {
-		switch(list.size()) {
-		case 0:
-			return emptyExpList;
-		case 1:
-			return list.get(0);
-		default:
-			if( list.get(list.size()-1) instanceof Expr ) {
-				return new ExprList1( toArray(list) );
-			} else {
-				Expressions last = list.remove(list.size()-1);
-				return new ExprList2( toArray(list), last );
-			}
-		}
-	}
-
-	private static class ExprList1 implements Expressions {
-		private final Expr[] exprs;
-
-		private ExprList1(Expr[] exprs) {
-			this.exprs = exprs;
-		}
-	
-		@Override public Object eval(LuanStateImpl luan) throws LuanException {
-			Object[] a = new Object[exprs.length];
-			for( int i=0; i<exprs.length; i++ ) {
-				a[i] = exprs[i].eval(luan);
-			}
-			return a;
-		}
-	
-		@Override public LuanSource.Element se() {
-			return new LuanSource.Element(exprs[0].se().source,exprs[0].se().start,exprs[exprs.length-1].se().end);
-		}
-	}
-
-	private static class ExprList2 implements Expressions {
-		private final Expr[] exprs;
-		private final Expressions last;
-	
-		private ExprList2(Expr[] exprs,Expressions last) {
-			this.exprs = exprs;
-			this.last = last;
-		}
-	
-		@Override public Object eval(LuanStateImpl luan) throws LuanException {
-			List<Object> list = new ArrayList<Object>();
-			for( Expr expr : exprs ) {
-				list.add( expr.eval(luan) );
-			}
-			list.addAll( Arrays.asList(Luan.array( last.eval(luan) )) );
-			return list.toArray();
-		}
-	
-		@Override public LuanSource.Element se() {
-			return new LuanSource.Element(exprs[0].se().source,exprs[0].se().start,last.se().end);
-		}
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/Expr.java
--- a/src/luan/interp/Expr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-
-
-interface Expr extends Expressions {}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/Expressions.java
--- a/src/luan/interp/Expressions.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-
-
-interface Expressions extends Code {
-	public Object eval(LuanStateImpl luan) throws LuanException;
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ExpressionsExpr.java
--- a/src/luan/interp/ExpressionsExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-package luan.interp;
-
-import java.util.List;
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class ExpressionsExpr implements Expr {
-	private final Expressions expressions;
-
-	ExpressionsExpr(Expressions expressions) {
-		this.expressions = expressions;
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		return Luan.first( expressions.eval(luan) );
-	}
-
-	public LuanSource.Element se() {
-		return expressions.se();
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ExpressionsStmt.java
--- a/src/luan/interp/ExpressionsStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-
-
-final class ExpressionsStmt implements Stmt {
-	private final Expressions expressions;
-
-	ExpressionsStmt(Expressions expressions) {
-		this.expressions = expressions;
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		expressions.eval(luan);
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/FnCall.java
--- a/src/luan/interp/FnCall.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanFunction;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class FnCall extends CodeImpl implements Expressions {
-	final Expr fnExpr;
-	final Expressions args;
-	final String fnName;
-
-	FnCall(LuanSource.Element se,Expr fnExpr,Expressions args) {
-		super(se);
-		this.fnExpr = fnExpr;
-		this.args = args;
-		this.fnName = fnExpr.se().text();
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		return call( luan, fnExpr.eval(luan) );
-	}
-
-	private Object call(LuanStateImpl luan,Object o) throws LuanException {
-		if( o instanceof LuanFunction ) {
-			LuanFunction fn = (LuanFunction)o;
-			return luan.bit(se).call( fn, fnName, Luan.array(args.eval(luan)) );
-		}
-		Object h = luan.getHandler("__call",o);
-		if( h != null )
-			return call(luan,h);
-		throw luan.bit(fnExpr.se()).exception( "attempt to call '"+fnExpr.se().text()+"' (a " + Luan.type(o) + " value)" );
-	}
-
-	@Override public String toString() {
-		return "(FnCall "+fnName+" "+fnExpr+" "+args+")";
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/FnDef.java
--- a/src/luan/interp/FnDef.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class FnDef extends CodeImpl implements Expr {
-	final Stmt block;
-	final int stackSize;
-	final int numArgs;
-	final boolean isVarArg;
-	final UpValue.Getter[] upValueGetters;
-
-	FnDef(LuanSource.Element se,Stmt block,int stackSize,int numArgs,boolean isVarArg,UpValue.Getter[] upValueGetters) {
-		super(se);
-		this.block = block;
-		this.stackSize = stackSize;
-		this.numArgs = numArgs;
-		this.isVarArg = isVarArg;
-		this.upValueGetters = upValueGetters;
-		fixReturns(block);
-	}
-
-	private static void fixReturns(Stmt stmt) {
-		if( stmt instanceof ReturnStmt ) {
-			ReturnStmt rs = (ReturnStmt)stmt;
-			rs.throwReturnException = false;
-		} else if( stmt instanceof Block ) {
-			Block b = (Block)stmt;
-			fixReturns( b.stmts[b.stmts.length-1] );
-		} else if( stmt instanceof IfStmt ) {
-			IfStmt is = (IfStmt)stmt;
-			fixReturns( is.thenStmt );
-			fixReturns( is.elseStmt );
-		}
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		return new Closure(luan,this);
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ForStmt.java
--- a/src/luan/interp/ForStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanFunction;
-import luan.LuanSource;
-import luan.LuanBit;
-
-
-final class ForStmt extends CodeImpl implements Stmt {
-	private final int iVars;
-	private final int nVars;
-	private final Expr iterExpr;
-	private final Stmt block;
-
-	ForStmt(LuanSource.Element se,int iVars,int nVars,Expr iterExpr,Stmt block) {
-		super(se);
-		this.iVars = iVars;
-		this.nVars = nVars;
-		this.iterExpr = iterExpr;
-		this.block = block;
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		LuanFunction iter = luan.bit(se).checkFunction( iterExpr.eval(luan) );
-		LuanBit bit = luan.bit(iterExpr.se());
-		String name = iterExpr.se().text();
-		try {
-			while(true) {
-				Object vals = bit.call(iter,name,LuanFunction.NOTHING);
-				if( vals==null )
-					break;
-				if( vals instanceof Object[] ) {
-					Object[] a = (Object[])vals;
-					if( a.length==0 )
-						break;
-					for( int i=0; i<nVars; i++ ) {
-						luan.stackSet( iVars+i, i < a.length ? a[i] : null );
-					}
-				} else {
-					luan.stackSet( iVars, vals );
-					for( int i=1; i<nVars; i++ ) {
-						luan.stackSet( iVars+i, null );
-					}
-				}
-				block.eval(luan);
-			}
-		} catch(BreakException e) {
-		} finally {
-			luan.stackClear(iVars,iVars+nVars);
-		}
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/GetLocalVar.java
--- a/src/luan/interp/GetLocalVar.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-final class GetLocalVar extends CodeImpl implements Expr {
-	private final int index;
-
-	GetLocalVar(LuanSource.Element se,int index) {
-		super(se);
-		this.index = index;
-	}
-
-	@Override public Object eval(LuanStateImpl luan) {
-		return luan.stackGet(index);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/GetUpVar.java
--- a/src/luan/interp/GetUpVar.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-final class GetUpVar extends CodeImpl implements Expr {
-	private final int index;
-
-	GetUpVar(LuanSource.Element se,int index) {
-		super(se);
-		this.index = index;
-	}
-
-	@Override public Object eval(LuanStateImpl luan) {
-		return luan.closure().upValues()[index].get();
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/IfStmt.java
--- a/src/luan/interp/IfStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class IfStmt extends CodeImpl implements Stmt {
-	private final Expr cnd;
-	final Stmt thenStmt;
-	final Stmt elseStmt;
-
-	IfStmt(LuanSource.Element se,Expr cnd,Stmt thenStmt,Stmt elseStmt) {
-		super(se);
-		this.cnd = cnd;
-		this.thenStmt = thenStmt;
-		this.elseStmt = elseStmt;
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		if( luan.bit(se).checkBoolean( cnd.eval(luan) ) ) {
-			thenStmt.eval(luan);
-		} else {
-			elseStmt.eval(luan);
-		}
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/IndexExpr.java
--- a/src/luan/interp/IndexExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanSource;
-
-
-final class IndexExpr extends BinaryOpExpr {
-
-	IndexExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		return index(luan,op1.eval(luan),op2.eval(luan));
-	}
-
-	private Object index(LuanStateImpl luan,Object t,Object key) throws LuanException {
-		Object h;
-		if( t instanceof LuanTable ) {
-			LuanTable tbl = (LuanTable)t;
-			Object value = tbl.get(key);
-			if( value != null )
-				return value;
-			h = luan.getHandler("__index",t);
-			if( h==null )
-				return null;
-		} else {
-			h = luan.getHandler("__index",t);
-			if( h==null )
-				throw luan.bit(op1.se()).exception( "attempt to index '"+op1.se().text()+"' (a " + Luan.type(t) + " value)" );
-		}
-		if( h instanceof LuanFunction ) {
-			LuanFunction fn = (LuanFunction)h;
-			return Luan.first(luan.bit(se).call(fn,"__index",new Object[]{t,key}));
-		}
-		return index(luan,h,key);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/LeExpr.java
--- a/src/luan/interp/LeExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanFunction;
-import luan.LuanException;
-import luan.LuanSource;
-import luan.LuanBit;
-
-
-final class LeExpr extends BinaryOpExpr {
-
-	LeExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		if( o1 instanceof Number && o2 instanceof Number ) {
-			Number n1 = (Number)o1;
-			Number n2 = (Number)o2;
-			return n1.doubleValue() <= n2.doubleValue();
-		}
-		if( o1 instanceof String && o2 instanceof String ) {
-			String s1 = (String)o1;
-			String s2 = (String)o2;
-			return s1.compareTo(s2) <= 0;
-		}
-		LuanBit bit = luan.bit(se);
-		LuanFunction fn = bit.getBinHandler("__le",o1,o2);
-		if( fn != null )
-			return Luan.toBoolean( Luan.first(bit.call(fn,"__le",new Object[]{o1,o2})) );
-		fn = bit.getBinHandler("__lt",o1,o2);
-		if( fn != null )
-			return !Luan.toBoolean( Luan.first(bit.call(fn,"__lt",new Object[]{o2,o1})) );
-		throw bit.exception( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) );
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/LenExpr.java
--- a/src/luan/interp/LenExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanException;
-import luan.LuanSource;
-import luan.LuanBit;
-
-
-final class LenExpr extends UnaryOpExpr {
-
-	LenExpr(LuanSource.Element se,Expr op) {
-		super(se,op);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o = op.eval(luan);
-		if( o instanceof String ) {
-			String s = (String)o;
-			return s.length();
-		}
-		if( o instanceof byte[] ) {
-			byte[] a = (byte[])o;
-			return a.length;
-		}
-		LuanBit bit = luan.bit(se);
-		LuanFunction fn = bit.getHandlerFunction("__len",o);
-		if( fn != null )
-			return Luan.first(bit.call(fn,"__len",new Object[]{o}));
-		if( o instanceof LuanTable ) {
-			LuanTable t = (LuanTable)o;
-			return t.length();
-		}
-		throw bit.exception( "attempt to get length of a " + Luan.type(o) + " value" );
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/LtExpr.java
--- a/src/luan/interp/LtExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanFunction;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class LtExpr extends BinaryOpExpr {
-
-	LtExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		return luan.bit(se).isLessThan(o1,o2);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/LuanCompiler.java
--- a/src/luan/interp/LuanCompiler.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-package luan.interp;
-
-import luan.LuanFunction;
-import luan.LuanState;
-import luan.LuanException;
-import luan.LuanSource;
-import luan.LuanElement;
-import luan.LuanTable;
-import java.util.Map;
-
-
-public final class LuanCompiler {
-	private LuanCompiler() {}  // never
-
-	public static LuanFunction compile(LuanState luan,LuanSource src,LuanTable env,boolean allowExpr) throws LuanException {
-		UpValue.Getter envGetter = env!=null ? new UpValue.ValueGetter(env) : new UpValue.EnvGetter();
-		LuanParser parser = new LuanParser(src,envGetter);
-		for( Map.Entry<Object,Object> entry : luan.global() ) {
-			Object key = entry.getKey();
-			if( key instanceof String )
-				parser.addVar( (String)key, entry.getValue() );
-		}
-		FnDef fnDef = parse(luan,parser,allowExpr);
-		if( env != null )
-			return new Closure((LuanStateImpl)luan,fnDef);
-		final Closure c = new Closure((LuanStateImpl)luan,fnDef);
-		return new LuanFunction() {
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				Object rtn = c.call(luan,args);
-				if( rtn instanceof Object[] && ((Object[])rtn).length==0 )
-					rtn = c.upValues()[0].get();
-				return rtn;
-			}
-		};
-	}
-
-	private static FnDef parse(LuanState luan,LuanParser parser,boolean allowExpr) throws LuanException {
-		try {
-			if( allowExpr ) {
-				FnDef fnDef = parser.Expression();
-				if( fnDef != null )
-					return fnDef;
-			}
-			return parser.RequiredModule();
-		} catch(ParseException e) {
-//e.printStackTrace();
-			LuanElement le = new LuanSource.CompilerElement(parser.source);
-			throw luan.bit(le).exception( e.getFancyMessage() );
-		}
-	}
-
-	public static LuanState newLuanState() {
-		return new LuanStateImpl();
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/LuanParser.java
--- a/src/luan/interp/LuanParser.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1360 +0,0 @@
-package luan.interp;
-
-import java.util.Set;
-import java.util.HashSet;
-import java.util.Arrays;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Scanner;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanSource;
-
-
-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(UpValue.Getter envGetter) {
-			this.parent = null;
-			upValueSymbols.add(_ENV);
-			upValueGetters.add(envGetter);
-		}
-
-		Frame(Frame parent) {
-			this.parent = parent;
-			if( upValueIndex(_ENV) != 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,template);
-		}
-
-		In template() {
-			return template ? this : new In(parens,true);
-		}
-	}
-
-	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;
-	private final boolean interactive;
-
-	LuanParser(LuanSource source,UpValue.Getter envGetter) {
-		this.source = source;
-		this.frame = new Frame(envGetter);
-		this.parser = new Parser(source);
-		this.interactive = envGetter instanceof UpValue.ValueGetter;
-	}
-
-	void addVar(String name,Object value) {
-		frame.addUpValueGetter(name,new UpValue.ValueGetter(value));
-	}
-
-	private LuanSource.Element se(int start) {
-		return new LuanSource.Element(source,start,parser.currentIndex());
-	}
-
-	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 instanceof Expr )
-			return (Expr)exprs; 
-		return new ExpressionsExpr(exprs);
-	}
-
-	private FnDef newFnDef(int start,Stmt stmt) {
-		return new FnDef( se(start), stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) );
-	}
-
-	FnDef Expression() throws ParseException {
-		Spaces(In.NOTHING);
-		int start = parser.begin();
-		Expressions expr = Expr(In.NOTHING);
-		if( expr != null && parser.endOfInput() ) {
-			Stmt stmt = new ReturnStmt( se(start), expr );
-			return parser.success(newFnDef(start,stmt));
-		}
-		return parser.failure(null);
-	}
-
-	FnDef RequiredModule() throws ParseException {
-		Spaces(In.NOTHING);
-		int start = parser.begin();
-		frame.isVarArg = true;
-		Stmt stmt = RequiredBlock();
-		if( parser.endOfInput() )
-			return parser.success(newFnDef(start,stmt));
-		throw parser.exception();
-	}
-
-	private Stmt RequiredBlock() throws ParseException {
-		List<Stmt> stmts = new ArrayList<Stmt>();
-		int stackStart = symbolsSize();
-		Stmt(stmts);
-		while( StmtSep(stmts) ) {
-			Spaces(In.NOTHING);
-			Stmt(stmts);
-		}
-		int stackEnd = symbolsSize();
-		popSymbols( stackEnd - stackStart );
-		if( stmts.isEmpty() )
-			return Stmt.EMPTY;
-		if( stmts.size()==1 && stackStart==stackEnd )
-			return stmts.get(0);
-		return new Block( stmts.toArray(new Stmt[0]), stackStart, stackEnd );
-	}
-
-	private boolean StmtSep(List<Stmt> stmts) throws ParseException {
-		parser.begin();
-		if( parser.match( ';' ) )
-			return parser.success();
-		if( parser.match( "--" ) ) {
-			while( parser.noneOf("\r\n") );
-		}
-		if( EndOfLine() )
-			return parser.success();
-		parser.rollback();
-		Stmt stmt = TemplateStmt();
-		if( stmt != null ) {
-			stmts.add(stmt);
-			return parser.success();
-		}
-		return parser.failure();
-	}
-
-	private boolean EndOfLine() {
-		return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' );
-	}
-
-	private void Stmt(List<Stmt> stmts) throws ParseException {
-		if( LocalStmt(stmts) )
-			return;
-		Stmt stmt;
-		if( (stmt=ReturnStmt()) != null
-			|| (stmt=FunctionStmt()) != null
-			|| (stmt=LocalFunctionStmt()) != null
-			|| (stmt=ImportStmt()) != null
-			|| (stmt=BreakStmt()) != null
-			|| (stmt=ForStmt()) != null
-			|| (stmt=TryStmt()) != null
-			|| (stmt=DoStmt()) != null
-			|| (stmt=WhileStmt()) != null
-			|| (stmt=FunctionStmt()) != null
-			|| (stmt=RepeatStmt()) != null
-			|| (stmt=IfStmt()) != null
-			|| (stmt=SetStmt()) != null
-			|| (stmt=ExpressionsStmt()) != null
-		) {
-			stmts.add(stmt);
-		}
-	}
-
-	private Stmt TemplateStmt() throws ParseException {
-		int start = parser.currentIndex();
-		Expressions exp = TemplateExpressions(In.NOTHING);
-		if( exp == null )
-			return null;
-		Expr fnExp = (Expr)nameVar(start,"Io").expr();
-		fnExp = new IndexExpr( se(start), fnExp, new ConstExpr("stdout") );
-		fnExp = new IndexExpr( se(start), fnExp, new ConstExpr("write") );
-		FnCall fnCall = new FnCall( se(start), fnExp, exp );
-		return new ExpressionsStmt(fnCall);
-	}
-
-	private Expressions TemplateExpressions(In in) throws ParseException {
-		if( in.template )
-			return null;
-		int start = parser.begin();
-		if( !parser.match( "%>" ) )
-			return parser.failure(null);
-		EndOfLine();
-		In inTemplate = in.template();
-		List<Expressions> builder = new ArrayList<Expressions>();
-		while(true) {
-			if( parser.match( "<%=" ) ) {
-				Spaces(inTemplate);
-				builder.add( RequiredExpr(inTemplate) );
-				RequiredMatch( "%>" );
-			} else if( parser.match( "<%" ) ) {
-				Spaces(inTemplate);
-				return parser.success(ExpList.build(builder));
-			} else {
-				int i = parser.currentIndex();
-				do {
-					if( parser.match( "%>" ) )
-						throw parser.exception("'%>' unexpected");
-					if( !parser.anyChar() )
-						throw parser.exception("Unclosed template expression");
-				} while( !parser.test( "<%" ) );
-				String match = parser.textFrom(i);
-				builder.add( new ConstExpr(match) );
-			}
-		}
-	}
-
-	private Stmt ReturnStmt() throws ParseException {
-		int start = parser.begin();
-		if( !Keyword("return",In.NOTHING) )
-			return parser.failure(null);
-		Expressions exprs = ExpList(In.NOTHING);
-		if( exprs==null )
-			exprs = ExpList.emptyExpList;
-		return parser.success( new ReturnStmt(se(start),exprs) );
-	}
-
-	private Stmt FunctionStmt() throws ParseException {
-		parser.begin();
-		if( !Keyword("function",In.NOTHING) )
-			return parser.failure(null);
-
-		int start = parser.currentIndex();
-		Var var = nameVar(start,RequiredName(In.NOTHING));
-		while( parser.match( '.' ) ) {
-			Spaces(In.NOTHING);
-			var = indexVar( start, expr(var.expr()), NameExpr(In.NOTHING) );
-		}
-		Settable fnName = var.settable();
-
-		FnDef fnDef = RequiredFunction(In.NOTHING);
-		return parser.success( new SetStmt(fnName,fnDef) );
-	}
-
-	private Stmt LocalFunctionStmt() throws ParseException {
-		parser.begin();
-		if( !(Keyword("local",In.NOTHING) && Keyword("function",In.NOTHING)) )
-			return parser.failure(null);
-		String name = RequiredName(In.NOTHING);
-		addSymbol( name );
-		FnDef fnDef = RequiredFunction(In.NOTHING);
-		return parser.success( new SetStmt( new SetLocalVar(symbolsSize()-1), fnDef ) );
-	}
-
-	private Stmt ImportStmt() throws ParseException {
-		int start = parser.begin();
-		if( !Keyword("import",In.NOTHING) )
-			return parser.failure(null);
-		Expr importExpr = (Expr)nameVar(start,"require").expr();
-		String modName = StringLiteral(In.NOTHING);
-		if( modName==null )
-			return parser.failure(null);
-		String varName = modName.substring(modName.lastIndexOf('.')+1);
-		LuanSource.Element se = se(start);
-		FnCall require = new FnCall( se, importExpr, new ConstExpr(modName) );
-		Settable settable;
-		if( interactive ) {
-			settable = nameVar(se,varName).settable();
-		} else {
-			addSymbol( varName );
-			settable = new SetLocalVar(symbolsSize()-1);
-		}
-		return parser.success( new SetStmt( settable, expr(require) ) );
-	}
-
-	private Stmt 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 BreakStmt() );
-	}
-
-	private Stmt ForStmt() throws ParseException {
-		int start = 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);
-		Expr expr = expr(RequiredExpr(In.NOTHING));
-		RequiredKeyword("do",In.NOTHING);
-		addSymbols(names);
-		Stmt loop = RequiredLoopBlock();
-		RequiredKeyword("end",In.NOTHING);
-		Stmt stmt = new ForStmt( se(start), stackStart, symbolsSize() - stackStart, expr, loop );
-		popSymbols( symbolsSize() - stackStart );
-		return parser.success(stmt);
-	}
-
-	private Stmt TryStmt() throws ParseException {
-		parser.begin();
-		if( !Keyword("try",In.NOTHING) )
-			return parser.failure(null);
-		Stmt tryBlock = RequiredBlock();
-		RequiredKeyword("catch",In.NOTHING);
-		String name = RequiredName(In.NOTHING);
-		addSymbol(name);
-		RequiredKeyword("do",In.NOTHING);
-		Stmt catchBlock = RequiredBlock();
-		RequiredKeyword("end",In.NOTHING);
-		Stmt stmt = new TryStmt( tryBlock, symbolsSize()-1, catchBlock );
-		popSymbols(1);
-		return parser.success(stmt);
-	}
-
-	private Stmt DoStmt() throws ParseException {
-		parser.begin();
-		if( !Keyword("do",In.NOTHING) )
-			return parser.failure(null);
-		Stmt stmt = RequiredBlock();
-		RequiredKeyword("end",In.NOTHING);
-		return parser.success(stmt);
-	}
-
-	private boolean LocalStmt(List<Stmt> stmts) throws ParseException {
-		parser.begin();
-		if( !Keyword("local",In.NOTHING) )
-			return parser.failure();
-		List<String> names = NameList(In.NOTHING);
-		if( names==null )
-			return parser.failure();
-		if( parser.match( '=' ) ) {
-			Spaces(In.NOTHING);
-			Expressions values = ExpList(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);
-			}
-			stmts.add( new SetStmt( vars, values ) );
-		}
-		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 Stmt WhileStmt() throws ParseException {
-		int start = parser.begin();
-		if( !Keyword("while",In.NOTHING) )
-			return parser.failure(null);
-		Expr cnd = expr(RequiredExpr(In.NOTHING));
-		RequiredKeyword("do",In.NOTHING);
-		Stmt loop = RequiredLoopBlock();
-		RequiredKeyword("end",In.NOTHING);
-		return parser.success( new WhileStmt(se(start),cnd,loop) );
-	}
-
-	private Stmt RepeatStmt() throws ParseException {
-		int start = parser.begin();
-		if( !Keyword("repeat",In.NOTHING) )
-			return parser.failure(null);
-		Stmt loop = RequiredLoopBlock();
-		RequiredKeyword("until",In.NOTHING);
-		Expr cnd = expr(RequiredExpr(In.NOTHING));
-		return parser.success( new RepeatStmt(se(start),loop,cnd) );
-	}
-
-	private Stmt RequiredLoopBlock() throws ParseException {
-		incLoops();
-		Stmt stmt = RequiredBlock();
-		decLoops();
-		return stmt;
-	}
-
-	private Stmt IfStmt() throws ParseException {
-		parser.begin();
-		if( !Keyword("if",In.NOTHING) )
-			return parser.failure(null);
-		return parser.success( IfStmt2() );
-	}
-
-	private Stmt IfStmt2() throws ParseException {
-		int start = parser.currentIndex();
-		Expr cnd = expr(RequiredExpr(In.NOTHING));
-		RequiredKeyword("then",In.NOTHING);
-		Stmt thenBlock = RequiredBlock();
-		Stmt elseBlock;
-		if( Keyword("elseif",In.NOTHING) ) {
-			elseBlock = IfStmt2();
-		} else {
-			elseBlock = Keyword("else",In.NOTHING) ? RequiredBlock() : Stmt.EMPTY;
-			RequiredKeyword("end",In.NOTHING);
-		}
-		return new IfStmt(se(start),cnd,thenBlock,elseBlock);
-	}
-
-	private Stmt SetStmt() throws ParseException {
-		parser.begin();
-		List<Settable> vars = new ArrayList<Settable>();
-		Settable s = SettableVar();
-		if( s == null )
-			return parser.failure(null);
-		vars.add(s);
-		while( parser.match( ',' ) ) {
-			Spaces(In.NOTHING);
-			s = SettableVar();
-			if( s == null )
-				return parser.failure(null);
-			vars.add(s);
-		}
-		if( !parser.match( '=' ) )
-			return parser.failure(null);
-		Spaces(In.NOTHING);
-		Expressions values = ExpList(In.NOTHING);
-		if( values==null )
-			throw parser.exception("Expressions expected");
-		return parser.success( new SetStmt( vars.toArray(new Settable[0]), values ) );
-	}
-
-	private Stmt ExpressionsStmt() throws ParseException {
-		parser.begin();
-		Expressions exp = Expr(In.NOTHING);
-		if( exp instanceof FnCall || exp instanceof AndExpr || exp instanceof OrExpr )
-			return parser.success( new ExpressionsStmt(exp) );
-		return parser.failure(null);
-	}
-
-	private Settable SettableVar() throws ParseException {
-		int start = parser.begin();
-		Var var = VarZ(In.NOTHING);
-		if( var==null )
-			return parser.failure(null);
-		return parser.success( var.settable() );
-	}
-
-	private Expressions RequiredExpr(In in) throws ParseException {
-		parser.begin();
-		return parser.success(required(Expr(in),"Bad expression"));
-	}
-
-	private Expressions Expr(In in) throws ParseException {
-		parser.begin();
-		Expressions exp;
-		return (exp = VarArgs(in)) != null
-			|| (exp = OrExpr(in)) != null
-			? parser.success(exp)
-			: parser.failure((Expressions)null)
-		;
-	}
-
-	private Expressions OrExpr(In in) throws ParseException {
-		int start = parser.begin();
-		Expressions exp = AndExpr(in);
-		if( exp==null )
-			return parser.failure(null);
-		while( Keyword("or",in) ) {
-			exp = new OrExpr( se(start), expr(exp), required(expr(AndExpr(in))) );
-		}
-		return parser.success(exp);
-	}
-
-	private Expressions AndExpr(In in) throws ParseException {
-		int start = parser.begin();
-		Expressions exp = RelExpr(in);
-		if( exp==null )
-			return parser.failure(null);
-		while( Keyword("and",in) ) {
-			exp = new AndExpr( se(start), expr(exp), required(expr(RelExpr(in))) );
-		}
-		return parser.success(exp);
-	}
-
-	private Expressions RelExpr(In in) throws ParseException {
-		int start = parser.begin();
-		Expressions exp = ConcatExpr(in);
-		if( exp==null )
-			return parser.failure(null);
-		while(true) {
-			if( parser.match("==") ) {
-				Spaces(in);
-				exp = new EqExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) );
-			} else if( parser.match("~=") ) {
-				Spaces(in);
-				exp = new NotExpr( se(start), new EqExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) ) );
-			} else if( parser.match("<=") ) {
-				Spaces(in);
-				exp = new LeExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) );
-			} else if( parser.match(">=") ) {
-				Spaces(in);
-				exp = new LeExpr( se(start), required(expr(ConcatExpr(in))), expr(exp) );
-			} else if( parser.match("<") ) {
-				Spaces(in);
-				exp = new LtExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) );
-			} else if( parser.match(">") ) {
-				Spaces(in);
-				exp = new LtExpr( se(start), required(expr(ConcatExpr(in))), expr(exp) );
-			} else
-				break;
-		}
-		return parser.success(exp);
-	}
-
-	private Expressions ConcatExpr(In in) throws ParseException {
-		int start = parser.begin();
-		Expressions exp = SumExpr(in);
-		if( exp==null )
-			return parser.failure(null);
-		if( parser.match("..") ) {
-			Spaces(in);
-			exp = new ConcatExpr( se(start), expr(exp), required(expr(ConcatExpr(in))) );
-		}
-		return parser.success(exp);
-	}
-
-	private Expressions SumExpr(In in) throws ParseException {
-		int start = parser.begin();
-		Expressions exp = TermExpr(in);
-		if( exp==null )
-			return parser.failure(null);
-		while(true) {
-			if( parser.match('+') ) {
-				Spaces(in);
-				exp = new AddExpr( se(start), expr(exp), required(expr(TermExpr(in))) );
-			} else if( Minus() ) {
-				Spaces(in);
-				exp = new SubExpr( se(start), expr(exp), required(expr(TermExpr(in))) );
-			} else
-				break;
-		}
-		return parser.success(exp);
-	}
-
-	private boolean Minus() {
-		parser.begin();
-		return parser.match('-') && !parser.match('-') ? parser.success() : parser.failure();
-	}
-
-	private Expressions TermExpr(In in) throws ParseException {
-		int start = parser.begin();
-		Expressions exp = UnaryExpr(in);
-		if( exp==null )
-			return parser.failure(null);
-		while(true) {
-			if( parser.match('*') ) {
-				Spaces(in);
-				exp = new MulExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) );
-			} else if( parser.match('/') ) {
-				Spaces(in);
-				exp = new DivExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) );
-			} else if( Mod() ) {
-				Spaces(in);
-				exp = new ModExpr( se(start), expr(exp), required(expr(UnaryExpr(in))) );
-			} else
-				break;
-		}
-		return parser.success(exp);
-	}
-
-	private boolean Mod() {
-		parser.begin();
-		return parser.match('%') && !parser.match('>') ? parser.success() : parser.failure();
-	}
-
-	private Expressions UnaryExpr(In in) throws ParseException {
-		int start = parser.begin();
-		if( parser.match('#') ) {
-			Spaces(in);
-			return parser.success( new LenExpr( se(start), required(expr(UnaryExpr(in))) ) );
-		}
-		if( Minus() ) {
-			Spaces(in);
-			return parser.success( new UnmExpr( se(start), required(expr(UnaryExpr(in))) ) );
-		}
-		if( Keyword("not",in) ) {
-			Spaces(in);
-			return parser.success( new NotExpr( se(start), required(expr(UnaryExpr(in))) ) );
-		}
-		Expressions exp = PowExpr(in);
-		if( exp==null )
-			return parser.failure(null);
-		return parser.success(exp);
-	}
-
-	private Expressions PowExpr(In in) throws ParseException {
-		int start = parser.begin();
-		Expressions exp = SingleExpr(in);
-		if( exp==null )
-			return parser.failure(null);
-		if( parser.match('^') ) {
-			Spaces(in);
-			exp = new ConcatExpr( se(start), expr(exp), required(expr(PowExpr(in))) );
-		}
-		return parser.success(exp);
-	}
-
-	private Expressions SingleExpr(In in) throws ParseException {
-		parser.begin();
-		Expressions exp;
-		exp = FunctionExpr(in);
-		if( exp != null )
-			return parser.success(exp);
-		exp = TableExpr(in);
-		if( exp != null )
-			return parser.success(exp);
-		exp = VarExp(in);
-		if( exp != null )
-			return parser.success(exp);
-		exp = Literal(in);
-		if( exp != null )
-			return parser.success(exp);
-		return parser.failure(null);
-	}
-
-	private Expr FunctionExpr(In in) throws ParseException {
-		if( !Keyword("function",in) )
-			return null;
-		return RequiredFunction(in);
-	}
-
-	private FnDef RequiredFunction(In in) throws ParseException {
-		int start = parser.begin();
-		RequiredMatch('(');
-		In inParens = in.parens();
-		Spaces(inParens);
-		frame = new Frame(frame);
-		List<String> names = NameList(in);
-		if( names != null ) {
-			addSymbols(names);
-			if( parser.match(',') ) {
-				Spaces(inParens);
-				if( !parser.match("...") )
-					throw parser.exception();
-				frame.isVarArg = true;
-			}
-		} else if( parser.match("...") ) {
-			Spaces(inParens);
-			frame.isVarArg = true;
-		}
-		RequiredMatch(')');
-		Spaces(in);
-		Stmt block = RequiredBlock();
-		RequiredKeyword("end",in);
-		FnDef fnDef = newFnDef(start,block);
-		frame = frame.parent;
-		return parser.success(fnDef);
-	}
-
-	private VarArgs VarArgs(In in) throws ParseException {
-		int start = parser.begin();
-		if( !frame.isVarArg || !parser.match("...") )
-			return parser.failure(null);
-		Spaces(in);
-		return parser.success( new VarArgs(se(start)) );
-	}
-
-	private Expr TableExpr(In in) throws ParseException {
-		int start = parser.begin();
-		if( !parser.match('{') )
-			return parser.failure(null);
-		In inParens = in.parens();
-		Spaces(inParens);
-		List<TableExpr.Field> fields = new ArrayList<TableExpr.Field>();
-		List<Expressions> builder = new ArrayList<Expressions>();
-		while( Field(fields,builder,in) && FieldSep(inParens) );
-		Spaces(inParens);
-		if( !parser.match('}') )
-			throw parser.exception("Expected table element or '}'");
-		return parser.success( new TableExpr( se(start), fields.toArray(new TableExpr.Field[0]), ExpList.build(builder) ) );
-	}
-
-	private boolean FieldSep(In in) throws ParseException {
-		if( !parser.anyOf(",;") )
-			return false;
-		Spaces(in);
-		return true;
-	}
-
-	private boolean Field(List<TableExpr.Field> fields,List<Expressions> builder,In in) throws ParseException {
-		parser.begin();
-		Expr exp = SubExpr(in);
-		if( exp==null )
-			exp = NameExpr(in);
-		if( exp!=null && parser.match('=') ) {
-			Spaces(in);
-			fields.add( new TableExpr.Field( exp, required(expr(Expr(in))) ) );
-			return parser.success();
-		}
-		parser.rollback();
-		Expressions exprs = Expr(in);
-		if( exprs != null ) {
-			builder.add(exprs);
-			return parser.success();
-		}
-		return parser.failure();
-	}
-
-	private Expressions VarExp(In in) throws ParseException {
-		Var var = VarZ(in);
-		return var==null ? null : var.expr();
-	}
-
-	private Var VarZ(In in) throws ParseException {
-		int start = parser.begin();
-		Var var = VarStart(in);
-		if( var==null )
-			return parser.failure(null);
-		Var var2;
-		while( (var2=Var2(in,start,var.expr())) != null ) {
-			var = var2;
-		}
-		return parser.success(var);
-	}
-
-	private Var Var2(In in,int start,Expressions exp1) throws ParseException {
-		parser.begin();
-		Var var = VarExt(in,start,exp1);
-		if( var != null )
-			return parser.success(var);
-		if( parser.match("->") ) {
-			Spaces(in);
-			List<Expressions> builder = new ArrayList<Expressions>();
-			builder.add(exp1);
-			Expr exp2 = expr(RequiredVarExpB(in));
-			FnCall fnCall = required(Args( in, start, exp2, builder ));
-			return parser.success(exprVar(fnCall));
-		}
-		FnCall fnCall = Args( in, start, expr(exp1), new ArrayList<Expressions>() );
-		if( fnCall != null )
-			return parser.success(exprVar(fnCall));
-		return parser.failure(null);
-	}
-
-	private Expressions RequiredVarExpB(In in) throws ParseException {
-		int start = parser.begin();
-		Var var = required(VarStart(in));
-		Var var2;
-		while( (var2=VarExt(in,start,var.expr())) != null ) {
-			var = var2;
-		}
-		return parser.success(var.expr());
-	}
-
-	private Var VarExt(In in,int start,Expressions exp1) throws ParseException {
-		parser.begin();
-		Expr exp2 = SubExpr(in);
-		if( exp2 != null )
-			return parser.success(indexVar(start,expr(exp1),exp2));
-		if( parser.match('.') ) {
-			Spaces(in);
-			exp2 = NameExpr(in);
-			if( exp2!=null )
-				return parser.success(indexVar(start,expr(exp1),exp2));
-		}
-		return parser.failure(null);
-	}
-
-	private Var VarStart(In in) throws ParseException {
-		int start = parser.begin();
-		if( parser.match('(') ) {
-			In inParens = in.parens();
-			Spaces(inParens);
-			Expr exp = expr(RequiredExpr(inParens));
-			RequiredMatch(')');
-			Spaces(in);
-			return parser.success(exprVar(exp));
-		}
-		String name = Name(in);
-		if( name != null )
-			return parser.success(nameVar(start,name));
-		return parser.failure(null);
-	}
-
-	private Expr env() {
-		int index = stackIndex(_ENV);
-		if( index != -1 )
-			return new GetLocalVar(null,index);
-		index = upValueIndex(_ENV);
-		if( index != -1 )
-			return new GetUpVar(null,index);
-		throw new RuntimeException("_ENV not found");
-	}
-
-	private interface Var {
-		public Expressions expr();
-		public Settable settable();
-	}
-
-	private Var nameVar(final int start,final String name) {
-		return nameVar(se(start),name);
-	}
-
-	private Var nameVar(final LuanSource.Element se,final String name) {
-		return new Var() {
-
-			public Expr expr() {
-				int index = stackIndex(name);
-				if( index != -1 )
-					return new GetLocalVar(se,index);
-				index = upValueIndex(name);
-				if( index != -1 )
-					return new GetUpVar(se,index);
-				return new IndexExpr( se, env(), new ConstExpr(name) );
-			}
-
-			public Settable settable() {
-				int index = stackIndex(name);
-				if( index != -1 )
-					return new SetLocalVar(index);
-				index = upValueIndex(name);
-				if( index != -1 )
-					return new SetUpVar(index);
-				return new SetTableEntry( se, env(), new ConstExpr(name) );
-			}
-		};
-	}
-
-	private Var exprVar(final Expressions expr) {
-		return new Var() {
-
-			public Expressions expr() {
-				return expr;
-			}
-
-			public Settable settable() {
-				return null;
-			}
-		};
-	}
-
-	private Var indexVar(final int start,final Expr table,final Expr key) {
-		return new Var() {
-
-			public Expr expr() {
-				return new IndexExpr( se(start), table, key );
-			}
-
-			public Settable settable() {
-				return new SetTableEntry(se(start),table,key);
-			}
-		};
-	}
-
-	private FnCall Args(In in,int start,Expr fn,List<Expressions> builder) throws ParseException {
-		parser.begin();
-		return args(in,builder)
-			? parser.success( new FnCall( se(start), fn, ExpList.build(builder) ) )
-			: parser.failure((FnCall)null);
-	}
-
-	private boolean args(In in,List<Expressions> builder) throws ParseException {
-		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 true;
-		}
-		Expr exp = TableExpr(in);
-		if( exp != null ) {
-			builder.add(exp);
-			return true;
-		}
-		String s = StringLiteral(in);
-		if( s != null ) {
-			builder.add( new ConstExpr(s) );
-			return true;
-		}
-		Expressions exps = TemplateExpressions(in);
-		if( exps != null ) {
-			builder.add(exps);
-			return true;
-		}
-		return false;
-	}
-
-	private Expressions ExpList(In in) throws ParseException {
-		List<Expressions> builder = new ArrayList<Expressions>();
-		return ExpList(in,builder) ? ExpList.build(builder) : null;
-	}
-
-	private boolean ExpList(In in,List<Expressions> builder) throws ParseException {
-		parser.begin();
-		Expressions exp = TemplateExpressions(in);
-		if( exp != null ) {
-			builder.add(exp);
-			return parser.success();
-		}
-		exp = Expr(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 Expr SubExpr(In in) throws ParseException {
-		parser.begin();
-		if( !parser.match('[') )
-			return parser.failure(null);
-		In inParens = in.parens();
-		Spaces(inParens);
-		Expr exp = expr(RequiredExpr(inParens));
-		RequiredMatch(']');
-		Spaces(in);
-		return parser.success(exp);
-	}
-
-	private Expr NameExpr(In in) throws ParseException {
-		String name = Name(in);
-		return name==null ? null : new ConstExpr(name);
-	}
-
-	private String RequiredName(In in) throws ParseException {
-		parser.begin();
-		String name = Name(in);
-		if( name==null )
-			throw parser.exception("Name expected");
-		return parser.success(name);
-	}
-
-	private String Name(In in) throws ParseException {
-		int start = parser.begin();
-		if( !NameFirstChar() )
-			return parser.failure(null);
-		while( NameChar() );
-		String match = parser.textFrom(start);
-		if( keywords.contains(match) )
-			return parser.failure(null);
-		Spaces(in);
-		return parser.success(match);
-	}
-
-	private boolean NameChar() {
-		return NameFirstChar() || Digit();
-	}
-
-	private boolean NameFirstChar() {
-		return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_');
-	}
-
-	private void RequiredMatch(char c) throws ParseException {
-		if( !parser.match(c) )
-			throw parser.exception("'"+c+"' expected");
-	}
-
-	private void RequiredMatch(String s) throws ParseException {
-		if( !parser.match(s) )
-			throw parser.exception("'"+s+"' expected");
-	}
-
-	private void RequiredKeyword(String keyword,In in) throws ParseException {
-		if( !Keyword(keyword,in) )
-			throw parser.exception("'"+keyword+"' expected");
-	}
-
-	private boolean Keyword(String keyword,In in) throws ParseException {
-		parser.begin();
-		if( !parser.match(keyword) || NameChar() )
-			return parser.failure();
-		Spaces(in);
-		return parser.success();
-	}
-
-	private static final Set<String> keywords = new HashSet<String>(Arrays.asList(
-		"and",
-		"break",
-		"catch",
-		"do",
-		"else",
-		"elseif",
-		"end",
-		"false",
-		"for",
-		"function",
-		"goto",
-		"if",
-		"import",
-		"in",
-		"local",
-		"nil",
-		"not",
-		"or",
-		"repeat",
-		"return",
-		"then",
-		"true",
-		"try",
-		"until",
-		"while"
-	));
-
-	private Expr Literal(In in) throws ParseException {
-		if( NilLiteral(in) )
-			return new ConstExpr(null);
-		Boolean b = BooleanLiteral(in);
-		if( b != null )
-			return new ConstExpr(b);
-		Number n = NumberLiteral(in);
-		if( n != null )
-			return new ConstExpr(n);
-		String s = StringLiteral(in);
-		if( s != null )
-			return new ConstExpr(s);
-		return null;
-	}
-
-	private boolean NilLiteral(In in) throws ParseException {
-		return Keyword("nil",in);
-	}
-
-	private Boolean BooleanLiteral(In in) throws ParseException {
-		if( Keyword("true",in) )
-			return true;
-		if( Keyword("false",in) )
-			return false;
-		return null;
-	}
-
-	private Number NumberLiteral(In in) throws ParseException {
-		parser.begin();
-		Number n;
-		if( parser.matchIgnoreCase("0x") ) {
-			n = HexNumber();
-		} else {
-			n = DecNumber();
-		}
-		if( n==null || NameChar() )
-			return parser.failure(null);
-		Spaces(in);
-		return parser.success(n);
-	}
-
-	private Number DecNumber() {
-		int start = parser.begin();
-		if( Int() ) {
-			if( parser.match('.') )
-				Int();  // optional
-		} else if( parser.match('.') && Int() ) {
-			// ok
-		} else
-			return parser.failure(null);
-		Exponent();  // optional
-		return parser.success(Double.valueOf(parser.textFrom(start)));
-	}
-
-	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();
-		double n;
-		if( HexInt() ) {
-			n = (double)Long.parseLong(parser.textFrom(start),16);
-			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)));
-		}
-		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.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)));
-		}
-		return parser.failure(null);
-	}
-
-	private void Spaces(In in) throws ParseException {
-		while( parser.anyOf(" \t") || Comment() || ContinueOnNextLine() || in.parens && NewLine() );
-	}
-
-	private boolean ContinueOnNextLine() {
-		parser.begin();
-		return parser.match('\\') &&  EndOfLine() ? parser.success() : parser.failure();
-	}
-
-	private boolean NewLine() {
-		if( !EndOfLine() )
-			return false;
-		if( parser.match("--") ) {
-			while( parser.noneOf("\r\n") );
-		}
-		return true;
-	}
-
-	private boolean Comment() 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();
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/LuanStateImpl.java
--- a/src/luan/interp/LuanStateImpl.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +0,0 @@
-package luan.interp;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.MetatableGetter;
-import luan.LuanException;
-import luan.LuanElement;
-import luan.DeepCloner;
-
-
-final class LuanStateImpl extends LuanState {
-
-	private static class Frame {
-		final Frame previousFrame;
-		final Closure closure;
-		final Object[] stack;
-		final Object[] varArgs;
-		UpValue[] downValues = null;
-
-		Frame( Frame previousFrame, Closure closure, int stackSize, Object[] varArgs) {
-			this.previousFrame = previousFrame;
-			this.closure = closure;
-			this.stack = new Object[stackSize];
-			this.varArgs = varArgs;
-		}
-
-		void stackClear(int start,int end) {
-			if( downValues != null ) {
-				for( int i=start; i<end; i++ ) {
-					UpValue downValue = downValues[i];
-					if( downValue != null ) {
-						downValue.close();
-						downValues[i] = null;
-					}
-				}
-			}
-			for( int i=start; i<end; i++ ) {
-				stack[i] = null;
-			}
-		}
-
-		UpValue getUpValue(int index) {
-			if( downValues==null )
-				downValues = new UpValue[stack.length];
-			if( downValues[index] == null )
-				downValues[index] = new UpValue(stack,index);
-			return downValues[index];
-		}
-	}
-
-	private Frame frame = null;
-	Object returnValues;
-	Closure tailFn;
-	Map<UpValue.EnvGetter,UpValue> envs = new HashMap<UpValue.EnvGetter,UpValue>();
-
-	LuanStateImpl() {}
-
-	private LuanStateImpl(LuanStateImpl luan) {
-		super(luan);
-	}
-
-	@Override public LuanState shallowClone() {
-//		if( frame != null )
-//			throw new IllegalStateException("frame isn't null");
-		return new LuanStateImpl(this);
-	}
-
-	@Override public void deepenClone(LuanState clone,DeepCloner cloner) {
-		super.deepenClone(clone,cloner);
-		LuanStateImpl cloneImpl = (LuanStateImpl)clone;
-		cloneImpl.envs = new HashMap<UpValue.EnvGetter,UpValue>();
-		for( Map.Entry<UpValue.EnvGetter,UpValue> entry : envs.entrySet() ) {
-			cloneImpl.envs.put( entry.getKey(), cloner.deepClone(entry.getValue()) );
-		}
-	}
-
-	// returns stack
-	Object[] newFrame(Closure closure, int stackSize, Object[] varArgs) {
-		returnValues = LuanFunction.NOTHING;
-		tailFn = null;
-		frame = new Frame(frame,closure,stackSize,varArgs);
-		return frame.stack;
-	}
-
-	void popFrame() {
-		returnValues = LuanFunction.NOTHING;
-		tailFn = null;
-		frame = frame.previousFrame;
-	}
-
-	Object stackGet(int index) {
-		return frame.stack[index];
-	}
-
-	void stackSet(int index,Object value) {
-		frame.stack[index] = value;
-	}
-
-	void stackClear(int start,int end) {
-		frame.stackClear(start,end);
-	}
-
-	Object[] varArgs() {
-		return frame.varArgs;
-	}
-
-	Closure closure() {
-		return frame.closure;
-	}
-
-	UpValue getUpValue(int index) {
-		return frame.getUpValue(index);
-	}
-
-	UpValue getUpValue(UpValue.EnvGetter getter) throws LuanException {
-		UpValue uv = envs.get(getter);
-		if( uv == null ) {
-			LuanTable env = new LuanTable();
-			uv = new UpValue(env);
-			envs.put(getter,uv);
-		}
-		return uv;
-	}
-
-	@Override public LuanTable currentEnvironment() {
-		if( frame==null )
-			return null;
-		return (LuanTable)frame.closure.upValues()[0].get();
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ModExpr.java
--- a/src/luan/interp/ModExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class ModExpr extends BinaryOpExpr {
-
-	ModExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		Number n1 = Luan.toNumber(o1);
-		Number n2 = Luan.toNumber(o2);
-		if( n1 != null && n2 != null )
-			return n1.doubleValue() % n2.doubleValue();
-		return arithmetic(luan,"__mod",o1,o2);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/MulExpr.java
--- a/src/luan/interp/MulExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class MulExpr extends BinaryOpExpr {
-
-	MulExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		Number n1 = Luan.toNumber(o1);
-		Number n2 = Luan.toNumber(o2);
-		if( n1 != null && n2 != null )
-			return n1.doubleValue() * n2.doubleValue();
-		return arithmetic(luan,"__mul",o1,o2);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/NotExpr.java
--- a/src/luan/interp/NotExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class NotExpr extends UnaryOpExpr {
-
-	NotExpr(LuanSource.Element se,Expr op) {
-		super(se,op);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		return !Luan.toBoolean(op.eval(luan));
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/OrExpr.java
--- a/src/luan/interp/OrExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class OrExpr extends BinaryOpExpr {
-
-	OrExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object v1 = op1.eval(luan);
-		return Luan.toBoolean(v1) ? v1 : op2.eval(luan);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ParseException.java
--- a/src/luan/interp/ParseException.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-public final class ParseException extends Exception {
-	public final LuanSource src;
-	public final int iCurrent;
-	public final int iHigh;
-
-	ParseException(String msg,LuanSource src,int iCurrent,int iHigh) {
-		super(msg);
-		this.src = src;
-		this.iCurrent = iCurrent;
-		this.iHigh = iHigh;
-//System.out.println("iCurrent = "+iCurrent);
-//System.out.println("iHigh = "+iHigh);
-	}
-
-	private class Location {
-		final int line;
-		final int pos;
-
-		Location(int index) {
-			int line = 0;
-			int i = -1;
-			while(true) {
-				int j = src.text.indexOf('\n',i+1);
-				if( j == -1 || j >= index )
-					break;
-				i = j;
-				line++;
-			}
-			this.line = line;
-			this.pos = index - i - 1;
-		}
-	}
-
-	private String[] lines() {
-		return src.text.split("\n",-1);
-	}
-
-	public String getFancyMessage() {
-		Location loc = new Location(iCurrent);
-		String line = lines()[loc.line];
-		String msg = getMessage() +  " (line " + (loc.line+1) + ", pos " + (loc.pos+1) + ") in " + src.name + "\n";
-		StringBuilder sb = new StringBuilder(msg);
-		sb.append( line + "\n" );
-		for( int i=0; i<loc.pos; i++ ) {
-			sb.append( line.charAt(i)=='\t' ? '\t' : ' ' );
-		}
-		sb.append("^\n");
-		return sb.toString();
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/Parser.java
--- a/src/luan/interp/Parser.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-final class Parser {
-	private final LuanSource src;
-	public final String text;
-	private final int len;
-	private int[] stack = new int[256];
-	private int frame = 0;
-	private int iHigh;
-
-	public Parser(LuanSource src) {
-		this.src = src;
-		this.text = src.text;
-		this.len = text.length();
-	}
-
-	private int i() {
-		return stack[frame];
-	}
-
-	private void i(int i) {
-		stack[frame] += i;
-		if( iHigh < stack[frame] )
-			iHigh = stack[frame];
-	}
-
-	public int begin() {
-		frame++;
-		if( frame == stack.length ) {
-			int[] a = new int[2*frame];
-			System.arraycopy(stack,0,a,0,frame);
-			stack = a;
-		}
-		stack[frame] = stack[frame-1];
-		return i();
-	}
-
-	public void rollback() {
-		stack[frame] = stack[frame-1];
-	}
-
-	public <T> T success(T t) {
-		success();
-		return t;
-	}
-
-	public boolean success() {
-		frame--;
-		stack[frame] = stack[frame+1];
-		return true;
-	}
-
-	public <T> T failure(T t) {
-		failure();
-		return t;
-	}
-
-	public boolean failure() {
-		frame--;
-		return false;
-	}
-
-	public ParseException exception(String msg) {
-		return new ParseException(msg,src,i(),iHigh);
-	}
-
-	public ParseException exception() {
-		return exception("Invalid input");
-	}
-
-	public int currentIndex() {
-		return i();
-	}
-
-	public char lastChar() {
-		return text.charAt(i()-1);
-	}
-
-	public char currentChar() {
-		return text.charAt(i());
-	}
-
-	public boolean endOfInput() {
-		return i() >= len;
-	}
-
-	public boolean match(char c) {
-		if( endOfInput() || text.charAt(i()) != c )
-			return false;
-		i(1);
-		return true;
-	}
-
-	public boolean match(String s) {
-		int n = s.length();
-		if( !text.regionMatches(i(),s,0,n) )
-			return false;
-		i(n);
-		return true;
-	}
-
-	public boolean matchIgnoreCase(String s) {
-		int n = s.length();
-		if( !text.regionMatches(true,i(),s,0,n) )
-			return false;
-		i(n);
-		return true;
-	}
-
-	public boolean anyOf(String s) {
-		if( endOfInput() || s.indexOf(text.charAt(i())) == -1 )
-			return false;
-		i(1);
-		return true;
-	}
-
-	public boolean noneOf(String s) {
-		if( endOfInput() || s.indexOf(text.charAt(i())) != -1 )
-			return false;
-		i(1);
-		return true;
-	}
-
-	public boolean inCharRange(char cLow, char cHigh) {
-		if( endOfInput() )
-			return false;
-		char c = text.charAt(i());
-		if( !(cLow <= c && c <= cHigh) )
-			return false;
-		i(1);
-		return true;
-	}
-
-	public boolean anyChar() {
-		if( endOfInput() )
-			return false;
-		i(1);
-		return true;
-	}
-
-	public boolean test(String s) {
-		return text.regionMatches(i(),s,0,s.length());
-	}
-
-	public String textFrom(int start) {
-		return text.substring(start,i());
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/PowExpr.java
--- a/src/luan/interp/PowExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class PowExpr extends BinaryOpExpr {
-
-	PowExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		Number n1 = Luan.toNumber(o1);
-		Number n2 = Luan.toNumber(o2);
-		if( n1 != null && n2 != null )
-			return Math.pow( n1.doubleValue(), n2.doubleValue() );
-		return arithmetic(luan,"__pow",o1,o2);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/RepeatStmt.java
--- a/src/luan/interp/RepeatStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class RepeatStmt extends CodeImpl implements Stmt {
-	private final Stmt doStmt;
-	private final Expr cnd;
-
-	RepeatStmt(LuanSource.Element se,Stmt doStmt,Expr cnd) {
-		super(se);
-		this.doStmt = doStmt;
-		this.cnd = cnd;
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		try {
-			do {
-				doStmt.eval(luan);
-			} while( !luan.bit(se).checkBoolean( cnd.eval(luan) ) );
-		} catch(BreakException e) {}
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ReturnException.java
--- a/src/luan/interp/ReturnException.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-package luan.interp;
-
-
-final class ReturnException extends RuntimeException {}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/ReturnStmt.java
--- a/src/luan/interp/ReturnStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanFunction;
-import luan.LuanSource;
-
-
-final class ReturnStmt extends CodeImpl implements Stmt {
-	private final Expressions expressions;
-	private final Expr tailFnExpr;
-	boolean throwReturnException = true;
-
-	ReturnStmt(LuanSource.Element se,Expressions expressions) {
-		super(se);
-		if( expressions instanceof FnCall ) {  // tail call
-			FnCall fnCall = (FnCall)expressions;
-			this.expressions = fnCall.args;
-			this.tailFnExpr = fnCall.fnExpr;
-		} else {
-			this.expressions = expressions;
-			this.tailFnExpr = null;
-		}
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		luan.returnValues = expressions.eval(luan);
-		if( tailFnExpr != null ) {
-			LuanFunction tailFn = luan.bit(se).checkFunction( tailFnExpr.eval(luan) );
-			if( tailFn instanceof Closure ) {
-				luan.tailFn = (Closure)tailFn;
-			} else {
-				luan.returnValues =  luan.bit(tailFnExpr.se()).call(tailFn,tailFnExpr.se().text(),Luan.array(luan.returnValues));
-			}
-		}
-		if( throwReturnException )
-			throw new ReturnException();
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/SetLocalVar.java
--- a/src/luan/interp/SetLocalVar.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-package luan.interp;
-
-
-final class SetLocalVar implements Settable {
-	private final int index;
-
-	SetLocalVar(int index) {
-		this.index = index;
-	}
-
-	@Override public void set(LuanStateImpl luan,Object value) {
-		luan.stackSet( index, value );
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/SetStmt.java
--- a/src/luan/interp/SetStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-
-
-final class SetStmt implements Stmt {
-	private final Settable[] vars;
-	private final Expressions expressions;
-
-	SetStmt(Settable var,Expr expr) {
-		this( new Settable[]{var}, expr );
-	}
-
-	SetStmt(Settable[] vars,Expressions expressions) {
-		this.vars = vars;
-		this.expressions = expressions;
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		final Object obj = expressions.eval(luan);
-		if( obj instanceof Object[] ) {
-			Object[] vals = (Object[])obj;
-			for( int i=0; i<vars.length; i++ ) {
-				Object val = i < vals.length ? vals[i] : null;
-				vars[i].set(luan,val);
-			}
-		} else {
-			vars[0].set(luan,obj);
-			for( int i=1; i<vars.length; i++ ) {
-				vars[i].set(luan,null);
-			}
-		}
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/SetTableEntry.java
--- a/src/luan/interp/SetTableEntry.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-import luan.LuanTable;
-import luan.Luan;
-import luan.LuanFunction;
-import luan.LuanSource;
-
-
-final class SetTableEntry extends CodeImpl implements Settable {
-	private final Expr tableExpr;
-	private final Expr keyExpr;
-
-	SetTableEntry(LuanSource.Element se,Expr tableExpr,Expr keyExpr) {
-		super(se);
-		this.tableExpr = tableExpr;
-		this.keyExpr = keyExpr;
-	}
-
-	@Override public void set(LuanStateImpl luan,Object value) throws LuanException {
-		newindex( luan, tableExpr.eval(luan), keyExpr.eval(luan), value );
-	}
-
-	private void newindex(LuanStateImpl luan,Object t,Object key,Object value) throws LuanException {
-		Object h;
-		if( t instanceof LuanTable ) {
-			LuanTable table = (LuanTable)t;
-			Object old = table.put(key,value);
-			if( old != null )
-				return;
-			h = luan.getHandler("__newindex",t);
-			if( h==null )
-				return;
-			table.put(key,old);
-		} else {
-			h = luan.getHandler("__newindex",t);
-			if( h==null )
-				throw luan.bit(se).exception( "attempt to index a " + Luan.type(t) + " value" );
-		}
-		if( h instanceof LuanFunction ) {
-			LuanFunction fn = (LuanFunction)h;
-			luan.bit(se).call(fn,"__newindex",new Object[]{t,key,value});
-			return;
-		}
-		newindex(luan,h,key,value);
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/SetUpVar.java
--- a/src/luan/interp/SetUpVar.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-package luan.interp;
-
-
-final class SetUpVar implements Settable {
-	private final int index;
-
-	SetUpVar(int index) {
-		this.index = index;
-	}
-
-	@Override public void set(LuanStateImpl luan,Object value) {
-		luan.closure().upValues()[index].set(value);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/Settable.java
--- a/src/luan/interp/Settable.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-
-
-interface Settable {
-	public void set(LuanStateImpl luan,Object value) throws LuanException;
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/Stmt.java
--- a/src/luan/interp/Stmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-
-
-interface Stmt {
-	public void eval(LuanStateImpl luan) throws LuanException;
-
-	static final Stmt EMPTY = new Stmt() {
-		@Override public void eval(LuanStateImpl luan) {}
-	};
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/SubExpr.java
--- a/src/luan/interp/SubExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class SubExpr extends BinaryOpExpr {
-
-	SubExpr(LuanSource.Element se,Expr op1,Expr op2) {
-		super(se,op1,op2);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o1 = op1.eval(luan);
-		Object o2 = op2.eval(luan);
-		Number n1 = Luan.toNumber(o1);
-		Number n2 = Luan.toNumber(o2);
-		if( n1 != null && n2 != null )
-			return n1.doubleValue() - n2.doubleValue();
-		return arithmetic(luan,"__sub",o1,o2);
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/TableExpr.java
--- a/src/luan/interp/TableExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-package luan.interp;
-
-import luan.LuanException;
-import luan.LuanTable;
-import luan.LuanSource;
-
-
-final class TableExpr extends CodeImpl implements Expr {
-
-	static class Field {
-		final Expr key;
-		final Expr value;
-
-		Field(Expr key,Expr value) {
-			this.key = key;
-			this.value = value;
-		}
-	}
-
-	private final Field[] fields;
-	private final Expressions expressions;
-
-	TableExpr(LuanSource.Element se,Field[] fields,Expressions expressions) {
-		super(se);
-		this.fields = fields;
-		this.expressions = expressions;
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		LuanTable table = new LuanTable();
-		for( Field field : fields ) {
-			table.put( field.key.eval(luan), field.value.eval(luan) );
-		}
-		Object obj = expressions.eval(luan);
-		if( obj instanceof Object[] ) {
-			Object[] a = (Object[])obj;
-			for( int i=0; i<a.length; i++ ) {
-				table.put( i+1, a[i] );
-			}
-		} else {
-			table.put( 1, obj );
-		}
-		return table;
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/TryStmt.java
--- a/src/luan/interp/TryStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-
-
-final class TryStmt implements Stmt {
-	private final Stmt tryBlock;
-	private final int iExceptionVar;
-	private final Stmt catchBlock;
-
-	TryStmt(Stmt tryBlock,int iExceptionVar,Stmt catchBlock) {
-		this.tryBlock = tryBlock;
-		this.iExceptionVar = iExceptionVar;
-		this.catchBlock = catchBlock;
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		try {
-			tryBlock.eval(luan);
-		} catch(LuanException e) {
-			try {
-				luan.stackSet( iExceptionVar, e );
-				catchBlock.eval(luan);
-			} finally {
-				luan.stackClear(iExceptionVar,iExceptionVar+1);
-			}
-		}
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/UnaryOpExpr.java
--- a/src/luan/interp/UnaryOpExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-abstract class UnaryOpExpr extends CodeImpl implements Expr {
-	final Expr op;
-
-	UnaryOpExpr(LuanSource.Element se,Expr op) {
-		super(se);
-		this.op = op;
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/UnmExpr.java
--- a/src/luan/interp/UnmExpr.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanFunction;
-import luan.LuanException;
-import luan.LuanSource;
-import luan.LuanBit;
-
-
-// unary minus
-final class UnmExpr extends UnaryOpExpr {
-
-	UnmExpr(LuanSource.Element se,Expr op) {
-		super(se,op);
-	}
-
-	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		Object o = op.eval(luan);
-		Number n = Luan.toNumber(o);
-		if( n != null )
-			return -n.doubleValue();
-		LuanBit bit = luan.bit(se);
-		LuanFunction fn = bit.getHandlerFunction("__unm",o);
-		if( fn != null ) {
-			return Luan.first(bit.call(fn,"__unm",new Object[]{o}));
-		}
-		throw bit.exception("attempt to perform arithmetic on a "+Luan.type(o)+" value");
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/UpValue.java
--- a/src/luan/interp/UpValue.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-package luan.interp;
-
-import luan.DeepCloner;
-import luan.DeepCloneable;
-import luan.LuanException;
-
-
-final class UpValue implements DeepCloneable<UpValue> {
-	private Object[] stack;
-	private int index;
-	private boolean isClosed = false;
-	private Object value;
-
-	UpValue(Object[] stack,int index) {
-		this.stack = stack;
-		this.index = index;
-	}
-
-	UpValue(Object value) {
-		this.value = value;
-		this.isClosed = true;
-	}
-
-	private UpValue() {}
-
-	@Override public UpValue shallowClone() {
-		return new UpValue();
-	}
-
-	@Override public void deepenClone(UpValue clone,DeepCloner cloner) {
-		clone.isClosed = isClosed;
-		if( isClosed ) {
-			clone.value = cloner.get(value);
-		} else {
-			clone.stack = cloner.deepClone(stack);
-			clone.index = index;
-		}
-	}
-
-	Object get() {
-		return isClosed ? value : stack[index];
-	}
-
-	void set(Object value) {
-		if( isClosed ) {
-			this.value = value;
-		} else {
-			stack[index] = value;
-		}
-	}
-
-	void close() {
-		value = stack[index];
-		isClosed = true;
-		stack = null;
-	}
-
-	static interface Getter {
-		public UpValue get(LuanStateImpl luan) throws LuanException;
-	}
-
-	static final class StackGetter implements Getter {
-		private final int index;
-
-		StackGetter(int index) {
-			this.index = index;
-		}
-
-		public UpValue get(LuanStateImpl luan) {
-			return luan.getUpValue(index);
-		}
-	}
-
-	static final class NestedGetter implements Getter {
-		private final int index;
-
-		NestedGetter(int index) {
-			this.index = index;
-		}
-
-		public UpValue get(LuanStateImpl luan) {
-			return luan.closure().upValues()[index];
-		}
-	}
-
-	static final class EnvGetter implements Getter {
-
-		public UpValue get(LuanStateImpl luan) throws LuanException {
-			return luan.getUpValue(this);
-		}
-	}
-
-	static final class ValueGetter implements Getter {
-		private final UpValue upValue;
-
-		ValueGetter(Object value) {
-			this.upValue = new UpValue(value);
-		}
-
-		public UpValue get(LuanStateImpl luan) {
-			return upValue;
-		}
-	}
-
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/VarArgs.java
--- a/src/luan/interp/VarArgs.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package luan.interp;
-
-import luan.LuanSource;
-
-
-final class VarArgs extends CodeImpl implements Expressions {
-
-	VarArgs(LuanSource.Element se) {
-		super(se);
-	}
-
-	@Override public Object[] eval(LuanStateImpl luan) {
-		return luan.varArgs();
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/interp/WhileStmt.java
--- a/src/luan/interp/WhileStmt.java	Sun Jun 22 04:10:59 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package luan.interp;
-
-import luan.Luan;
-import luan.LuanException;
-import luan.LuanSource;
-
-
-final class WhileStmt extends CodeImpl implements Stmt {
-	private final Expr cnd;
-	private final Stmt doStmt;
-
-	WhileStmt(LuanSource.Element se,Expr cnd,Stmt doStmt) {
-		super(se);
-		this.cnd = cnd;
-		this.doStmt = doStmt;
-	}
-
-	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		try {
-			while( luan.bit(se).checkBoolean( cnd.eval(luan) ) ) {
-				doStmt.eval(luan);
-			}
-		} catch(BreakException e) {}
-	}
-}
diff -r 94bbc4cbc106 -r 4eaee12f6c65 src/luan/lib/BasicLib.java
--- a/src/luan/lib/BasicLib.java	Sun Jun 22 04:10:59 2014 +0000
+++ b/src/luan/lib/BasicLib.java	Sun Jun 22 04:17:38 2014 +0000
@@ -16,7 +16,7 @@
 import luan.LuanException;
 import luan.LuanSource;
 import luan.LuanElement;
-import luan.interp.LuanCompiler;
+import luan.impl.LuanCompiler;
 
 
 public final class BasicLib {