changeset 171:3dcb0f9bee82

add core component git-svn-id: https://luan-java.googlecode.com/svn/trunk@172 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Sun, 22 Jun 2014 05:41:22 +0000
parents 7c792a328a83
children 4edbf3204ca6
files core/src/luan/DeepCloneable.java core/src/luan/DeepCloner.java core/src/luan/Luan.java core/src/luan/LuanBit.java core/src/luan/LuanElement.java core/src/luan/LuanException.java core/src/luan/LuanExitException.java core/src/luan/LuanFunction.java core/src/luan/LuanJavaFunction.java core/src/luan/LuanRepr.java core/src/luan/LuanRuntimeException.java core/src/luan/LuanSource.java core/src/luan/LuanState.java core/src/luan/LuanTable.java core/src/luan/MetatableGetter.java core/src/luan/StackTraceElement.java core/src/luan/cmd_line.luan core/src/luan/impl/AddExpr.java core/src/luan/impl/AndExpr.java core/src/luan/impl/BinaryOpExpr.java core/src/luan/impl/Block.java core/src/luan/impl/BreakException.java core/src/luan/impl/BreakStmt.java core/src/luan/impl/Closure.java core/src/luan/impl/Code.java core/src/luan/impl/CodeImpl.java core/src/luan/impl/ConcatExpr.java core/src/luan/impl/ConstExpr.java core/src/luan/impl/DivExpr.java core/src/luan/impl/EqExpr.java core/src/luan/impl/ExpList.java core/src/luan/impl/Expr.java core/src/luan/impl/Expressions.java core/src/luan/impl/ExpressionsExpr.java core/src/luan/impl/ExpressionsStmt.java core/src/luan/impl/FnCall.java core/src/luan/impl/FnDef.java core/src/luan/impl/ForStmt.java core/src/luan/impl/GetLocalVar.java core/src/luan/impl/GetUpVar.java core/src/luan/impl/IfStmt.java core/src/luan/impl/IndexExpr.java core/src/luan/impl/LeExpr.java core/src/luan/impl/LenExpr.java core/src/luan/impl/LtExpr.java core/src/luan/impl/LuanCompiler.java core/src/luan/impl/LuanParser.java core/src/luan/impl/LuanStateImpl.java core/src/luan/impl/ModExpr.java core/src/luan/impl/MulExpr.java core/src/luan/impl/NotExpr.java core/src/luan/impl/OrExpr.java core/src/luan/impl/ParseException.java core/src/luan/impl/Parser.java core/src/luan/impl/PowExpr.java core/src/luan/impl/RepeatStmt.java core/src/luan/impl/ReturnException.java core/src/luan/impl/ReturnStmt.java core/src/luan/impl/SetLocalVar.java core/src/luan/impl/SetStmt.java core/src/luan/impl/SetTableEntry.java core/src/luan/impl/SetUpVar.java core/src/luan/impl/Settable.java core/src/luan/impl/Stmt.java core/src/luan/impl/SubExpr.java core/src/luan/impl/TableExpr.java core/src/luan/impl/TryStmt.java core/src/luan/impl/UnaryOpExpr.java core/src/luan/impl/UnmExpr.java core/src/luan/impl/UpValue.java core/src/luan/impl/VarArgs.java core/src/luan/impl/WhileStmt.java core/src/luan/init.luan core/src/luan/modules/BasicLuan.java core/src/luan/modules/BinaryLuan.java core/src/luan/modules/HtmlLuan.java core/src/luan/modules/IoLuan.java core/src/luan/modules/JavaLuan.java core/src/luan/modules/MathLuan.java core/src/luan/modules/PackageLuan.java core/src/luan/modules/PickleClient.java core/src/luan/modules/PickleCon.java core/src/luan/modules/PickleServer.java core/src/luan/modules/Reactionary.luan core/src/luan/modules/StringLuan.java core/src/luan/modules/TableLuan.java core/src/luan/modules/ThreadLuan.java core/src/luan/modules/Utils.java src/luan/DeepCloneable.java src/luan/DeepCloner.java src/luan/Luan.java src/luan/LuanBit.java src/luan/LuanElement.java src/luan/LuanException.java src/luan/LuanExitException.java src/luan/LuanFunction.java src/luan/LuanJavaFunction.java src/luan/LuanRepr.java src/luan/LuanRuntimeException.java src/luan/LuanSource.java src/luan/LuanState.java src/luan/LuanTable.java src/luan/MetatableGetter.java src/luan/StackTraceElement.java src/luan/cmd_line.luan 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/init.luan src/luan/modules/BasicLuan.java src/luan/modules/BinaryLuan.java src/luan/modules/HtmlLuan.java src/luan/modules/IoLuan.java src/luan/modules/JavaLuan.java src/luan/modules/MathLuan.java src/luan/modules/PackageLuan.java src/luan/modules/PickleClient.java src/luan/modules/PickleCon.java src/luan/modules/PickleServer.java src/luan/modules/Reactionary.luan src/luan/modules/StringLuan.java src/luan/modules/TableLuan.java src/luan/modules/ThreadLuan.java src/luan/modules/Utils.java
diffstat 176 files changed, 7419 insertions(+), 7419 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/DeepCloneable.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,7 @@
+package luan;
+
+
+public interface DeepCloneable<T extends DeepCloneable> {
+	public T shallowClone();
+	public void deepenClone(T clone,DeepCloner cloner);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/DeepCloner.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,45 @@
+package luan;
+
+import java.util.Map;
+import java.util.IdentityHashMap;
+
+
+public final class DeepCloner {
+	private final Map<Object,Object> cloned = new IdentityHashMap<Object,Object>();
+
+	public <T extends DeepCloneable<T>> T deepClone(T obj) {
+		@SuppressWarnings("unchecked")
+		T rtn = (T)cloned.get(obj);
+		if( rtn == null ) {
+			rtn = obj.shallowClone();
+			cloned.put(obj,rtn);
+			obj.deepenClone(rtn,this);
+		}
+		return rtn;
+	}
+
+	public <T> T[] deepClone(T[] obj) {
+		if( obj.length == 0 )
+			return obj;
+		@SuppressWarnings("unchecked")
+		T[] rtn = (T[])cloned.get(obj);
+		if( rtn == null ) {
+			rtn = obj.clone();
+			cloned.put(obj,rtn);
+			for( int i=0; i<rtn.length; i++ ) {
+				@SuppressWarnings("unchecked")
+				T t = get(rtn[i]);
+				rtn[i] = t;
+			}
+		}
+		return rtn;
+	}
+
+	public <T> T get(T obj) {
+		if( !(obj instanceof DeepCloneable) )
+			return obj;
+		@SuppressWarnings("unchecked")
+		T dc = (T)deepClone((DeepCloneable)obj);
+		return dc;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/Luan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,150 @@
+package luan;
+
+import luan.modules.BasicLuan;
+
+
+public final class Luan {
+	public static final String version = "Luan 0.1";
+
+	public static void main(String[] args) {
+		LuanState luan = LuanState.newStandard();
+		try {
+			LuanFunction standalone = (LuanFunction)BasicLuan.load_file(luan,"java:luan/cmd_line.luan");
+			luan.call(standalone,args);
+		} catch(LuanException e) {
+			System.err.println(e.getMessage());
+//			e.printStackTrace();
+			System.exit(-1);
+		}
+	}
+
+	public static Object first(Object obj) {
+		if( !(obj instanceof Object[]) )
+			return obj;
+		Object[] a = (Object[])obj;
+		return a.length==0 ? null : a[0];
+	}
+
+	public static Object[] array(Object obj) {
+		return obj instanceof Object[] ? (Object[])obj : new Object[]{obj};
+	}
+
+	public static String type(Object obj) {
+		if( obj == null )
+			return "nil";
+		if( obj instanceof String )
+			return "string";
+		if( obj instanceof Boolean )
+			return "boolean";
+		if( obj instanceof Number )
+			return "number";
+		if( obj instanceof LuanTable )
+			return "table";
+		if( obj instanceof LuanFunction )
+			return "function";
+		if( obj instanceof byte[] )
+			return "binary";
+		return "userdata";
+	}
+
+	public static boolean toBoolean(Object obj) {
+		return obj != null && !Boolean.FALSE.equals(obj);
+	}
+
+	public static String asString(Object obj) {
+		if( obj instanceof String )
+			return (String)obj;
+		if( obj instanceof Number )
+			return toString((Number)obj);
+		return null;
+	}
+
+	public static Number toNumber(Object obj) {
+		return toNumber(obj,null);
+	}
+
+	public static Number toNumber(Object obj,Integer base) {
+		if( obj instanceof Number )
+			return (Number)obj;
+		if( obj instanceof String ) {
+			String s = (String)obj;
+			try {
+				if( base==null )
+					return Double.valueOf(s);
+				else
+					return Long.valueOf(s,base);
+			} catch(NumberFormatException e) {}
+		}
+		return null;
+	}
+
+	public static String toString(Number n) {
+		if( n instanceof Integer )
+			return n.toString();
+		String s = n.toString();
+		int iE = s.indexOf('E');
+		String ending  = null;
+		if( iE != -1 ) {
+			ending = s.substring(iE);
+			s = s.substring(0,iE);
+		}
+		if( s.endsWith(".0") )
+			s = s.substring(0,s.length()-2);
+		if( ending != null )
+			s += ending;
+		return s;
+	}
+
+	public static Integer asInteger(Object obj) {
+		if( obj instanceof Integer )
+			return (Integer)obj;
+		if( !(obj instanceof Number) )
+			return null;
+		Number n = (Number)obj;
+		int i = n.intValue();
+		return i==n.doubleValue() ? Integer.valueOf(i) : null;
+	}
+
+	public static String toString(Object obj) {
+		if( obj == null )
+			return "nil";
+		if( obj instanceof Number )
+			return Luan.toString((Number)obj);
+		if( obj instanceof LuanException ) {
+			LuanException le = (LuanException)obj;
+			return le.getMessage();
+		}
+		if( obj instanceof byte[] )
+			return "binary: " + Integer.toHexString(obj.hashCode());
+		return obj.toString();
+	}
+
+	public static String stringEncode(String s) {
+		s = s.replace("\\","\\\\");
+		s = s.replace("\u0007","\\a");
+		s = s.replace("\b","\\b");
+		s = s.replace("\f","\\f");
+		s = s.replace("\n","\\n");
+		s = s.replace("\r","\\r");
+		s = s.replace("\t","\\t");
+		s = s.replace("\u000b","\\v");
+		s = s.replace("\"","\\\"");
+		return s;
+	}
+
+	public static String repr(Object obj) {
+		if( obj == null )
+			return "nil";
+		if( obj instanceof Boolean )
+			return Luan.toString((Boolean)obj);
+		if( obj instanceof Number )
+			return Luan.toString((Number)obj);
+		if( obj instanceof String )
+			return "\"" + stringEncode((String)obj) + "\"";
+		if( obj instanceof LuanRepr )
+			return ((LuanRepr)obj).repr();
+		return null;
+	}
+
+	private Luan() {}  // never
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanBit.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,126 @@
+package luan;
+
+import java.util.List;
+
+
+public final class LuanBit {
+	public final LuanState luan;
+	public final LuanElement el;
+
+	LuanBit(LuanState luan,LuanElement el) {
+		this.luan = luan;
+		this.el = el;
+	}
+
+	public LuanException exception(Object msg) {
+		return new LuanException(this,msg);
+	}
+
+	public String stackTrace() {
+		StringBuilder buf = new StringBuilder();
+		LuanElement el = this.el;
+		for( int i  = luan.stackTrace.size() - 1; i>=0; i-- ) {
+			StackTraceElement stackTraceElement = luan.stackTrace.get(i);
+			buf.append( "\n\t" ).append( el.toString(stackTraceElement.fnName) );
+			el = stackTraceElement.call;
+		}
+		return buf.toString();
+	}
+
+	public void dumpStack() {
+		System.err.println( stackTrace() );
+	}
+
+	public Object call(LuanFunction fn,String fnName,Object[] args) throws LuanException {
+		List<StackTraceElement> stackTrace = luan.stackTrace;
+		stackTrace.add( new StackTraceElement(el,fnName) );
+		try {
+			return fn.call(luan,args);
+		} finally {
+			stackTrace.remove(stackTrace.size()-1);
+		}
+	}
+
+	public String checkString(Object obj) throws LuanException {
+		String s = Luan.asString(obj);
+		if( s == null )
+			throw exception( "attempt to use a " + Luan.type(obj) + " as a string" );
+		return s;
+	}
+
+	public Number checkNumber(Object obj) throws LuanException {
+		Number n = Luan.toNumber(obj);
+		if( n == null )
+			throw exception( "attempt to perform arithmetic on a " + Luan.type(obj) + " value" );
+		return n;
+	}
+
+	public LuanFunction checkFunction(Object obj) throws LuanException {
+		if( obj instanceof LuanFunction )
+			return (LuanFunction)obj;
+		throw exception( "attempt to call a " + Luan.type(obj) + " value" );
+	}
+
+	public Boolean checkBoolean(Object obj) throws LuanException {
+		if( obj instanceof Boolean )
+			return (Boolean)obj;
+		throw exception( "attempt to use a " + Luan.type(obj) + " as a boolean" );
+	}
+
+	public String toString(Object obj) throws LuanException {
+		LuanFunction fn = getHandlerFunction("__tostring",obj);
+		if( fn != null )
+			return checkString( Luan.first( call(fn,"__tostring",new Object[]{obj}) ) );
+		return Luan.toString(obj);
+	}
+
+	public String repr(Object obj) throws LuanException {
+		LuanFunction fn = getHandlerFunction("__repr",obj);
+		if( fn != null )
+			return checkString( Luan.first( call(fn,"__repr",new Object[]{obj}) ) );
+		String repr = Luan.repr(obj);
+		if( repr==null )
+			throw exception( "value '" + obj + "' doesn't support repr()" );
+		return repr;
+	}
+
+	public LuanFunction getHandlerFunction(String op,Object obj) throws LuanException {
+		Object f = luan.getHandler(op,obj);
+		if( f == null )
+			return null;
+		return checkFunction(f);
+	}
+
+	public LuanFunction getBinHandler(String op,Object o1,Object o2) throws LuanException {
+		LuanFunction f1 = getHandlerFunction(op,o1);
+		if( f1 != null )
+			return f1;
+		return getHandlerFunction(op,o2);
+	}
+
+	public boolean isLessThan(Object o1,Object o2) throws LuanException {
+		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;
+		}
+		LuanFunction fn = getBinHandler("__lt",o1,o2);
+		if( fn != null )
+			return Luan.toBoolean( Luan.first(call(fn,"__lt",new Object[]{o1,o2})) );
+		throw exception( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) );
+	}
+
+	public Object arithmetic(String op,Object o1,Object o2) throws LuanException {
+		LuanFunction fn = getBinHandler(op,o1,o2);
+		if( fn != null )
+			return Luan.first(call(fn,op,new Object[]{o1,o2}));
+		String type = Luan.toNumber(o1)==null ? Luan.type(o1) : Luan.type(o2);
+		throw exception("attempt to perform arithmetic on a "+type+" value");
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanElement.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,20 @@
+package luan;
+
+
+public abstract class LuanElement {
+
+	final String toString(String fnName) {
+		String s = location();
+		if( fnName != null )
+			s += ": in function '" + fnName + "'";
+		return s;
+	}
+
+	abstract String location();
+
+	public static final LuanElement JAVA = new LuanElement(){
+		@Override String location() {
+			return "Java";
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanException.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,47 @@
+package luan;
+
+
+public class LuanException extends Exception {
+	private final String stackTrace;
+
+	LuanException(LuanBit bit,Object msg) {
+		super(message(msg),cause(msg));
+		stackTrace = stackTrace(bit,msg);
+	}
+
+	@Override public String getMessage() {
+		return super.getMessage() + stackTrace;
+	}
+
+	private String message() {
+		return super.getMessage();
+	}
+
+	private static Throwable cause(Object msg) {
+		return msg instanceof Throwable ? (Throwable)msg : null;
+	}
+
+	private static String message(Object msg) {
+		if( msg instanceof LuanException ) {
+			LuanException le = (LuanException)msg;
+			return le.message();
+/*
+		} else if( msg instanceof Throwable ) {
+			Throwable t = (Throwable)msg;
+			return t.getMessage();
+*/
+		} else {
+			return msg.toString();
+		}
+	}
+
+	private static String stackTrace(LuanBit bit,Object msg) {
+		StringBuilder buf = new StringBuilder();
+		buf.append( bit.stackTrace() );
+		if( msg instanceof LuanException ) {
+			LuanException le = (LuanException)msg;
+			buf.append( "\ncaused by:" ).append( le.stackTrace );
+		}
+		return buf.toString();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanExitException.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,4 @@
+package luan;
+
+
+public final class LuanExitException extends RuntimeException {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanFunction.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,18 @@
+package luan;
+
+
+public abstract class LuanFunction implements LuanRepr {
+
+	public abstract Object call(LuanState luan,Object[] args) throws LuanException;
+
+	public static final Object[] NOTHING = new Object[0];
+
+	@Override public String toString() {
+		return "function: " + Integer.toHexString(hashCode());
+	}
+
+	@Override public String repr() {
+		return "<function>";
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanJavaFunction.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,516 @@
+package luan;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.Arrays;
+
+
+public final class LuanJavaFunction extends LuanFunction {
+	private final JavaMethod method;
+	private final Object obj;
+	private final RtnConverter rtnConverter;
+	private final boolean takesLuaState;
+	private final ArgConverter[] argConverters;
+	private final Class<?> varArgCls;
+
+	public LuanJavaFunction(Method method,Object obj) {
+		this( JavaMethod.of(method), obj );
+	}
+
+	public LuanJavaFunction(Constructor constr,Object obj) {
+		this( JavaMethod.of(constr), obj );
+	}
+
+	LuanJavaFunction(JavaMethod method,Object obj) {
+		this.method = method;
+		this.obj = obj;
+		this.rtnConverter = getRtnConverter(method);
+		this.takesLuaState = takesLuaState(method);
+		this.argConverters = getArgConverters(takesLuaState,method);
+		if( method.isVarArgs() ) {
+			Class<?>[] paramTypes = method.getParameterTypes();
+			this.varArgCls = paramTypes[paramTypes.length-1].getComponentType();
+		} else {
+			this.varArgCls = null;
+		}
+	}
+
+	@Override public String toString() {
+		return "java-function: " + method;
+	}
+
+	public Class<?>[] getParameterTypes() {
+		return method.getParameterTypes();
+	}
+
+	@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+		args = fixArgs(luan,args);
+		try {
+			return doCall(luan,args);
+		} catch(IllegalArgumentException e) {
+			checkArgs(luan,args);
+			throw e;
+		}
+	}
+
+	public Object rawCall(LuanState luan,Object[] args) throws LuanException {
+		args = fixArgs(luan,args);
+		return doCall(luan,args);
+	}
+
+	private Object doCall(LuanState luan,Object[] args) throws LuanException {
+		Object rtn;
+		try {
+			rtn = method.invoke(obj,args);
+		} catch(IllegalAccessException e) {
+			throw new RuntimeException("method = "+method,e);
+		} catch(InvocationTargetException e) {
+			Throwable cause = e.getCause();
+			if( cause instanceof Error )
+				throw (Error)cause;
+			if( cause instanceof LuanException )
+				throw (LuanException)cause;
+			throw luan.exception(cause);
+		} catch(InstantiationException e) {
+			throw new RuntimeException(e);
+		}
+		return rtnConverter.convert(rtn);
+	}
+
+	private static final Map<Class,Class> primitiveMap = new HashMap<Class,Class>();
+	static {
+		primitiveMap.put(Boolean.TYPE,Boolean.class);
+		primitiveMap.put(Character.TYPE,Character.class);
+		primitiveMap.put(Byte.TYPE,Byte.class);
+		primitiveMap.put(Short.TYPE,Short.class);
+		primitiveMap.put(Integer.TYPE,Integer.class);
+		primitiveMap.put(Long.TYPE,Long.class);
+		primitiveMap.put(Float.TYPE,Float.class);
+		primitiveMap.put(Double.TYPE,Double.class);
+		primitiveMap.put(Void.TYPE,Void.class);
+	}
+
+	private void checkArgs(LuanState luan,Object[] args) throws LuanException {
+		Class<?>[] a = getParameterTypes();
+		int start = takesLuaState ? 1 : 0;
+		for( int i=start; i<a.length; i++ ) {
+			Class<?> paramType = a[i];
+			Class<?> type = paramType;
+			if( type.isPrimitive() )
+				type = primitiveMap.get(type);
+			Object arg = args[i];
+			if( !type.isInstance(arg) ) {
+				String expected = paramType.getSimpleName();
+				if( arg==null ) {
+					if( paramType.isPrimitive() )
+						throw luan.exception("bad argument #"+(i+1-start)+" ("+expected+" expected, got nil)");
+				} else {
+					String got = arg.getClass().getSimpleName();
+					throw luan.exception("bad argument #"+(i+1-start)+" ("+expected+" expected, got "+got+")");
+				}
+			}
+		}
+	}
+
+	private Object[] fixArgs(LuanState luan,Object[] args) {
+		int n = argConverters.length;
+		Object[] rtn;
+		int start = 0;
+		if( !takesLuaState && varArgCls==null && args.length == n ) {
+			rtn = args;
+		} else {
+			if( takesLuaState )
+				n++;
+			rtn = new Object[n];
+			if( takesLuaState ) {
+				rtn[start++] = luan;
+			}
+			n = argConverters.length;
+			if( varArgCls != null ) {
+				n--;
+				if( args.length < argConverters.length ) {
+					rtn[rtn.length-1] = Array.newInstance(varArgCls,0);
+				} else {
+					int len = args.length - n;
+					Object varArgs = Array.newInstance(varArgCls,len);
+					ArgConverter ac = argConverters[n];
+					for( int i=0; i<len; i++ ) {
+						Array.set( varArgs, i, ac.convert(args[n+i]) );
+					}
+					rtn[rtn.length-1] = varArgs;
+				}
+			}
+			System.arraycopy(args,0,rtn,start,Math.min(args.length,n));
+		}
+		for( int i=0; i<n; i++ ) {
+			rtn[start+i] = argConverters[i].convert(rtn[start+i]);
+		}
+		return rtn;
+	}
+
+
+	private interface RtnConverter {
+		public Object convert(Object obj);
+	}
+
+	private static final RtnConverter RTN_NOTHING = new RtnConverter() {
+		@Override public Object[] convert(Object obj) {
+			return NOTHING;
+		}
+	};
+
+	private static final RtnConverter RTN_SAME = new RtnConverter() {
+		@Override public Object convert(Object obj) {
+			return obj;
+		}
+	};
+
+	private static RtnConverter getRtnConverter(JavaMethod m) {
+		Class<?> rtnType = m.getReturnType();
+		if( rtnType == Void.TYPE )
+			return RTN_NOTHING;
+		return RTN_SAME;
+	}
+
+	private static boolean isNumber(Class<?> rtnType) {
+		return rtnType == Short.TYPE
+			|| rtnType == Integer.TYPE
+			|| rtnType == Long.TYPE
+			|| rtnType == Float.TYPE
+			|| rtnType == Double.TYPE
+		;
+	}
+
+	private interface ArgConverter {
+		public Object convert(Object obj);
+	}
+
+	private static final ArgConverter ARG_SAME = new ArgConverter() {
+		public Object convert(Object obj) {
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_BOOLEAN = new ArgConverter() {
+		public Object convert(Object obj) {
+			return Luan.toBoolean(obj);
+		}
+	};
+
+	private static final ArgConverter ARG_BOOLEAN_OBJ = new ArgConverter() {
+		public Object convert(Object obj) {
+			return obj==null ? null : Luan.toBoolean(obj);
+		}
+	};
+
+	private static final ArgConverter ARG_DOUBLE = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof Double )
+				return obj;
+			if( obj instanceof Number ) {
+				Number n = (Number)obj;
+				return n.doubleValue();
+			}
+			if( obj instanceof String ) {
+				String s = (String)obj;
+				try {
+					return Double.valueOf(s);
+				} catch(NumberFormatException e) {}
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_FLOAT = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof Float )
+				return obj;
+			if( obj instanceof Number ) {
+				Number n = (Number)obj;
+				return n.floatValue();
+			}
+			if( obj instanceof String ) {
+				String s = (String)obj;
+				try {
+					return Float.valueOf(s);
+				} catch(NumberFormatException e) {}
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_LONG = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof Long )
+				return obj;
+			if( obj instanceof Number ) {
+				Number n = (Number)obj;
+				long r = n.longValue();
+				if( r==n.doubleValue() )
+					return r;
+			}
+			else if( obj instanceof String ) {
+				String s = (String)obj;
+				try {
+					return Long.valueOf(s);
+				} catch(NumberFormatException e) {}
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_INTEGER = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof Integer )
+				return obj;
+			if( obj instanceof Number ) {
+				Number n = (Number)obj;
+				int r = n.intValue();
+				if( r==n.doubleValue() )
+					return r;
+			}
+			else if( obj instanceof String ) {
+				String s = (String)obj;
+				try {
+					return Integer.valueOf(s);
+				} catch(NumberFormatException e) {}
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_SHORT = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof Short )
+				return obj;
+			if( obj instanceof Number ) {
+				Number n = (Number)obj;
+				short r = n.shortValue();
+				if( r==n.doubleValue() )
+					return r;
+			}
+			else if( obj instanceof String ) {
+				String s = (String)obj;
+				try {
+					return Short.valueOf(s);
+				} catch(NumberFormatException e) {}
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_BYTE = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof Byte )
+				return obj;
+			if( obj instanceof Number ) {
+				Number n = (Number)obj;
+				byte r = n.byteValue();
+				if( r==n.doubleValue() )
+					return r;
+			}
+			else if( obj instanceof String ) {
+				String s = (String)obj;
+				try {
+					return Byte.valueOf(s);
+				} catch(NumberFormatException e) {}
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_TABLE = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj == null )
+				return null;
+			if( obj instanceof List ) {
+				@SuppressWarnings("unchecked")
+				List<Object> list = (List<Object>)obj;
+				return new LuanTable(list);
+			}
+			if( obj instanceof Map ) {
+				@SuppressWarnings("unchecked")
+				Map<Object,Object> map = (Map<Object,Object>)obj;
+				return new LuanTable(map);
+			}
+			if( obj instanceof Set ) {
+				@SuppressWarnings("unchecked")
+				Set<Object> set = (Set<Object>)obj;
+				return new LuanTable(set);
+			}
+			Class cls = obj.getClass();
+			if( cls.isArray() && !cls.getComponentType().isPrimitive() ) {
+				Object[] a = (Object[])obj;
+				return new LuanTable(Arrays.asList(a));
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_MAP = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof LuanTable ) {
+				LuanTable t = (LuanTable)obj;
+				return t.asMap();
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_LIST = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof LuanTable ) {
+				LuanTable t = (LuanTable)obj;
+				if( t.isList() )
+					return t.asList();
+			}
+			return obj;
+		}
+	};
+
+	private static final ArgConverter ARG_SET = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof LuanTable ) {
+				LuanTable t = (LuanTable)obj;
+				if( t.isSet() )
+					return t.asSet();
+			}
+			return obj;
+		}
+	};
+
+	private static class ArgArray implements ArgConverter {
+		private final Object[] a;
+
+		ArgArray(Class cls) {
+			a = (Object[])Array.newInstance(cls.getComponentType(),0);
+		}
+
+		public Object convert(Object obj) {
+			if( obj instanceof LuanTable ) {
+				LuanTable t = (LuanTable)obj;
+				if( t.isList() ) {
+					try {
+						return t.asList().toArray(a);
+					} catch(ArrayStoreException e) {}
+				}
+			}
+			return obj;
+		}
+	}
+
+	private static boolean takesLuaState(JavaMethod m) {
+		Class<?>[] paramTypes = m.getParameterTypes();
+		return paramTypes.length > 0 && paramTypes[0].equals(LuanState.class);
+	}
+
+	private static ArgConverter[] getArgConverters(boolean takesLuaState,JavaMethod m) {
+		final boolean isVarArgs = m.isVarArgs();
+		Class<?>[] paramTypes = m.getParameterTypes();
+		if( takesLuaState ) {
+			Class<?>[] t = new Class<?>[paramTypes.length-1];
+			System.arraycopy(paramTypes,1,t,0,t.length);
+			paramTypes = t;
+		}
+		ArgConverter[] a = new ArgConverter[paramTypes.length];
+		for( int i=0; i<a.length; i++ ) {
+			Class<?> paramType = paramTypes[i];
+			if( isVarArgs && i == a.length-1 )
+				paramType = paramType.getComponentType();
+			a[i] = getArgConverter(paramType);
+		}
+		return a;
+	}
+
+	private static ArgConverter getArgConverter(Class<?> cls) {
+		if( cls == Boolean.TYPE )
+			return ARG_BOOLEAN;
+		if( cls.equals(Boolean.class) )
+			return ARG_BOOLEAN_OBJ;
+		if( cls == Double.TYPE || cls.equals(Double.class) )
+			return ARG_DOUBLE;
+		if( cls == Float.TYPE || cls.equals(Float.class) )
+			return ARG_FLOAT;
+		if( cls == Long.TYPE || cls.equals(Long.class) )
+			return ARG_LONG;
+		if( cls == Integer.TYPE || cls.equals(Integer.class) )
+			return ARG_INTEGER;
+		if( cls == Short.TYPE || cls.equals(Short.class) )
+			return ARG_SHORT;
+		if( cls == Byte.TYPE || cls.equals(Byte.class) )
+			return ARG_BYTE;
+		if( cls.equals(LuanTable.class) )
+			return ARG_TABLE;
+		if( cls.equals(Map.class) )
+			return ARG_MAP;
+		if( cls.equals(List.class) )
+			return ARG_LIST;
+		if( cls.equals(Set.class) )
+			return ARG_SET;
+		if( cls.isArray() && !cls.getComponentType().isPrimitive() )
+			return new ArgArray(cls);
+		return ARG_SAME;
+	}
+
+
+
+	private static abstract class JavaMethod {
+		abstract boolean isVarArgs();
+		abstract Class<?>[] getParameterTypes();
+		abstract Object invoke(Object obj,Object... args)
+			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException;
+		abstract Class<?> getReturnType();
+	
+		static JavaMethod of(final Method m) {
+			return new JavaMethod() {
+				@Override boolean isVarArgs() {
+					return m.isVarArgs();
+				}
+				@Override Class<?>[] getParameterTypes() {
+					return m.getParameterTypes();
+				}
+				@Override Object invoke(Object obj,Object... args)
+					throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
+				{
+					return m.invoke(obj,args);
+				}
+				@Override Class<?> getReturnType() {
+					return m.getReturnType();
+				}
+				@Override public String toString() {
+					return m.toString();
+				}
+			};
+		}
+	
+		static JavaMethod of(final Constructor c) {
+			return new JavaMethod() {
+				@Override boolean isVarArgs() {
+					return c.isVarArgs();
+				}
+				@Override Class<?>[] getParameterTypes() {
+					return c.getParameterTypes();
+				}
+				@Override Object invoke(Object obj,Object... args)
+					throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException
+				{
+					return c.newInstance(args);
+				}
+				@Override Class<?> getReturnType() {
+					return c.getDeclaringClass();
+				}
+				@Override public String toString() {
+					return c.toString();
+				}
+			};
+		}
+	
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanRepr.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,6 @@
+package luan;
+
+
+public interface LuanRepr {
+	public String repr();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanRuntimeException.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,8 @@
+package luan;
+
+
+public final class LuanRuntimeException extends RuntimeException {
+	public LuanRuntimeException(LuanException e) {
+		super(e);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanSource.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,62 @@
+package luan;
+
+
+public final class LuanSource {
+	public final String name;
+	public final String text;
+
+	public LuanSource(String name,String text) {
+		this.name = name;
+		this.text = text;
+	}
+
+	public static final class CompilerElement extends LuanElement {
+		public final LuanSource source;
+
+		public CompilerElement(LuanSource source) {
+			if( source==null )
+				throw new NullPointerException("source is null");
+			this.source = source;
+		}
+
+		@Override String location() {
+			return "Compiling " + source.name;
+		}
+	}
+
+	public static final class Element extends LuanElement {
+		public final LuanSource source;
+		public final int start;
+		public final int end;
+
+		public Element(LuanSource source,int start,int end) {
+			if( source==null )
+				throw new NullPointerException("source is null");
+			this.source = source;
+			this.start = start;
+			while( end > 0 && Character.isWhitespace(source.text.charAt(end-1)) ) {
+				end--;
+			}
+			this.end = end;
+		}
+
+		public String text() {
+			return source.text.substring(start,end);
+		}
+
+		@Override String location() {
+			return source.name + ':' + lineNumber();
+		}
+
+		private int lineNumber() {
+			int line = 0;
+			int i = -1;
+			do {
+				line++;
+				i = source.text.indexOf('\n',i+1);
+			} while( i != -1 && i < start );
+			return line;
+		}
+
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanState.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,181 @@
+package luan;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.LinkedHashMap;
+import luan.impl.LuanCompiler;
+import luan.modules.BasicLuan;
+import luan.modules.PackageLuan;
+
+
+public abstract class LuanState implements DeepCloneable<LuanState> {
+	private final LuanBit JAVA = bit(LuanElement.JAVA);
+
+	public LuanException exception(Object msg) {
+		return JAVA.exception(msg);
+	}
+
+	public Object call(LuanFunction fn) throws LuanException {
+		return call(fn,null,LuanFunction.NOTHING);
+	}
+
+	public Object call(LuanFunction fn,String fnName) throws LuanException {
+		return call(fn,fnName,LuanFunction.NOTHING);
+	}
+
+	public Object call(LuanFunction fn,Object[] args) throws LuanException {
+		return call(fn,null,args);
+	}
+
+	public Object call(LuanFunction fn,String fnName,Object[] args) throws LuanException {
+		return JAVA.call(fn,fnName,args);
+	}
+
+	public LuanFunction checkFunction(Object obj) throws LuanException {
+		return JAVA.checkFunction(obj);
+	}
+
+	public String toString(Object obj) throws LuanException {
+		return JAVA.toString(obj);
+	}
+
+	public String repr(Object obj) throws LuanException {
+		return JAVA.repr(obj);
+	}
+
+	public boolean isLessThan(Object o1,Object o2) throws LuanException {
+		return JAVA.isLessThan(o1,o2);
+	}
+
+
+
+	private LuanTable global;
+	private LuanTable loaded;
+	private LuanTable preload;
+	private LuanTable searchers;
+
+	private final List<MetatableGetter> mtGetters;
+	final List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
+
+	protected LuanState() {
+		global = new LuanTable();
+		global.put("_G",global);
+		global.put( "_VERSION", Luan.version );
+		loaded = new LuanTable();
+		preload = new LuanTable();
+		searchers = new LuanTable();
+		mtGetters = new ArrayList<MetatableGetter>();
+	}
+
+	protected LuanState(LuanState luan) {
+		mtGetters = new ArrayList<MetatableGetter>(luan.mtGetters);
+	}
+
+	@Override public void deepenClone(LuanState clone,DeepCloner cloner) {
+		clone.global = cloner.deepClone(global);
+		clone.loaded = cloner.deepClone(loaded);
+		clone.preload = cloner.deepClone(preload);
+		clone.searchers = cloner.deepClone(searchers);
+	}
+
+	public abstract LuanTable currentEnvironment();
+
+	public final LuanTable global() {
+		return global;
+	}
+
+	public final LuanTable loaded() {
+		return loaded;
+	}
+
+	public final LuanTable preload() {
+		return preload;
+	}
+
+	public final LuanTable searchers() {
+		return searchers;
+	}
+
+	public final Object get(String name) {
+		String[] a = name.split("\\.");
+		LuanTable t = loaded;
+		for( int i=0; i<a.length-1; i++ ) {
+			Object obj = t.get(a[i]);
+			if( !(obj instanceof LuanTable) )
+				return null;
+			t = (LuanTable)obj;
+		}
+		return t.get(a[a.length-1]);
+	}
+
+	public final Object set(String name,Object value) {
+		String[] a = name.split("\\.");
+		LuanTable t = loaded;
+		for( int i=0; i<a.length-1; i++ ) {
+			Object obj = t.get(a[i]);
+			if( !(obj instanceof LuanTable) )
+				return null;
+			t = (LuanTable)obj;
+		}
+		return t.put(a[a.length-1],value);
+	}
+
+	public final void globalImport(String modName) throws LuanException {
+		Object mod = PackageLuan.require(this,modName);
+		global.put(modName,mod);
+	}
+
+	public static LuanState newStandard() {
+		try {
+			LuanState luan = LuanCompiler.newLuanState();
+			luan.globalImport("Package");
+			BasicLuan.do_file(luan,"java:luan/init.luan");
+			return luan;
+		} catch(LuanException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public final Object eval(String cmd) {
+		return eval(cmd,new LuanTable());
+	}
+
+	public final Object eval(String cmd,LuanTable env) {
+		try {
+			LuanFunction fn = BasicLuan.load(this,cmd,"eval",env,true);
+			return call(fn);
+		} catch(LuanException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public final LuanTable getMetatable(Object obj) {
+		if( obj instanceof LuanTable ) {
+			LuanTable table = (LuanTable)obj;
+			return table.getMetatable();
+		}
+		for( MetatableGetter mg : mtGetters ) {
+			LuanTable table = mg.getMetatable(obj);
+			if( table != null )
+				return table;
+		}
+		return null;
+	}
+
+	public final void addMetatableGetter(MetatableGetter mg) {
+		mtGetters.add(mg);
+	}
+
+	public final LuanBit bit(LuanElement el) {
+		return new LuanBit(this,el);
+	}
+
+	public final Object getHandler(String op,Object obj) {
+		LuanTable t = getMetatable(obj);
+		return t==null ? null : t.get(op);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanTable.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,388 @@
+package luan;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.regex.Pattern;
+
+
+public final class LuanTable implements DeepCloneable<LuanTable>, Iterable<Map.Entry<Object,Object>>, LuanRepr {
+	private Map<Object,Object> map = null;
+	private List<Object> list = null;
+	private LuanTable metatable = null;
+
+	public LuanTable() {}
+
+	public LuanTable(LuanTable tbl) {
+		if( tbl.map != null )
+			this.map = new HashMap<Object,Object>(tbl.map);
+		if( tbl.list != null )
+			this.list = new ArrayList<Object>(tbl.list);
+	}
+
+	public LuanTable(List<Object> list) {
+		this.list = list;
+		this.map = new HashMap<Object,Object>();
+		map.put("n",list.size());
+		for( int i=0; i<list.size(); i++ ) {
+			if( list.get(i) == null ) {
+				listToMap(i);
+				break;
+			}
+		}
+	}
+
+	public LuanTable(Map<Object,Object> map) {
+		map.remove(null);
+		for( Iterator<Object> i=map.values().iterator(); i.hasNext(); ) {
+			if( i.next() == null )
+				i.remove();
+		}
+		this.map = map;
+	}
+
+	public LuanTable(Set<Object> set) {
+		map = new HashMap<Object,Object>();
+		for( Object obj : set ) {
+			if( obj != null )
+				map.put(obj,Boolean.TRUE);
+		}
+	}
+
+	@Override public LuanTable shallowClone() {
+		return new LuanTable();
+	}
+
+	@Override public void deepenClone(LuanTable clone,DeepCloner cloner) {
+		if( map != null ) {
+			clone.map = new HashMap<Object,Object>();
+			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
+				clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) );
+			}
+		}
+		if( list != null ) {
+			clone.list = new ArrayList<Object>();
+			for( Object obj : list ) {
+				clone.list.add( cloner.get(obj) );
+			}
+		}
+		if( metatable != null )
+			clone.metatable = cloner.deepClone(metatable);
+	}
+
+	public boolean isList() {
+		return map==null || map.isEmpty();
+	}
+
+	public List<Object> asList() {
+		return list!=null ? list : Collections.emptyList();
+	}
+
+	public Map<Object,Object> asMap() {
+		if( list == null || list.isEmpty() )
+			return map!=null ? map : Collections.emptyMap();
+		Map<Object,Object> rtn = map!=null ? new HashMap<Object,Object>(map) : new HashMap<Object,Object>();
+		for( ListIterator iter = list.listIterator(); iter.hasNext(); ) {
+			int i = iter.nextIndex();
+			rtn.put(i+1,iter.next());
+		}
+		return rtn;
+	}
+
+	public boolean isSet() {
+		if( list != null ) {
+			for( Object obj : list ) {
+				if( obj!=null && !obj.equals(Boolean.TRUE) )
+					return false;
+			}
+		}
+		if( map != null ) {
+			for( Object obj : map.values() ) {
+				if( !obj.equals(Boolean.TRUE) )
+					return false;
+			}
+		}
+		return true;
+	}
+
+	public Set<Object> asSet() {
+		if( list == null || list.isEmpty() )
+			return map!=null ? map.keySet() : Collections.emptySet();
+		Set<Object> rtn = map!=null ? new HashSet<Object>(map.keySet()) : new HashSet<Object>();
+		for( int i=1; i<=list.size(); i++ ) {
+			rtn.add(i);
+		}
+		return rtn;
+	}
+
+	@Override public String toString() {
+		return "table: " + Integer.toHexString(hashCode());
+	}
+
+	@Override public String repr() {
+		return repr( Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
+	}
+
+	private String repr(Set<LuanTable> set) {
+		if( !set.add(this) ) {
+			return "\"<circular reference>\"";
+		}
+		StringBuilder sb = new StringBuilder();
+		sb.append('{');
+		boolean isFirst = true;
+		if( list != null ) {
+			boolean gotNull = false;
+			for( int i=0; i<list.size(); i++ ) {
+				Object obj = list.get(i);
+				if( obj==null ) {
+					gotNull = true;
+				} else {
+					if( isFirst ) {
+						isFirst = false;
+					} else {
+						sb.append(", ");
+					}
+					if( gotNull )
+						sb.append(i+1).append('=');
+					sb.append(repr(set,obj));
+				}
+			}
+		}
+		if( map != null ) {
+			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
+				if( isFirst ) {
+					isFirst = false;
+				} else {
+					sb.append(", ");
+				}
+				sb.append(reprKey(set,entry.getKey())).append('=').append(repr(set,entry.getValue()));
+			}
+		}
+		sb.append('}');
+		return sb.toString();
+	}
+
+	private static final Pattern namePtn = Pattern.compile("[a-zA-Z_][a-zA-Z_0-9]*");
+
+	private static String reprKey(Set<LuanTable> set,Object obj) {
+		if( obj instanceof String ) {
+			String s = (String)obj;
+			if( namePtn.matcher(s).matches() )
+				return s;
+		}
+		return "[" + repr(set,obj) + "]";
+	}
+
+	private static String repr(Set<LuanTable> set,Object obj) {
+		if( obj instanceof LuanTable ) {
+			LuanTable t = (LuanTable)obj;
+			return t.repr(set);
+		} else {
+			String s = Luan.repr(obj);
+			if( s == null )
+				s = "<couldn't repr: " + Luan.stringEncode(Luan.toString(obj)) + ">";
+			return s;
+		}
+	}
+
+	public Object get(Object key) {
+		if( list != null ) {
+			Integer iT = Luan.asInteger(key);
+			if( iT != null ) {
+				int i = iT - 1;
+				if( i>=0 && i<list.size() )
+					return list.get(i);
+			}
+		}
+		if( map==null )
+			return null;
+		return map.get(key);
+	}
+
+	public Object put(Object key,Object val) {
+		Integer iT = Luan.asInteger(key);
+		if( iT != null ) {
+			int i = iT - 1;
+			if( list != null || i == 0 ) {
+				if( i == list().size() ) {
+					if( val != null ) {
+						list.add(val);
+						mapToList();
+					}
+					return null;
+				} else if( i>=0 && i<list.size() ) {
+					Object old = list.get(i);
+					list.set(i,val);
+					if( val == null ) {
+						listToMap(i);
+					}
+					return old;
+				}
+			}
+		}
+		if( map==null ) {
+			map = new HashMap<Object,Object>();
+		}
+		if( key instanceof Number && !(key instanceof Double) ) {
+			Number n = (Number)key;
+			key = Double.valueOf(n.doubleValue());
+		}
+		if( val == null ) {
+			return map.remove(key);
+		} else {
+			return map.put(key,val);
+		}
+	}
+
+	private void mapToList() {
+		if( map != null ) {
+			while(true) {
+				Object v = map.remove(Double.valueOf(list.size()+1));
+				if( v == null )
+					break;
+				list.add(v);
+			}
+		}
+	}
+
+	private void listToMap(int from) {
+		if( list != null ) {
+			while( list.size() > from ) {
+				int i = list.size() - 1;
+				Object v = list.remove(i);
+				if( v != null ) {
+					if( map==null )
+						map = new HashMap<Object,Object>();
+					map.put(i+1,v);
+				}
+			}
+		}
+	}
+
+	private List<Object> list() {
+		if( list == null ) {
+			list = new ArrayList<Object>();
+			mapToList();
+		}
+		return list;
+	}
+
+	public void insert(int pos,Object value) {
+		if( value==null )
+			throw new UnsupportedOperationException();
+		list().add(pos-1,value);
+		mapToList();
+	}
+
+	public void add(Object value) {
+		if( value==null )
+			throw new UnsupportedOperationException();
+		list().add(value);
+		mapToList();
+	}
+
+	public Object remove(int pos) {
+		return list().remove(pos-1);
+	}
+
+	public void sort(Comparator<Object> cmp) {
+		Collections.sort(list(),cmp);
+	}
+
+	public int length() {
+		return list==null ? 0 : list.size();
+	}
+
+	public Iterator<Map.Entry<Object,Object>> iterator() {
+		if( list == null ) {
+			if( map == null )
+				return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
+			return map.entrySet().iterator();
+		}
+		if( map == null )
+			return listIterator();
+		return new Iterator<Map.Entry<Object,Object>>() {
+			Iterator<Map.Entry<Object,Object>> iter = listIterator();
+			boolean isList = true;
+			public boolean hasNext() {
+				boolean b = iter.hasNext();
+				if( !b && isList ) {
+					iter = map.entrySet().iterator();
+					isList = false;
+					b = iter.hasNext();
+				}
+				return b;
+			}
+			public Map.Entry<Object,Object> next() {
+				return iter.next();
+			}
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+
+	public Iterator<Map.Entry<Object,Object>> listIterator() {
+		if( list == null )
+			return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
+		final ListIterator iter = list.listIterator();
+		return new Iterator<Map.Entry<Object,Object>>() {
+			public boolean hasNext() {
+				return iter.hasNext();
+			}
+			public Map.Entry<Object,Object> next() {
+				Double key = Double.valueOf(iter.nextIndex()+1);
+				return new MapEntry(key,iter.next());
+			}
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+/*
+	public Object[] listToArray() {
+		return list==null ? new Object[0] : list.toArray();
+	}
+*/
+	public LuanTable subList(int from,int to) {
+		return new LuanTable(new ArrayList<Object>(list().subList(from-1,to-1)));
+	}
+
+	public LuanTable getMetatable() {
+		return metatable;
+	}
+
+	public void setMetatable(LuanTable metatable) {
+		this.metatable = metatable;
+	}
+
+	private static final class MapEntry implements Map.Entry<Object,Object> {
+		private final Object key;
+		private final Object value;
+
+		MapEntry(Object key,Object value) {
+			this.key = key;
+			this.value = value;
+		}
+
+		@Override public Object getKey() {
+			return key;
+		}
+
+		@Override public Object getValue() {
+			return value;
+		}
+
+		@Override public Object setValue(Object value) {
+			throw new UnsupportedOperationException();
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/MetatableGetter.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,5 @@
+package luan;
+
+public interface MetatableGetter {
+	public LuanTable getMetatable(Object obj);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/StackTraceElement.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,12 @@
+package luan;
+
+
+final class StackTraceElement {
+	final LuanElement call;
+	final String fnName;
+
+	StackTraceElement(LuanElement call,String fnName) {
+		this.call = call;
+		this.fnName = fnName;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/cmd_line.luan	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,70 @@
+
+local standalone_usage = [=[
+usage: java luan.CmdLine [options] [script [args]]
+Available options are:
+  -e stat  execute string 'stat'
+  -i       enter interactive mode after executing 'script'
+  -v       show version information
+  --       stop handling options
+  -        stop handling options and execute stdin
+]=]
+
+local function standalone_error(msg)
+	Io.stderr.write( msg, "\n", standalone_usage )
+end
+
+
+local args = {...}
+local interactive = false
+local showVersion = false
+local i = 1
+if #args == 0 then
+	interactive = true
+	showVersion = true
+else
+	while i <= #args do
+		local arg = args[i]
+		if arg.sub(1,1) ~= "-" or arg == "--" then
+			break
+		end
+		if arg == "-i" then
+			interactive = true
+		elseif arg == "-v" then
+			showVersion = true
+		elseif arg == "-e" then
+			i = i + 1
+			if i == #args then
+				standalone_error "'-e' needs argument"
+				return
+			end
+			local cmd = args[i]
+			local stat = load(cmd,"(command line)",true,true)
+			local result = Table.pack( stat() )
+			if result.n > 0 then
+				print( Table.unpack(result,1,result.n) )
+			end
+		elseif arg == "-" then
+			local src = Io.stdin.read_text()
+			local stdin = load(src,"stdin")
+			stdin()
+			return
+		else
+			standalone_error( "unrecognized option '"..arg.."'" )
+			return
+		end
+		i = i + 1
+	end
+end
+if showVersion then print(_VERSION) end
+if i <= #args then
+	local file = args[i]
+	_G.arg = {}
+	for j,v in ipairs(args) do
+		_G.arg[j-i] = v
+	end
+	local main_file = load_file(file)
+	main_file( Table.unpack(_G.arg) )
+end
+if interactive then
+	Debug.debug("> ")
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/AddExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/AndExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/BinaryOpExpr.java	Sun Jun 22 05:41:22 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);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/Block.java	Sun Jun 22 05:41:22 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);
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/BreakException.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,4 @@
+package luan.impl;
+
+
+final class BreakException extends RuntimeException {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/BreakStmt.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,9 @@
+package luan.impl;
+
+
+final class BreakStmt implements Stmt {
+
+	@Override public void eval(LuanStateImpl luan) {
+		throw new BreakException();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/Closure.java	Sun Jun 22 05:41:22 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);
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/Code.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,8 @@
+package luan.impl;
+
+import luan.LuanSource;
+
+
+interface Code {
+	public LuanSource.Element se();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/CodeImpl.java	Sun Jun 22 05:41:22 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;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ConcatExpr.java	Sun Jun 22 05:41:22 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;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ConstExpr.java	Sun Jun 22 05:41:22 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+")";
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/DivExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/EqExpr.java	Sun Jun 22 05:41:22 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})) );
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ExpList.java	Sun Jun 22 05:41:22 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);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/Expr.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,6 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+interface Expr extends Expressions {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/Expressions.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,8 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+interface Expressions extends Code {
+	public Object eval(LuanStateImpl luan) throws LuanException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ExpressionsExpr.java	Sun Jun 22 05:41:22 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();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ExpressionsStmt.java	Sun Jun 22 05:41:22 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);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/FnCall.java	Sun Jun 22 05:41:22 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+")";
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/FnDef.java	Sun Jun 22 05:41:22 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);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ForStmt.java	Sun Jun 22 05:41:22 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);
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/GetLocalVar.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/GetUpVar.java	Sun Jun 22 05:41:22 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();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/IfStmt.java	Sun Jun 22 05:41:22 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);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/IndexExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/LeExpr.java	Sun Jun 22 05:41:22 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) );
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/LenExpr.java	Sun Jun 22 05:41:22 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" );
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/LtExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/LuanCompiler.java	Sun Jun 22 05:41:22 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();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/LuanParser.java	Sun Jun 22 05:41:22 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();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/LuanStateImpl.java	Sun Jun 22 05:41:22 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();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ModExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/MulExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/NotExpr.java	Sun Jun 22 05:41:22 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));
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/OrExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ParseException.java	Sun Jun 22 05:41:22 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();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/Parser.java	Sun Jun 22 05:41:22 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());
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/PowExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/RepeatStmt.java	Sun Jun 22 05:41:22 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) {}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ReturnException.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,4 @@
+package luan.impl;
+
+
+final class ReturnException extends RuntimeException {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ReturnStmt.java	Sun Jun 22 05:41:22 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();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/SetLocalVar.java	Sun Jun 22 05:41:22 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 );
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/SetStmt.java	Sun Jun 22 05:41:22 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);
+			}
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/SetTableEntry.java	Sun Jun 22 05:41:22 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);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/SetUpVar.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/Settable.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,8 @@
+package luan.impl;
+
+import luan.LuanException;
+
+
+interface Settable {
+	public void set(LuanStateImpl luan,Object value) throws LuanException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/Stmt.java	Sun Jun 22 05:41:22 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) {}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/SubExpr.java	Sun Jun 22 05:41:22 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);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/TableExpr.java	Sun Jun 22 05:41:22 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;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/TryStmt.java	Sun Jun 22 05:41:22 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);
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/UnaryOpExpr.java	Sun Jun 22 05:41:22 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;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/UnmExpr.java	Sun Jun 22 05:41:22 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");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/UpValue.java	Sun Jun 22 05:41:22 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;
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/VarArgs.java	Sun Jun 22 05:41:22 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();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/WhileStmt.java	Sun Jun 22 05:41:22 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) {}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/init.luan	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,107 @@
+function Package.global(module,fn_name)
+	local function fn(...)
+		return module[fn_name](...)
+	end
+	_G[fn_name] = fn
+	return fn
+end
+
+local require = Package.global(Package,"require")
+
+function Package.global_import(name)
+	local mod = require(name)
+	_G[name] = mod
+	return mod
+end
+
+local Basic = Package.global_import("Basic","luan.lib.BasicLib.LOADER")
+Package.global(Basic,"assert")
+Package.global(Basic,"assert_boolean")
+Package.global(Basic,"assert_nil")
+Package.global(Basic,"assert_number")
+Package.global(Basic,"assert_string")
+Package.global(Basic,"assert_table")
+Package.global(Basic,"do_file")
+Package.global(Basic,"error")
+Package.global(Basic,"get_metatable")
+Package.global(Basic,"ipairs")
+local load = Package.global(Basic,"load")
+Package.global(Basic,"load_file")
+Package.global(Basic,"pairs")
+Package.global(Basic,"range")
+Package.global(Basic,"raw_equal")
+Package.global(Basic,"raw_get")
+Package.global(Basic,"raw_len")
+Package.global(Basic,"raw_set")
+Package.global(Basic,"repr")
+Package.global(Basic,"set_metatable")
+Package.global(Basic,"to_number")
+local to_string = Package.global(Basic,"to_string")
+Package.global(Basic,"type")
+
+local String = Package.global_import("String","luan.lib.StringLib.LOADER")
+
+-- improved global_import
+function Package.global_import(name)
+	local short = name.match("\.([^.]+)$") or name
+	local mod = require(name)
+	_G[short] = mod
+	return mod
+end
+
+local Table = Package.global_import("Table","luan.lib.TableLib.LOADER")
+local Io = Package.global_import("Io","luan.lib.IoLib.LOADER")
+Package.global_import("Math","luan.lib.MathLib.LOADER")
+Package.global_import("Html","luan.lib.HtmlLib.LOADER")
+Package.global_import("Thread","luan.lib.ThreadLib.LOADER")
+Package.global_import("Binary","luan.lib.BinaryLib.LOADER")
+
+
+function Io.print_to(out,...)
+	local list = {}
+	for _,v in Basic.values(...) do
+		list[#list+1] = to_string(v)
+		list[#list+1] = '\t'
+	end
+	if #list == 0 then
+		out.write( '\n' )
+	else
+		list[#list] = '\n'
+		out.write( Table.unpack(list) )
+	end
+end
+
+function Basic.print(...)
+	Io.print_to(Io.stdout,...)
+end
+local print = Package.global(Basic,"print")
+
+local Debug = {}
+Package.loaded.Debug = Debug
+_G.Debug = Debug
+
+function Debug.print_if_something(...)
+	if Table.pack(...).n > 0 then
+		print(...)
+	end
+end
+
+function Debug.debug(prompt)
+	prompt = prompt or "luan_debug> "
+	local function console()
+		return Io.read_console_line(prompt)
+	end
+	local env = {}
+	for line in console do
+		try
+			local fn = load(line,"stdin",env,true)
+			Debug.print_if_something( fn() )
+		catch e do
+			print(e)
+		end
+	end
+end
+
+
+-- import modules
+Package.global_import("Reactionary")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/BasicLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,222 @@
+package luan.modules;
+
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+import luan.LuanSource;
+import luan.LuanElement;
+import luan.impl.LuanCompiler;
+
+
+public final class BasicLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				module.put( "assert", new LuanJavaFunction(BasicLuan.class.getMethod("assert_",LuanState.class,Object.class,String.class),null) );
+				add( module, "assert_boolean", LuanState.class, Boolean.TYPE );
+				add( module, "assert_nil", LuanState.class, Object.class );
+				add( module, "assert_number", LuanState.class, Number.class );
+				add( module, "assert_string", LuanState.class, String.class );
+				add( module, "assert_table", LuanState.class, LuanTable.class );
+				add( module, "do_file", LuanState.class, String.class );
+				add( module, "error", LuanState.class, Object.class );
+				add( module, "get_metatable", LuanState.class, Object.class );
+				add( module, "ipairs", LuanState.class, LuanTable.class );
+				add( module, "load", LuanState.class, String.class, String.class, LuanTable.class, Boolean.class );
+				add( module, "load_file", LuanState.class, String.class );
+				add( module, "pairs", LuanState.class, LuanTable.class );
+				add( module, "range", LuanState.class, Double.TYPE, Double.TYPE, Double.class );
+				add( module, "raw_equal", Object.class, Object.class );
+				add( module, "raw_get", LuanTable.class, Object.class );
+				add( module, "raw_len", LuanState.class, Object.class );
+				add( module, "raw_set", LuanTable.class, Object.class, Object.class );
+				add( module, "repr", LuanState.class, Object.class );
+				add( module, "set_metatable", LuanTable.class, LuanTable.class );
+				add( module, "to_number", Object.class, Integer.class );
+				add( module, "to_string", LuanState.class, Object.class );
+				add( module, "type", Object.class );
+				add( module, "values", new Object[0].getClass() );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(BasicLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static String type(Object obj) {
+		return Luan.type(obj);
+	}
+
+	public static LuanFunction load(LuanState luan,String text,String sourceName,LuanTable env,Boolean allowExpr)
+		throws LuanException
+	{
+		if( allowExpr==null )
+			allowExpr = false;
+		return LuanCompiler.compile(luan,new LuanSource(sourceName,text),env,allowExpr);
+	}
+
+	public static LuanFunction load_file(LuanState luan,String fileName) throws LuanException {
+		try {
+			String src = fileName==null ? Utils.readAll(new InputStreamReader(System.in)) : IoLuan.luanIo(luan,fileName).read_text();
+			return load(luan,src,fileName,null,false);
+		} catch(IOException e) {
+			throw luan.exception(e);
+		}
+	}
+
+	public static Object do_file(LuanState luan,String fileName) throws LuanException {
+		LuanFunction fn = load_file(luan,fileName);
+		return luan.call(fn);
+	}
+
+	private static LuanFunction pairs(final Iterator<Map.Entry<Object,Object>> iter) {
+		return new LuanFunction() {
+			@Override public Object[] call(LuanState luan,Object[] args) {
+				if( !iter.hasNext() )
+					return LuanFunction.NOTHING;
+				Map.Entry<Object,Object> entry = iter.next();
+				return new Object[]{entry.getKey(),entry.getValue()};
+			}
+		};
+	}
+
+	public static LuanFunction pairs(LuanState luan,LuanTable t) throws LuanException {
+		Utils.checkNotNull(luan,t,"table");
+		return pairs( t.iterator() );
+	}
+
+	public static LuanFunction ipairs(LuanState luan,LuanTable t) throws LuanException {
+		Utils.checkNotNull(luan,t,"table");
+		return pairs( t.listIterator() );
+	}
+
+	public static LuanTable get_metatable(LuanState luan,Object obj) {
+		return luan.getMetatable(obj);
+	}
+
+	public static LuanTable set_metatable(LuanTable table,LuanTable metatable) {
+		table.setMetatable(metatable);
+		return table;
+	}
+
+	public static boolean raw_equal(Object v1,Object v2) {
+		return v1 == v2 || v1 != null && v1.equals(v2);
+	}
+
+	public static Object raw_get(LuanTable table,Object index) {
+		return table.get(index);
+	}
+
+	public static LuanTable raw_set(LuanTable table,Object index,Object value) {
+		table.put(index,value);
+		return table;
+	}
+
+	public static int raw_len(LuanState luan,Object v) throws LuanException {
+		if( v instanceof String ) {
+			String s = (String)v;
+			return s.length();
+		}
+		if( v instanceof LuanTable ) {
+			LuanTable t = (LuanTable)v;
+			return t.length();
+		}
+		throw luan.exception( "bad argument #1 to 'raw_len' (table or string expected)" );
+	}
+
+	public static Number to_number(Object e,Integer base) {
+		return Luan.toNumber(e,base);
+	}
+
+	public static String to_string(LuanState luan,Object v) throws LuanException {
+		return luan.toString(v);
+	}
+
+	public static void error(LuanState luan,Object msg) throws LuanException {
+		throw luan.exception(msg);
+	}
+
+	public static Object assert_(LuanState luan,Object v,String msg) throws LuanException {
+		if( Luan.toBoolean(v) )
+			return v;
+		if( msg == null )
+			msg = "assertion failed!";
+		throw luan.exception( msg );
+	}
+
+	public static String assert_string(LuanState luan,String v) throws LuanException {
+		Utils.checkNotNull(luan,v,"string");
+		return v;
+	}
+
+	public static Number assert_number(LuanState luan,Number v) throws LuanException {
+		Utils.checkNotNull(luan,v,"number");
+		return v;
+	}
+
+	public static LuanTable assert_table(LuanState luan,LuanTable v) throws LuanException {
+		Utils.checkNotNull(luan,v,"table");
+		return v;
+	}
+
+	public static boolean assert_boolean(LuanState luan,boolean v) throws LuanException {
+		return v;
+	}
+
+	public static Object assert_nil(LuanState luan,Object v) throws LuanException {
+		if( v != null )
+			throw luan.exception("bad argument #1 (nil expected, got "+Luan.type(v)+")");
+		return v;
+	}
+
+	public static String repr(LuanState luan,Object v) throws LuanException {
+		return luan.repr(v);
+	}
+
+	public static LuanFunction range(LuanState luan,final double from,final double to,Double stepV) throws LuanException {
+		final double step = stepV==null ? 1.0 : stepV;
+		if( step == 0.0 )
+			throw luan.exception("bad argument #3 (step may not be zero)");
+		return new LuanFunction() {
+			double v = from;
+
+			@Override public Object call(LuanState luan,Object[] args) {
+				if( step > 0.0 && v > to || step < 0.0 && v < to )
+					return LuanFunction.NOTHING;
+				double rtn = v;
+				v += step;
+				return rtn;
+			}
+		};
+	}
+
+	public static LuanFunction values(final Object... args) throws LuanException {
+		return new LuanFunction() {
+			int i = 0;
+
+			@Override public Object call(LuanState luan,Object[] unused) {
+				if( ++i > args.length )
+					return LuanFunction.NOTHING;
+				return new Object[]{i,args[i-1]};
+			}
+		};
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/BinaryLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,32 @@
+package luan.modules;
+
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class BinaryLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "to_string", new byte[0].getClass() );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(BinaryLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static String to_string(byte[] bytes) {
+		return new String(bytes);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/HtmlLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,53 @@
+package luan.modules;
+
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class HtmlLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "encode", String.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(HtmlLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static String encode(String s) {
+		char[] a = s.toCharArray();
+		StringBuilder buf = new StringBuilder();
+		for( int i=0; i<a.length; i++ ) {
+			char c = a[i];
+			switch(c) {
+			case '&':
+				buf.append("&amp;");
+				break;
+			case '<':
+				buf.append("&lt;");
+				break;
+			case '>':
+				buf.append("&gt;");
+				break;
+			case '"':
+				buf.append("&quot;");
+				break;
+			default:
+				buf.append(c);
+			}
+		}
+		return buf.toString();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/IoLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,504 @@
+package luan.modules;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.net.MalformedURLException;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class IoLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "File", LuanState.class, String.class );
+				add( module, "read_console_line", String.class );
+
+				LuanTable stdin = new LuanTable();
+				stdin.put( "read_text", new LuanJavaFunction(
+					IoLuan.class.getMethod( "stdin_read_text" ), null
+				) );
+				stdin.put( "read_binary", new LuanJavaFunction(
+					IoLuan.class.getMethod( "stdin_read_binary" ), null
+				) );
+				stdin.put( "read_lines", new LuanJavaFunction(
+					IoLuan.class.getMethod( "stdin_read_lines" ), null
+				) );
+				stdin.put( "read_blocks", new LuanJavaFunction(
+					IoLuan.class.getMethod( "stdin_read_blocks", Integer.class ), null
+				) );
+				module.put( "stdin", stdin );
+
+				add( module, "Socket", String.class, Integer.TYPE );
+				add( module, "socket_server", Integer.TYPE );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			module.put( "stdout", textWriter(System.out) );
+			module.put( "stderr", textWriter(System.err) );
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(IoLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+
+	public static String stdin_read_text() throws IOException {
+		return Utils.readAll(new InputStreamReader(System.in));
+	}
+
+	public static byte[] stdin_read_binary() throws IOException {
+		return Utils.readAll(System.in);
+	}
+
+	public static LuanFunction stdin_read_lines() throws IOException {
+		return lines(new BufferedReader(new InputStreamReader(System.in)));
+	}
+
+	public static LuanFunction stdin_read_blocks(Integer blockSize) throws IOException {
+		int n = blockSize!=null ? blockSize : Utils.bufSize;
+		return blocks(System.in,n);
+	}
+
+	public static String read_console_line(String prompt) throws IOException {
+		if( prompt==null )
+			prompt = "> ";
+		return System.console().readLine(prompt);
+	}
+
+
+	public interface LuanWriter {
+		public void write(LuanState luan,Object... args) throws LuanException, IOException;
+		public void close() throws IOException;
+	}
+
+	public static LuanTable textWriter(final PrintStream out) {
+		LuanWriter luanWriter = new LuanWriter() {
+
+			public void write(LuanState luan,Object... args) throws LuanException {
+				for( Object obj : args ) {
+					out.print( luan.toString(obj) );
+				}
+			}
+
+			public void close() {
+				out.close();
+			}
+		};
+		return writer(luanWriter);
+	}
+
+	public static LuanTable textWriter(final Writer out) {
+		LuanWriter luanWriter = new LuanWriter() {
+
+			public void write(LuanState luan,Object... args) throws LuanException, IOException {
+				for( Object obj : args ) {
+					out.write( luan.toString(obj) );
+				}
+			}
+
+			public void close() throws IOException {
+				out.close();
+			}
+		};
+		return writer(luanWriter);
+	}
+
+	private static LuanTable writer(LuanWriter luanWriter) {
+		LuanTable writer = new LuanTable();
+		try {
+			writer.put( "write", new LuanJavaFunction(
+				LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter
+			) );
+			writer.put( "close", new LuanJavaFunction(
+				LuanWriter.class.getMethod( "close" ), luanWriter
+			) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		return writer;
+	}
+
+
+	public static LuanTable binaryWriter(final OutputStream out) {
+		LuanTable writer = new LuanTable();
+		try {
+			writer.put( "write", new LuanJavaFunction(
+				OutputStream.class.getMethod( "write", new byte[0].getClass() ), out
+			) );
+			writer.put( "close", new LuanJavaFunction(
+				OutputStream.class.getMethod( "close" ), out
+			) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		return writer;
+	}
+
+	static LuanFunction lines(final BufferedReader in) {
+		return new LuanFunction() {
+			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+				try {
+					if( args.length > 0 ) {
+						if( args.length > 1 || !"close".equals(args[0]) )
+							throw luan.exception( "the only argument allowed is 'close'" );
+						in.close();
+						return null;
+					}
+					String rtn = in.readLine();
+					if( rtn==null )
+						in.close();
+					return rtn;
+				} catch(IOException e) {
+					throw luan.exception(e);
+				}
+			}
+		};
+	}
+
+	static LuanFunction blocks(final InputStream in,final int blockSize) {
+		return new LuanFunction() {
+			final byte[] a = new byte[blockSize];
+
+			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+				try {
+					if( args.length > 0 ) {
+						if( args.length > 1 || !"close".equals(args[0]) )
+							throw luan.exception( "the only argument allowed is 'close'" );
+						in.close();
+						return null;
+					}
+					if( in.read(a) == -1 ) {
+						in.close();
+						return null;
+					}
+					return a;
+				} catch(IOException e) {
+					throw luan.exception(e);
+				}
+			}
+		};
+	}
+
+
+
+	public static abstract class LuanIn {
+		abstract InputStream inputStream() throws IOException;
+		public abstract String to_string();
+
+		public String read_text() throws IOException {
+			Reader in = new InputStreamReader(inputStream());
+			String s = Utils.readAll(in);
+			in.close();
+			return s;
+		}
+
+		public byte[] read_binary() throws IOException {
+			InputStream in = inputStream();
+			byte[] a = Utils.readAll(in);
+			in.close();
+			return a;
+		}
+
+		public LuanFunction read_lines() throws IOException {
+			return lines(new BufferedReader(new InputStreamReader(inputStream())));
+		}
+
+		public LuanFunction read_blocks(Integer blockSize) throws IOException {
+			int n = blockSize!=null ? blockSize : Utils.bufSize;
+			return blocks(inputStream(),n);
+		}
+
+		LuanTable table() {
+			LuanTable tbl = new LuanTable();
+			try {
+				tbl.put( "to_string", new LuanJavaFunction(
+					LuanIn.class.getMethod( "to_string" ), this
+				) );
+				tbl.put( "read_text", new LuanJavaFunction(
+					LuanIn.class.getMethod( "read_text" ), this
+				) );
+				tbl.put( "read_binary", new LuanJavaFunction(
+					LuanIn.class.getMethod( "read_binary" ), this
+				) );
+				tbl.put( "read_lines", new LuanJavaFunction(
+					LuanIn.class.getMethod( "read_lines" ), this
+				) );
+				tbl.put( "read_blocks", new LuanJavaFunction(
+					LuanIn.class.getMethod( "read_blocks", Integer.class ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+	}
+
+	public static abstract class LuanIO extends LuanIn {
+		abstract OutputStream outputStream() throws IOException;
+
+		public void write(LuanState luan,Object obj) throws LuanException, IOException {
+			if( obj instanceof String ) {
+				String s = (String)obj;
+				Writer out = new OutputStreamWriter(outputStream());
+				out.write(s);
+				out.close();
+				return;
+			}
+			if( obj instanceof byte[] ) {
+				byte[] a = (byte[])obj;
+				OutputStream out = outputStream();
+				Utils.copyAll(new ByteArrayInputStream(a),out);
+				out.close();
+				return;
+			}
+			throw luan.exception( "bad argument #1 to 'write' (string or binary expected)" );
+		}
+
+		public LuanTable text_writer() throws IOException {
+			return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream())));
+		}
+
+		public LuanTable binary_writer() throws IOException {
+			return binaryWriter(new BufferedOutputStream(outputStream()));
+		}
+
+		@Override LuanTable table() {
+			LuanTable tbl = super.table();
+			try {
+				tbl.put( "write", new LuanJavaFunction(
+					LuanIO.class.getMethod( "write", LuanState.class, Object.class ), this
+				) );
+				tbl.put( "text_writer", new LuanJavaFunction(
+					LuanIO.class.getMethod( "text_writer" ), this
+				) );
+				tbl.put( "binary_writer", new LuanJavaFunction(
+					LuanIO.class.getMethod( "binary_writer" ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+	}
+
+	public static final class LuanUrl extends LuanIn {
+		private final URL url;
+
+		public LuanUrl(String s) throws MalformedURLException {
+			this.url = new URL(s);
+		}
+
+		@Override InputStream inputStream() throws IOException {
+			return url.openStream();
+		}
+
+		@Override public String to_string() {
+			return url.toString();
+		}
+	}
+
+	public static final class LuanFile extends LuanIO {
+		private final File file;
+
+		public LuanFile(String name) {
+			this(new File(name));
+		}
+
+		public LuanFile(File file) {
+			this.file = file;
+		}
+
+		@Override InputStream inputStream() throws IOException {
+			return new FileInputStream(file);
+		}
+
+		@Override OutputStream outputStream() throws IOException {
+			return new FileOutputStream(file);
+		}
+
+		@Override public String to_string() {
+			return file.toString();
+		}
+
+		public LuanTable child(String name) {
+			return new LuanFile(new File(file,name)).table();
+		}
+
+		public LuanTable children() {
+			File[] files = file.listFiles();
+			if( files==null )
+				return null;
+			LuanTable list = new LuanTable();
+			for( File f : files ) {
+				list.add(new LuanFile(f).table());
+			}
+			return list;
+		}
+
+		public boolean exists() {
+			return Utils.exists(file);
+		}
+
+		@Override LuanTable table() {
+			LuanTable tbl = super.table();
+			try {
+				tbl.put( "name", new LuanJavaFunction(
+					File.class.getMethod( "getName" ), file
+				) );
+				tbl.put( "exists", new LuanJavaFunction(
+					LuanFile.class.getMethod( "exists" ), this
+				) );
+				tbl.put( "is_directory", new LuanJavaFunction(
+					File.class.getMethod( "isDirectory" ), file
+				) );
+				tbl.put( "is_file", new LuanJavaFunction(
+					File.class.getMethod( "isFile" ), file
+				) );
+				tbl.put( "delete", new LuanJavaFunction(
+					File.class.getMethod( "delete" ), file
+				) );
+				tbl.put( "mkdir", new LuanJavaFunction(
+					File.class.getMethod( "mkdir" ), file
+				) );
+				tbl.put( "mkdirs", new LuanJavaFunction(
+					File.class.getMethod( "mkdirs" ), file
+				) );
+				tbl.put( "last_modified", new LuanJavaFunction(
+					File.class.getMethod( "lastModified" ), file
+				) );
+				tbl.put( "child", new LuanJavaFunction(
+					LuanFile.class.getMethod( "child", String.class ), this
+				) );
+				tbl.put( "children", new LuanJavaFunction(
+					LuanFile.class.getMethod( "children" ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+	}
+
+	public static LuanIn luanIo(LuanState luan,String name) throws LuanException {
+		if( Utils.isFile(name) )
+			return new LuanFile(name);
+		String url = Utils.toUrl(name);
+		if( url != null ) {
+			try {
+				return new LuanUrl(url);
+			} catch(MalformedURLException e) {
+				throw new RuntimeException(e);
+			}
+		}
+		throw luan.exception( "file '"+name+"' not found" );
+	}
+
+	public static LuanTable File(LuanState luan,String name) throws LuanException {
+		return luanIo(luan,name).table();
+	}
+
+	public static final class LuanSocket extends LuanIO {
+		private final Socket socket;
+
+		public LuanSocket(String host,int port) throws IOException {
+			this(new Socket(host,port));
+		}
+
+		public LuanSocket(Socket socket) throws IOException {
+			this.socket = socket;
+		}
+
+		@Override InputStream inputStream() throws IOException {
+			return socket.getInputStream();
+		}
+
+		@Override OutputStream outputStream() throws IOException {
+			return socket.getOutputStream();
+		}
+
+		@Override public String to_string() {
+			return socket.toString();
+		}
+
+		public LuanTable Pickle_client(LuanState luan) throws IOException {
+			DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream()));
+			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outputStream()));
+			return new PickleClient(luan,in,out).table();
+		}
+
+		public void run_pickle_server(LuanState luan) throws IOException {
+			DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream()));
+			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outputStream()));
+			new PickleServer(luan,in,out).run();
+		}
+
+		@Override LuanTable table() {
+			LuanTable tbl = super.table();
+			try {
+				tbl.put( "Pickle_client", new LuanJavaFunction(
+					LuanSocket.class.getMethod( "Pickle_client", LuanState.class ), this
+				) );
+				tbl.put( "run_pickle_server", new LuanJavaFunction(
+					LuanSocket.class.getMethod( "run_pickle_server", LuanState.class ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+	}
+
+	public static LuanTable Socket(String host,int port) throws IOException {
+		return new LuanSocket(host,port).table();
+	}
+
+	public static LuanFunction socket_server(int port) throws IOException {
+		final ServerSocket ss = new ServerSocket(port);
+		return new LuanFunction() {
+			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+				try {
+					if( args.length > 0 ) {
+						if( args.length > 1 || !"close".equals(args[0]) )
+							throw luan.exception( "the only argument allowed is 'close'" );
+						ss.close();
+						return null;
+					}
+					return new LuanSocket(ss.accept()).table();
+				} catch(IOException e) {
+					throw luan.exception(e);
+				}
+			}
+		};
+	}
+
+	private void IoLuan() {}  // never
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/JavaLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,450 @@
+package luan.modules;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Member;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.Arrays;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.MetatableGetter;
+import luan.LuanException;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanElement;
+
+
+public final class JavaLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			luan.addMetatableGetter(mg);
+			LuanTable module = new LuanTable();
+			try {
+				module.put( "class", new LuanJavaFunction(JavaLuan.class.getMethod("getClass",LuanState.class,String.class),null) );
+				add( module, "proxy", LuanState.class, Static.class, LuanTable.class, Object.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			luan.searchers().add(javaSearcher);
+			return module;
+		}
+	};
+
+	public static final LuanFunction javaSearcher = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			String modName = (String)args[0];
+			final Static s = JavaLuan.getClass(luan,modName);
+			if( s==null )
+				return null;
+			LuanFunction loader = new LuanFunction() {
+				@Override public Object call(LuanState luan,Object[] args) {
+					return s;
+				}
+			};
+			return loader;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) {
+		try {
+			t.put( method, new LuanJavaFunction(JavaLuan.class.getMethod(method,parameterTypes),null) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private static final LuanTable mt = new LuanTable();
+	static {
+		add( mt, "__index", LuanState.class, Object.class, Object.class );
+		add( mt, "__newindex", LuanState.class, Object.class, Object.class, Object.class );
+	}
+
+	private static final MetatableGetter mg = new MetatableGetter() {
+		public LuanTable getMetatable(Object obj) {
+			if( obj==null )
+				return null;
+			return mt;
+		}
+	};
+
+	public static Object __index(LuanState luan,Object obj,Object key) throws LuanException {
+		if( obj instanceof Static ) {
+			if( key instanceof String ) {
+				String name = (String)key;
+				Static st = (Static)obj;
+				Class cls = st.cls;
+				if( "class".equals(name) ) {
+					return cls;
+				} else if( "new".equals(name) ) {
+					Constructor<?>[] constructors = cls.getConstructors();
+					if( constructors.length > 0 ) {
+						if( constructors.length==1 ) {
+							return new LuanJavaFunction(constructors[0],null);
+						} else {
+							List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
+							for( Constructor constructor : constructors ) {
+								fns.add(new LuanJavaFunction(constructor,null));
+							}
+							return new AmbiguousJavaFunction(fns);
+						}
+					}
+				} else if( "assert".equals(name) ) {
+					return new LuanJavaFunction(assertClass,new AssertClass(cls));
+				} else {
+					List<Member> members = getStaticMembers(cls,name);
+					if( !members.isEmpty() ) {
+						return member(null,members);
+					}
+				}
+			}
+			throw luan.exception("invalid member '"+key+"' for: "+obj);
+		}
+		Class cls = obj.getClass();
+		if( cls.isArray() ) {
+			if( "length".equals(key) ) {
+				return Array.getLength(obj);
+			}
+			Integer i = Luan.asInteger(key);
+			if( i != null ) {
+				return Array.get(obj,i);
+			}
+			throw luan.exception("invalid member '"+key+"' for java array: "+obj);
+		}
+		if( key instanceof String ) {
+			String name = (String)key;
+			if( "instanceof".equals(name) ) {
+				return new LuanJavaFunction(instanceOf,new InstanceOf(obj));
+			} else {
+				List<Member> members = getMembers(cls,name);
+				if( !members.isEmpty() ) {
+					return member(obj,members);
+				}
+			}
+		}
+//		throw luan.exception("invalid member '"+key+"' for java object: "+obj);
+		return null;
+	}
+
+	private static Object member(Object obj,List<Member> members) throws LuanException {
+		try {
+			if( members.size()==1 ) {
+				Member member = members.get(0);
+				if( member instanceof Static ) {
+					return member;
+				} else if( member instanceof Field ) {
+					Field field = (Field)member;
+					Object rtn = field.get(obj);
+					return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn;
+				} else {
+					Method method = (Method)member;
+					return new LuanJavaFunction(method,obj);
+				}
+			} else {
+				List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
+				for( Member member : members ) {
+					Method method = (Method)member;
+					fns.add(new LuanJavaFunction(method,obj));
+				}
+				return new AmbiguousJavaFunction(fns);
+			}
+		} catch(IllegalAccessException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static void __newindex(LuanState luan,Object obj,Object key,Object value) throws LuanException {
+		if( obj instanceof Static ) {
+			if( key instanceof String ) {
+				String name = (String)key;
+				Static st = (Static)obj;
+				Class cls = st.cls;
+				List<Member> members = getStaticMembers(cls,name);
+				if( !members.isEmpty() ) {
+					if( members.size() != 1 )
+						throw new RuntimeException("not field '"+name+"' of "+obj);
+					setMember(obj,members,value);
+					return;
+				}
+			}
+			throw luan.exception("invalid member '"+key+"' for: "+obj);
+		}
+		Class cls = obj.getClass();
+		if( cls.isArray() ) {
+			Integer i = Luan.asInteger(key);
+			if( i != null ) {
+				Array.set(obj,i,value);
+				return;
+			}
+			throw luan.exception("invalid member '"+key+"' for java array: "+obj);
+		}
+		if( key instanceof String ) {
+			String name = (String)key;
+			List<Member> members = getMembers(cls,name);
+			if( !members.isEmpty() ) {
+				if( members.size() != 1 )
+					throw new RuntimeException("not field '"+name+"' of "+obj);
+				setMember(obj,members,value);
+				return;
+			}
+		}
+		throw luan.exception("invalid member '"+key+"' for java object: "+obj);
+	}
+
+	private static void setMember(Object obj,List<Member> members,Object value) {
+		Field field = (Field)members.get(0);
+		try {
+			try {
+				field.set(obj,value);
+			} catch(IllegalArgumentException e) {
+				Class cls = field.getType();
+				if( value instanceof Number ) {
+					Number n = (Number)value;
+					if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) {
+						int r = n.intValue();
+						if( r==n.doubleValue() ) {
+							field.setInt(obj,r);
+							return;
+						}
+					}
+				}
+				throw e;
+			}
+		} catch(IllegalAccessException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static boolean privateAccess = false;
+	private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>();
+
+	private static synchronized List<Member> getMembers(Class cls,String name) {
+		Map<String,List<Member>> clsMap = memberMap.get(cls);
+		if( clsMap == null ) {
+			clsMap = new HashMap<String,List<Member>>();
+			for( Class c : cls.getClasses() ) {
+				String s = c.getSimpleName();
+				List<Member> list = new ArrayList<Member>();
+				clsMap.put(s,list);
+				list.add(new Static(c));
+			}
+			for( Field field : cls.getFields() ) {
+				String s = field.getName();
+				try {
+					if( !cls.getField(s).equals(field) )
+						continue;  // not accessible
+				} catch(NoSuchFieldException e) {
+					throw new RuntimeException(e);
+				}
+				List<Member> list = new ArrayList<Member>();
+				clsMap.put(s,list);
+				list.add(field);
+			}
+			for( Method method : cls.getMethods() ) {
+				String s = method.getName();
+				List<Member> list = clsMap.get(s);
+				if( list == null || !(list.get(0) instanceof Method) ) {
+					list = new ArrayList<Member>();
+					clsMap.put(s,list);
+				}
+				list.add(method);
+			}
+			if( privateAccess ) {
+				for( Method method : cls.getDeclaredMethods() ) {
+					String s = method.getName();
+					List<Member> list = clsMap.get(s);
+					if( list == null ) {
+						list = new ArrayList<Member>();
+						clsMap.put(s,list);
+					} else if( !(list.get(0) instanceof Method) )
+						continue;
+					if( !list.contains(method) ) {
+						list.add(method);
+					}
+				}
+				for( Field field : cls.getDeclaredFields() ) {
+					String s = field.getName();
+					List<Member> list = clsMap.get(s);
+					if( list == null ) {
+						list = new ArrayList<Member>();
+						clsMap.put(s,list);
+						list.add(field);
+					}
+				}
+			}
+			for( List<Member> members : clsMap.values() ) {
+				for( Member m : members ) {
+					if( m instanceof AccessibleObject )
+						((AccessibleObject)m).setAccessible(true);
+				}
+			}
+			memberMap.put(cls,clsMap);
+		}
+		List<Member> rtn = clsMap.get(name);
+		if( rtn==null )
+			rtn = Collections.emptyList();
+		return rtn;
+	}
+
+	private static synchronized List<Member> getStaticMembers(Class cls,String name) {
+		List<Member> staticMembers = new ArrayList<Member>();
+		for( Member m : getMembers(cls,name) ) {
+			if( Modifier.isStatic(m.getModifiers()) )
+				staticMembers.add(m);
+		}
+		return staticMembers;
+	}
+
+	static final class Static implements Member {
+		final Class cls;
+
+		Static(Class cls) {
+			this.cls = cls;
+		}
+
+		@Override public String toString() {
+			return cls.toString();
+		}
+
+		@Override public Class<?> getDeclaringClass() {
+			return cls.getDeclaringClass();
+		}
+
+		@Override public String getName() {
+			return cls.getName();
+		}
+
+		@Override public int getModifiers() {
+			return cls.getModifiers();
+		}
+
+		@Override public boolean isSynthetic() {
+			return cls.isSynthetic();
+		}
+	}
+
+	public static Static getClass(LuanState luan,String name) throws LuanException {
+		Class cls;
+		try {
+			cls = Class.forName(name);
+		} catch(ClassNotFoundException e) {
+			try {
+				cls = Thread.currentThread().getContextClassLoader().loadClass(name);
+			} catch(ClassNotFoundException e2) {
+				return null;
+			}
+		}
+		return new Static(cls);
+	}
+/*
+	public static void importClass(LuanState luan,String name) throws LuanException {
+		luan.currentEnvironment().put( name.substring(name.lastIndexOf('.')+1), getClass(luan,name) );
+	}
+*/
+	static class AmbiguousJavaFunction extends LuanFunction {
+		private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>();
+
+		AmbiguousJavaFunction(List<LuanJavaFunction> fns) {
+			for( LuanJavaFunction fn : fns ) {
+				Integer n = fn.getParameterTypes().length;
+				List<LuanJavaFunction> list = fnMap.get(n);
+				if( list==null ) {
+					list = new ArrayList<LuanJavaFunction>();
+					fnMap.put(n,list);
+				}
+				list.add(fn);
+			}
+		}
+
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			for( LuanJavaFunction fn : fnMap.get(args.length) ) {
+				try {
+					return fn.rawCall(luan,args);
+				} catch(IllegalArgumentException e) {}
+			}
+			throw luan.exception("no method matched args");
+		}
+	}
+
+	private static class InstanceOf {
+		private final Object obj;
+
+		InstanceOf(Object obj) {
+			this.obj = obj;
+		}
+
+		public boolean instanceOf(Static st) {
+			return st.cls.isInstance(obj);
+		}
+	}
+	private static final Method instanceOf;
+	static {
+		try {
+			instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class);
+			instanceOf.setAccessible(true);
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+
+	private static class AssertClass {
+		private final Class cls;
+
+		AssertClass(Class cls) {
+			this.cls = cls;
+		}
+
+		public Object assertClass(LuanState luan,Object v) throws LuanException {
+			if( !cls.isInstance(v) ) {
+				String got = v.getClass().getSimpleName();
+				String expected = cls.getSimpleName();
+				throw luan.exception("bad argument #1 ("+expected+" expected, got "+got+")");
+			}
+			return v;
+		}
+	}
+	private static final Method assertClass;
+	static {
+		try {
+			assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class);
+			assertClass.setAccessible(true);
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+
+	public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException {
+		return Proxy.newProxyInstance(
+			st.cls.getClassLoader(),
+			new Class[]{st.cls},
+			new InvocationHandler() {
+				public Object invoke(Object proxy,Method method, Object[] args)
+					throws Throwable
+				{
+					if( args==null )
+						args = new Object[0];
+					String name = method.getName();
+					Object fnObj = t.get(name);
+					if( fnObj==null && base!=null )
+						return method.invoke(base,args);
+					LuanFunction fn = luan.checkFunction(fnObj);
+					return Luan.first(luan.call(fn,name,args));
+				}
+			}
+		);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/MathLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,152 @@
+package luan.modules;
+
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class MathLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "abs", Double.TYPE );
+				add( module, "acos", Double.TYPE );
+				add( module, "asin", Double.TYPE );
+				add( module, "atan", Double.TYPE );
+				add( module, "atan2", Double.TYPE, Double.TYPE );
+				add( module, "ceil", Double.TYPE );
+				add( module, "cos", Double.TYPE );
+				add( module, "cosh", Double.TYPE );
+				add( module, "deg", Double.TYPE );
+				add( module, "exp", Double.TYPE );
+				add( module, "floor", Double.TYPE );
+				add( module, "log", Double.TYPE );
+				add( module, "min", Double.TYPE, new double[0].getClass() );
+				add( module, "max", Double.TYPE, new double[0].getClass() );
+				add( module, "modf", Double.TYPE );
+				module.put("pi",Math.PI);
+				add( module, "pow", Double.TYPE, Double.TYPE );
+				add( module, "rad", Double.TYPE );
+				add( module, "random" );
+				add( module, "sin", Double.TYPE );
+				add( module, "sinh", Double.TYPE );
+				add( module, "sqrt", Double.TYPE );
+				add( module, "tan", Double.TYPE );
+				add( module, "tanh", Double.TYPE );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(MathLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static double abs(double x) {
+		return Math.abs(x);
+	}
+
+	public static double acos(double x) {
+		return Math.acos(x);
+	}
+
+	public static double asin(double x) {
+		return Math.asin(x);
+	}
+
+	public static double atan(double x) {
+		return Math.atan(x);
+	}
+
+	public static double atan2(double y,double x) {
+		return Math.atan2(y,x);
+	}
+
+	public static double ceil(double x) {
+		return Math.ceil(x);
+	}
+
+	public static double cos(double x) {
+		return Math.cos(x);
+	}
+
+	public static double cosh(double x) {
+		return Math.cosh(x);
+	}
+
+	public static double deg(double x) {
+		return Math.toDegrees(x);
+	}
+
+	public static double exp(double x) {
+		return Math.exp(x);
+	}
+
+	public static double floor(double x) {
+		return Math.floor(x);
+	}
+
+	public static double log(double x) {
+		return Math.log(x);
+	}
+
+	public static double min(double x,double... a) {
+		for( double d : a ) {
+			if( x > d )
+				x = d;
+		}
+		return x;
+	}
+
+	public static double max(double x,double... a) {
+		for( double d : a ) {
+			if( x < d )
+				x = d;
+		}
+		return x;
+	}
+
+	public static double[] modf(double x) {
+		double i = (int)x;
+		return new double[]{i,x-i};
+	}
+
+	public static double pow(double x,double y) {
+		return Math.pow(x,y);
+	}
+
+	public static double rad(double x) {
+		return Math.toRadians(x);
+	}
+
+	public static double random() {
+		return Math.random();
+	}
+
+	public static double sin(double x) {
+		return Math.sin(x);
+	}
+
+	public static double sinh(double x) {
+		return Math.sinh(x);
+	}
+
+	public static double sqrt(double x) {
+		return Math.sqrt(x);
+	}
+
+	public static double tan(double x) {
+		return Math.tan(x);
+	}
+
+	public static double tanh(double x) {
+		return Math.tanh(x);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/PackageLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,170 @@
+package luan.modules;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanElement;
+import luan.LuanException;
+
+
+public final class PackageLuan {
+
+	private static final String jpath = "luan.modules.?Luan.LOADER";
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			module.put("loaded",luan.loaded());
+			module.put("preload",luan.preload());
+			module.put("path","?.luan;java:luan/modules/?.luan");
+			module.put("jpath",jpath);
+			try {
+				add( module, "require", LuanState.class, String.class );
+				add( module, "load_lib", String.class );
+				add( module, "search_path", String.class, String.class );
+				add( module, "search", LuanState.class, String.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			LuanTable searchers = luan.searchers();
+			searchers.add(preloadSearcher);
+			searchers.add(fileSearcher);
+			searchers.add(javaSearcher);
+			module.put("searchers",searchers);
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(PackageLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static Object require(LuanState luan,String modName) throws LuanException {
+		LuanTable loaded = luan.loaded();
+		Object mod = loaded.get(modName);
+		if( mod == null ) {
+			Object[] a = search(luan,modName);
+			if( a == null )
+				throw luan.exception( "module '"+modName+"' not found" );
+			LuanFunction loader = (LuanFunction)a[0];
+			a[0] = modName;
+			mod = Luan.first(luan.call(loader,"<require \""+modName+"\">",a));
+			if( mod != null ) {
+				loaded.put(modName,mod);
+			} else {
+				mod = loaded.get(modName);
+				if( mod==null )
+					loaded.put(modName,true);
+			}
+		}
+		return mod;
+	}
+
+	public static Object[] search(LuanState luan,String modName) throws LuanException {
+		List<Object> list = null;
+		LuanTable searchers = (LuanTable)luan.get("Package.searchers");
+		if( searchers == null ) {
+			list = Collections.<Object>singletonList(javaSearcher);
+		} else {
+			list = searchers.asList();
+		}
+		for( Object s : list ) {
+			LuanFunction searcher = (LuanFunction)s;
+			Object[] a = Luan.array(luan.call(searcher,"<searcher>",new Object[]{modName}));
+			if( a.length >= 1 && a[0] instanceof LuanFunction )
+				return a;
+		}
+		return null;
+	}
+
+	public static final LuanFunction preloadSearcher = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			String modName = (String)args[0];
+			return luan.preload().get(modName);
+		}
+	};
+
+	public static String search_path(String name,String path) {
+		name = name.replace('.','/');
+		for( String s : path.split(";") ) {
+			String file = s.replaceAll("\\?",name);
+			if( Utils.exists(file) )
+				return file;
+		}
+		return null;
+	}
+
+	public static final LuanFunction fileLoader = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			String fileName = (String)args[1];
+			LuanFunction fn = BasicLuan.load_file(luan,fileName);
+			return fn.call(luan,args);
+		}
+	};
+
+	public static final LuanFunction fileSearcher = new LuanFunction() {
+		@Override public Object[] call(LuanState luan,Object[] args) {
+			String modName = (String)args[0];
+			String path = (String)luan.get("Package.path");
+			if( path==null )
+				return LuanFunction.NOTHING;
+			String file = search_path(modName,path);
+			return file==null ? LuanFunction.NOTHING : new Object[]{fileLoader,file};
+		}
+	};
+
+
+	public static final LuanFunction javaLoader = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			try {
+				String objName = (String)args[1];
+				LuanFunction fn = load_lib(objName);
+				return fn.call(luan,args);
+			} catch(ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			} catch(NoSuchFieldException e) {
+				throw new RuntimeException(e);
+			} catch(IllegalAccessException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	};
+
+	public static final LuanFunction javaSearcher = new LuanFunction() {
+		@Override public Object[] call(LuanState luan,Object[] args) {
+			String modName = (String)args[0];
+			String path = (String)luan.get("Package.jpath");
+			if( path==null )
+				path = jpath;
+			for( String s : path.split(";") ) {
+				String objName = s.replaceAll("\\?",modName);
+				try {
+					load_lib(objName);  // throws exception if not found
+					return new Object[]{javaLoader,objName};
+				} catch(ClassNotFoundException e) {
+				} catch(NoSuchFieldException e) {
+				} catch(IllegalAccessException e) {
+				}
+			}
+			return LuanFunction.NOTHING;
+		}
+	};
+
+
+	public static LuanFunction load_lib(String path)
+		throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException
+	{
+		int i = path.lastIndexOf('.');
+		String clsPath = path.substring(0,i);
+		String fld = path.substring(i+1);
+		Class cls = Class.forName(clsPath);
+		return (LuanFunction)cls.getField(fld).get(null);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/PickleClient.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,106 @@
+package luan.modules;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanException;
+import luan.LuanTable;
+import luan.LuanJavaFunction;
+import luan.LuanFunction;
+
+
+public final class PickleClient {
+
+	private final PickleCon con;
+	private final LuanFunction _reversed_pickle;
+
+	PickleClient(LuanState luan,DataInputStream in,DataOutputStream out) {
+		this(new PickleCon(luan,in,out));
+	}
+
+	PickleClient(PickleCon con) {
+		this.con = con;
+		try {
+			this._reversed_pickle = new LuanJavaFunction(
+				PickleClient.class.getMethod( "_reversed_pickle" ), this
+			);
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public Object _reversed_pickle() throws LuanException, IOException {
+		new PickleServer(con).run();
+		return con.read();
+	}
+
+	public Object call(Object... args) throws LuanException, IOException {
+		con.write(args);
+		Object[] result;
+		con.ioModule.put("_reversed_pickle",_reversed_pickle);
+		try {
+			result = Luan.array(con.read());
+		} finally {
+			con.ioModule.put("_reversed_pickle",null);
+		}
+		boolean ok = (boolean)result[0];
+		if( ok ) {
+			Object[] rtn = new Object[result.length-1];
+			System.arraycopy(result,1,rtn,0,rtn.length);
+			return rtn;
+		} else {
+			String msg = (String)result[1];
+			String src = (String)result[2];
+			throw con.luan.exception(
+				msg + "\n"
+				+ "in:\n"
+				+ "------------------\n"
+				+ formatCode(src) + "\n"
+				+ "------------------\n"
+			);
+		}
+	}
+
+	LuanTable table() {
+		LuanTable tbl = new LuanTable();
+		try {
+			tbl.put( "pickle", new LuanJavaFunction(
+				PickleCon.class.getMethod( "pickle", Object.class ), con
+			) );
+			tbl.put( "call", new LuanJavaFunction(
+				PickleClient.class.getMethod( "call", new Object[0].getClass() ), this
+			) );
+			tbl.put( "close", new LuanJavaFunction(
+				PickleCon.class.getMethod( "close" ), con
+			) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		return tbl;
+	}
+
+
+	public static String formatCode(String s) {
+		StringBuilder buf = new StringBuilder();
+		int line = 1;
+		int i = 0;
+		int i2 = 0;
+		while( i2 != -1 ) {
+			buf.append( line++ );
+			buf.append( '\t' );
+			i2 = s.indexOf('\n',i);
+			String lineStr = i2 == -1 ? s.substring(i) : s.substring(i,i2+1);
+			int j;
+			for( j=0; j<lineStr.length() && lineStr.charAt(j)=='\t'; j++ ) {
+				buf.append( "    " );
+			}
+			buf.append( lineStr.substring(j) );
+			i = i2 + 1;
+		}
+		return buf.toString();
+	}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/PickleCon.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,134 @@
+package luan.modules;
+
+import java.io.OutputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Set;
+import java.util.IdentityHashMap;
+import java.util.Collections;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import luan.Luan;
+import luan.LuanTable;
+import luan.LuanState;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class PickleCon {
+	final LuanState luan;
+	private final DataInputStream in;
+	private final LuanFunction _read_binary;
+	final LuanTable ioModule;
+	private final DataOutputStream out;
+	private final List<byte[]> binaries = new ArrayList<byte[]>();
+	String src;
+	private final LuanTable env = new LuanTable();
+
+	PickleCon(LuanState luan,DataInputStream in,DataOutputStream out) {
+		this.in = in;
+		this.luan = luan;
+		try {
+			this._read_binary = new LuanJavaFunction(
+				PickleCon.class.getMethod( "_read_binary", Integer.TYPE ), this
+			);
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		this.ioModule = (LuanTable)luan.loaded().get("Io");
+
+		this.out = out;
+	}
+
+	public byte[] _read_binary(int size) throws IOException, LuanException {
+		byte[] a = new byte[size];
+		int i = 0;
+		while( i < size ) {
+			int n = in.read(a,i,size-i);
+			if( n == -1 )
+				throw luan.exception( "end of stream" );
+			i += n;
+		}
+		return a;
+	}
+
+	public Object read() throws IOException, LuanException {
+		ioModule.put("_read_binary",_read_binary);
+		try {
+			src = in.readUTF();
+			LuanFunction fn = BasicLuan.load(luan,src,"pickle-reader",env,false);
+			return luan.call(fn);
+		} finally {
+			ioModule.put("_binaries",null);
+			ioModule.put("_read_binary",null);
+		}
+	}
+
+	public String pickle(Object obj) throws LuanException {
+		if( obj == null )
+			return "nil";
+		if( obj instanceof Boolean )
+			return Luan.toString((Boolean)obj);
+		if( obj instanceof Number )
+			return Luan.toString((Number)obj);
+		if( obj instanceof String )
+			return "\"" + Luan.stringEncode((String)obj) + "\"";
+		if( obj instanceof LuanTable )
+			return pickle( (LuanTable)obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
+		if( obj instanceof byte[] ) {
+			byte[] a = (byte[])obj;
+			binaries.add(a);
+			return "Io._binaries[" + binaries.size() + "]";
+		}
+		throw luan.exception( "invalid type: " + obj.getClass() );
+	}
+
+	private String pickle(Object obj,Set<LuanTable> set) throws LuanException {
+		return obj instanceof LuanTable ? pickle((LuanTable)obj,set) : pickle(obj);
+	}
+
+	private String pickle(LuanTable tbl,Set<LuanTable> set) throws LuanException {
+		if( !set.add(tbl) ) {
+			throw luan.exception( "circular reference in table" );
+		}
+		StringBuilder sb = new StringBuilder();
+		sb.append( "{" );
+		for( Map.Entry<Object,Object> entry : tbl ) {
+			sb.append( "[" );
+			sb.append( pickle(entry.getKey(),set) );
+			sb.append( "]=" );
+			sb.append( pickle(entry.getValue(),set) );
+			sb.append( ", " );
+		}
+		sb.append( "}" );
+		return sb.toString();
+	}
+
+	public void write(Object... args) throws LuanException, IOException {
+		StringBuilder sb = new StringBuilder();
+		if( !binaries.isEmpty() ) {
+			sb.append( "Io._binaries = {}\n" );
+			for( byte[] a : binaries ) {
+				sb.append( "Io._binaries[#Io._binaries+1] = Io._read_binary(" + a.length + ")\n" );
+			}
+		}
+		for( Object obj : args ) {
+			sb.append( luan.toString(obj) );
+		}
+		out.writeUTF( sb.toString() );
+		for( byte[] a : binaries ) {
+			out.write(a);
+		}
+		out.flush();
+		binaries.clear();
+	}
+
+	public void close() throws IOException {
+		in.close();
+		out.close();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/PickleServer.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,115 @@
+package luan.modules;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.util.List;
+import java.util.ArrayList;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class PickleServer {
+
+	private final PickleCon con;
+	private boolean isRunning;
+
+	PickleServer(LuanState luan,DataInputStream in,DataOutputStream out) {
+		this(new PickleCon(luan,in,out));
+	}
+
+	PickleServer(PickleCon con) {
+		this.con = con;
+	}
+
+	void next() throws IOException {
+		try {
+			List<String> list = new ArrayList<String>();
+			try {
+				Object[] result = Luan.array(con.read());
+				list.add( "return true" );
+				for( Object obj : result ) {
+					list.add( ", " );
+					list.add( con.pickle(obj) );
+				}
+			} catch(LuanException e) {
+//				System.out.println(e);
+//e.printStackTrace();
+				list.add( "return false, " );
+				list.add( con.pickle(e.getMessage()) );
+				list.add( ", " );
+				list.add( con.pickle(con.src) );
+			}
+			list.add( "\n" );
+			con.write( list.toArray() );
+		} catch(LuanException e2) {
+			throw new RuntimeException(e2);
+		}
+	}
+
+	public void run() {
+		LuanTable ioModule = con.ioModule;
+		Object old_reverse_pickle = ioModule.get("reverse_pickle");
+		Object old_unreverse_pickle = ioModule.get("_unreverse_pickle");
+		try {
+			try {
+				ioModule.put("reverse_pickle", new LuanJavaFunction(
+					PickleServer.class.getMethod( "reverse_pickle", LuanFunction.class ), this
+				) );
+				ioModule.put("_unreverse_pickle", new LuanJavaFunction(
+					PickleServer.class.getMethod( "_unreverse_pickle" ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			isRunning = true;
+			try {
+				while( isRunning ) {
+					next();
+				}
+			} catch(EOFException e) {
+				// done
+			} catch(IOException e) {
+				e.printStackTrace();
+			}
+			if( isRunning ) {
+				try {
+					con.close();
+				} catch(IOException e) {
+					throw new RuntimeException(e);
+				}
+			}
+		} finally {
+			ioModule.put("reverse_pickle",old_reverse_pickle);
+			ioModule.put("_unreverse_pickle",old_unreverse_pickle);
+		}
+	}
+
+	public void reverse_pickle(LuanFunction fn) throws IOException, LuanException {
+		try {
+			con.write( "return Io._reversed_pickle()\n" );
+		} catch(LuanException e) {
+			throw new RuntimeException(e);
+		}
+		PickleClient pc = new PickleClient(con);
+		try {
+			con.luan.call(fn,new Object[]{pc.table()});
+		} finally {
+			try {
+				pc.call( "Io._unreverse_pickle()\n" );
+			} catch(LuanException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
+	public void _unreverse_pickle() {
+		isRunning = false;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/Reactionary.luan	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,22 @@
+-- Reactionary
+
+host = "localhost"
+port = 9101
+
+function put_site(domain,password,dir)
+	local pc = Io.Socket(host,port).Pickle_client()
+	local pickle = pc.pickle
+	pc.call %>
+		Reactionary.do_put_site(<%=pickle(domain)%>,<%=pickle(password)%>,<%=pickle(dir)%>)
+	<%
+	pc.close()
+end
+
+function delete_site(domain,password)
+	local pc = Io.Socket(host,port).Pickle_client()
+	local pickle = pc.pickle
+	pc.call %>
+		Reactionary.do_delete_site(<%=pickle(domain)%>,<%=pickle(password)%>)
+	<%
+	pc.close()
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/StringLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,264 @@
+package luan.modules;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanElement;
+import luan.LuanException;
+import luan.MetatableGetter;
+
+
+public final class StringLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			luan.addMetatableGetter(mg);
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "to_binary", String.class );
+				add( module, "to_integers", String.class );
+				add( module, "from_integers", new int[0].getClass() );
+				add( module, "find", String.class, String.class, Integer.class, Boolean.class );
+				add( module, "format", String.class, new Object[0].getClass() );
+				add( module, "gmatch", String.class, String.class );
+				add( module, "gsub", LuanState.class, String.class, String.class, Object.class, Integer.class );
+				add( module, "len", String.class );
+				add( module, "lower", String.class );
+				add( module, "match", String.class, String.class, Integer.class );
+				add( module, "rep", String.class, Integer.TYPE, String.class );
+				add( module, "reverse", String.class );
+				add( module, "sub", String.class, Integer.TYPE, Integer.class );
+				add( module, "upper", String.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(StringLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+	private static final LuanTable mt = new LuanTable();
+	static {
+		try {
+			add( mt, "__index", LuanState.class, String.class, Object.class );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private static final MetatableGetter mg = new MetatableGetter() {
+		public LuanTable getMetatable(Object obj) {
+			return obj instanceof String ? mt : null;
+		}
+	};
+
+	public static Object __index(LuanState luan,final String s,Object key) throws LuanException {
+		LuanTable mod = (LuanTable)luan.loaded().get("String");
+		if( mod!=null ) {
+			Object obj = mod.get(key);
+			if( obj instanceof LuanFunction ) {
+				final LuanFunction fn = (LuanFunction)obj;
+				return new LuanFunction() {
+					@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+						Object[] a = new Object[args.length+1];
+						a[0] = s;
+						System.arraycopy(args,0,a,1,args.length);
+						return fn.call(luan,a);
+					}
+				};
+			}
+		}
+		if( luan.loaded().get("Java") != null )
+			return JavaLuan.__index(luan,s,key);
+		return null;
+	}
+
+	static int start(String s,int i) {
+		return i==0 ? 0 : i > 0 ? i - 1 : s.length() + i;
+	}
+
+	static int start(String s,Integer i,int dflt) {
+		return i==null ? dflt : start(s,i);
+	}
+
+	static int end(String s,int i) {
+		return i==0 ? 0 : i > 0 ? i : s.length() + i + 1;
+	}
+
+	static int end(String s,Integer i,int dflt) {
+		return i==null ? dflt : end(s,i);
+	}
+
+	public static byte[] to_binary(String s) {
+		return s.getBytes();
+	}
+
+	public static int[] to_integers(String s) {
+		char[] a = s.toCharArray();
+		int[] chars = new int[a.length];
+		for( int i=0; i<a.length; i++ ) {
+			chars[i] = a[i];
+		}
+		return chars;
+	}
+
+	public static String from_integers(int... chars) {
+		char[] a = new char[chars.length];
+		for( int i=0; i<chars.length; i++ ) {
+			a[i] = (char)chars[i];
+		}
+		return new String(a);
+	}
+
+	public static int len(String s) {
+		return s.length();
+	}
+
+	public static String lower(String s) {
+		return s.toLowerCase();
+	}
+
+	public static String upper(String s) {
+		return s.toUpperCase();
+	}
+
+	public static String reverse(String s) {
+		return new StringBuilder(s).reverse().toString();
+	}
+
+	public static String rep(String s,int n,String sep) {
+		if( n < 1 )
+			return "";
+		StringBuilder buf = new StringBuilder(s);
+		while( --n > 0 ) {
+			if( sep != null )
+				buf.append(sep);
+			buf.append(s);
+		}
+		return buf.toString();
+	}
+
+	public static String sub(String s,int i,Integer j) {
+		int start = start(s,i);
+		int end = end(s,j,s.length());
+		return s.substring(start,end);
+	}
+
+	public static int[] find(String s,String pattern,Integer init,Boolean plain) {
+		int start = start(s,init,0);
+		if( Boolean.TRUE.equals(plain) ) {
+			int i = s.indexOf(pattern,start);
+			return i == -1 ? null : new int[]{i+1,i+pattern.length()};
+		}
+		Matcher m = Pattern.compile(pattern).matcher(s);
+		return m.find(start) ? new int[]{m.start()+1,m.end()} : null;
+	}
+
+	public static String[] match(String s,String pattern,Integer init) {
+		int start = start(s,init,0);
+		Matcher m = Pattern.compile(pattern).matcher(s);
+		if( !m.find(start) )
+			return null;
+		final int n = m.groupCount();
+		if( n == 0 )
+			return new String[]{m.group()};
+		String[] rtn = new String[n];
+		for( int i=0; i<n; i++ ) {
+			rtn[i] = m.group(i+1);
+		}
+		return rtn;
+	}
+
+	public static LuanFunction gmatch(String s,String pattern) {
+		final Matcher m = Pattern.compile(pattern).matcher(s);
+		return new LuanFunction() {
+			@Override public Object call(LuanState luan,Object[] args) {
+				if( !m.find() )
+					return null;
+				final int n = m.groupCount();
+				if( n == 0 )
+					return m.group();
+				String[] rtn = new String[n];
+				for( int i=0; i<n; i++ ) {
+					rtn[i] = m.group(i+1);
+				}
+				return rtn;
+			}
+		};
+	}
+
+	public static Object[] gsub(LuanState luan,String s,String pattern,Object repl,Integer n) throws LuanException {
+		int max = n==null ? Integer.MAX_VALUE : n;
+		final Matcher m = Pattern.compile(pattern).matcher(s);
+		if( repl instanceof String ) {
+			String replacement = (String)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				m.appendReplacement(sb,replacement);
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), i };
+		}
+		if( repl instanceof LuanTable ) {
+			LuanTable t = (LuanTable)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				String match = m.groupCount()==0 ? m.group() : m.group(1);
+				Object val = t.get(match);
+				if( Luan.toBoolean(val) ) {
+					String replacement = Luan.asString(val);
+					if( replacement==null )
+						throw luan.exception( "invalid replacement value (a "+Luan.type(val)+")" );
+					m.appendReplacement(sb,replacement);
+				}
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), i };
+		}
+		if( repl instanceof LuanFunction ) {
+			LuanFunction fn = (LuanFunction)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				Object[] args;
+				final int count = m.groupCount();
+				if( count == 0 ) {
+					args = new Object[]{m.group()};
+				} else {
+					args = new Object[count];
+					for( int j=0; j<count; j++ ) {
+						args[j] = m.group(j);
+					}
+				}
+				Object val = Luan.first( luan.call(fn,"repl-arg",args) );
+				if( Luan.toBoolean(val) ) {
+					String replacement = Luan.asString(val);
+					if( replacement==null )
+						throw luan.exception( "invalid replacement value (a "+Luan.type(val)+")" );
+					m.appendReplacement(sb,replacement);
+				}
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), i };
+		}
+		throw luan.exception( "bad argument #3 to 'gsub' (string/function/table expected)" );
+	}
+
+	// note - String.format() is too stupid to convert between ints and floats.
+	public static String format(String format,Object... args) {
+		return String.format(format,args);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/TableLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,131 @@
+package luan.modules;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanElement;
+import luan.LuanException;
+import luan.LuanRuntimeException;
+
+
+public final class TableLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "concat", LuanState.class, LuanTable.class, String.class, Integer.class, Integer.class );
+				add( module, "insert", LuanState.class, LuanTable.class, Integer.TYPE, Object.class );
+				add( module, "pack", new Object[0].getClass() );
+				add( module, "remove", LuanState.class, LuanTable.class, Integer.TYPE );
+				add( module, "sort", LuanState.class, LuanTable.class, LuanFunction.class );
+				add( module, "sub_list", LuanTable.class, Integer.TYPE, Integer.TYPE );
+				add( module, "unpack", LuanTable.class, Integer.class, Integer.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(TableLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static String concat(LuanState luan,LuanTable list,String sep,Integer i,Integer j) throws LuanException {
+		int first = i==null ? 1 : i;
+		int last = j==null ? list.length() : j;
+		StringBuilder buf = new StringBuilder();
+		for( int k=first; k<=last; k++ ) {
+			Object val = list.get(k);
+			if( val==null )
+				break;
+			if( sep!=null && k > first )
+				buf.append(sep);
+			String s = Luan.asString(val);
+			if( s==null )
+				throw luan.exception( "invalid value ("+Luan.type(val)+") at index "+k+" in table for 'concat'" );
+			buf.append(val);
+		}
+		return buf.toString();
+	}
+
+	public static void insert(LuanState luan,LuanTable list,int pos,Object value) throws LuanException {
+		try {
+			list.insert(pos,value);
+		} catch(IndexOutOfBoundsException e) {
+			throw luan.exception(e);
+		}
+	}
+
+	public static Object remove(LuanState luan,LuanTable list,int pos) throws LuanException {
+		try {
+			return list.remove(pos);
+		} catch(IndexOutOfBoundsException e) {
+			throw luan.exception(e);
+		}
+	}
+
+	private static interface LessThan {
+		public boolean isLessThan(Object o1,Object o2);
+	}
+
+	public static void sort(final LuanState luan,LuanTable list,final LuanFunction comp) throws LuanException {
+		final LessThan lt;
+		if( comp==null ) {
+			lt = new LessThan() {
+				public boolean isLessThan(Object o1,Object o2) {
+					try {
+						return luan.isLessThan(o1,o2);
+					} catch(LuanException e) {
+						throw new LuanRuntimeException(e);
+					}
+				}
+			};
+		} else {
+			lt = new LessThan() {
+				public boolean isLessThan(Object o1,Object o2) {
+					try {
+						return Luan.toBoolean(Luan.first(luan.call(comp,"comp-arg",new Object[]{o1,o2})));
+					} catch(LuanException e) {
+						throw new LuanRuntimeException(e);
+					}
+				}
+			};
+		}
+		try {
+			list.sort( new Comparator<Object>() {
+				public int compare(Object o1,Object o2) {
+					return lt.isLessThan(o1,o2) ? -1 : lt.isLessThan(o2,o1) ? 1 : 0;
+				}
+			} );
+		} catch(LuanRuntimeException e) {
+			throw (LuanException)e.getCause();
+		}
+	}
+
+	public static LuanTable pack(Object... args) {
+		return new LuanTable(new ArrayList<Object>(Arrays.asList(args)));
+	}
+
+	public static Object[] unpack(LuanTable tbl,Integer iFrom,Integer iTo) {
+		int from = iFrom!=null ? iFrom : 1;
+		int to = iTo!=null ? iTo : tbl.length();
+		List<Object> list = new ArrayList<Object>();
+		for( int i=from; i<=to; i++ ) {
+			list.add( tbl.get(i) );
+		}
+		return list.toArray();
+	}
+
+	public static LuanTable sub_list(LuanTable list,int from,int to) {
+		return list.subList(from,to);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/ThreadLuan.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,47 @@
+package luan.modules;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import luan.LuanState;
+import luan.LuanFunction;
+import luan.LuanTable;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+import luan.DeepCloner;
+
+
+public final class ThreadLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "fork", LuanState.class, LuanFunction.class, new Object[0].getClass() );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(ThreadLuan.class.getMethod(method,parameterTypes),null) );
+	}
+
+	private static final Executor exec = Executors.newCachedThreadPool();
+
+	public static void fork(LuanState luan,LuanFunction fn,Object... args) {
+		DeepCloner cloner = new DeepCloner();
+		final LuanState newLuan = cloner.deepClone(luan);
+		final LuanFunction newFn = cloner.get(fn);
+		final Object[] newArgs = cloner.deepClone(args);
+		exec.execute(new Runnable(){public void run() {
+			try {
+				newLuan.call(newFn,"<forked>",newArgs);
+			} catch(LuanException e) {
+				e.printStackTrace();
+			}
+		}});
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/Utils.java	Sun Jun 22 05:41:22 2014 +0000
@@ -0,0 +1,85 @@
+package luan.modules;
+
+import java.io.Reader;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.File;
+import java.net.URL;
+import java.net.MalformedURLException;
+import luan.LuanState;
+import luan.LuanException;
+
+
+public final class Utils {
+	private Utils() {}  // never
+
+	static final int bufSize = 8192;
+
+	public static void checkNotNull(LuanState luan,Object v,String expected) throws LuanException {
+		if( v == null )
+			throw luan.exception("bad argument #1 ("+expected+" expected, got nil)");
+	}
+
+	public static String readAll(Reader in)
+		throws IOException
+	{
+		char[] a = new char[bufSize];
+		StringBuilder buf = new StringBuilder();
+		int n;
+		while( (n=in.read(a)) != -1 ) {
+			buf.append(a,0,n);
+		}
+		return buf.toString();
+	}
+
+	public static void copyAll(InputStream in,OutputStream out)
+		throws IOException
+	{
+		byte[] a = new byte[bufSize];
+		int n;
+		while( (n=in.read(a)) != -1 ) {
+			out.write(a,0,n);
+		}
+	}
+
+	public static byte[] readAll(InputStream in)
+		throws IOException
+	{
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		copyAll(in,out);
+		return out.toByteArray();
+	}
+
+	public static boolean exists(File file) {
+		try {
+			return file.exists() && file.getName().equals(file.getCanonicalFile().getName());
+		} catch(IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static boolean isFile(String path) {
+		return exists(new File(path));
+	}
+
+	public static String toUrl(String path) {
+		if( path.indexOf(':') == -1 )
+			return null;
+		if( path.startsWith("java:") ) {
+			path = path.substring(5);
+			URL url = ClassLoader.getSystemResource(path);
+			return url==null ? null : url.toString();
+		}
+		try {
+			new URL(path);
+			return path;
+		} catch(MalformedURLException e) {}
+		return null;
+	}
+
+	public static boolean exists(String path) {
+		return isFile(path) || toUrl(path)!=null;
+	}
+}
--- a/src/luan/DeepCloneable.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-package luan;
-
-
-public interface DeepCloneable<T extends DeepCloneable> {
-	public T shallowClone();
-	public void deepenClone(T clone,DeepCloner cloner);
-}
--- a/src/luan/DeepCloner.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-package luan;
-
-import java.util.Map;
-import java.util.IdentityHashMap;
-
-
-public final class DeepCloner {
-	private final Map<Object,Object> cloned = new IdentityHashMap<Object,Object>();
-
-	public <T extends DeepCloneable<T>> T deepClone(T obj) {
-		@SuppressWarnings("unchecked")
-		T rtn = (T)cloned.get(obj);
-		if( rtn == null ) {
-			rtn = obj.shallowClone();
-			cloned.put(obj,rtn);
-			obj.deepenClone(rtn,this);
-		}
-		return rtn;
-	}
-
-	public <T> T[] deepClone(T[] obj) {
-		if( obj.length == 0 )
-			return obj;
-		@SuppressWarnings("unchecked")
-		T[] rtn = (T[])cloned.get(obj);
-		if( rtn == null ) {
-			rtn = obj.clone();
-			cloned.put(obj,rtn);
-			for( int i=0; i<rtn.length; i++ ) {
-				@SuppressWarnings("unchecked")
-				T t = get(rtn[i]);
-				rtn[i] = t;
-			}
-		}
-		return rtn;
-	}
-
-	public <T> T get(T obj) {
-		if( !(obj instanceof DeepCloneable) )
-			return obj;
-		@SuppressWarnings("unchecked")
-		T dc = (T)deepClone((DeepCloneable)obj);
-		return dc;
-	}
-}
--- a/src/luan/Luan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-package luan;
-
-import luan.modules.BasicLuan;
-
-
-public final class Luan {
-	public static final String version = "Luan 0.1";
-
-	public static void main(String[] args) {
-		LuanState luan = LuanState.newStandard();
-		try {
-			LuanFunction standalone = (LuanFunction)BasicLuan.load_file(luan,"java:luan/cmd_line.luan");
-			luan.call(standalone,args);
-		} catch(LuanException e) {
-			System.err.println(e.getMessage());
-//			e.printStackTrace();
-			System.exit(-1);
-		}
-	}
-
-	public static Object first(Object obj) {
-		if( !(obj instanceof Object[]) )
-			return obj;
-		Object[] a = (Object[])obj;
-		return a.length==0 ? null : a[0];
-	}
-
-	public static Object[] array(Object obj) {
-		return obj instanceof Object[] ? (Object[])obj : new Object[]{obj};
-	}
-
-	public static String type(Object obj) {
-		if( obj == null )
-			return "nil";
-		if( obj instanceof String )
-			return "string";
-		if( obj instanceof Boolean )
-			return "boolean";
-		if( obj instanceof Number )
-			return "number";
-		if( obj instanceof LuanTable )
-			return "table";
-		if( obj instanceof LuanFunction )
-			return "function";
-		if( obj instanceof byte[] )
-			return "binary";
-		return "userdata";
-	}
-
-	public static boolean toBoolean(Object obj) {
-		return obj != null && !Boolean.FALSE.equals(obj);
-	}
-
-	public static String asString(Object obj) {
-		if( obj instanceof String )
-			return (String)obj;
-		if( obj instanceof Number )
-			return toString((Number)obj);
-		return null;
-	}
-
-	public static Number toNumber(Object obj) {
-		return toNumber(obj,null);
-	}
-
-	public static Number toNumber(Object obj,Integer base) {
-		if( obj instanceof Number )
-			return (Number)obj;
-		if( obj instanceof String ) {
-			String s = (String)obj;
-			try {
-				if( base==null )
-					return Double.valueOf(s);
-				else
-					return Long.valueOf(s,base);
-			} catch(NumberFormatException e) {}
-		}
-		return null;
-	}
-
-	public static String toString(Number n) {
-		if( n instanceof Integer )
-			return n.toString();
-		String s = n.toString();
-		int iE = s.indexOf('E');
-		String ending  = null;
-		if( iE != -1 ) {
-			ending = s.substring(iE);
-			s = s.substring(0,iE);
-		}
-		if( s.endsWith(".0") )
-			s = s.substring(0,s.length()-2);
-		if( ending != null )
-			s += ending;
-		return s;
-	}
-
-	public static Integer asInteger(Object obj) {
-		if( obj instanceof Integer )
-			return (Integer)obj;
-		if( !(obj instanceof Number) )
-			return null;
-		Number n = (Number)obj;
-		int i = n.intValue();
-		return i==n.doubleValue() ? Integer.valueOf(i) : null;
-	}
-
-	public static String toString(Object obj) {
-		if( obj == null )
-			return "nil";
-		if( obj instanceof Number )
-			return Luan.toString((Number)obj);
-		if( obj instanceof LuanException ) {
-			LuanException le = (LuanException)obj;
-			return le.getMessage();
-		}
-		if( obj instanceof byte[] )
-			return "binary: " + Integer.toHexString(obj.hashCode());
-		return obj.toString();
-	}
-
-	public static String stringEncode(String s) {
-		s = s.replace("\\","\\\\");
-		s = s.replace("\u0007","\\a");
-		s = s.replace("\b","\\b");
-		s = s.replace("\f","\\f");
-		s = s.replace("\n","\\n");
-		s = s.replace("\r","\\r");
-		s = s.replace("\t","\\t");
-		s = s.replace("\u000b","\\v");
-		s = s.replace("\"","\\\"");
-		return s;
-	}
-
-	public static String repr(Object obj) {
-		if( obj == null )
-			return "nil";
-		if( obj instanceof Boolean )
-			return Luan.toString((Boolean)obj);
-		if( obj instanceof Number )
-			return Luan.toString((Number)obj);
-		if( obj instanceof String )
-			return "\"" + stringEncode((String)obj) + "\"";
-		if( obj instanceof LuanRepr )
-			return ((LuanRepr)obj).repr();
-		return null;
-	}
-
-	private Luan() {}  // never
-}
--- a/src/luan/LuanBit.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-package luan;
-
-import java.util.List;
-
-
-public final class LuanBit {
-	public final LuanState luan;
-	public final LuanElement el;
-
-	LuanBit(LuanState luan,LuanElement el) {
-		this.luan = luan;
-		this.el = el;
-	}
-
-	public LuanException exception(Object msg) {
-		return new LuanException(this,msg);
-	}
-
-	public String stackTrace() {
-		StringBuilder buf = new StringBuilder();
-		LuanElement el = this.el;
-		for( int i  = luan.stackTrace.size() - 1; i>=0; i-- ) {
-			StackTraceElement stackTraceElement = luan.stackTrace.get(i);
-			buf.append( "\n\t" ).append( el.toString(stackTraceElement.fnName) );
-			el = stackTraceElement.call;
-		}
-		return buf.toString();
-	}
-
-	public void dumpStack() {
-		System.err.println( stackTrace() );
-	}
-
-	public Object call(LuanFunction fn,String fnName,Object[] args) throws LuanException {
-		List<StackTraceElement> stackTrace = luan.stackTrace;
-		stackTrace.add( new StackTraceElement(el,fnName) );
-		try {
-			return fn.call(luan,args);
-		} finally {
-			stackTrace.remove(stackTrace.size()-1);
-		}
-	}
-
-	public String checkString(Object obj) throws LuanException {
-		String s = Luan.asString(obj);
-		if( s == null )
-			throw exception( "attempt to use a " + Luan.type(obj) + " as a string" );
-		return s;
-	}
-
-	public Number checkNumber(Object obj) throws LuanException {
-		Number n = Luan.toNumber(obj);
-		if( n == null )
-			throw exception( "attempt to perform arithmetic on a " + Luan.type(obj) + " value" );
-		return n;
-	}
-
-	public LuanFunction checkFunction(Object obj) throws LuanException {
-		if( obj instanceof LuanFunction )
-			return (LuanFunction)obj;
-		throw exception( "attempt to call a " + Luan.type(obj) + " value" );
-	}
-
-	public Boolean checkBoolean(Object obj) throws LuanException {
-		if( obj instanceof Boolean )
-			return (Boolean)obj;
-		throw exception( "attempt to use a " + Luan.type(obj) + " as a boolean" );
-	}
-
-	public String toString(Object obj) throws LuanException {
-		LuanFunction fn = getHandlerFunction("__tostring",obj);
-		if( fn != null )
-			return checkString( Luan.first( call(fn,"__tostring",new Object[]{obj}) ) );
-		return Luan.toString(obj);
-	}
-
-	public String repr(Object obj) throws LuanException {
-		LuanFunction fn = getHandlerFunction("__repr",obj);
-		if( fn != null )
-			return checkString( Luan.first( call(fn,"__repr",new Object[]{obj}) ) );
-		String repr = Luan.repr(obj);
-		if( repr==null )
-			throw exception( "value '" + obj + "' doesn't support repr()" );
-		return repr;
-	}
-
-	public LuanFunction getHandlerFunction(String op,Object obj) throws LuanException {
-		Object f = luan.getHandler(op,obj);
-		if( f == null )
-			return null;
-		return checkFunction(f);
-	}
-
-	public LuanFunction getBinHandler(String op,Object o1,Object o2) throws LuanException {
-		LuanFunction f1 = getHandlerFunction(op,o1);
-		if( f1 != null )
-			return f1;
-		return getHandlerFunction(op,o2);
-	}
-
-	public boolean isLessThan(Object o1,Object o2) throws LuanException {
-		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;
-		}
-		LuanFunction fn = getBinHandler("__lt",o1,o2);
-		if( fn != null )
-			return Luan.toBoolean( Luan.first(call(fn,"__lt",new Object[]{o1,o2})) );
-		throw exception( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) );
-	}
-
-	public Object arithmetic(String op,Object o1,Object o2) throws LuanException {
-		LuanFunction fn = getBinHandler(op,o1,o2);
-		if( fn != null )
-			return Luan.first(call(fn,op,new Object[]{o1,o2}));
-		String type = Luan.toNumber(o1)==null ? Luan.type(o1) : Luan.type(o2);
-		throw exception("attempt to perform arithmetic on a "+type+" value");
-	}
-
-}
--- a/src/luan/LuanElement.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-package luan;
-
-
-public abstract class LuanElement {
-
-	final String toString(String fnName) {
-		String s = location();
-		if( fnName != null )
-			s += ": in function '" + fnName + "'";
-		return s;
-	}
-
-	abstract String location();
-
-	public static final LuanElement JAVA = new LuanElement(){
-		@Override String location() {
-			return "Java";
-		}
-	};
-}
--- a/src/luan/LuanException.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-package luan;
-
-
-public class LuanException extends Exception {
-	private final String stackTrace;
-
-	LuanException(LuanBit bit,Object msg) {
-		super(message(msg),cause(msg));
-		stackTrace = stackTrace(bit,msg);
-	}
-
-	@Override public String getMessage() {
-		return super.getMessage() + stackTrace;
-	}
-
-	private String message() {
-		return super.getMessage();
-	}
-
-	private static Throwable cause(Object msg) {
-		return msg instanceof Throwable ? (Throwable)msg : null;
-	}
-
-	private static String message(Object msg) {
-		if( msg instanceof LuanException ) {
-			LuanException le = (LuanException)msg;
-			return le.message();
-/*
-		} else if( msg instanceof Throwable ) {
-			Throwable t = (Throwable)msg;
-			return t.getMessage();
-*/
-		} else {
-			return msg.toString();
-		}
-	}
-
-	private static String stackTrace(LuanBit bit,Object msg) {
-		StringBuilder buf = new StringBuilder();
-		buf.append( bit.stackTrace() );
-		if( msg instanceof LuanException ) {
-			LuanException le = (LuanException)msg;
-			buf.append( "\ncaused by:" ).append( le.stackTrace );
-		}
-		return buf.toString();
-	}
-}
--- a/src/luan/LuanExitException.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-package luan;
-
-
-public final class LuanExitException extends RuntimeException {}
--- a/src/luan/LuanFunction.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package luan;
-
-
-public abstract class LuanFunction implements LuanRepr {
-
-	public abstract Object call(LuanState luan,Object[] args) throws LuanException;
-
-	public static final Object[] NOTHING = new Object[0];
-
-	@Override public String toString() {
-		return "function: " + Integer.toHexString(hashCode());
-	}
-
-	@Override public String repr() {
-		return "<function>";
-	}
-
-}
--- a/src/luan/LuanJavaFunction.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,516 +0,0 @@
-package luan;
-
-import java.lang.reflect.Array;
-import java.lang.reflect.Method;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Set;
-import java.util.Arrays;
-
-
-public final class LuanJavaFunction extends LuanFunction {
-	private final JavaMethod method;
-	private final Object obj;
-	private final RtnConverter rtnConverter;
-	private final boolean takesLuaState;
-	private final ArgConverter[] argConverters;
-	private final Class<?> varArgCls;
-
-	public LuanJavaFunction(Method method,Object obj) {
-		this( JavaMethod.of(method), obj );
-	}
-
-	public LuanJavaFunction(Constructor constr,Object obj) {
-		this( JavaMethod.of(constr), obj );
-	}
-
-	LuanJavaFunction(JavaMethod method,Object obj) {
-		this.method = method;
-		this.obj = obj;
-		this.rtnConverter = getRtnConverter(method);
-		this.takesLuaState = takesLuaState(method);
-		this.argConverters = getArgConverters(takesLuaState,method);
-		if( method.isVarArgs() ) {
-			Class<?>[] paramTypes = method.getParameterTypes();
-			this.varArgCls = paramTypes[paramTypes.length-1].getComponentType();
-		} else {
-			this.varArgCls = null;
-		}
-	}
-
-	@Override public String toString() {
-		return "java-function: " + method;
-	}
-
-	public Class<?>[] getParameterTypes() {
-		return method.getParameterTypes();
-	}
-
-	@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-		args = fixArgs(luan,args);
-		try {
-			return doCall(luan,args);
-		} catch(IllegalArgumentException e) {
-			checkArgs(luan,args);
-			throw e;
-		}
-	}
-
-	public Object rawCall(LuanState luan,Object[] args) throws LuanException {
-		args = fixArgs(luan,args);
-		return doCall(luan,args);
-	}
-
-	private Object doCall(LuanState luan,Object[] args) throws LuanException {
-		Object rtn;
-		try {
-			rtn = method.invoke(obj,args);
-		} catch(IllegalAccessException e) {
-			throw new RuntimeException("method = "+method,e);
-		} catch(InvocationTargetException e) {
-			Throwable cause = e.getCause();
-			if( cause instanceof Error )
-				throw (Error)cause;
-			if( cause instanceof LuanException )
-				throw (LuanException)cause;
-			throw luan.exception(cause);
-		} catch(InstantiationException e) {
-			throw new RuntimeException(e);
-		}
-		return rtnConverter.convert(rtn);
-	}
-
-	private static final Map<Class,Class> primitiveMap = new HashMap<Class,Class>();
-	static {
-		primitiveMap.put(Boolean.TYPE,Boolean.class);
-		primitiveMap.put(Character.TYPE,Character.class);
-		primitiveMap.put(Byte.TYPE,Byte.class);
-		primitiveMap.put(Short.TYPE,Short.class);
-		primitiveMap.put(Integer.TYPE,Integer.class);
-		primitiveMap.put(Long.TYPE,Long.class);
-		primitiveMap.put(Float.TYPE,Float.class);
-		primitiveMap.put(Double.TYPE,Double.class);
-		primitiveMap.put(Void.TYPE,Void.class);
-	}
-
-	private void checkArgs(LuanState luan,Object[] args) throws LuanException {
-		Class<?>[] a = getParameterTypes();
-		int start = takesLuaState ? 1 : 0;
-		for( int i=start; i<a.length; i++ ) {
-			Class<?> paramType = a[i];
-			Class<?> type = paramType;
-			if( type.isPrimitive() )
-				type = primitiveMap.get(type);
-			Object arg = args[i];
-			if( !type.isInstance(arg) ) {
-				String expected = paramType.getSimpleName();
-				if( arg==null ) {
-					if( paramType.isPrimitive() )
-						throw luan.exception("bad argument #"+(i+1-start)+" ("+expected+" expected, got nil)");
-				} else {
-					String got = arg.getClass().getSimpleName();
-					throw luan.exception("bad argument #"+(i+1-start)+" ("+expected+" expected, got "+got+")");
-				}
-			}
-		}
-	}
-
-	private Object[] fixArgs(LuanState luan,Object[] args) {
-		int n = argConverters.length;
-		Object[] rtn;
-		int start = 0;
-		if( !takesLuaState && varArgCls==null && args.length == n ) {
-			rtn = args;
-		} else {
-			if( takesLuaState )
-				n++;
-			rtn = new Object[n];
-			if( takesLuaState ) {
-				rtn[start++] = luan;
-			}
-			n = argConverters.length;
-			if( varArgCls != null ) {
-				n--;
-				if( args.length < argConverters.length ) {
-					rtn[rtn.length-1] = Array.newInstance(varArgCls,0);
-				} else {
-					int len = args.length - n;
-					Object varArgs = Array.newInstance(varArgCls,len);
-					ArgConverter ac = argConverters[n];
-					for( int i=0; i<len; i++ ) {
-						Array.set( varArgs, i, ac.convert(args[n+i]) );
-					}
-					rtn[rtn.length-1] = varArgs;
-				}
-			}
-			System.arraycopy(args,0,rtn,start,Math.min(args.length,n));
-		}
-		for( int i=0; i<n; i++ ) {
-			rtn[start+i] = argConverters[i].convert(rtn[start+i]);
-		}
-		return rtn;
-	}
-
-
-	private interface RtnConverter {
-		public Object convert(Object obj);
-	}
-
-	private static final RtnConverter RTN_NOTHING = new RtnConverter() {
-		@Override public Object[] convert(Object obj) {
-			return NOTHING;
-		}
-	};
-
-	private static final RtnConverter RTN_SAME = new RtnConverter() {
-		@Override public Object convert(Object obj) {
-			return obj;
-		}
-	};
-
-	private static RtnConverter getRtnConverter(JavaMethod m) {
-		Class<?> rtnType = m.getReturnType();
-		if( rtnType == Void.TYPE )
-			return RTN_NOTHING;
-		return RTN_SAME;
-	}
-
-	private static boolean isNumber(Class<?> rtnType) {
-		return rtnType == Short.TYPE
-			|| rtnType == Integer.TYPE
-			|| rtnType == Long.TYPE
-			|| rtnType == Float.TYPE
-			|| rtnType == Double.TYPE
-		;
-	}
-
-	private interface ArgConverter {
-		public Object convert(Object obj);
-	}
-
-	private static final ArgConverter ARG_SAME = new ArgConverter() {
-		public Object convert(Object obj) {
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_BOOLEAN = new ArgConverter() {
-		public Object convert(Object obj) {
-			return Luan.toBoolean(obj);
-		}
-	};
-
-	private static final ArgConverter ARG_BOOLEAN_OBJ = new ArgConverter() {
-		public Object convert(Object obj) {
-			return obj==null ? null : Luan.toBoolean(obj);
-		}
-	};
-
-	private static final ArgConverter ARG_DOUBLE = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof Double )
-				return obj;
-			if( obj instanceof Number ) {
-				Number n = (Number)obj;
-				return n.doubleValue();
-			}
-			if( obj instanceof String ) {
-				String s = (String)obj;
-				try {
-					return Double.valueOf(s);
-				} catch(NumberFormatException e) {}
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_FLOAT = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof Float )
-				return obj;
-			if( obj instanceof Number ) {
-				Number n = (Number)obj;
-				return n.floatValue();
-			}
-			if( obj instanceof String ) {
-				String s = (String)obj;
-				try {
-					return Float.valueOf(s);
-				} catch(NumberFormatException e) {}
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_LONG = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof Long )
-				return obj;
-			if( obj instanceof Number ) {
-				Number n = (Number)obj;
-				long r = n.longValue();
-				if( r==n.doubleValue() )
-					return r;
-			}
-			else if( obj instanceof String ) {
-				String s = (String)obj;
-				try {
-					return Long.valueOf(s);
-				} catch(NumberFormatException e) {}
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_INTEGER = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof Integer )
-				return obj;
-			if( obj instanceof Number ) {
-				Number n = (Number)obj;
-				int r = n.intValue();
-				if( r==n.doubleValue() )
-					return r;
-			}
-			else if( obj instanceof String ) {
-				String s = (String)obj;
-				try {
-					return Integer.valueOf(s);
-				} catch(NumberFormatException e) {}
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_SHORT = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof Short )
-				return obj;
-			if( obj instanceof Number ) {
-				Number n = (Number)obj;
-				short r = n.shortValue();
-				if( r==n.doubleValue() )
-					return r;
-			}
-			else if( obj instanceof String ) {
-				String s = (String)obj;
-				try {
-					return Short.valueOf(s);
-				} catch(NumberFormatException e) {}
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_BYTE = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof Byte )
-				return obj;
-			if( obj instanceof Number ) {
-				Number n = (Number)obj;
-				byte r = n.byteValue();
-				if( r==n.doubleValue() )
-					return r;
-			}
-			else if( obj instanceof String ) {
-				String s = (String)obj;
-				try {
-					return Byte.valueOf(s);
-				} catch(NumberFormatException e) {}
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_TABLE = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj == null )
-				return null;
-			if( obj instanceof List ) {
-				@SuppressWarnings("unchecked")
-				List<Object> list = (List<Object>)obj;
-				return new LuanTable(list);
-			}
-			if( obj instanceof Map ) {
-				@SuppressWarnings("unchecked")
-				Map<Object,Object> map = (Map<Object,Object>)obj;
-				return new LuanTable(map);
-			}
-			if( obj instanceof Set ) {
-				@SuppressWarnings("unchecked")
-				Set<Object> set = (Set<Object>)obj;
-				return new LuanTable(set);
-			}
-			Class cls = obj.getClass();
-			if( cls.isArray() && !cls.getComponentType().isPrimitive() ) {
-				Object[] a = (Object[])obj;
-				return new LuanTable(Arrays.asList(a));
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_MAP = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof LuanTable ) {
-				LuanTable t = (LuanTable)obj;
-				return t.asMap();
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_LIST = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof LuanTable ) {
-				LuanTable t = (LuanTable)obj;
-				if( t.isList() )
-					return t.asList();
-			}
-			return obj;
-		}
-	};
-
-	private static final ArgConverter ARG_SET = new ArgConverter() {
-		public Object convert(Object obj) {
-			if( obj instanceof LuanTable ) {
-				LuanTable t = (LuanTable)obj;
-				if( t.isSet() )
-					return t.asSet();
-			}
-			return obj;
-		}
-	};
-
-	private static class ArgArray implements ArgConverter {
-		private final Object[] a;
-
-		ArgArray(Class cls) {
-			a = (Object[])Array.newInstance(cls.getComponentType(),0);
-		}
-
-		public Object convert(Object obj) {
-			if( obj instanceof LuanTable ) {
-				LuanTable t = (LuanTable)obj;
-				if( t.isList() ) {
-					try {
-						return t.asList().toArray(a);
-					} catch(ArrayStoreException e) {}
-				}
-			}
-			return obj;
-		}
-	}
-
-	private static boolean takesLuaState(JavaMethod m) {
-		Class<?>[] paramTypes = m.getParameterTypes();
-		return paramTypes.length > 0 && paramTypes[0].equals(LuanState.class);
-	}
-
-	private static ArgConverter[] getArgConverters(boolean takesLuaState,JavaMethod m) {
-		final boolean isVarArgs = m.isVarArgs();
-		Class<?>[] paramTypes = m.getParameterTypes();
-		if( takesLuaState ) {
-			Class<?>[] t = new Class<?>[paramTypes.length-1];
-			System.arraycopy(paramTypes,1,t,0,t.length);
-			paramTypes = t;
-		}
-		ArgConverter[] a = new ArgConverter[paramTypes.length];
-		for( int i=0; i<a.length; i++ ) {
-			Class<?> paramType = paramTypes[i];
-			if( isVarArgs && i == a.length-1 )
-				paramType = paramType.getComponentType();
-			a[i] = getArgConverter(paramType);
-		}
-		return a;
-	}
-
-	private static ArgConverter getArgConverter(Class<?> cls) {
-		if( cls == Boolean.TYPE )
-			return ARG_BOOLEAN;
-		if( cls.equals(Boolean.class) )
-			return ARG_BOOLEAN_OBJ;
-		if( cls == Double.TYPE || cls.equals(Double.class) )
-			return ARG_DOUBLE;
-		if( cls == Float.TYPE || cls.equals(Float.class) )
-			return ARG_FLOAT;
-		if( cls == Long.TYPE || cls.equals(Long.class) )
-			return ARG_LONG;
-		if( cls == Integer.TYPE || cls.equals(Integer.class) )
-			return ARG_INTEGER;
-		if( cls == Short.TYPE || cls.equals(Short.class) )
-			return ARG_SHORT;
-		if( cls == Byte.TYPE || cls.equals(Byte.class) )
-			return ARG_BYTE;
-		if( cls.equals(LuanTable.class) )
-			return ARG_TABLE;
-		if( cls.equals(Map.class) )
-			return ARG_MAP;
-		if( cls.equals(List.class) )
-			return ARG_LIST;
-		if( cls.equals(Set.class) )
-			return ARG_SET;
-		if( cls.isArray() && !cls.getComponentType().isPrimitive() )
-			return new ArgArray(cls);
-		return ARG_SAME;
-	}
-
-
-
-	private static abstract class JavaMethod {
-		abstract boolean isVarArgs();
-		abstract Class<?>[] getParameterTypes();
-		abstract Object invoke(Object obj,Object... args)
-			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException;
-		abstract Class<?> getReturnType();
-	
-		static JavaMethod of(final Method m) {
-			return new JavaMethod() {
-				@Override boolean isVarArgs() {
-					return m.isVarArgs();
-				}
-				@Override Class<?>[] getParameterTypes() {
-					return m.getParameterTypes();
-				}
-				@Override Object invoke(Object obj,Object... args)
-					throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
-				{
-					return m.invoke(obj,args);
-				}
-				@Override Class<?> getReturnType() {
-					return m.getReturnType();
-				}
-				@Override public String toString() {
-					return m.toString();
-				}
-			};
-		}
-	
-		static JavaMethod of(final Constructor c) {
-			return new JavaMethod() {
-				@Override boolean isVarArgs() {
-					return c.isVarArgs();
-				}
-				@Override Class<?>[] getParameterTypes() {
-					return c.getParameterTypes();
-				}
-				@Override Object invoke(Object obj,Object... args)
-					throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException
-				{
-					return c.newInstance(args);
-				}
-				@Override Class<?> getReturnType() {
-					return c.getDeclaringClass();
-				}
-				@Override public String toString() {
-					return c.toString();
-				}
-			};
-		}
-	
-	}
-
-}
--- a/src/luan/LuanRepr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-package luan;
-
-
-public interface LuanRepr {
-	public String repr();
-}
--- a/src/luan/LuanRuntimeException.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package luan;
-
-
-public final class LuanRuntimeException extends RuntimeException {
-	public LuanRuntimeException(LuanException e) {
-		super(e);
-	}
-}
--- a/src/luan/LuanSource.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-package luan;
-
-
-public final class LuanSource {
-	public final String name;
-	public final String text;
-
-	public LuanSource(String name,String text) {
-		this.name = name;
-		this.text = text;
-	}
-
-	public static final class CompilerElement extends LuanElement {
-		public final LuanSource source;
-
-		public CompilerElement(LuanSource source) {
-			if( source==null )
-				throw new NullPointerException("source is null");
-			this.source = source;
-		}
-
-		@Override String location() {
-			return "Compiling " + source.name;
-		}
-	}
-
-	public static final class Element extends LuanElement {
-		public final LuanSource source;
-		public final int start;
-		public final int end;
-
-		public Element(LuanSource source,int start,int end) {
-			if( source==null )
-				throw new NullPointerException("source is null");
-			this.source = source;
-			this.start = start;
-			while( end > 0 && Character.isWhitespace(source.text.charAt(end-1)) ) {
-				end--;
-			}
-			this.end = end;
-		}
-
-		public String text() {
-			return source.text.substring(start,end);
-		}
-
-		@Override String location() {
-			return source.name + ':' + lineNumber();
-		}
-
-		private int lineNumber() {
-			int line = 0;
-			int i = -1;
-			do {
-				line++;
-				i = source.text.indexOf('\n',i+1);
-			} while( i != -1 && i < start );
-			return line;
-		}
-
-	}
-}
--- a/src/luan/LuanState.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-package luan;
-
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.LinkedHashMap;
-import luan.impl.LuanCompiler;
-import luan.modules.BasicLuan;
-import luan.modules.PackageLuan;
-
-
-public abstract class LuanState implements DeepCloneable<LuanState> {
-	private final LuanBit JAVA = bit(LuanElement.JAVA);
-
-	public LuanException exception(Object msg) {
-		return JAVA.exception(msg);
-	}
-
-	public Object call(LuanFunction fn) throws LuanException {
-		return call(fn,null,LuanFunction.NOTHING);
-	}
-
-	public Object call(LuanFunction fn,String fnName) throws LuanException {
-		return call(fn,fnName,LuanFunction.NOTHING);
-	}
-
-	public Object call(LuanFunction fn,Object[] args) throws LuanException {
-		return call(fn,null,args);
-	}
-
-	public Object call(LuanFunction fn,String fnName,Object[] args) throws LuanException {
-		return JAVA.call(fn,fnName,args);
-	}
-
-	public LuanFunction checkFunction(Object obj) throws LuanException {
-		return JAVA.checkFunction(obj);
-	}
-
-	public String toString(Object obj) throws LuanException {
-		return JAVA.toString(obj);
-	}
-
-	public String repr(Object obj) throws LuanException {
-		return JAVA.repr(obj);
-	}
-
-	public boolean isLessThan(Object o1,Object o2) throws LuanException {
-		return JAVA.isLessThan(o1,o2);
-	}
-
-
-
-	private LuanTable global;
-	private LuanTable loaded;
-	private LuanTable preload;
-	private LuanTable searchers;
-
-	private final List<MetatableGetter> mtGetters;
-	final List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
-
-	protected LuanState() {
-		global = new LuanTable();
-		global.put("_G",global);
-		global.put( "_VERSION", Luan.version );
-		loaded = new LuanTable();
-		preload = new LuanTable();
-		searchers = new LuanTable();
-		mtGetters = new ArrayList<MetatableGetter>();
-	}
-
-	protected LuanState(LuanState luan) {
-		mtGetters = new ArrayList<MetatableGetter>(luan.mtGetters);
-	}
-
-	@Override public void deepenClone(LuanState clone,DeepCloner cloner) {
-		clone.global = cloner.deepClone(global);
-		clone.loaded = cloner.deepClone(loaded);
-		clone.preload = cloner.deepClone(preload);
-		clone.searchers = cloner.deepClone(searchers);
-	}
-
-	public abstract LuanTable currentEnvironment();
-
-	public final LuanTable global() {
-		return global;
-	}
-
-	public final LuanTable loaded() {
-		return loaded;
-	}
-
-	public final LuanTable preload() {
-		return preload;
-	}
-
-	public final LuanTable searchers() {
-		return searchers;
-	}
-
-	public final Object get(String name) {
-		String[] a = name.split("\\.");
-		LuanTable t = loaded;
-		for( int i=0; i<a.length-1; i++ ) {
-			Object obj = t.get(a[i]);
-			if( !(obj instanceof LuanTable) )
-				return null;
-			t = (LuanTable)obj;
-		}
-		return t.get(a[a.length-1]);
-	}
-
-	public final Object set(String name,Object value) {
-		String[] a = name.split("\\.");
-		LuanTable t = loaded;
-		for( int i=0; i<a.length-1; i++ ) {
-			Object obj = t.get(a[i]);
-			if( !(obj instanceof LuanTable) )
-				return null;
-			t = (LuanTable)obj;
-		}
-		return t.put(a[a.length-1],value);
-	}
-
-	public final void globalImport(String modName) throws LuanException {
-		Object mod = PackageLuan.require(this,modName);
-		global.put(modName,mod);
-	}
-
-	public static LuanState newStandard() {
-		try {
-			LuanState luan = LuanCompiler.newLuanState();
-			luan.globalImport("Package");
-			BasicLuan.do_file(luan,"java:luan/init.luan");
-			return luan;
-		} catch(LuanException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public final Object eval(String cmd) {
-		return eval(cmd,new LuanTable());
-	}
-
-	public final Object eval(String cmd,LuanTable env) {
-		try {
-			LuanFunction fn = BasicLuan.load(this,cmd,"eval",env,true);
-			return call(fn);
-		} catch(LuanException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public final LuanTable getMetatable(Object obj) {
-		if( obj instanceof LuanTable ) {
-			LuanTable table = (LuanTable)obj;
-			return table.getMetatable();
-		}
-		for( MetatableGetter mg : mtGetters ) {
-			LuanTable table = mg.getMetatable(obj);
-			if( table != null )
-				return table;
-		}
-		return null;
-	}
-
-	public final void addMetatableGetter(MetatableGetter mg) {
-		mtGetters.add(mg);
-	}
-
-	public final LuanBit bit(LuanElement el) {
-		return new LuanBit(this,el);
-	}
-
-	public final Object getHandler(String op,Object obj) {
-		LuanTable t = getMetatable(obj);
-		return t==null ? null : t.get(op);
-	}
-
-}
--- a/src/luan/LuanTable.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,388 +0,0 @@
-package luan;
-
-import java.util.Iterator;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.regex.Pattern;
-
-
-public final class LuanTable implements DeepCloneable<LuanTable>, Iterable<Map.Entry<Object,Object>>, LuanRepr {
-	private Map<Object,Object> map = null;
-	private List<Object> list = null;
-	private LuanTable metatable = null;
-
-	public LuanTable() {}
-
-	public LuanTable(LuanTable tbl) {
-		if( tbl.map != null )
-			this.map = new HashMap<Object,Object>(tbl.map);
-		if( tbl.list != null )
-			this.list = new ArrayList<Object>(tbl.list);
-	}
-
-	public LuanTable(List<Object> list) {
-		this.list = list;
-		this.map = new HashMap<Object,Object>();
-		map.put("n",list.size());
-		for( int i=0; i<list.size(); i++ ) {
-			if( list.get(i) == null ) {
-				listToMap(i);
-				break;
-			}
-		}
-	}
-
-	public LuanTable(Map<Object,Object> map) {
-		map.remove(null);
-		for( Iterator<Object> i=map.values().iterator(); i.hasNext(); ) {
-			if( i.next() == null )
-				i.remove();
-		}
-		this.map = map;
-	}
-
-	public LuanTable(Set<Object> set) {
-		map = new HashMap<Object,Object>();
-		for( Object obj : set ) {
-			if( obj != null )
-				map.put(obj,Boolean.TRUE);
-		}
-	}
-
-	@Override public LuanTable shallowClone() {
-		return new LuanTable();
-	}
-
-	@Override public void deepenClone(LuanTable clone,DeepCloner cloner) {
-		if( map != null ) {
-			clone.map = new HashMap<Object,Object>();
-			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
-				clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) );
-			}
-		}
-		if( list != null ) {
-			clone.list = new ArrayList<Object>();
-			for( Object obj : list ) {
-				clone.list.add( cloner.get(obj) );
-			}
-		}
-		if( metatable != null )
-			clone.metatable = cloner.deepClone(metatable);
-	}
-
-	public boolean isList() {
-		return map==null || map.isEmpty();
-	}
-
-	public List<Object> asList() {
-		return list!=null ? list : Collections.emptyList();
-	}
-
-	public Map<Object,Object> asMap() {
-		if( list == null || list.isEmpty() )
-			return map!=null ? map : Collections.emptyMap();
-		Map<Object,Object> rtn = map!=null ? new HashMap<Object,Object>(map) : new HashMap<Object,Object>();
-		for( ListIterator iter = list.listIterator(); iter.hasNext(); ) {
-			int i = iter.nextIndex();
-			rtn.put(i+1,iter.next());
-		}
-		return rtn;
-	}
-
-	public boolean isSet() {
-		if( list != null ) {
-			for( Object obj : list ) {
-				if( obj!=null && !obj.equals(Boolean.TRUE) )
-					return false;
-			}
-		}
-		if( map != null ) {
-			for( Object obj : map.values() ) {
-				if( !obj.equals(Boolean.TRUE) )
-					return false;
-			}
-		}
-		return true;
-	}
-
-	public Set<Object> asSet() {
-		if( list == null || list.isEmpty() )
-			return map!=null ? map.keySet() : Collections.emptySet();
-		Set<Object> rtn = map!=null ? new HashSet<Object>(map.keySet()) : new HashSet<Object>();
-		for( int i=1; i<=list.size(); i++ ) {
-			rtn.add(i);
-		}
-		return rtn;
-	}
-
-	@Override public String toString() {
-		return "table: " + Integer.toHexString(hashCode());
-	}
-
-	@Override public String repr() {
-		return repr( Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
-	}
-
-	private String repr(Set<LuanTable> set) {
-		if( !set.add(this) ) {
-			return "\"<circular reference>\"";
-		}
-		StringBuilder sb = new StringBuilder();
-		sb.append('{');
-		boolean isFirst = true;
-		if( list != null ) {
-			boolean gotNull = false;
-			for( int i=0; i<list.size(); i++ ) {
-				Object obj = list.get(i);
-				if( obj==null ) {
-					gotNull = true;
-				} else {
-					if( isFirst ) {
-						isFirst = false;
-					} else {
-						sb.append(", ");
-					}
-					if( gotNull )
-						sb.append(i+1).append('=');
-					sb.append(repr(set,obj));
-				}
-			}
-		}
-		if( map != null ) {
-			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
-				if( isFirst ) {
-					isFirst = false;
-				} else {
-					sb.append(", ");
-				}
-				sb.append(reprKey(set,entry.getKey())).append('=').append(repr(set,entry.getValue()));
-			}
-		}
-		sb.append('}');
-		return sb.toString();
-	}
-
-	private static final Pattern namePtn = Pattern.compile("[a-zA-Z_][a-zA-Z_0-9]*");
-
-	private static String reprKey(Set<LuanTable> set,Object obj) {
-		if( obj instanceof String ) {
-			String s = (String)obj;
-			if( namePtn.matcher(s).matches() )
-				return s;
-		}
-		return "[" + repr(set,obj) + "]";
-	}
-
-	private static String repr(Set<LuanTable> set,Object obj) {
-		if( obj instanceof LuanTable ) {
-			LuanTable t = (LuanTable)obj;
-			return t.repr(set);
-		} else {
-			String s = Luan.repr(obj);
-			if( s == null )
-				s = "<couldn't repr: " + Luan.stringEncode(Luan.toString(obj)) + ">";
-			return s;
-		}
-	}
-
-	public Object get(Object key) {
-		if( list != null ) {
-			Integer iT = Luan.asInteger(key);
-			if( iT != null ) {
-				int i = iT - 1;
-				if( i>=0 && i<list.size() )
-					return list.get(i);
-			}
-		}
-		if( map==null )
-			return null;
-		return map.get(key);
-	}
-
-	public Object put(Object key,Object val) {
-		Integer iT = Luan.asInteger(key);
-		if( iT != null ) {
-			int i = iT - 1;
-			if( list != null || i == 0 ) {
-				if( i == list().size() ) {
-					if( val != null ) {
-						list.add(val);
-						mapToList();
-					}
-					return null;
-				} else if( i>=0 && i<list.size() ) {
-					Object old = list.get(i);
-					list.set(i,val);
-					if( val == null ) {
-						listToMap(i);
-					}
-					return old;
-				}
-			}
-		}
-		if( map==null ) {
-			map = new HashMap<Object,Object>();
-		}
-		if( key instanceof Number && !(key instanceof Double) ) {
-			Number n = (Number)key;
-			key = Double.valueOf(n.doubleValue());
-		}
-		if( val == null ) {
-			return map.remove(key);
-		} else {
-			return map.put(key,val);
-		}
-	}
-
-	private void mapToList() {
-		if( map != null ) {
-			while(true) {
-				Object v = map.remove(Double.valueOf(list.size()+1));
-				if( v == null )
-					break;
-				list.add(v);
-			}
-		}
-	}
-
-	private void listToMap(int from) {
-		if( list != null ) {
-			while( list.size() > from ) {
-				int i = list.size() - 1;
-				Object v = list.remove(i);
-				if( v != null ) {
-					if( map==null )
-						map = new HashMap<Object,Object>();
-					map.put(i+1,v);
-				}
-			}
-		}
-	}
-
-	private List<Object> list() {
-		if( list == null ) {
-			list = new ArrayList<Object>();
-			mapToList();
-		}
-		return list;
-	}
-
-	public void insert(int pos,Object value) {
-		if( value==null )
-			throw new UnsupportedOperationException();
-		list().add(pos-1,value);
-		mapToList();
-	}
-
-	public void add(Object value) {
-		if( value==null )
-			throw new UnsupportedOperationException();
-		list().add(value);
-		mapToList();
-	}
-
-	public Object remove(int pos) {
-		return list().remove(pos-1);
-	}
-
-	public void sort(Comparator<Object> cmp) {
-		Collections.sort(list(),cmp);
-	}
-
-	public int length() {
-		return list==null ? 0 : list.size();
-	}
-
-	public Iterator<Map.Entry<Object,Object>> iterator() {
-		if( list == null ) {
-			if( map == null )
-				return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
-			return map.entrySet().iterator();
-		}
-		if( map == null )
-			return listIterator();
-		return new Iterator<Map.Entry<Object,Object>>() {
-			Iterator<Map.Entry<Object,Object>> iter = listIterator();
-			boolean isList = true;
-			public boolean hasNext() {
-				boolean b = iter.hasNext();
-				if( !b && isList ) {
-					iter = map.entrySet().iterator();
-					isList = false;
-					b = iter.hasNext();
-				}
-				return b;
-			}
-			public Map.Entry<Object,Object> next() {
-				return iter.next();
-			}
-			public void remove() {
-				throw new UnsupportedOperationException();
-			}
-		};
-	}
-
-	public Iterator<Map.Entry<Object,Object>> listIterator() {
-		if( list == null )
-			return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
-		final ListIterator iter = list.listIterator();
-		return new Iterator<Map.Entry<Object,Object>>() {
-			public boolean hasNext() {
-				return iter.hasNext();
-			}
-			public Map.Entry<Object,Object> next() {
-				Double key = Double.valueOf(iter.nextIndex()+1);
-				return new MapEntry(key,iter.next());
-			}
-			public void remove() {
-				throw new UnsupportedOperationException();
-			}
-		};
-	}
-/*
-	public Object[] listToArray() {
-		return list==null ? new Object[0] : list.toArray();
-	}
-*/
-	public LuanTable subList(int from,int to) {
-		return new LuanTable(new ArrayList<Object>(list().subList(from-1,to-1)));
-	}
-
-	public LuanTable getMetatable() {
-		return metatable;
-	}
-
-	public void setMetatable(LuanTable metatable) {
-		this.metatable = metatable;
-	}
-
-	private static final class MapEntry implements Map.Entry<Object,Object> {
-		private final Object key;
-		private final Object value;
-
-		MapEntry(Object key,Object value) {
-			this.key = key;
-			this.value = value;
-		}
-
-		@Override public Object getKey() {
-			return key;
-		}
-
-		@Override public Object getValue() {
-			return value;
-		}
-
-		@Override public Object setValue(Object value) {
-			throw new UnsupportedOperationException();
-		}
-	}
-}
--- a/src/luan/MetatableGetter.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-package luan;
-
-public interface MetatableGetter {
-	public LuanTable getMetatable(Object obj);
-}
--- a/src/luan/StackTraceElement.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-package luan;
-
-
-final class StackTraceElement {
-	final LuanElement call;
-	final String fnName;
-
-	StackTraceElement(LuanElement call,String fnName) {
-		this.call = call;
-		this.fnName = fnName;
-	}
-}
--- a/src/luan/cmd_line.luan	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-
-local standalone_usage = [=[
-usage: java luan.CmdLine [options] [script [args]]
-Available options are:
-  -e stat  execute string 'stat'
-  -i       enter interactive mode after executing 'script'
-  -v       show version information
-  --       stop handling options
-  -        stop handling options and execute stdin
-]=]
-
-local function standalone_error(msg)
-	Io.stderr.write( msg, "\n", standalone_usage )
-end
-
-
-local args = {...}
-local interactive = false
-local showVersion = false
-local i = 1
-if #args == 0 then
-	interactive = true
-	showVersion = true
-else
-	while i <= #args do
-		local arg = args[i]
-		if arg.sub(1,1) ~= "-" or arg == "--" then
-			break
-		end
-		if arg == "-i" then
-			interactive = true
-		elseif arg == "-v" then
-			showVersion = true
-		elseif arg == "-e" then
-			i = i + 1
-			if i == #args then
-				standalone_error "'-e' needs argument"
-				return
-			end
-			local cmd = args[i]
-			local stat = load(cmd,"(command line)",true,true)
-			local result = Table.pack( stat() )
-			if result.n > 0 then
-				print( Table.unpack(result,1,result.n) )
-			end
-		elseif arg == "-" then
-			local src = Io.stdin.read_text()
-			local stdin = load(src,"stdin")
-			stdin()
-			return
-		else
-			standalone_error( "unrecognized option '"..arg.."'" )
-			return
-		end
-		i = i + 1
-	end
-end
-if showVersion then print(_VERSION) end
-if i <= #args then
-	local file = args[i]
-	_G.arg = {}
-	for j,v in ipairs(args) do
-		_G.arg[j-i] = v
-	end
-	local main_file = load_file(file)
-	main_file( Table.unpack(_G.arg) )
-end
-if interactive then
-	Debug.debug("> ")
-end
--- a/src/luan/impl/AddExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/AndExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/BinaryOpExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-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);
-	}
-
-}
--- a/src/luan/impl/Block.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-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);
-		}
-	}
-
-}
--- a/src/luan/impl/BreakException.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-package luan.impl;
-
-
-final class BreakException extends RuntimeException {}
--- a/src/luan/impl/BreakStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-package luan.impl;
-
-
-final class BreakStmt implements Stmt {
-
-	@Override public void eval(LuanStateImpl luan) {
-		throw new BreakException();
-	}
-}
--- a/src/luan/impl/Closure.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-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);
-		}
-	}
-
-}
--- a/src/luan/impl/Code.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package luan.impl;
-
-import luan.LuanSource;
-
-
-interface Code {
-	public LuanSource.Element se();
-}
--- a/src/luan/impl/CodeImpl.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-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;
-	}
-}
--- a/src/luan/impl/ConcatExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-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;
-	}
-}
--- a/src/luan/impl/ConstExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-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+")";
-	}
-}
--- a/src/luan/impl/DivExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/EqExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-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})) );
-	}
-}
--- a/src/luan/impl/ExpList.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-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);
-		}
-	}
-}
--- a/src/luan/impl/Expr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-package luan.impl;
-
-import luan.LuanException;
-
-
-interface Expr extends Expressions {}
--- a/src/luan/impl/Expressions.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package luan.impl;
-
-import luan.LuanException;
-
-
-interface Expressions extends Code {
-	public Object eval(LuanStateImpl luan) throws LuanException;
-}
--- a/src/luan/impl/ExpressionsExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-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();
-	}
-
-}
--- a/src/luan/impl/ExpressionsStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-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);
-	}
-
-}
--- a/src/luan/impl/FnCall.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-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+")";
-	}
-}
--- a/src/luan/impl/FnDef.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-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);
-	}
-
-}
--- a/src/luan/impl/ForStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-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);
-		}
-	}
-
-}
--- a/src/luan/impl/GetLocalVar.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/GetUpVar.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-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();
-	}
-}
--- a/src/luan/impl/IfStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-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);
-		}
-	}
-}
--- a/src/luan/impl/IndexExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/LeExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-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) );
-	}
-}
--- a/src/luan/impl/LenExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-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" );
-	}
-}
--- a/src/luan/impl/LtExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/LuanCompiler.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-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();
-	}
-}
--- a/src/luan/impl/LuanParser.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1360 +0,0 @@
-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();
-	}
-
-}
--- a/src/luan/impl/LuanStateImpl.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +0,0 @@
-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();
-	}
-
-}
--- a/src/luan/impl/ModExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/MulExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/NotExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-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));
-	}
-}
--- a/src/luan/impl/OrExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/ParseException.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-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();
-	}
-}
--- a/src/luan/impl/Parser.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-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());
-	}
-
-}
--- a/src/luan/impl/PowExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/RepeatStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-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) {}
-	}
-}
--- a/src/luan/impl/ReturnException.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-package luan.impl;
-
-
-final class ReturnException extends RuntimeException {}
--- a/src/luan/impl/ReturnStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-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();
-	}
-}
--- a/src/luan/impl/SetLocalVar.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-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 );
-	}
-}
--- a/src/luan/impl/SetStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-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);
-			}
-		}
-	}
-
-}
--- a/src/luan/impl/SetTableEntry.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-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);
-	}
-
-}
--- a/src/luan/impl/SetUpVar.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/Settable.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package luan.impl;
-
-import luan.LuanException;
-
-
-interface Settable {
-	public void set(LuanStateImpl luan,Object value) throws LuanException;
-}
--- a/src/luan/impl/Stmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-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) {}
-	};
-}
--- a/src/luan/impl/SubExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-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);
-	}
-}
--- a/src/luan/impl/TableExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-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;
-	}
-}
--- a/src/luan/impl/TryStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-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);
-			}
-		}
-	}
-}
--- a/src/luan/impl/UnaryOpExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-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;
-	}
-}
--- a/src/luan/impl/UnmExpr.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-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");
-	}
-}
--- a/src/luan/impl/UpValue.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-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;
-		}
-	}
-
-}
--- a/src/luan/impl/VarArgs.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-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();
-	}
-}
--- a/src/luan/impl/WhileStmt.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-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) {}
-	}
-}
--- a/src/luan/init.luan	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-function Package.global(module,fn_name)
-	local function fn(...)
-		return module[fn_name](...)
-	end
-	_G[fn_name] = fn
-	return fn
-end
-
-local require = Package.global(Package,"require")
-
-function Package.global_import(name)
-	local mod = require(name)
-	_G[name] = mod
-	return mod
-end
-
-local Basic = Package.global_import("Basic","luan.lib.BasicLib.LOADER")
-Package.global(Basic,"assert")
-Package.global(Basic,"assert_boolean")
-Package.global(Basic,"assert_nil")
-Package.global(Basic,"assert_number")
-Package.global(Basic,"assert_string")
-Package.global(Basic,"assert_table")
-Package.global(Basic,"do_file")
-Package.global(Basic,"error")
-Package.global(Basic,"get_metatable")
-Package.global(Basic,"ipairs")
-local load = Package.global(Basic,"load")
-Package.global(Basic,"load_file")
-Package.global(Basic,"pairs")
-Package.global(Basic,"range")
-Package.global(Basic,"raw_equal")
-Package.global(Basic,"raw_get")
-Package.global(Basic,"raw_len")
-Package.global(Basic,"raw_set")
-Package.global(Basic,"repr")
-Package.global(Basic,"set_metatable")
-Package.global(Basic,"to_number")
-local to_string = Package.global(Basic,"to_string")
-Package.global(Basic,"type")
-
-local String = Package.global_import("String","luan.lib.StringLib.LOADER")
-
--- improved global_import
-function Package.global_import(name)
-	local short = name.match("\.([^.]+)$") or name
-	local mod = require(name)
-	_G[short] = mod
-	return mod
-end
-
-local Table = Package.global_import("Table","luan.lib.TableLib.LOADER")
-local Io = Package.global_import("Io","luan.lib.IoLib.LOADER")
-Package.global_import("Math","luan.lib.MathLib.LOADER")
-Package.global_import("Html","luan.lib.HtmlLib.LOADER")
-Package.global_import("Thread","luan.lib.ThreadLib.LOADER")
-Package.global_import("Binary","luan.lib.BinaryLib.LOADER")
-
-
-function Io.print_to(out,...)
-	local list = {}
-	for _,v in Basic.values(...) do
-		list[#list+1] = to_string(v)
-		list[#list+1] = '\t'
-	end
-	if #list == 0 then
-		out.write( '\n' )
-	else
-		list[#list] = '\n'
-		out.write( Table.unpack(list) )
-	end
-end
-
-function Basic.print(...)
-	Io.print_to(Io.stdout,...)
-end
-local print = Package.global(Basic,"print")
-
-local Debug = {}
-Package.loaded.Debug = Debug
-_G.Debug = Debug
-
-function Debug.print_if_something(...)
-	if Table.pack(...).n > 0 then
-		print(...)
-	end
-end
-
-function Debug.debug(prompt)
-	prompt = prompt or "luan_debug> "
-	local function console()
-		return Io.read_console_line(prompt)
-	end
-	local env = {}
-	for line in console do
-		try
-			local fn = load(line,"stdin",env,true)
-			Debug.print_if_something( fn() )
-		catch e do
-			print(e)
-		end
-	end
-end
-
-
--- import modules
-Package.global_import("Reactionary")
--- a/src/luan/modules/BasicLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,222 +0,0 @@
-package luan.modules;
-
-import java.io.File;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-import luan.LuanSource;
-import luan.LuanElement;
-import luan.impl.LuanCompiler;
-
-
-public final class BasicLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				module.put( "assert", new LuanJavaFunction(BasicLuan.class.getMethod("assert_",LuanState.class,Object.class,String.class),null) );
-				add( module, "assert_boolean", LuanState.class, Boolean.TYPE );
-				add( module, "assert_nil", LuanState.class, Object.class );
-				add( module, "assert_number", LuanState.class, Number.class );
-				add( module, "assert_string", LuanState.class, String.class );
-				add( module, "assert_table", LuanState.class, LuanTable.class );
-				add( module, "do_file", LuanState.class, String.class );
-				add( module, "error", LuanState.class, Object.class );
-				add( module, "get_metatable", LuanState.class, Object.class );
-				add( module, "ipairs", LuanState.class, LuanTable.class );
-				add( module, "load", LuanState.class, String.class, String.class, LuanTable.class, Boolean.class );
-				add( module, "load_file", LuanState.class, String.class );
-				add( module, "pairs", LuanState.class, LuanTable.class );
-				add( module, "range", LuanState.class, Double.TYPE, Double.TYPE, Double.class );
-				add( module, "raw_equal", Object.class, Object.class );
-				add( module, "raw_get", LuanTable.class, Object.class );
-				add( module, "raw_len", LuanState.class, Object.class );
-				add( module, "raw_set", LuanTable.class, Object.class, Object.class );
-				add( module, "repr", LuanState.class, Object.class );
-				add( module, "set_metatable", LuanTable.class, LuanTable.class );
-				add( module, "to_number", Object.class, Integer.class );
-				add( module, "to_string", LuanState.class, Object.class );
-				add( module, "type", Object.class );
-				add( module, "values", new Object[0].getClass() );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(BasicLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static String type(Object obj) {
-		return Luan.type(obj);
-	}
-
-	public static LuanFunction load(LuanState luan,String text,String sourceName,LuanTable env,Boolean allowExpr)
-		throws LuanException
-	{
-		if( allowExpr==null )
-			allowExpr = false;
-		return LuanCompiler.compile(luan,new LuanSource(sourceName,text),env,allowExpr);
-	}
-
-	public static LuanFunction load_file(LuanState luan,String fileName) throws LuanException {
-		try {
-			String src = fileName==null ? Utils.readAll(new InputStreamReader(System.in)) : IoLuan.luanIo(luan,fileName).read_text();
-			return load(luan,src,fileName,null,false);
-		} catch(IOException e) {
-			throw luan.exception(e);
-		}
-	}
-
-	public static Object do_file(LuanState luan,String fileName) throws LuanException {
-		LuanFunction fn = load_file(luan,fileName);
-		return luan.call(fn);
-	}
-
-	private static LuanFunction pairs(final Iterator<Map.Entry<Object,Object>> iter) {
-		return new LuanFunction() {
-			@Override public Object[] call(LuanState luan,Object[] args) {
-				if( !iter.hasNext() )
-					return LuanFunction.NOTHING;
-				Map.Entry<Object,Object> entry = iter.next();
-				return new Object[]{entry.getKey(),entry.getValue()};
-			}
-		};
-	}
-
-	public static LuanFunction pairs(LuanState luan,LuanTable t) throws LuanException {
-		Utils.checkNotNull(luan,t,"table");
-		return pairs( t.iterator() );
-	}
-
-	public static LuanFunction ipairs(LuanState luan,LuanTable t) throws LuanException {
-		Utils.checkNotNull(luan,t,"table");
-		return pairs( t.listIterator() );
-	}
-
-	public static LuanTable get_metatable(LuanState luan,Object obj) {
-		return luan.getMetatable(obj);
-	}
-
-	public static LuanTable set_metatable(LuanTable table,LuanTable metatable) {
-		table.setMetatable(metatable);
-		return table;
-	}
-
-	public static boolean raw_equal(Object v1,Object v2) {
-		return v1 == v2 || v1 != null && v1.equals(v2);
-	}
-
-	public static Object raw_get(LuanTable table,Object index) {
-		return table.get(index);
-	}
-
-	public static LuanTable raw_set(LuanTable table,Object index,Object value) {
-		table.put(index,value);
-		return table;
-	}
-
-	public static int raw_len(LuanState luan,Object v) throws LuanException {
-		if( v instanceof String ) {
-			String s = (String)v;
-			return s.length();
-		}
-		if( v instanceof LuanTable ) {
-			LuanTable t = (LuanTable)v;
-			return t.length();
-		}
-		throw luan.exception( "bad argument #1 to 'raw_len' (table or string expected)" );
-	}
-
-	public static Number to_number(Object e,Integer base) {
-		return Luan.toNumber(e,base);
-	}
-
-	public static String to_string(LuanState luan,Object v) throws LuanException {
-		return luan.toString(v);
-	}
-
-	public static void error(LuanState luan,Object msg) throws LuanException {
-		throw luan.exception(msg);
-	}
-
-	public static Object assert_(LuanState luan,Object v,String msg) throws LuanException {
-		if( Luan.toBoolean(v) )
-			return v;
-		if( msg == null )
-			msg = "assertion failed!";
-		throw luan.exception( msg );
-	}
-
-	public static String assert_string(LuanState luan,String v) throws LuanException {
-		Utils.checkNotNull(luan,v,"string");
-		return v;
-	}
-
-	public static Number assert_number(LuanState luan,Number v) throws LuanException {
-		Utils.checkNotNull(luan,v,"number");
-		return v;
-	}
-
-	public static LuanTable assert_table(LuanState luan,LuanTable v) throws LuanException {
-		Utils.checkNotNull(luan,v,"table");
-		return v;
-	}
-
-	public static boolean assert_boolean(LuanState luan,boolean v) throws LuanException {
-		return v;
-	}
-
-	public static Object assert_nil(LuanState luan,Object v) throws LuanException {
-		if( v != null )
-			throw luan.exception("bad argument #1 (nil expected, got "+Luan.type(v)+")");
-		return v;
-	}
-
-	public static String repr(LuanState luan,Object v) throws LuanException {
-		return luan.repr(v);
-	}
-
-	public static LuanFunction range(LuanState luan,final double from,final double to,Double stepV) throws LuanException {
-		final double step = stepV==null ? 1.0 : stepV;
-		if( step == 0.0 )
-			throw luan.exception("bad argument #3 (step may not be zero)");
-		return new LuanFunction() {
-			double v = from;
-
-			@Override public Object call(LuanState luan,Object[] args) {
-				if( step > 0.0 && v > to || step < 0.0 && v < to )
-					return LuanFunction.NOTHING;
-				double rtn = v;
-				v += step;
-				return rtn;
-			}
-		};
-	}
-
-	public static LuanFunction values(final Object... args) throws LuanException {
-		return new LuanFunction() {
-			int i = 0;
-
-			@Override public Object call(LuanState luan,Object[] unused) {
-				if( ++i > args.length )
-					return LuanFunction.NOTHING;
-				return new Object[]{i,args[i-1]};
-			}
-		};
-	}
-
-}
--- a/src/luan/modules/BinaryLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-package luan.modules;
-
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class BinaryLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "to_string", new byte[0].getClass() );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(BinaryLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static String to_string(byte[] bytes) {
-		return new String(bytes);
-	}
-
-}
--- a/src/luan/modules/HtmlLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package luan.modules;
-
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class HtmlLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "encode", String.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(HtmlLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static String encode(String s) {
-		char[] a = s.toCharArray();
-		StringBuilder buf = new StringBuilder();
-		for( int i=0; i<a.length; i++ ) {
-			char c = a[i];
-			switch(c) {
-			case '&':
-				buf.append("&amp;");
-				break;
-			case '<':
-				buf.append("&lt;");
-				break;
-			case '>':
-				buf.append("&gt;");
-				break;
-			case '"':
-				buf.append("&quot;");
-				break;
-			default:
-				buf.append(c);
-			}
-		}
-		return buf.toString();
-	}
-
-}
--- a/src/luan/modules/IoLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,504 +0,0 @@
-package luan.modules;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.Reader;
-import java.io.Writer;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.net.Socket;
-import java.net.ServerSocket;
-import java.net.MalformedURLException;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class IoLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "File", LuanState.class, String.class );
-				add( module, "read_console_line", String.class );
-
-				LuanTable stdin = new LuanTable();
-				stdin.put( "read_text", new LuanJavaFunction(
-					IoLuan.class.getMethod( "stdin_read_text" ), null
-				) );
-				stdin.put( "read_binary", new LuanJavaFunction(
-					IoLuan.class.getMethod( "stdin_read_binary" ), null
-				) );
-				stdin.put( "read_lines", new LuanJavaFunction(
-					IoLuan.class.getMethod( "stdin_read_lines" ), null
-				) );
-				stdin.put( "read_blocks", new LuanJavaFunction(
-					IoLuan.class.getMethod( "stdin_read_blocks", Integer.class ), null
-				) );
-				module.put( "stdin", stdin );
-
-				add( module, "Socket", String.class, Integer.TYPE );
-				add( module, "socket_server", Integer.TYPE );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			module.put( "stdout", textWriter(System.out) );
-			module.put( "stderr", textWriter(System.err) );
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(IoLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-
-	public static String stdin_read_text() throws IOException {
-		return Utils.readAll(new InputStreamReader(System.in));
-	}
-
-	public static byte[] stdin_read_binary() throws IOException {
-		return Utils.readAll(System.in);
-	}
-
-	public static LuanFunction stdin_read_lines() throws IOException {
-		return lines(new BufferedReader(new InputStreamReader(System.in)));
-	}
-
-	public static LuanFunction stdin_read_blocks(Integer blockSize) throws IOException {
-		int n = blockSize!=null ? blockSize : Utils.bufSize;
-		return blocks(System.in,n);
-	}
-
-	public static String read_console_line(String prompt) throws IOException {
-		if( prompt==null )
-			prompt = "> ";
-		return System.console().readLine(prompt);
-	}
-
-
-	public interface LuanWriter {
-		public void write(LuanState luan,Object... args) throws LuanException, IOException;
-		public void close() throws IOException;
-	}
-
-	public static LuanTable textWriter(final PrintStream out) {
-		LuanWriter luanWriter = new LuanWriter() {
-
-			public void write(LuanState luan,Object... args) throws LuanException {
-				for( Object obj : args ) {
-					out.print( luan.toString(obj) );
-				}
-			}
-
-			public void close() {
-				out.close();
-			}
-		};
-		return writer(luanWriter);
-	}
-
-	public static LuanTable textWriter(final Writer out) {
-		LuanWriter luanWriter = new LuanWriter() {
-
-			public void write(LuanState luan,Object... args) throws LuanException, IOException {
-				for( Object obj : args ) {
-					out.write( luan.toString(obj) );
-				}
-			}
-
-			public void close() throws IOException {
-				out.close();
-			}
-		};
-		return writer(luanWriter);
-	}
-
-	private static LuanTable writer(LuanWriter luanWriter) {
-		LuanTable writer = new LuanTable();
-		try {
-			writer.put( "write", new LuanJavaFunction(
-				LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter
-			) );
-			writer.put( "close", new LuanJavaFunction(
-				LuanWriter.class.getMethod( "close" ), luanWriter
-			) );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		return writer;
-	}
-
-
-	public static LuanTable binaryWriter(final OutputStream out) {
-		LuanTable writer = new LuanTable();
-		try {
-			writer.put( "write", new LuanJavaFunction(
-				OutputStream.class.getMethod( "write", new byte[0].getClass() ), out
-			) );
-			writer.put( "close", new LuanJavaFunction(
-				OutputStream.class.getMethod( "close" ), out
-			) );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		return writer;
-	}
-
-	static LuanFunction lines(final BufferedReader in) {
-		return new LuanFunction() {
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				try {
-					if( args.length > 0 ) {
-						if( args.length > 1 || !"close".equals(args[0]) )
-							throw luan.exception( "the only argument allowed is 'close'" );
-						in.close();
-						return null;
-					}
-					String rtn = in.readLine();
-					if( rtn==null )
-						in.close();
-					return rtn;
-				} catch(IOException e) {
-					throw luan.exception(e);
-				}
-			}
-		};
-	}
-
-	static LuanFunction blocks(final InputStream in,final int blockSize) {
-		return new LuanFunction() {
-			final byte[] a = new byte[blockSize];
-
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				try {
-					if( args.length > 0 ) {
-						if( args.length > 1 || !"close".equals(args[0]) )
-							throw luan.exception( "the only argument allowed is 'close'" );
-						in.close();
-						return null;
-					}
-					if( in.read(a) == -1 ) {
-						in.close();
-						return null;
-					}
-					return a;
-				} catch(IOException e) {
-					throw luan.exception(e);
-				}
-			}
-		};
-	}
-
-
-
-	public static abstract class LuanIn {
-		abstract InputStream inputStream() throws IOException;
-		public abstract String to_string();
-
-		public String read_text() throws IOException {
-			Reader in = new InputStreamReader(inputStream());
-			String s = Utils.readAll(in);
-			in.close();
-			return s;
-		}
-
-		public byte[] read_binary() throws IOException {
-			InputStream in = inputStream();
-			byte[] a = Utils.readAll(in);
-			in.close();
-			return a;
-		}
-
-		public LuanFunction read_lines() throws IOException {
-			return lines(new BufferedReader(new InputStreamReader(inputStream())));
-		}
-
-		public LuanFunction read_blocks(Integer blockSize) throws IOException {
-			int n = blockSize!=null ? blockSize : Utils.bufSize;
-			return blocks(inputStream(),n);
-		}
-
-		LuanTable table() {
-			LuanTable tbl = new LuanTable();
-			try {
-				tbl.put( "to_string", new LuanJavaFunction(
-					LuanIn.class.getMethod( "to_string" ), this
-				) );
-				tbl.put( "read_text", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_text" ), this
-				) );
-				tbl.put( "read_binary", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_binary" ), this
-				) );
-				tbl.put( "read_lines", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_lines" ), this
-				) );
-				tbl.put( "read_blocks", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_blocks", Integer.class ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return tbl;
-		}
-	}
-
-	public static abstract class LuanIO extends LuanIn {
-		abstract OutputStream outputStream() throws IOException;
-
-		public void write(LuanState luan,Object obj) throws LuanException, IOException {
-			if( obj instanceof String ) {
-				String s = (String)obj;
-				Writer out = new OutputStreamWriter(outputStream());
-				out.write(s);
-				out.close();
-				return;
-			}
-			if( obj instanceof byte[] ) {
-				byte[] a = (byte[])obj;
-				OutputStream out = outputStream();
-				Utils.copyAll(new ByteArrayInputStream(a),out);
-				out.close();
-				return;
-			}
-			throw luan.exception( "bad argument #1 to 'write' (string or binary expected)" );
-		}
-
-		public LuanTable text_writer() throws IOException {
-			return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream())));
-		}
-
-		public LuanTable binary_writer() throws IOException {
-			return binaryWriter(new BufferedOutputStream(outputStream()));
-		}
-
-		@Override LuanTable table() {
-			LuanTable tbl = super.table();
-			try {
-				tbl.put( "write", new LuanJavaFunction(
-					LuanIO.class.getMethod( "write", LuanState.class, Object.class ), this
-				) );
-				tbl.put( "text_writer", new LuanJavaFunction(
-					LuanIO.class.getMethod( "text_writer" ), this
-				) );
-				tbl.put( "binary_writer", new LuanJavaFunction(
-					LuanIO.class.getMethod( "binary_writer" ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return tbl;
-		}
-	}
-
-	public static final class LuanUrl extends LuanIn {
-		private final URL url;
-
-		public LuanUrl(String s) throws MalformedURLException {
-			this.url = new URL(s);
-		}
-
-		@Override InputStream inputStream() throws IOException {
-			return url.openStream();
-		}
-
-		@Override public String to_string() {
-			return url.toString();
-		}
-	}
-
-	public static final class LuanFile extends LuanIO {
-		private final File file;
-
-		public LuanFile(String name) {
-			this(new File(name));
-		}
-
-		public LuanFile(File file) {
-			this.file = file;
-		}
-
-		@Override InputStream inputStream() throws IOException {
-			return new FileInputStream(file);
-		}
-
-		@Override OutputStream outputStream() throws IOException {
-			return new FileOutputStream(file);
-		}
-
-		@Override public String to_string() {
-			return file.toString();
-		}
-
-		public LuanTable child(String name) {
-			return new LuanFile(new File(file,name)).table();
-		}
-
-		public LuanTable children() {
-			File[] files = file.listFiles();
-			if( files==null )
-				return null;
-			LuanTable list = new LuanTable();
-			for( File f : files ) {
-				list.add(new LuanFile(f).table());
-			}
-			return list;
-		}
-
-		public boolean exists() {
-			return Utils.exists(file);
-		}
-
-		@Override LuanTable table() {
-			LuanTable tbl = super.table();
-			try {
-				tbl.put( "name", new LuanJavaFunction(
-					File.class.getMethod( "getName" ), file
-				) );
-				tbl.put( "exists", new LuanJavaFunction(
-					LuanFile.class.getMethod( "exists" ), this
-				) );
-				tbl.put( "is_directory", new LuanJavaFunction(
-					File.class.getMethod( "isDirectory" ), file
-				) );
-				tbl.put( "is_file", new LuanJavaFunction(
-					File.class.getMethod( "isFile" ), file
-				) );
-				tbl.put( "delete", new LuanJavaFunction(
-					File.class.getMethod( "delete" ), file
-				) );
-				tbl.put( "mkdir", new LuanJavaFunction(
-					File.class.getMethod( "mkdir" ), file
-				) );
-				tbl.put( "mkdirs", new LuanJavaFunction(
-					File.class.getMethod( "mkdirs" ), file
-				) );
-				tbl.put( "last_modified", new LuanJavaFunction(
-					File.class.getMethod( "lastModified" ), file
-				) );
-				tbl.put( "child", new LuanJavaFunction(
-					LuanFile.class.getMethod( "child", String.class ), this
-				) );
-				tbl.put( "children", new LuanJavaFunction(
-					LuanFile.class.getMethod( "children" ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return tbl;
-		}
-	}
-
-	public static LuanIn luanIo(LuanState luan,String name) throws LuanException {
-		if( Utils.isFile(name) )
-			return new LuanFile(name);
-		String url = Utils.toUrl(name);
-		if( url != null ) {
-			try {
-				return new LuanUrl(url);
-			} catch(MalformedURLException e) {
-				throw new RuntimeException(e);
-			}
-		}
-		throw luan.exception( "file '"+name+"' not found" );
-	}
-
-	public static LuanTable File(LuanState luan,String name) throws LuanException {
-		return luanIo(luan,name).table();
-	}
-
-	public static final class LuanSocket extends LuanIO {
-		private final Socket socket;
-
-		public LuanSocket(String host,int port) throws IOException {
-			this(new Socket(host,port));
-		}
-
-		public LuanSocket(Socket socket) throws IOException {
-			this.socket = socket;
-		}
-
-		@Override InputStream inputStream() throws IOException {
-			return socket.getInputStream();
-		}
-
-		@Override OutputStream outputStream() throws IOException {
-			return socket.getOutputStream();
-		}
-
-		@Override public String to_string() {
-			return socket.toString();
-		}
-
-		public LuanTable Pickle_client(LuanState luan) throws IOException {
-			DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream()));
-			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outputStream()));
-			return new PickleClient(luan,in,out).table();
-		}
-
-		public void run_pickle_server(LuanState luan) throws IOException {
-			DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream()));
-			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outputStream()));
-			new PickleServer(luan,in,out).run();
-		}
-
-		@Override LuanTable table() {
-			LuanTable tbl = super.table();
-			try {
-				tbl.put( "Pickle_client", new LuanJavaFunction(
-					LuanSocket.class.getMethod( "Pickle_client", LuanState.class ), this
-				) );
-				tbl.put( "run_pickle_server", new LuanJavaFunction(
-					LuanSocket.class.getMethod( "run_pickle_server", LuanState.class ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return tbl;
-		}
-	}
-
-	public static LuanTable Socket(String host,int port) throws IOException {
-		return new LuanSocket(host,port).table();
-	}
-
-	public static LuanFunction socket_server(int port) throws IOException {
-		final ServerSocket ss = new ServerSocket(port);
-		return new LuanFunction() {
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				try {
-					if( args.length > 0 ) {
-						if( args.length > 1 || !"close".equals(args[0]) )
-							throw luan.exception( "the only argument allowed is 'close'" );
-						ss.close();
-						return null;
-					}
-					return new LuanSocket(ss.accept()).table();
-				} catch(IOException e) {
-					throw luan.exception(e);
-				}
-			}
-		};
-	}
-
-	private void IoLuan() {}  // never
-}
--- a/src/luan/modules/JavaLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,450 +0,0 @@
-package luan.modules;
-
-import java.lang.reflect.Array;
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Member;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Proxy;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Collections;
-import java.util.Arrays;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.MetatableGetter;
-import luan.LuanException;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanElement;
-
-
-public final class JavaLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			luan.addMetatableGetter(mg);
-			LuanTable module = new LuanTable();
-			try {
-				module.put( "class", new LuanJavaFunction(JavaLuan.class.getMethod("getClass",LuanState.class,String.class),null) );
-				add( module, "proxy", LuanState.class, Static.class, LuanTable.class, Object.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			luan.searchers().add(javaSearcher);
-			return module;
-		}
-	};
-
-	public static final LuanFunction javaSearcher = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-			String modName = (String)args[0];
-			final Static s = JavaLuan.getClass(luan,modName);
-			if( s==null )
-				return null;
-			LuanFunction loader = new LuanFunction() {
-				@Override public Object call(LuanState luan,Object[] args) {
-					return s;
-				}
-			};
-			return loader;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) {
-		try {
-			t.put( method, new LuanJavaFunction(JavaLuan.class.getMethod(method,parameterTypes),null) );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	private static final LuanTable mt = new LuanTable();
-	static {
-		add( mt, "__index", LuanState.class, Object.class, Object.class );
-		add( mt, "__newindex", LuanState.class, Object.class, Object.class, Object.class );
-	}
-
-	private static final MetatableGetter mg = new MetatableGetter() {
-		public LuanTable getMetatable(Object obj) {
-			if( obj==null )
-				return null;
-			return mt;
-		}
-	};
-
-	public static Object __index(LuanState luan,Object obj,Object key) throws LuanException {
-		if( obj instanceof Static ) {
-			if( key instanceof String ) {
-				String name = (String)key;
-				Static st = (Static)obj;
-				Class cls = st.cls;
-				if( "class".equals(name) ) {
-					return cls;
-				} else if( "new".equals(name) ) {
-					Constructor<?>[] constructors = cls.getConstructors();
-					if( constructors.length > 0 ) {
-						if( constructors.length==1 ) {
-							return new LuanJavaFunction(constructors[0],null);
-						} else {
-							List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
-							for( Constructor constructor : constructors ) {
-								fns.add(new LuanJavaFunction(constructor,null));
-							}
-							return new AmbiguousJavaFunction(fns);
-						}
-					}
-				} else if( "assert".equals(name) ) {
-					return new LuanJavaFunction(assertClass,new AssertClass(cls));
-				} else {
-					List<Member> members = getStaticMembers(cls,name);
-					if( !members.isEmpty() ) {
-						return member(null,members);
-					}
-				}
-			}
-			throw luan.exception("invalid member '"+key+"' for: "+obj);
-		}
-		Class cls = obj.getClass();
-		if( cls.isArray() ) {
-			if( "length".equals(key) ) {
-				return Array.getLength(obj);
-			}
-			Integer i = Luan.asInteger(key);
-			if( i != null ) {
-				return Array.get(obj,i);
-			}
-			throw luan.exception("invalid member '"+key+"' for java array: "+obj);
-		}
-		if( key instanceof String ) {
-			String name = (String)key;
-			if( "instanceof".equals(name) ) {
-				return new LuanJavaFunction(instanceOf,new InstanceOf(obj));
-			} else {
-				List<Member> members = getMembers(cls,name);
-				if( !members.isEmpty() ) {
-					return member(obj,members);
-				}
-			}
-		}
-//		throw luan.exception("invalid member '"+key+"' for java object: "+obj);
-		return null;
-	}
-
-	private static Object member(Object obj,List<Member> members) throws LuanException {
-		try {
-			if( members.size()==1 ) {
-				Member member = members.get(0);
-				if( member instanceof Static ) {
-					return member;
-				} else if( member instanceof Field ) {
-					Field field = (Field)member;
-					Object rtn = field.get(obj);
-					return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn;
-				} else {
-					Method method = (Method)member;
-					return new LuanJavaFunction(method,obj);
-				}
-			} else {
-				List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
-				for( Member member : members ) {
-					Method method = (Method)member;
-					fns.add(new LuanJavaFunction(method,obj));
-				}
-				return new AmbiguousJavaFunction(fns);
-			}
-		} catch(IllegalAccessException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public static void __newindex(LuanState luan,Object obj,Object key,Object value) throws LuanException {
-		if( obj instanceof Static ) {
-			if( key instanceof String ) {
-				String name = (String)key;
-				Static st = (Static)obj;
-				Class cls = st.cls;
-				List<Member> members = getStaticMembers(cls,name);
-				if( !members.isEmpty() ) {
-					if( members.size() != 1 )
-						throw new RuntimeException("not field '"+name+"' of "+obj);
-					setMember(obj,members,value);
-					return;
-				}
-			}
-			throw luan.exception("invalid member '"+key+"' for: "+obj);
-		}
-		Class cls = obj.getClass();
-		if( cls.isArray() ) {
-			Integer i = Luan.asInteger(key);
-			if( i != null ) {
-				Array.set(obj,i,value);
-				return;
-			}
-			throw luan.exception("invalid member '"+key+"' for java array: "+obj);
-		}
-		if( key instanceof String ) {
-			String name = (String)key;
-			List<Member> members = getMembers(cls,name);
-			if( !members.isEmpty() ) {
-				if( members.size() != 1 )
-					throw new RuntimeException("not field '"+name+"' of "+obj);
-				setMember(obj,members,value);
-				return;
-			}
-		}
-		throw luan.exception("invalid member '"+key+"' for java object: "+obj);
-	}
-
-	private static void setMember(Object obj,List<Member> members,Object value) {
-		Field field = (Field)members.get(0);
-		try {
-			try {
-				field.set(obj,value);
-			} catch(IllegalArgumentException e) {
-				Class cls = field.getType();
-				if( value instanceof Number ) {
-					Number n = (Number)value;
-					if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) {
-						int r = n.intValue();
-						if( r==n.doubleValue() ) {
-							field.setInt(obj,r);
-							return;
-						}
-					}
-				}
-				throw e;
-			}
-		} catch(IllegalAccessException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public static boolean privateAccess = false;
-	private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>();
-
-	private static synchronized List<Member> getMembers(Class cls,String name) {
-		Map<String,List<Member>> clsMap = memberMap.get(cls);
-		if( clsMap == null ) {
-			clsMap = new HashMap<String,List<Member>>();
-			for( Class c : cls.getClasses() ) {
-				String s = c.getSimpleName();
-				List<Member> list = new ArrayList<Member>();
-				clsMap.put(s,list);
-				list.add(new Static(c));
-			}
-			for( Field field : cls.getFields() ) {
-				String s = field.getName();
-				try {
-					if( !cls.getField(s).equals(field) )
-						continue;  // not accessible
-				} catch(NoSuchFieldException e) {
-					throw new RuntimeException(e);
-				}
-				List<Member> list = new ArrayList<Member>();
-				clsMap.put(s,list);
-				list.add(field);
-			}
-			for( Method method : cls.getMethods() ) {
-				String s = method.getName();
-				List<Member> list = clsMap.get(s);
-				if( list == null || !(list.get(0) instanceof Method) ) {
-					list = new ArrayList<Member>();
-					clsMap.put(s,list);
-				}
-				list.add(method);
-			}
-			if( privateAccess ) {
-				for( Method method : cls.getDeclaredMethods() ) {
-					String s = method.getName();
-					List<Member> list = clsMap.get(s);
-					if( list == null ) {
-						list = new ArrayList<Member>();
-						clsMap.put(s,list);
-					} else if( !(list.get(0) instanceof Method) )
-						continue;
-					if( !list.contains(method) ) {
-						list.add(method);
-					}
-				}
-				for( Field field : cls.getDeclaredFields() ) {
-					String s = field.getName();
-					List<Member> list = clsMap.get(s);
-					if( list == null ) {
-						list = new ArrayList<Member>();
-						clsMap.put(s,list);
-						list.add(field);
-					}
-				}
-			}
-			for( List<Member> members : clsMap.values() ) {
-				for( Member m : members ) {
-					if( m instanceof AccessibleObject )
-						((AccessibleObject)m).setAccessible(true);
-				}
-			}
-			memberMap.put(cls,clsMap);
-		}
-		List<Member> rtn = clsMap.get(name);
-		if( rtn==null )
-			rtn = Collections.emptyList();
-		return rtn;
-	}
-
-	private static synchronized List<Member> getStaticMembers(Class cls,String name) {
-		List<Member> staticMembers = new ArrayList<Member>();
-		for( Member m : getMembers(cls,name) ) {
-			if( Modifier.isStatic(m.getModifiers()) )
-				staticMembers.add(m);
-		}
-		return staticMembers;
-	}
-
-	static final class Static implements Member {
-		final Class cls;
-
-		Static(Class cls) {
-			this.cls = cls;
-		}
-
-		@Override public String toString() {
-			return cls.toString();
-		}
-
-		@Override public Class<?> getDeclaringClass() {
-			return cls.getDeclaringClass();
-		}
-
-		@Override public String getName() {
-			return cls.getName();
-		}
-
-		@Override public int getModifiers() {
-			return cls.getModifiers();
-		}
-
-		@Override public boolean isSynthetic() {
-			return cls.isSynthetic();
-		}
-	}
-
-	public static Static getClass(LuanState luan,String name) throws LuanException {
-		Class cls;
-		try {
-			cls = Class.forName(name);
-		} catch(ClassNotFoundException e) {
-			try {
-				cls = Thread.currentThread().getContextClassLoader().loadClass(name);
-			} catch(ClassNotFoundException e2) {
-				return null;
-			}
-		}
-		return new Static(cls);
-	}
-/*
-	public static void importClass(LuanState luan,String name) throws LuanException {
-		luan.currentEnvironment().put( name.substring(name.lastIndexOf('.')+1), getClass(luan,name) );
-	}
-*/
-	static class AmbiguousJavaFunction extends LuanFunction {
-		private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>();
-
-		AmbiguousJavaFunction(List<LuanJavaFunction> fns) {
-			for( LuanJavaFunction fn : fns ) {
-				Integer n = fn.getParameterTypes().length;
-				List<LuanJavaFunction> list = fnMap.get(n);
-				if( list==null ) {
-					list = new ArrayList<LuanJavaFunction>();
-					fnMap.put(n,list);
-				}
-				list.add(fn);
-			}
-		}
-
-		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-			for( LuanJavaFunction fn : fnMap.get(args.length) ) {
-				try {
-					return fn.rawCall(luan,args);
-				} catch(IllegalArgumentException e) {}
-			}
-			throw luan.exception("no method matched args");
-		}
-	}
-
-	private static class InstanceOf {
-		private final Object obj;
-
-		InstanceOf(Object obj) {
-			this.obj = obj;
-		}
-
-		public boolean instanceOf(Static st) {
-			return st.cls.isInstance(obj);
-		}
-	}
-	private static final Method instanceOf;
-	static {
-		try {
-			instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class);
-			instanceOf.setAccessible(true);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-
-	private static class AssertClass {
-		private final Class cls;
-
-		AssertClass(Class cls) {
-			this.cls = cls;
-		}
-
-		public Object assertClass(LuanState luan,Object v) throws LuanException {
-			if( !cls.isInstance(v) ) {
-				String got = v.getClass().getSimpleName();
-				String expected = cls.getSimpleName();
-				throw luan.exception("bad argument #1 ("+expected+" expected, got "+got+")");
-			}
-			return v;
-		}
-	}
-	private static final Method assertClass;
-	static {
-		try {
-			assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class);
-			assertClass.setAccessible(true);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-
-	public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException {
-		return Proxy.newProxyInstance(
-			st.cls.getClassLoader(),
-			new Class[]{st.cls},
-			new InvocationHandler() {
-				public Object invoke(Object proxy,Method method, Object[] args)
-					throws Throwable
-				{
-					if( args==null )
-						args = new Object[0];
-					String name = method.getName();
-					Object fnObj = t.get(name);
-					if( fnObj==null && base!=null )
-						return method.invoke(base,args);
-					LuanFunction fn = luan.checkFunction(fnObj);
-					return Luan.first(luan.call(fn,name,args));
-				}
-			}
-		);
-	}
-}
--- a/src/luan/modules/MathLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package luan.modules;
-
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class MathLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "abs", Double.TYPE );
-				add( module, "acos", Double.TYPE );
-				add( module, "asin", Double.TYPE );
-				add( module, "atan", Double.TYPE );
-				add( module, "atan2", Double.TYPE, Double.TYPE );
-				add( module, "ceil", Double.TYPE );
-				add( module, "cos", Double.TYPE );
-				add( module, "cosh", Double.TYPE );
-				add( module, "deg", Double.TYPE );
-				add( module, "exp", Double.TYPE );
-				add( module, "floor", Double.TYPE );
-				add( module, "log", Double.TYPE );
-				add( module, "min", Double.TYPE, new double[0].getClass() );
-				add( module, "max", Double.TYPE, new double[0].getClass() );
-				add( module, "modf", Double.TYPE );
-				module.put("pi",Math.PI);
-				add( module, "pow", Double.TYPE, Double.TYPE );
-				add( module, "rad", Double.TYPE );
-				add( module, "random" );
-				add( module, "sin", Double.TYPE );
-				add( module, "sinh", Double.TYPE );
-				add( module, "sqrt", Double.TYPE );
-				add( module, "tan", Double.TYPE );
-				add( module, "tanh", Double.TYPE );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(MathLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static double abs(double x) {
-		return Math.abs(x);
-	}
-
-	public static double acos(double x) {
-		return Math.acos(x);
-	}
-
-	public static double asin(double x) {
-		return Math.asin(x);
-	}
-
-	public static double atan(double x) {
-		return Math.atan(x);
-	}
-
-	public static double atan2(double y,double x) {
-		return Math.atan2(y,x);
-	}
-
-	public static double ceil(double x) {
-		return Math.ceil(x);
-	}
-
-	public static double cos(double x) {
-		return Math.cos(x);
-	}
-
-	public static double cosh(double x) {
-		return Math.cosh(x);
-	}
-
-	public static double deg(double x) {
-		return Math.toDegrees(x);
-	}
-
-	public static double exp(double x) {
-		return Math.exp(x);
-	}
-
-	public static double floor(double x) {
-		return Math.floor(x);
-	}
-
-	public static double log(double x) {
-		return Math.log(x);
-	}
-
-	public static double min(double x,double... a) {
-		for( double d : a ) {
-			if( x > d )
-				x = d;
-		}
-		return x;
-	}
-
-	public static double max(double x,double... a) {
-		for( double d : a ) {
-			if( x < d )
-				x = d;
-		}
-		return x;
-	}
-
-	public static double[] modf(double x) {
-		double i = (int)x;
-		return new double[]{i,x-i};
-	}
-
-	public static double pow(double x,double y) {
-		return Math.pow(x,y);
-	}
-
-	public static double rad(double x) {
-		return Math.toRadians(x);
-	}
-
-	public static double random() {
-		return Math.random();
-	}
-
-	public static double sin(double x) {
-		return Math.sin(x);
-	}
-
-	public static double sinh(double x) {
-		return Math.sinh(x);
-	}
-
-	public static double sqrt(double x) {
-		return Math.sqrt(x);
-	}
-
-	public static double tan(double x) {
-		return Math.tan(x);
-	}
-
-	public static double tanh(double x) {
-		return Math.tanh(x);
-	}
-
-}
--- a/src/luan/modules/PackageLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-package luan.modules;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanElement;
-import luan.LuanException;
-
-
-public final class PackageLuan {
-
-	private static final String jpath = "luan.modules.?Luan.LOADER";
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			module.put("loaded",luan.loaded());
-			module.put("preload",luan.preload());
-			module.put("path","?.luan;java:luan/modules/?.luan");
-			module.put("jpath",jpath);
-			try {
-				add( module, "require", LuanState.class, String.class );
-				add( module, "load_lib", String.class );
-				add( module, "search_path", String.class, String.class );
-				add( module, "search", LuanState.class, String.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			LuanTable searchers = luan.searchers();
-			searchers.add(preloadSearcher);
-			searchers.add(fileSearcher);
-			searchers.add(javaSearcher);
-			module.put("searchers",searchers);
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(PackageLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static Object require(LuanState luan,String modName) throws LuanException {
-		LuanTable loaded = luan.loaded();
-		Object mod = loaded.get(modName);
-		if( mod == null ) {
-			Object[] a = search(luan,modName);
-			if( a == null )
-				throw luan.exception( "module '"+modName+"' not found" );
-			LuanFunction loader = (LuanFunction)a[0];
-			a[0] = modName;
-			mod = Luan.first(luan.call(loader,"<require \""+modName+"\">",a));
-			if( mod != null ) {
-				loaded.put(modName,mod);
-			} else {
-				mod = loaded.get(modName);
-				if( mod==null )
-					loaded.put(modName,true);
-			}
-		}
-		return mod;
-	}
-
-	public static Object[] search(LuanState luan,String modName) throws LuanException {
-		List<Object> list = null;
-		LuanTable searchers = (LuanTable)luan.get("Package.searchers");
-		if( searchers == null ) {
-			list = Collections.<Object>singletonList(javaSearcher);
-		} else {
-			list = searchers.asList();
-		}
-		for( Object s : list ) {
-			LuanFunction searcher = (LuanFunction)s;
-			Object[] a = Luan.array(luan.call(searcher,"<searcher>",new Object[]{modName}));
-			if( a.length >= 1 && a[0] instanceof LuanFunction )
-				return a;
-		}
-		return null;
-	}
-
-	public static final LuanFunction preloadSearcher = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			String modName = (String)args[0];
-			return luan.preload().get(modName);
-		}
-	};
-
-	public static String search_path(String name,String path) {
-		name = name.replace('.','/');
-		for( String s : path.split(";") ) {
-			String file = s.replaceAll("\\?",name);
-			if( Utils.exists(file) )
-				return file;
-		}
-		return null;
-	}
-
-	public static final LuanFunction fileLoader = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-			String fileName = (String)args[1];
-			LuanFunction fn = BasicLuan.load_file(luan,fileName);
-			return fn.call(luan,args);
-		}
-	};
-
-	public static final LuanFunction fileSearcher = new LuanFunction() {
-		@Override public Object[] call(LuanState luan,Object[] args) {
-			String modName = (String)args[0];
-			String path = (String)luan.get("Package.path");
-			if( path==null )
-				return LuanFunction.NOTHING;
-			String file = search_path(modName,path);
-			return file==null ? LuanFunction.NOTHING : new Object[]{fileLoader,file};
-		}
-	};
-
-
-	public static final LuanFunction javaLoader = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-			try {
-				String objName = (String)args[1];
-				LuanFunction fn = load_lib(objName);
-				return fn.call(luan,args);
-			} catch(ClassNotFoundException e) {
-				throw new RuntimeException(e);
-			} catch(NoSuchFieldException e) {
-				throw new RuntimeException(e);
-			} catch(IllegalAccessException e) {
-				throw new RuntimeException(e);
-			}
-		}
-	};
-
-	public static final LuanFunction javaSearcher = new LuanFunction() {
-		@Override public Object[] call(LuanState luan,Object[] args) {
-			String modName = (String)args[0];
-			String path = (String)luan.get("Package.jpath");
-			if( path==null )
-				path = jpath;
-			for( String s : path.split(";") ) {
-				String objName = s.replaceAll("\\?",modName);
-				try {
-					load_lib(objName);  // throws exception if not found
-					return new Object[]{javaLoader,objName};
-				} catch(ClassNotFoundException e) {
-				} catch(NoSuchFieldException e) {
-				} catch(IllegalAccessException e) {
-				}
-			}
-			return LuanFunction.NOTHING;
-		}
-	};
-
-
-	public static LuanFunction load_lib(String path)
-		throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException
-	{
-		int i = path.lastIndexOf('.');
-		String clsPath = path.substring(0,i);
-		String fld = path.substring(i+1);
-		Class cls = Class.forName(clsPath);
-		return (LuanFunction)cls.getField(fld).get(null);
-	}
-
-}
--- a/src/luan/modules/PickleClient.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-package luan.modules;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanException;
-import luan.LuanTable;
-import luan.LuanJavaFunction;
-import luan.LuanFunction;
-
-
-public final class PickleClient {
-
-	private final PickleCon con;
-	private final LuanFunction _reversed_pickle;
-
-	PickleClient(LuanState luan,DataInputStream in,DataOutputStream out) {
-		this(new PickleCon(luan,in,out));
-	}
-
-	PickleClient(PickleCon con) {
-		this.con = con;
-		try {
-			this._reversed_pickle = new LuanJavaFunction(
-				PickleClient.class.getMethod( "_reversed_pickle" ), this
-			);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public Object _reversed_pickle() throws LuanException, IOException {
-		new PickleServer(con).run();
-		return con.read();
-	}
-
-	public Object call(Object... args) throws LuanException, IOException {
-		con.write(args);
-		Object[] result;
-		con.ioModule.put("_reversed_pickle",_reversed_pickle);
-		try {
-			result = Luan.array(con.read());
-		} finally {
-			con.ioModule.put("_reversed_pickle",null);
-		}
-		boolean ok = (boolean)result[0];
-		if( ok ) {
-			Object[] rtn = new Object[result.length-1];
-			System.arraycopy(result,1,rtn,0,rtn.length);
-			return rtn;
-		} else {
-			String msg = (String)result[1];
-			String src = (String)result[2];
-			throw con.luan.exception(
-				msg + "\n"
-				+ "in:\n"
-				+ "------------------\n"
-				+ formatCode(src) + "\n"
-				+ "------------------\n"
-			);
-		}
-	}
-
-	LuanTable table() {
-		LuanTable tbl = new LuanTable();
-		try {
-			tbl.put( "pickle", new LuanJavaFunction(
-				PickleCon.class.getMethod( "pickle", Object.class ), con
-			) );
-			tbl.put( "call", new LuanJavaFunction(
-				PickleClient.class.getMethod( "call", new Object[0].getClass() ), this
-			) );
-			tbl.put( "close", new LuanJavaFunction(
-				PickleCon.class.getMethod( "close" ), con
-			) );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		return tbl;
-	}
-
-
-	public static String formatCode(String s) {
-		StringBuilder buf = new StringBuilder();
-		int line = 1;
-		int i = 0;
-		int i2 = 0;
-		while( i2 != -1 ) {
-			buf.append( line++ );
-			buf.append( '\t' );
-			i2 = s.indexOf('\n',i);
-			String lineStr = i2 == -1 ? s.substring(i) : s.substring(i,i2+1);
-			int j;
-			for( j=0; j<lineStr.length() && lineStr.charAt(j)=='\t'; j++ ) {
-				buf.append( "    " );
-			}
-			buf.append( lineStr.substring(j) );
-			i = i2 + 1;
-		}
-		return buf.toString();
-	}
-
-
-}
--- a/src/luan/modules/PickleCon.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-package luan.modules;
-
-import java.io.OutputStream;
-import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.Set;
-import java.util.IdentityHashMap;
-import java.util.Collections;
-import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import luan.Luan;
-import luan.LuanTable;
-import luan.LuanState;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class PickleCon {
-	final LuanState luan;
-	private final DataInputStream in;
-	private final LuanFunction _read_binary;
-	final LuanTable ioModule;
-	private final DataOutputStream out;
-	private final List<byte[]> binaries = new ArrayList<byte[]>();
-	String src;
-	private final LuanTable env = new LuanTable();
-
-	PickleCon(LuanState luan,DataInputStream in,DataOutputStream out) {
-		this.in = in;
-		this.luan = luan;
-		try {
-			this._read_binary = new LuanJavaFunction(
-				PickleCon.class.getMethod( "_read_binary", Integer.TYPE ), this
-			);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		this.ioModule = (LuanTable)luan.loaded().get("Io");
-
-		this.out = out;
-	}
-
-	public byte[] _read_binary(int size) throws IOException, LuanException {
-		byte[] a = new byte[size];
-		int i = 0;
-		while( i < size ) {
-			int n = in.read(a,i,size-i);
-			if( n == -1 )
-				throw luan.exception( "end of stream" );
-			i += n;
-		}
-		return a;
-	}
-
-	public Object read() throws IOException, LuanException {
-		ioModule.put("_read_binary",_read_binary);
-		try {
-			src = in.readUTF();
-			LuanFunction fn = BasicLuan.load(luan,src,"pickle-reader",env,false);
-			return luan.call(fn);
-		} finally {
-			ioModule.put("_binaries",null);
-			ioModule.put("_read_binary",null);
-		}
-	}
-
-	public String pickle(Object obj) throws LuanException {
-		if( obj == null )
-			return "nil";
-		if( obj instanceof Boolean )
-			return Luan.toString((Boolean)obj);
-		if( obj instanceof Number )
-			return Luan.toString((Number)obj);
-		if( obj instanceof String )
-			return "\"" + Luan.stringEncode((String)obj) + "\"";
-		if( obj instanceof LuanTable )
-			return pickle( (LuanTable)obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
-		if( obj instanceof byte[] ) {
-			byte[] a = (byte[])obj;
-			binaries.add(a);
-			return "Io._binaries[" + binaries.size() + "]";
-		}
-		throw luan.exception( "invalid type: " + obj.getClass() );
-	}
-
-	private String pickle(Object obj,Set<LuanTable> set) throws LuanException {
-		return obj instanceof LuanTable ? pickle((LuanTable)obj,set) : pickle(obj);
-	}
-
-	private String pickle(LuanTable tbl,Set<LuanTable> set) throws LuanException {
-		if( !set.add(tbl) ) {
-			throw luan.exception( "circular reference in table" );
-		}
-		StringBuilder sb = new StringBuilder();
-		sb.append( "{" );
-		for( Map.Entry<Object,Object> entry : tbl ) {
-			sb.append( "[" );
-			sb.append( pickle(entry.getKey(),set) );
-			sb.append( "]=" );
-			sb.append( pickle(entry.getValue(),set) );
-			sb.append( ", " );
-		}
-		sb.append( "}" );
-		return sb.toString();
-	}
-
-	public void write(Object... args) throws LuanException, IOException {
-		StringBuilder sb = new StringBuilder();
-		if( !binaries.isEmpty() ) {
-			sb.append( "Io._binaries = {}\n" );
-			for( byte[] a : binaries ) {
-				sb.append( "Io._binaries[#Io._binaries+1] = Io._read_binary(" + a.length + ")\n" );
-			}
-		}
-		for( Object obj : args ) {
-			sb.append( luan.toString(obj) );
-		}
-		out.writeUTF( sb.toString() );
-		for( byte[] a : binaries ) {
-			out.write(a);
-		}
-		out.flush();
-		binaries.clear();
-	}
-
-	public void close() throws IOException {
-		in.close();
-		out.close();
-	}
-}
--- a/src/luan/modules/PickleServer.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-package luan.modules;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.EOFException;
-import java.util.List;
-import java.util.ArrayList;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class PickleServer {
-
-	private final PickleCon con;
-	private boolean isRunning;
-
-	PickleServer(LuanState luan,DataInputStream in,DataOutputStream out) {
-		this(new PickleCon(luan,in,out));
-	}
-
-	PickleServer(PickleCon con) {
-		this.con = con;
-	}
-
-	void next() throws IOException {
-		try {
-			List<String> list = new ArrayList<String>();
-			try {
-				Object[] result = Luan.array(con.read());
-				list.add( "return true" );
-				for( Object obj : result ) {
-					list.add( ", " );
-					list.add( con.pickle(obj) );
-				}
-			} catch(LuanException e) {
-//				System.out.println(e);
-//e.printStackTrace();
-				list.add( "return false, " );
-				list.add( con.pickle(e.getMessage()) );
-				list.add( ", " );
-				list.add( con.pickle(con.src) );
-			}
-			list.add( "\n" );
-			con.write( list.toArray() );
-		} catch(LuanException e2) {
-			throw new RuntimeException(e2);
-		}
-	}
-
-	public void run() {
-		LuanTable ioModule = con.ioModule;
-		Object old_reverse_pickle = ioModule.get("reverse_pickle");
-		Object old_unreverse_pickle = ioModule.get("_unreverse_pickle");
-		try {
-			try {
-				ioModule.put("reverse_pickle", new LuanJavaFunction(
-					PickleServer.class.getMethod( "reverse_pickle", LuanFunction.class ), this
-				) );
-				ioModule.put("_unreverse_pickle", new LuanJavaFunction(
-					PickleServer.class.getMethod( "_unreverse_pickle" ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			isRunning = true;
-			try {
-				while( isRunning ) {
-					next();
-				}
-			} catch(EOFException e) {
-				// done
-			} catch(IOException e) {
-				e.printStackTrace();
-			}
-			if( isRunning ) {
-				try {
-					con.close();
-				} catch(IOException e) {
-					throw new RuntimeException(e);
-				}
-			}
-		} finally {
-			ioModule.put("reverse_pickle",old_reverse_pickle);
-			ioModule.put("_unreverse_pickle",old_unreverse_pickle);
-		}
-	}
-
-	public void reverse_pickle(LuanFunction fn) throws IOException, LuanException {
-		try {
-			con.write( "return Io._reversed_pickle()\n" );
-		} catch(LuanException e) {
-			throw new RuntimeException(e);
-		}
-		PickleClient pc = new PickleClient(con);
-		try {
-			con.luan.call(fn,new Object[]{pc.table()});
-		} finally {
-			try {
-				pc.call( "Io._unreverse_pickle()\n" );
-			} catch(LuanException e) {
-				throw new RuntimeException(e);
-			}
-		}
-	}
-
-	public void _unreverse_pickle() {
-		isRunning = false;
-	}
-
-}
--- a/src/luan/modules/Reactionary.luan	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
--- Reactionary
-
-host = "localhost"
-port = 9101
-
-function put_site(domain,password,dir)
-	local pc = Io.Socket(host,port).Pickle_client()
-	local pickle = pc.pickle
-	pc.call %>
-		Reactionary.do_put_site(<%=pickle(domain)%>,<%=pickle(password)%>,<%=pickle(dir)%>)
-	<%
-	pc.close()
-end
-
-function delete_site(domain,password)
-	local pc = Io.Socket(host,port).Pickle_client()
-	local pickle = pc.pickle
-	pc.call %>
-		Reactionary.do_delete_site(<%=pickle(domain)%>,<%=pickle(password)%>)
-	<%
-	pc.close()
-end
--- a/src/luan/modules/StringLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-package luan.modules;
-
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanElement;
-import luan.LuanException;
-import luan.MetatableGetter;
-
-
-public final class StringLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			luan.addMetatableGetter(mg);
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "to_binary", String.class );
-				add( module, "to_integers", String.class );
-				add( module, "from_integers", new int[0].getClass() );
-				add( module, "find", String.class, String.class, Integer.class, Boolean.class );
-				add( module, "format", String.class, new Object[0].getClass() );
-				add( module, "gmatch", String.class, String.class );
-				add( module, "gsub", LuanState.class, String.class, String.class, Object.class, Integer.class );
-				add( module, "len", String.class );
-				add( module, "lower", String.class );
-				add( module, "match", String.class, String.class, Integer.class );
-				add( module, "rep", String.class, Integer.TYPE, String.class );
-				add( module, "reverse", String.class );
-				add( module, "sub", String.class, Integer.TYPE, Integer.class );
-				add( module, "upper", String.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(StringLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-	private static final LuanTable mt = new LuanTable();
-	static {
-		try {
-			add( mt, "__index", LuanState.class, String.class, Object.class );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	private static final MetatableGetter mg = new MetatableGetter() {
-		public LuanTable getMetatable(Object obj) {
-			return obj instanceof String ? mt : null;
-		}
-	};
-
-	public static Object __index(LuanState luan,final String s,Object key) throws LuanException {
-		LuanTable mod = (LuanTable)luan.loaded().get("String");
-		if( mod!=null ) {
-			Object obj = mod.get(key);
-			if( obj instanceof LuanFunction ) {
-				final LuanFunction fn = (LuanFunction)obj;
-				return new LuanFunction() {
-					@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-						Object[] a = new Object[args.length+1];
-						a[0] = s;
-						System.arraycopy(args,0,a,1,args.length);
-						return fn.call(luan,a);
-					}
-				};
-			}
-		}
-		if( luan.loaded().get("Java") != null )
-			return JavaLuan.__index(luan,s,key);
-		return null;
-	}
-
-	static int start(String s,int i) {
-		return i==0 ? 0 : i > 0 ? i - 1 : s.length() + i;
-	}
-
-	static int start(String s,Integer i,int dflt) {
-		return i==null ? dflt : start(s,i);
-	}
-
-	static int end(String s,int i) {
-		return i==0 ? 0 : i > 0 ? i : s.length() + i + 1;
-	}
-
-	static int end(String s,Integer i,int dflt) {
-		return i==null ? dflt : end(s,i);
-	}
-
-	public static byte[] to_binary(String s) {
-		return s.getBytes();
-	}
-
-	public static int[] to_integers(String s) {
-		char[] a = s.toCharArray();
-		int[] chars = new int[a.length];
-		for( int i=0; i<a.length; i++ ) {
-			chars[i] = a[i];
-		}
-		return chars;
-	}
-
-	public static String from_integers(int... chars) {
-		char[] a = new char[chars.length];
-		for( int i=0; i<chars.length; i++ ) {
-			a[i] = (char)chars[i];
-		}
-		return new String(a);
-	}
-
-	public static int len(String s) {
-		return s.length();
-	}
-
-	public static String lower(String s) {
-		return s.toLowerCase();
-	}
-
-	public static String upper(String s) {
-		return s.toUpperCase();
-	}
-
-	public static String reverse(String s) {
-		return new StringBuilder(s).reverse().toString();
-	}
-
-	public static String rep(String s,int n,String sep) {
-		if( n < 1 )
-			return "";
-		StringBuilder buf = new StringBuilder(s);
-		while( --n > 0 ) {
-			if( sep != null )
-				buf.append(sep);
-			buf.append(s);
-		}
-		return buf.toString();
-	}
-
-	public static String sub(String s,int i,Integer j) {
-		int start = start(s,i);
-		int end = end(s,j,s.length());
-		return s.substring(start,end);
-	}
-
-	public static int[] find(String s,String pattern,Integer init,Boolean plain) {
-		int start = start(s,init,0);
-		if( Boolean.TRUE.equals(plain) ) {
-			int i = s.indexOf(pattern,start);
-			return i == -1 ? null : new int[]{i+1,i+pattern.length()};
-		}
-		Matcher m = Pattern.compile(pattern).matcher(s);
-		return m.find(start) ? new int[]{m.start()+1,m.end()} : null;
-	}
-
-	public static String[] match(String s,String pattern,Integer init) {
-		int start = start(s,init,0);
-		Matcher m = Pattern.compile(pattern).matcher(s);
-		if( !m.find(start) )
-			return null;
-		final int n = m.groupCount();
-		if( n == 0 )
-			return new String[]{m.group()};
-		String[] rtn = new String[n];
-		for( int i=0; i<n; i++ ) {
-			rtn[i] = m.group(i+1);
-		}
-		return rtn;
-	}
-
-	public static LuanFunction gmatch(String s,String pattern) {
-		final Matcher m = Pattern.compile(pattern).matcher(s);
-		return new LuanFunction() {
-			@Override public Object call(LuanState luan,Object[] args) {
-				if( !m.find() )
-					return null;
-				final int n = m.groupCount();
-				if( n == 0 )
-					return m.group();
-				String[] rtn = new String[n];
-				for( int i=0; i<n; i++ ) {
-					rtn[i] = m.group(i+1);
-				}
-				return rtn;
-			}
-		};
-	}
-
-	public static Object[] gsub(LuanState luan,String s,String pattern,Object repl,Integer n) throws LuanException {
-		int max = n==null ? Integer.MAX_VALUE : n;
-		final Matcher m = Pattern.compile(pattern).matcher(s);
-		if( repl instanceof String ) {
-			String replacement = (String)repl;
-			int i = 0;
-			StringBuffer sb = new StringBuffer();
-			while( i<max && m.find() ) {
-				m.appendReplacement(sb,replacement);
-				i++;
-			}
-			m.appendTail(sb);
-			return new Object[]{ sb.toString(), i };
-		}
-		if( repl instanceof LuanTable ) {
-			LuanTable t = (LuanTable)repl;
-			int i = 0;
-			StringBuffer sb = new StringBuffer();
-			while( i<max && m.find() ) {
-				String match = m.groupCount()==0 ? m.group() : m.group(1);
-				Object val = t.get(match);
-				if( Luan.toBoolean(val) ) {
-					String replacement = Luan.asString(val);
-					if( replacement==null )
-						throw luan.exception( "invalid replacement value (a "+Luan.type(val)+")" );
-					m.appendReplacement(sb,replacement);
-				}
-				i++;
-			}
-			m.appendTail(sb);
-			return new Object[]{ sb.toString(), i };
-		}
-		if( repl instanceof LuanFunction ) {
-			LuanFunction fn = (LuanFunction)repl;
-			int i = 0;
-			StringBuffer sb = new StringBuffer();
-			while( i<max && m.find() ) {
-				Object[] args;
-				final int count = m.groupCount();
-				if( count == 0 ) {
-					args = new Object[]{m.group()};
-				} else {
-					args = new Object[count];
-					for( int j=0; j<count; j++ ) {
-						args[j] = m.group(j);
-					}
-				}
-				Object val = Luan.first( luan.call(fn,"repl-arg",args) );
-				if( Luan.toBoolean(val) ) {
-					String replacement = Luan.asString(val);
-					if( replacement==null )
-						throw luan.exception( "invalid replacement value (a "+Luan.type(val)+")" );
-					m.appendReplacement(sb,replacement);
-				}
-				i++;
-			}
-			m.appendTail(sb);
-			return new Object[]{ sb.toString(), i };
-		}
-		throw luan.exception( "bad argument #3 to 'gsub' (string/function/table expected)" );
-	}
-
-	// note - String.format() is too stupid to convert between ints and floats.
-	public static String format(String format,Object... args) {
-		return String.format(format,args);
-	}
-
-}
--- a/src/luan/modules/TableLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-package luan.modules;
-
-import java.util.Comparator;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Arrays;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanElement;
-import luan.LuanException;
-import luan.LuanRuntimeException;
-
-
-public final class TableLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "concat", LuanState.class, LuanTable.class, String.class, Integer.class, Integer.class );
-				add( module, "insert", LuanState.class, LuanTable.class, Integer.TYPE, Object.class );
-				add( module, "pack", new Object[0].getClass() );
-				add( module, "remove", LuanState.class, LuanTable.class, Integer.TYPE );
-				add( module, "sort", LuanState.class, LuanTable.class, LuanFunction.class );
-				add( module, "sub_list", LuanTable.class, Integer.TYPE, Integer.TYPE );
-				add( module, "unpack", LuanTable.class, Integer.class, Integer.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(TableLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static String concat(LuanState luan,LuanTable list,String sep,Integer i,Integer j) throws LuanException {
-		int first = i==null ? 1 : i;
-		int last = j==null ? list.length() : j;
-		StringBuilder buf = new StringBuilder();
-		for( int k=first; k<=last; k++ ) {
-			Object val = list.get(k);
-			if( val==null )
-				break;
-			if( sep!=null && k > first )
-				buf.append(sep);
-			String s = Luan.asString(val);
-			if( s==null )
-				throw luan.exception( "invalid value ("+Luan.type(val)+") at index "+k+" in table for 'concat'" );
-			buf.append(val);
-		}
-		return buf.toString();
-	}
-
-	public static void insert(LuanState luan,LuanTable list,int pos,Object value) throws LuanException {
-		try {
-			list.insert(pos,value);
-		} catch(IndexOutOfBoundsException e) {
-			throw luan.exception(e);
-		}
-	}
-
-	public static Object remove(LuanState luan,LuanTable list,int pos) throws LuanException {
-		try {
-			return list.remove(pos);
-		} catch(IndexOutOfBoundsException e) {
-			throw luan.exception(e);
-		}
-	}
-
-	private static interface LessThan {
-		public boolean isLessThan(Object o1,Object o2);
-	}
-
-	public static void sort(final LuanState luan,LuanTable list,final LuanFunction comp) throws LuanException {
-		final LessThan lt;
-		if( comp==null ) {
-			lt = new LessThan() {
-				public boolean isLessThan(Object o1,Object o2) {
-					try {
-						return luan.isLessThan(o1,o2);
-					} catch(LuanException e) {
-						throw new LuanRuntimeException(e);
-					}
-				}
-			};
-		} else {
-			lt = new LessThan() {
-				public boolean isLessThan(Object o1,Object o2) {
-					try {
-						return Luan.toBoolean(Luan.first(luan.call(comp,"comp-arg",new Object[]{o1,o2})));
-					} catch(LuanException e) {
-						throw new LuanRuntimeException(e);
-					}
-				}
-			};
-		}
-		try {
-			list.sort( new Comparator<Object>() {
-				public int compare(Object o1,Object o2) {
-					return lt.isLessThan(o1,o2) ? -1 : lt.isLessThan(o2,o1) ? 1 : 0;
-				}
-			} );
-		} catch(LuanRuntimeException e) {
-			throw (LuanException)e.getCause();
-		}
-	}
-
-	public static LuanTable pack(Object... args) {
-		return new LuanTable(new ArrayList<Object>(Arrays.asList(args)));
-	}
-
-	public static Object[] unpack(LuanTable tbl,Integer iFrom,Integer iTo) {
-		int from = iFrom!=null ? iFrom : 1;
-		int to = iTo!=null ? iTo : tbl.length();
-		List<Object> list = new ArrayList<Object>();
-		for( int i=from; i<=to; i++ ) {
-			list.add( tbl.get(i) );
-		}
-		return list.toArray();
-	}
-
-	public static LuanTable sub_list(LuanTable list,int from,int to) {
-		return list.subList(from,to);
-	}
-
-}
--- a/src/luan/modules/ThreadLuan.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-package luan.modules;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import luan.LuanState;
-import luan.LuanFunction;
-import luan.LuanTable;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-import luan.DeepCloner;
-
-
-public final class ThreadLuan {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "fork", LuanState.class, LuanFunction.class, new Object[0].getClass() );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(ThreadLuan.class.getMethod(method,parameterTypes),null) );
-	}
-
-	private static final Executor exec = Executors.newCachedThreadPool();
-
-	public static void fork(LuanState luan,LuanFunction fn,Object... args) {
-		DeepCloner cloner = new DeepCloner();
-		final LuanState newLuan = cloner.deepClone(luan);
-		final LuanFunction newFn = cloner.get(fn);
-		final Object[] newArgs = cloner.deepClone(args);
-		exec.execute(new Runnable(){public void run() {
-			try {
-				newLuan.call(newFn,"<forked>",newArgs);
-			} catch(LuanException e) {
-				e.printStackTrace();
-			}
-		}});
-	}
-
-}
--- a/src/luan/modules/Utils.java	Sun Jun 22 05:20:30 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-package luan.modules;
-
-import java.io.Reader;
-import java.io.IOException;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.File;
-import java.net.URL;
-import java.net.MalformedURLException;
-import luan.LuanState;
-import luan.LuanException;
-
-
-public final class Utils {
-	private Utils() {}  // never
-
-	static final int bufSize = 8192;
-
-	public static void checkNotNull(LuanState luan,Object v,String expected) throws LuanException {
-		if( v == null )
-			throw luan.exception("bad argument #1 ("+expected+" expected, got nil)");
-	}
-
-	public static String readAll(Reader in)
-		throws IOException
-	{
-		char[] a = new char[bufSize];
-		StringBuilder buf = new StringBuilder();
-		int n;
-		while( (n=in.read(a)) != -1 ) {
-			buf.append(a,0,n);
-		}
-		return buf.toString();
-	}
-
-	public static void copyAll(InputStream in,OutputStream out)
-		throws IOException
-	{
-		byte[] a = new byte[bufSize];
-		int n;
-		while( (n=in.read(a)) != -1 ) {
-			out.write(a,0,n);
-		}
-	}
-
-	public static byte[] readAll(InputStream in)
-		throws IOException
-	{
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		copyAll(in,out);
-		return out.toByteArray();
-	}
-
-	public static boolean exists(File file) {
-		try {
-			return file.exists() && file.getName().equals(file.getCanonicalFile().getName());
-		} catch(IOException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public static boolean isFile(String path) {
-		return exists(new File(path));
-	}
-
-	public static String toUrl(String path) {
-		if( path.indexOf(':') == -1 )
-			return null;
-		if( path.startsWith("java:") ) {
-			path = path.substring(5);
-			URL url = ClassLoader.getSystemResource(path);
-			return url==null ? null : url.toString();
-		}
-		try {
-			new URL(path);
-			return path;
-		} catch(MalformedURLException e) {}
-		return null;
-	}
-
-	public static boolean exists(String path) {
-		return isFile(path) || toUrl(path)!=null;
-	}
-}