changeset 168:ebe9db183eb7

rename *Lib.java to *Luan.java git-svn-id: https://luan-java.googlecode.com/svn/trunk@169 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Sun, 22 Jun 2014 04:42:07 +0000
parents 4c0131c2b650
children 9169581dc8fc
files src/luan/Luan.java src/luan/LuanState.java src/luan/modules/BasicLib.java src/luan/modules/BasicLuan.java src/luan/modules/BinaryLib.java src/luan/modules/BinaryLuan.java src/luan/modules/HtmlLib.java src/luan/modules/HtmlLuan.java src/luan/modules/HttpLib.java src/luan/modules/HttpLuan.java src/luan/modules/IoLib.java src/luan/modules/IoLuan.java src/luan/modules/JavaLib.java src/luan/modules/JavaLuan.java src/luan/modules/MathLib.java src/luan/modules/MathLuan.java src/luan/modules/PackageLib.java src/luan/modules/PackageLuan.java src/luan/modules/PickleCon.java src/luan/modules/StringLib.java src/luan/modules/StringLuan.java src/luan/modules/TableLib.java src/luan/modules/TableLuan.java src/luan/modules/ThreadLib.java src/luan/modules/ThreadLuan.java
diffstat 25 files changed, 2318 insertions(+), 2318 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/Luan.java	Sun Jun 22 04:28:32 2014 +0000
+++ b/src/luan/Luan.java	Sun Jun 22 04:42:07 2014 +0000
@@ -1,6 +1,6 @@
 package luan;
 
-import luan.modules.BasicLib;
+import luan.modules.BasicLuan;
 
 
 public final class Luan {
@@ -9,7 +9,7 @@
 	public static void main(String[] args) {
 		LuanState luan = LuanState.newStandard();
 		try {
-			LuanFunction standalone = (LuanFunction)BasicLib.load_file(luan,"java:luan/tools/cmd_line.luan");
+			LuanFunction standalone = (LuanFunction)BasicLuan.load_file(luan,"java:luan/tools/cmd_line.luan");
 			luan.call(standalone,args);
 		} catch(LuanException e) {
 			System.err.println(e.getMessage());
--- a/src/luan/LuanState.java	Sun Jun 22 04:28:32 2014 +0000
+++ b/src/luan/LuanState.java	Sun Jun 22 04:42:07 2014 +0000
@@ -7,8 +7,8 @@
 import java.util.Map;
 import java.util.LinkedHashMap;
 import luan.impl.LuanCompiler;
-import luan.modules.BasicLib;
-import luan.modules.PackageLib;
+import luan.modules.BasicLuan;
+import luan.modules.PackageLuan;
 
 
 public abstract class LuanState implements DeepCloneable<LuanState> {
@@ -124,7 +124,7 @@
 	}
 
 	public final void globalImport(String modName) throws LuanException {
-		Object mod = PackageLib.require(this,modName);
+		Object mod = PackageLuan.require(this,modName);
 		global.put(modName,mod);
 	}
 
@@ -132,7 +132,7 @@
 		try {
 			LuanState luan = LuanCompiler.newLuanState();
 			luan.globalImport("Package");
-			BasicLib.do_file(luan,"java:luan/init.luan");
+			BasicLuan.do_file(luan,"java:luan/init.luan");
 			return luan;
 		} catch(LuanException e) {
 			throw new RuntimeException(e);
@@ -145,7 +145,7 @@
 
 	public final Object eval(String cmd,LuanTable env) {
 		try {
-			LuanFunction fn = BasicLib.load(this,cmd,"eval",env,true);
+			LuanFunction fn = BasicLuan.load(this,cmd,"eval",env,true);
 			return call(fn);
 		} catch(LuanException e) {
 			throw new RuntimeException(e);
--- a/src/luan/modules/BasicLib.java	Sun Jun 22 04:28:32 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 BasicLib {
-
-	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(BasicLib.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(BasicLib.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)) : IoLib.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/src/luan/modules/BasicLuan.java	Sun Jun 22 04:42:07 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]};
+			}
+		};
+	}
+
+}
--- a/src/luan/modules/BinaryLib.java	Sun Jun 22 04:28:32 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 BinaryLib {
-
-	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(BinaryLib.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/src/luan/modules/BinaryLuan.java	Sun Jun 22 04:42:07 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);
+	}
+
+}
--- a/src/luan/modules/HtmlLib.java	Sun Jun 22 04:28:32 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 HtmlLib {
-
-	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(HtmlLib.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/src/luan/modules/HtmlLuan.java	Sun Jun 22 04:42:07 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();
+	}
+
+}
--- a/src/luan/modules/HttpLib.java	Sun Jun 22 04:28:32 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,285 +0,0 @@
-package luan.modules;
-
-import java.io.PrintWriter;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Set;
-import java.util.Arrays;
-import java.util.Enumeration;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanFunction;
-import luan.LuanElement;
-import luan.LuanException;
-import luan.LuanTable;
-import luan.LuanJavaFunction;
-import luan.LuanExitException;
-import luan.DeepCloner;
-
-
-public final class HttpLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			return new LuanTable();  // starts empty
-		}
-	};
-
-	public static void service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName)
-		throws LuanException
-	{
-		LuanFunction fn;
-		synchronized(luan) {
-			Object mod = PackageLib.require(luan,modName);
-			if( !(mod instanceof LuanTable) )
-				throw luan.exception( "module '"+modName+"' must return a table" );
-			LuanTable tbl = (LuanTable)mod;
-			if( Luan.toBoolean( tbl.get("per_session") ) ) {
-				HttpSession session = request.getSession();
-				LuanState sessionLuan  = (LuanState)session.getValue("luan");
-				if( sessionLuan!=null ) {
-					luan = sessionLuan;
-				} else {
-					DeepCloner cloner = new DeepCloner();
-					luan = cloner.deepClone(luan);
-					session.putValue("luan",luan);
-				}
-				tbl = (LuanTable)PackageLib.require(luan,modName);
-				fn = (LuanFunction)tbl.get("page");
-			} else {
-				fn = (LuanFunction)tbl.get("page");
-				if( fn == null )
-					throw luan.exception( "function 'page' is not defined" );
-				DeepCloner cloner = new DeepCloner();
-				luan = cloner.deepClone(luan);
-				fn = cloner.get(fn);
-			}
-		}
-
-		LuanTable module = (LuanTable)luan.loaded().get("Http");
-		if( module == null )
-			throw luan.exception( "module 'Http' not defined" );
-		HttpLib lib = new HttpLib(request,response);
-		try {
-			module.put( "request", lib.requestTable() );
-			module.put( "response", lib.responseTable() );
-/*
-			module.put( "write", new LuanJavaFunction(
-				HttpLib.class.getMethod( "text_write", LuanState.class, new Object[0].getClass() ), lib
-			) );
-*/
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-
-		luan.call(fn,"<http>");
-	}
-
-
-
-	private final HttpServletRequest request;
-	private final HttpServletResponse response;
-//	private PrintWriter writer = null;
-//	private ServletOutputStream sos = null;
-
-	private HttpLib(HttpServletRequest request,HttpServletResponse response) {
-		this.request = request;
-		this.response = response;
-	}
-
-	private LuanTable requestTable() throws NoSuchMethodException {
-		LuanTable req = new LuanTable();
-		req.put("java",request);
-		req.put( "get_attribute", new LuanJavaFunction(HttpServletRequest.class.getMethod("getAttribute",String.class),request) );
-		req.put( "set_attribute", new LuanJavaFunction(HttpServletRequest.class.getMethod("setAttribute",String.class,Object.class),request) );
-		req.put( "get_parameter", new LuanJavaFunction(HttpServletRequest.class.getMethod("getParameter",String.class),request) );
-		req.put( "get_parameter_values", new LuanJavaFunction(HttpServletRequest.class.getMethod("getParameterValues",String.class),request) );
-		req.put( "get_header", new LuanJavaFunction(HttpServletRequest.class.getMethod("getHeader",String.class),request) );
-		add( req, "get_cookie_value", String.class );
-		req.put( "method", new LuanJavaFunction(HttpServletRequest.class.getMethod("getMethod"),request) );
-		req.put( "servlet_path", new LuanJavaFunction(HttpServletRequest.class.getMethod("getServletPath"),request) );
-		req.put( "server_name", new LuanJavaFunction(HttpServletRequest.class.getMethod("getServerName"),request) );
-		add( req, "current_url" );
-		req.put( "remote_address", new LuanJavaFunction(HttpServletRequest.class.getMethod("getRemoteAddr"),request) );
-		add( req, "get_session_attribute", String.class );
-		add( req, "set_session_attribute", String.class, Object.class );
-		return req;
-	}
-
-	private LuanTable responseTable() throws NoSuchMethodException {
-		LuanTable resp = new LuanTable();
-		resp.put("java",response);
-		add( resp, "send_redirect", String.class );
-		add( resp, "send_error", Integer.TYPE, String.class );
-		resp.put( "contains_header", new LuanJavaFunction(HttpServletResponse.class.getMethod("containsHeader",String.class),response) );
-		resp.put( "set_header", new LuanJavaFunction(HttpServletResponse.class.getMethod("setHeader",String.class,String.class),response) );
-		add( resp, "set_cookie", String.class, String.class, Boolean.TYPE, String.class );
-		add( resp, "remove_cookie", String.class, String.class );
-		resp.put( "set_content_type", new LuanJavaFunction(HttpServletResponse.class.getMethod("setContentType",String.class),response) );
-		add( resp, "text_writer" );
-		return resp;
-	}
-
-	private void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(HttpLib.class.getMethod(method,parameterTypes),this) );
-	}
-/*
-	public void text_write(LuanState luan,Object... args) throws LuanException, IOException {
-		if( writer == null )
-			writer = response.getWriter();
-		for( Object obj : args ) {
-			writer.print( luan.toString(obj) );
-		}
-	}
-*/
-	public LuanTable text_writer() throws IOException {
-		return IoLib.textWriter(response.getWriter());
-	}
-
-	public String get_cookie_value(String name) {
-		return getCookieValue(request, name);
-	}
-
-	public String current_url() {
-		return getCurrentURL(request);
-	}
-
-	public void send_redirect(String redirectUrl)
-		throws IOException
-	{
-		response.sendRedirect(redirectUrl);
-		throw new LuanExitException();
-	}
-
-	public void send_error(int code,String text)
-		throws IOException
-	{
-		response.sendError(code, text);
-		throw new LuanExitException();
-	}
-
-	public void set_cookie(String name,String value,boolean isPersistent, String domain) {
-		setCookie(request,response,name,value,isPersistent,domain);
-	}
-
-	public void remove_cookie(String name, String domain) {
-		removeCookie(request,response,name,domain);
-	}
-
-	public Object get_session_attribute(String name) {
-		return request.getSession().getAttribute(name);
-	}
-
-	public void set_session_attribute(String name,Object value) {
-		request.getSession().setAttribute(name,value);
-	}
-
-
-	// static utils
-
-	public static String getQueryString(HttpServletRequest request) {
-		return getQueryString(request,0);
-	}
-
-	public static String getQueryString(HttpServletRequest request,int maxValueLen) {
-		String method = request.getMethod();
-		if( method.equals("GET") )
-			return request.getQueryString();
-		if( !method.equals("POST") && !method.equals("HEAD") )
-			throw new RuntimeException(method);
-		Enumeration en = request.getParameterNames();
-		StringBuilder queryBuf = new StringBuilder();
-		if( !en.hasMoreElements() )
-			return null;
-		do {
-			String param = (String)en.nextElement();
-			String value = request.getParameter(param);
-			if( maxValueLen > 0 ) {
-				int len = value.length();
-				if( len > maxValueLen )
-					value = value.substring(0,maxValueLen) + "..." + (len-maxValueLen);
-			}
-			queryBuf.append(param);
-			queryBuf.append('=');
-			queryBuf.append(value);
-			queryBuf.append('&');
-		} while( en.hasMoreElements() );
-		queryBuf.deleteCharAt(queryBuf.length() - 1);
-		return queryBuf.toString();
-	}
-
-	public static String getCurrentURL(HttpServletRequest request) {
-		return getCurrentURL(request,0);
-	}
-
-	public static String getCurrentURL(HttpServletRequest request,int maxValueLen) {
-//		StringBuffer buf = HttpUtils.getRequestURL(request);
-		StringBuffer buf = request.getRequestURL();
-		String qStr = getQueryString(request,maxValueLen);
-		if(qStr != null && qStr.length() > 0) {
-			buf.append('?');
-			buf.append(qStr);
-		}
-		return buf.toString();
-	}
-
-	private static String escape(String value) {
-		return value.replaceAll(";", "%3B");
-	}
-
-	private static String unescape(String value) {
-		return value.replaceAll("%3B", ";");
-	}
-
-	private static Cookie getCookie(HttpServletRequest request,String name) {
-		Cookie[] cookies = request.getCookies();
-		if( cookies == null )
-			return null;
-		for (Cookie cookie : cookies) {
-			if (cookie.getName().equals(name))
-				return cookie;
-		}
-		return null;
-	}
-
-	public static String getCookieValue(HttpServletRequest request,String name) {
-		Cookie cookie = getCookie(request,name);
-		return cookie==null ? null : unescape(cookie.getValue());
-	}
-
-	public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) {
-		Cookie cookie = getCookie(request,name);
-		if( cookie==null || !cookie.getValue().equals(value) ) {
-			cookie = new Cookie(name, escape(value));
-			cookie.setPath("/");
-			if (domain != null && domain.length() > 0)
-				cookie.setDomain(domain);
-			if( isPersistent )
-				cookie.setMaxAge(10000000);
-			response.addCookie(cookie);
-		}
-	}
-
-	public static void removeCookie(HttpServletRequest request,
-									HttpServletResponse response,
-									String name,
-									String domain
-
-	) {
-		Cookie cookie = getCookie(request, name);
-		if(cookie != null) {
-			Cookie delCookie = new Cookie(name, "delete");
-			delCookie.setPath("/");
-			delCookie.setMaxAge(0);
-			if (domain != null && domain.length() > 0)
-				delCookie.setDomain(domain);
-			response.addCookie(delCookie);
-		}
-	}
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/HttpLuan.java	Sun Jun 22 04:42:07 2014 +0000
@@ -0,0 +1,285 @@
+package luan.modules;
+
+import java.io.PrintWriter;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+import java.util.Arrays;
+import java.util.Enumeration;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanFunction;
+import luan.LuanElement;
+import luan.LuanException;
+import luan.LuanTable;
+import luan.LuanJavaFunction;
+import luan.LuanExitException;
+import luan.DeepCloner;
+
+
+public final class HttpLuan {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			return new LuanTable();  // starts empty
+		}
+	};
+
+	public static void service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName)
+		throws LuanException
+	{
+		LuanFunction fn;
+		synchronized(luan) {
+			Object mod = PackageLuan.require(luan,modName);
+			if( !(mod instanceof LuanTable) )
+				throw luan.exception( "module '"+modName+"' must return a table" );
+			LuanTable tbl = (LuanTable)mod;
+			if( Luan.toBoolean( tbl.get("per_session") ) ) {
+				HttpSession session = request.getSession();
+				LuanState sessionLuan  = (LuanState)session.getValue("luan");
+				if( sessionLuan!=null ) {
+					luan = sessionLuan;
+				} else {
+					DeepCloner cloner = new DeepCloner();
+					luan = cloner.deepClone(luan);
+					session.putValue("luan",luan);
+				}
+				tbl = (LuanTable)PackageLuan.require(luan,modName);
+				fn = (LuanFunction)tbl.get("page");
+			} else {
+				fn = (LuanFunction)tbl.get("page");
+				if( fn == null )
+					throw luan.exception( "function 'page' is not defined" );
+				DeepCloner cloner = new DeepCloner();
+				luan = cloner.deepClone(luan);
+				fn = cloner.get(fn);
+			}
+		}
+
+		LuanTable module = (LuanTable)luan.loaded().get("Http");
+		if( module == null )
+			throw luan.exception( "module 'Http' not defined" );
+		HttpLuan lib = new HttpLuan(request,response);
+		try {
+			module.put( "request", lib.requestTable() );
+			module.put( "response", lib.responseTable() );
+/*
+			module.put( "write", new LuanJavaFunction(
+				HttpLuan.class.getMethod( "text_write", LuanState.class, new Object[0].getClass() ), lib
+			) );
+*/
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+
+		luan.call(fn,"<http>");
+	}
+
+
+
+	private final HttpServletRequest request;
+	private final HttpServletResponse response;
+//	private PrintWriter writer = null;
+//	private ServletOutputStream sos = null;
+
+	private HttpLuan(HttpServletRequest request,HttpServletResponse response) {
+		this.request = request;
+		this.response = response;
+	}
+
+	private LuanTable requestTable() throws NoSuchMethodException {
+		LuanTable req = new LuanTable();
+		req.put("java",request);
+		req.put( "get_attribute", new LuanJavaFunction(HttpServletRequest.class.getMethod("getAttribute",String.class),request) );
+		req.put( "set_attribute", new LuanJavaFunction(HttpServletRequest.class.getMethod("setAttribute",String.class,Object.class),request) );
+		req.put( "get_parameter", new LuanJavaFunction(HttpServletRequest.class.getMethod("getParameter",String.class),request) );
+		req.put( "get_parameter_values", new LuanJavaFunction(HttpServletRequest.class.getMethod("getParameterValues",String.class),request) );
+		req.put( "get_header", new LuanJavaFunction(HttpServletRequest.class.getMethod("getHeader",String.class),request) );
+		add( req, "get_cookie_value", String.class );
+		req.put( "method", new LuanJavaFunction(HttpServletRequest.class.getMethod("getMethod"),request) );
+		req.put( "servlet_path", new LuanJavaFunction(HttpServletRequest.class.getMethod("getServletPath"),request) );
+		req.put( "server_name", new LuanJavaFunction(HttpServletRequest.class.getMethod("getServerName"),request) );
+		add( req, "current_url" );
+		req.put( "remote_address", new LuanJavaFunction(HttpServletRequest.class.getMethod("getRemoteAddr"),request) );
+		add( req, "get_session_attribute", String.class );
+		add( req, "set_session_attribute", String.class, Object.class );
+		return req;
+	}
+
+	private LuanTable responseTable() throws NoSuchMethodException {
+		LuanTable resp = new LuanTable();
+		resp.put("java",response);
+		add( resp, "send_redirect", String.class );
+		add( resp, "send_error", Integer.TYPE, String.class );
+		resp.put( "contains_header", new LuanJavaFunction(HttpServletResponse.class.getMethod("containsHeader",String.class),response) );
+		resp.put( "set_header", new LuanJavaFunction(HttpServletResponse.class.getMethod("setHeader",String.class,String.class),response) );
+		add( resp, "set_cookie", String.class, String.class, Boolean.TYPE, String.class );
+		add( resp, "remove_cookie", String.class, String.class );
+		resp.put( "set_content_type", new LuanJavaFunction(HttpServletResponse.class.getMethod("setContentType",String.class),response) );
+		add( resp, "text_writer" );
+		return resp;
+	}
+
+	private void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(HttpLuan.class.getMethod(method,parameterTypes),this) );
+	}
+/*
+	public void text_write(LuanState luan,Object... args) throws LuanException, IOException {
+		if( writer == null )
+			writer = response.getWriter();
+		for( Object obj : args ) {
+			writer.print( luan.toString(obj) );
+		}
+	}
+*/
+	public LuanTable text_writer() throws IOException {
+		return IoLuan.textWriter(response.getWriter());
+	}
+
+	public String get_cookie_value(String name) {
+		return getCookieValue(request, name);
+	}
+
+	public String current_url() {
+		return getCurrentURL(request);
+	}
+
+	public void send_redirect(String redirectUrl)
+		throws IOException
+	{
+		response.sendRedirect(redirectUrl);
+		throw new LuanExitException();
+	}
+
+	public void send_error(int code,String text)
+		throws IOException
+	{
+		response.sendError(code, text);
+		throw new LuanExitException();
+	}
+
+	public void set_cookie(String name,String value,boolean isPersistent, String domain) {
+		setCookie(request,response,name,value,isPersistent,domain);
+	}
+
+	public void remove_cookie(String name, String domain) {
+		removeCookie(request,response,name,domain);
+	}
+
+	public Object get_session_attribute(String name) {
+		return request.getSession().getAttribute(name);
+	}
+
+	public void set_session_attribute(String name,Object value) {
+		request.getSession().setAttribute(name,value);
+	}
+
+
+	// static utils
+
+	public static String getQueryString(HttpServletRequest request) {
+		return getQueryString(request,0);
+	}
+
+	public static String getQueryString(HttpServletRequest request,int maxValueLen) {
+		String method = request.getMethod();
+		if( method.equals("GET") )
+			return request.getQueryString();
+		if( !method.equals("POST") && !method.equals("HEAD") )
+			throw new RuntimeException(method);
+		Enumeration en = request.getParameterNames();
+		StringBuilder queryBuf = new StringBuilder();
+		if( !en.hasMoreElements() )
+			return null;
+		do {
+			String param = (String)en.nextElement();
+			String value = request.getParameter(param);
+			if( maxValueLen > 0 ) {
+				int len = value.length();
+				if( len > maxValueLen )
+					value = value.substring(0,maxValueLen) + "..." + (len-maxValueLen);
+			}
+			queryBuf.append(param);
+			queryBuf.append('=');
+			queryBuf.append(value);
+			queryBuf.append('&');
+		} while( en.hasMoreElements() );
+		queryBuf.deleteCharAt(queryBuf.length() - 1);
+		return queryBuf.toString();
+	}
+
+	public static String getCurrentURL(HttpServletRequest request) {
+		return getCurrentURL(request,0);
+	}
+
+	public static String getCurrentURL(HttpServletRequest request,int maxValueLen) {
+//		StringBuffer buf = HttpUtils.getRequestURL(request);
+		StringBuffer buf = request.getRequestURL();
+		String qStr = getQueryString(request,maxValueLen);
+		if(qStr != null && qStr.length() > 0) {
+			buf.append('?');
+			buf.append(qStr);
+		}
+		return buf.toString();
+	}
+
+	private static String escape(String value) {
+		return value.replaceAll(";", "%3B");
+	}
+
+	private static String unescape(String value) {
+		return value.replaceAll("%3B", ";");
+	}
+
+	private static Cookie getCookie(HttpServletRequest request,String name) {
+		Cookie[] cookies = request.getCookies();
+		if( cookies == null )
+			return null;
+		for (Cookie cookie : cookies) {
+			if (cookie.getName().equals(name))
+				return cookie;
+		}
+		return null;
+	}
+
+	public static String getCookieValue(HttpServletRequest request,String name) {
+		Cookie cookie = getCookie(request,name);
+		return cookie==null ? null : unescape(cookie.getValue());
+	}
+
+	public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) {
+		Cookie cookie = getCookie(request,name);
+		if( cookie==null || !cookie.getValue().equals(value) ) {
+			cookie = new Cookie(name, escape(value));
+			cookie.setPath("/");
+			if (domain != null && domain.length() > 0)
+				cookie.setDomain(domain);
+			if( isPersistent )
+				cookie.setMaxAge(10000000);
+			response.addCookie(cookie);
+		}
+	}
+
+	public static void removeCookie(HttpServletRequest request,
+									HttpServletResponse response,
+									String name,
+									String domain
+
+	) {
+		Cookie cookie = getCookie(request, name);
+		if(cookie != null) {
+			Cookie delCookie = new Cookie(name, "delete");
+			delCookie.setPath("/");
+			delCookie.setMaxAge(0);
+			if (domain != null && domain.length() > 0)
+				delCookie.setDomain(domain);
+			response.addCookie(delCookie);
+		}
+	}
+
+}
--- a/src/luan/modules/IoLib.java	Sun Jun 22 04:28:32 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 IoLib {
-
-	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(
-					IoLib.class.getMethod( "stdin_read_text" ), null
-				) );
-				stdin.put( "read_binary", new LuanJavaFunction(
-					IoLib.class.getMethod( "stdin_read_binary" ), null
-				) );
-				stdin.put( "read_lines", new LuanJavaFunction(
-					IoLib.class.getMethod( "stdin_read_lines" ), null
-				) );
-				stdin.put( "read_blocks", new LuanJavaFunction(
-					IoLib.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(IoLib.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 IoLib() {}  // never
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/IoLuan.java	Sun Jun 22 04:42:07 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
+}
--- a/src/luan/modules/JavaLib.java	Sun Jun 22 04:28:32 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 JavaLib {
-
-	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(JavaLib.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 = JavaLib.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(JavaLib.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/src/luan/modules/JavaLuan.java	Sun Jun 22 04:42:07 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));
+				}
+			}
+		);
+	}
+}
--- a/src/luan/modules/MathLib.java	Sun Jun 22 04:28:32 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 MathLib {
-
-	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(MathLib.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/src/luan/modules/MathLuan.java	Sun Jun 22 04:42:07 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);
+	}
+
+}
--- a/src/luan/modules/PackageLib.java	Sun Jun 22 04:28:32 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 PackageLib {
-
-	private static final String jpath = "luan.modules.?Lib.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(PackageLib.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 = BasicLib.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/src/luan/modules/PackageLuan.java	Sun Jun 22 04:42:07 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);
+	}
+
+}
--- a/src/luan/modules/PickleCon.java	Sun Jun 22 04:28:32 2014 +0000
+++ b/src/luan/modules/PickleCon.java	Sun Jun 22 04:42:07 2014 +0000
@@ -60,7 +60,7 @@
 		ioModule.put("_read_binary",_read_binary);
 		try {
 			src = in.readUTF();
-			LuanFunction fn = BasicLib.load(luan,src,"pickle-reader",env,false);
+			LuanFunction fn = BasicLuan.load(luan,src,"pickle-reader",env,false);
 			return luan.call(fn);
 		} finally {
 			ioModule.put("_binaries",null);
--- a/src/luan/modules/StringLib.java	Sun Jun 22 04:28:32 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 StringLib {
-
-	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(StringLib.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 JavaLib.__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/src/luan/modules/StringLuan.java	Sun Jun 22 04:42:07 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);
+	}
+
+}
--- a/src/luan/modules/TableLib.java	Sun Jun 22 04:28:32 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 TableLib {
-
-	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(TableLib.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/src/luan/modules/TableLuan.java	Sun Jun 22 04:42:07 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);
+	}
+
+}
--- a/src/luan/modules/ThreadLib.java	Sun Jun 22 04:28:32 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 ThreadLib {
-
-	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(ThreadLib.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/src/luan/modules/ThreadLuan.java	Sun Jun 22 04:42:07 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();
+			}
+		}});
+	}
+
+}