changeset 221:ec016471c6eb

make LuanTable an interface git-svn-id: https://luan-java.googlecode.com/svn/trunk@222 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Thu, 17 Jul 2014 07:49:26 +0000
parents 61afe2a1ce96
children b76fcb72d97d
files core/src/luan/Luan.java core/src/luan/LuanJavaFunction.java core/src/luan/LuanState.java core/src/luan/LuanTable.java core/src/luan/LuanTableImpl.java core/src/luan/impl/LuanCompiler.java core/src/luan/impl/TableExpr.java core/src/luan/modules/BasicLuan.java core/src/luan/modules/BinaryLuan.java core/src/luan/modules/HtmlLuan.java core/src/luan/modules/IoLuan.java core/src/luan/modules/JavaLuan.java core/src/luan/modules/MathLuan.java core/src/luan/modules/PackageLuan.java core/src/luan/modules/PickleClient.java core/src/luan/modules/PickleCon.java core/src/luan/modules/StringLuan.java core/src/luan/modules/TableLuan.java core/src/luan/modules/ThreadLuan.java dist/luan-core-trunk.jar dist/luan-logging-trunk.jar dist/luan-web-trunk.jar logging/src/luan/modules/logging/LuanLogger.java web/src/luan/modules/web/HttpLuan.java
diffstat 24 files changed, 494 insertions(+), 441 deletions(-) [+]
line wrap: on
line diff
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/Luan.java
--- a/core/src/luan/Luan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/Luan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -145,5 +145,9 @@
 		return null;
 	}
 
+	public static LuanTable newTable() {
+		return new LuanTableImpl();
+	}
+
 	private Luan() {}  // never
 }
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/LuanJavaFunction.java
--- a/core/src/luan/LuanJavaFunction.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/LuanJavaFunction.java	Thu Jul 17 07:49:26 2014 +0000
@@ -202,7 +202,7 @@
 			for( int i=0; i<a.length; i++ ) {
 				a[i] = Array.get(obj,i);
 			}
-			return new LuanTable(new ArrayList<Object>(Arrays.asList(a)));
+			return new LuanTableImpl(new ArrayList<Object>(Arrays.asList(a)));
 		}
 	};
 
@@ -369,22 +369,22 @@
 			if( obj instanceof List ) {
 				@SuppressWarnings("unchecked")
 				List<Object> list = (List<Object>)obj;
-				return new LuanTable(list);
+				return new LuanTableImpl(list);
 			}
 			if( obj instanceof Map ) {
 				@SuppressWarnings("unchecked")
 				Map<Object,Object> map = (Map<Object,Object>)obj;
-				return new LuanTable(map);
+				return new LuanTableImpl(map);
 			}
 			if( obj instanceof Set ) {
 				@SuppressWarnings("unchecked")
 				Set<Object> set = (Set<Object>)obj;
-				return new LuanTable(set);
+				return new LuanTableImpl(set);
 			}
 			Class cls = obj.getClass();
 			if( cls.isArray() && !cls.getComponentType().isPrimitive() ) {
 				Object[] a = (Object[])obj;
-				return new LuanTable(Arrays.asList(a));
+				return new LuanTableImpl(Arrays.asList(a));
 			}
 			return obj;
 		}
@@ -392,8 +392,8 @@
 
 	private static final ArgConverter ARG_MAP = new ArgConverter() {
 		public Object convert(Object obj) {
-			if( obj instanceof LuanTable ) {
-				LuanTable t = (LuanTable)obj;
+			if( obj instanceof LuanTableImpl ) {
+				LuanTableImpl t = (LuanTableImpl)obj;
 				return t.asMap();
 			}
 			return obj;
@@ -402,8 +402,8 @@
 
 	private static final ArgConverter ARG_LIST = new ArgConverter() {
 		public Object convert(Object obj) {
-			if( obj instanceof LuanTable ) {
-				LuanTable t = (LuanTable)obj;
+			if( obj instanceof LuanTableImpl ) {
+				LuanTableImpl t = (LuanTableImpl)obj;
 				if( t.isList() )
 					return t.asList();
 			}
@@ -413,8 +413,8 @@
 
 	private static final ArgConverter ARG_SET = new ArgConverter() {
 		public Object convert(Object obj) {
-			if( obj instanceof LuanTable ) {
-				LuanTable t = (LuanTable)obj;
+			if( obj instanceof LuanTableImpl ) {
+				LuanTableImpl t = (LuanTableImpl)obj;
 				if( t.isSet() )
 					return t.asSet();
 			}
@@ -430,8 +430,8 @@
 		}
 
 		public Object convert(Object obj) {
-			if( obj instanceof LuanTable ) {
-				LuanTable t = (LuanTable)obj;
+			if( obj instanceof LuanTableImpl ) {
+				LuanTableImpl t = (LuanTableImpl)obj;
 				if( t.isList() ) {
 					try {
 						return t.asList().toArray(a);
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/LuanState.java
--- a/core/src/luan/LuanState.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/LuanState.java	Thu Jul 17 07:49:26 2014 +0000
@@ -19,13 +19,13 @@
 
 	final List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
 
-	private LuanTable registry;
-	private LuanTable global;
-	private LuanTable metatable;  // generic metatable
+	private LuanTableImpl registry;
+	private LuanTableImpl global;
+	private LuanTableImpl metatable;  // generic metatable
 
 	protected LuanState() {
-		registry = new LuanTable();
-		global = new LuanTable();
+		registry = new LuanTableImpl();
+		global = new LuanTableImpl();
 		global.put("_G",global);
 		metatable = newMetatable();
 	}
@@ -47,7 +47,7 @@
 	public final LuanTable registryTable(Object key) {
 		LuanTable tbl = (LuanTable)registry.get(key);
 		if( tbl == null ) {
-			tbl = new LuanTable();
+			tbl = new LuanTableImpl();
 			registry.put(key,tbl);
 		}
 		return tbl;
@@ -68,7 +68,7 @@
 	}
 
 	public final Object eval(String cmd) {
-		return eval(cmd,new LuanTable());
+		return eval(cmd,new LuanTableImpl());
 	}
 
 	public final Object eval(String cmd,LuanTable env) {
@@ -99,8 +99,8 @@
 		return t==null ? null : t.get(op);
 	}
 
-	private static LuanTable newMetatable() {
-		LuanTable metatable = new LuanTable();
+	private static LuanTableImpl newMetatable() {
+		LuanTableImpl metatable = new LuanTableImpl();
 		try {
 			metatable.put( "__index", new LuanJavaFunction(
 				LuanState.class.getMethod("__index",LuanState.class,Object.class,Object.class), null
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/LuanTable.java
--- a/core/src/luan/LuanTable.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/LuanTable.java	Thu Jul 17 07:49:26 2014 +0000
@@ -1,392 +1,21 @@
 package luan;
 
-import java.util.Iterator;
-import java.util.ListIterator;
 import java.util.Map;
-import java.util.HashMap;
 import java.util.List;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Comparator;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.regex.Pattern;
 
 
-public final class LuanTable implements DeepCloneable<LuanTable>, Iterable<Map.Entry<Object,Object>>, LuanRepr {
-	private Map<Object,Object> map = null;
-	private List<Object> list = null;
-	private LuanTable metatable = null;
-
-	public LuanTable() {}
-
-	public LuanTable(LuanTable tbl) {
-		if( tbl.map != null )
-			this.map = new HashMap<Object,Object>(tbl.map);
-		if( tbl.list != null )
-			this.list = new ArrayList<Object>(tbl.list);
-	}
-
-	public LuanTable(List<Object> list) {
-		this.list = list;
-		this.map = new HashMap<Object,Object>();
-		map.put("n",list.size());
-		for( int i=0; i<list.size(); i++ ) {
-			if( list.get(i) == null ) {
-				listToMap(i);
-				break;
-			}
-		}
-	}
-
-	public LuanTable(Map<Object,Object> map) {
-		map.remove(null);
-		for( Iterator<Object> i=map.values().iterator(); i.hasNext(); ) {
-			if( i.next() == null )
-				i.remove();
-		}
-		this.map = map;
-	}
-
-	public LuanTable(Set<Object> set) {
-		map = new HashMap<Object,Object>();
-		for( Object obj : set ) {
-			if( obj != null )
-				map.put(obj,Boolean.TRUE);
-		}
-	}
-
-	@Override public LuanTable shallowClone() {
-		return new LuanTable();
-	}
-
-	@Override public void deepenClone(LuanTable clone,DeepCloner cloner) {
-		if( map != null ) {
-			clone.map = new HashMap<Object,Object>();
-			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
-				clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) );
-			}
-		}
-		if( list != null ) {
-			clone.list = new ArrayList<Object>();
-			for( Object obj : list ) {
-				clone.list.add( cloner.get(obj) );
-			}
-		}
-		if( metatable != null )
-			clone.metatable = cloner.deepClone(metatable);
-	}
-
-	public boolean isList() {
-		return map==null || map.isEmpty();
-	}
-
-	public List<Object> asList() {
-		return list!=null ? list : Collections.emptyList();
-	}
-
-	public Map<Object,Object> asMap() {
-		if( list == null || list.isEmpty() )
-			return map!=null ? map : Collections.emptyMap();
-		Map<Object,Object> rtn = map!=null ? new HashMap<Object,Object>(map) : new HashMap<Object,Object>();
-		for( ListIterator iter = list.listIterator(); iter.hasNext(); ) {
-			int i = iter.nextIndex();
-			rtn.put(i+1,iter.next());
-		}
-		return rtn;
-	}
-
-	public boolean isSet() {
-		if( list != null ) {
-			for( Object obj : list ) {
-				if( obj!=null && !obj.equals(Boolean.TRUE) )
-					return false;
-			}
-		}
-		if( map != null ) {
-			for( Object obj : map.values() ) {
-				if( !obj.equals(Boolean.TRUE) )
-					return false;
-			}
-		}
-		return true;
-	}
-
-	public Set<Object> asSet() {
-		if( list == null || list.isEmpty() )
-			return map!=null ? map.keySet() : Collections.emptySet();
-		Set<Object> rtn = map!=null ? new HashSet<Object>(map.keySet()) : new HashSet<Object>();
-		for( int i=1; i<=list.size(); i++ ) {
-			rtn.add(i);
-		}
-		return rtn;
-	}
-
-	@Override public String toString() {
-		return "table: " + Integer.toHexString(hashCode());
-	}
-
-	@Override public String repr() {
-		return repr( Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
-	}
-
-	private String repr(Set<LuanTable> set) {
-		if( !set.add(this) ) {
-			return "\"<circular reference>\"";
-		}
-		StringBuilder sb = new StringBuilder();
-		sb.append('{');
-		boolean isFirst = true;
-		if( list != null ) {
-			boolean gotNull = false;
-			for( int i=0; i<list.size(); i++ ) {
-				Object obj = list.get(i);
-				if( obj==null ) {
-					gotNull = true;
-				} else {
-					if( isFirst ) {
-						isFirst = false;
-					} else {
-						sb.append(", ");
-					}
-					if( gotNull )
-						sb.append(i+1).append('=');
-					sb.append(repr(set,obj));
-				}
-			}
-		}
-		if( map != null ) {
-			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
-				if( isFirst ) {
-					isFirst = false;
-				} else {
-					sb.append(", ");
-				}
-				sb.append(reprKey(set,entry.getKey())).append('=').append(repr(set,entry.getValue()));
-			}
-		}
-		sb.append('}');
-		return sb.toString();
-	}
-
-	private static final Pattern namePtn = Pattern.compile("[a-zA-Z_][a-zA-Z_0-9]*");
-
-	private static String reprKey(Set<LuanTable> set,Object obj) {
-		if( obj instanceof String ) {
-			String s = (String)obj;
-			if( namePtn.matcher(s).matches() )
-				return s;
-		}
-		return "[" + repr(set,obj) + "]";
-	}
-
-	private static String repr(Set<LuanTable> set,Object obj) {
-		if( obj instanceof LuanTable ) {
-			LuanTable t = (LuanTable)obj;
-			return t.repr(set);
-		} else {
-			String s = Luan.repr(obj);
-			if( s == null )
-				s = "<couldn't repr: " + Luan.stringEncode(Luan.toString(obj)) + ">";
-			return s;
-		}
-	}
-
-	public Object get(Object key) {
-		if( list != null ) {
-			Integer iT = Luan.asInteger(key);
-			if( iT != null ) {
-				int i = iT - 1;
-				if( i>=0 && i<list.size() )
-					return list.get(i);
-			}
-		}
-		if( map==null )
-			return null;
-		return map.get(key);
-	}
-
-	public Object put(Object key,Object val) {
-		Integer iT = Luan.asInteger(key);
-		if( iT != null ) {
-			int i = iT - 1;
-			if( list != null || i == 0 ) {
-				if( i == list().size() ) {
-					if( val != null ) {
-						list.add(val);
-						mapToList();
-					}
-					return null;
-				} else if( i>=0 && i<list.size() ) {
-					Object old = list.get(i);
-					list.set(i,val);
-					if( val == null ) {
-						listToMap(i);
-					}
-					return old;
-				}
-			}
-		}
-		if( map==null ) {
-			map = new HashMap<Object,Object>();
-		}
-		if( key instanceof Number && !(key instanceof Double) ) {
-			Number n = (Number)key;
-			key = Double.valueOf(n.doubleValue());
-		}
-		if( val == null ) {
-			return map.remove(key);
-		} else {
-			return map.put(key,val);
-		}
-	}
-
-	private void mapToList() {
-		if( map != null ) {
-			while(true) {
-				Object v = map.remove(Double.valueOf(list.size()+1));
-				if( v == null )
-					break;
-				list.add(v);
-			}
-		}
-	}
-
-	private void listToMap(int from) {
-		if( list != null ) {
-			while( list.size() > from ) {
-				int i = list.size() - 1;
-				Object v = list.remove(i);
-				if( v != null ) {
-					if( map==null )
-						map = new HashMap<Object,Object>();
-					map.put(i+1,v);
-				}
-			}
-		}
-	}
-
-	private List<Object> list() {
-		if( list == null ) {
-			list = new ArrayList<Object>();
-			mapToList();
-		}
-		return list;
-	}
-
-	public void insert(int pos,Object value) {
-		if( value==null )
-			throw new UnsupportedOperationException();
-		list().add(pos-1,value);
-		mapToList();
-	}
-
-	public void add(Object value) {
-		if( value==null )
-			throw new UnsupportedOperationException();
-		list().add(value);
-		mapToList();
-	}
-
-	public Object remove(int pos) {
-		return list().remove(pos-1);
-	}
-
-	public void sort(Comparator<Object> cmp) {
-		Collections.sort(list(),cmp);
-	}
-
-	public int length() {
-		return list==null ? 0 : list.size();
-	}
-
-	public Iterator<Map.Entry<Object,Object>> iterator() {
-		if( list == null ) {
-			if( map == null )
-				return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
-			return map.entrySet().iterator();
-		}
-		if( map == null )
-			return listIterator();
-		return new Iterator<Map.Entry<Object,Object>>() {
-			Iterator<Map.Entry<Object,Object>> iter = listIterator();
-			boolean isList = true;
-			public boolean hasNext() {
-				boolean b = iter.hasNext();
-				if( !b && isList ) {
-					iter = map.entrySet().iterator();
-					isList = false;
-					b = iter.hasNext();
-				}
-				return b;
-			}
-			public Map.Entry<Object,Object> next() {
-				return iter.next();
-			}
-			public void remove() {
-				throw new UnsupportedOperationException();
-			}
-		};
-	}
-
-	public Iterator<Map.Entry<Object,Object>> listIterator() {
-		if( list == null )
-			return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
-		final ListIterator iter = list.listIterator();
-		return new Iterator<Map.Entry<Object,Object>>() {
-			public boolean hasNext() {
-				return iter.hasNext();
-			}
-			public Map.Entry<Object,Object> next() {
-				Double key = Double.valueOf(iter.nextIndex()+1);
-				return new MapEntry(key,iter.next());
-			}
-			public void remove() {
-				throw new UnsupportedOperationException();
-			}
-		};
-	}
-/*
-	public Object[] listToArray() {
-		return list==null ? new Object[0] : list.toArray();
-	}
-*/
-	public LuanTable subList(int from,int to) {
-		return new LuanTable(new ArrayList<Object>(list().subList(from-1,to-1)));
-	}
-
-	public LuanTable getMetatable() {
-		return metatable;
-	}
-
-	public void setMetatable(LuanTable metatable) {
-		this.metatable = metatable;
-	}
-
-	private static final class MapEntry implements Map.Entry<Object,Object> {
-		private final Object key;
-		private final Object value;
-
-		MapEntry(Object key,Object value) {
-			this.key = key;
-			this.value = value;
-		}
-
-		@Override public Object getKey() {
-			return key;
-		}
-
-		@Override public Object getValue() {
-			return value;
-		}
-
-		@Override public Object setValue(Object value) {
-			throw new UnsupportedOperationException();
-		}
-	}
-
-	public boolean isEmpty() {
-		return (list==null || list.isEmpty()) && (map==null || map.isEmpty());
-	}
+public interface LuanTable extends Iterable<Map.Entry<Object,Object>> {
+	public List<Object> asList();
+	public Map<Object,Object> asMap();
+	public Object get(Object key);
+	public Object put(Object key,Object val);
+	public void insert(int pos,Object value);
+	public void add(Object value);
+	public Object remove(int pos);
+	public void sort(Comparator<Object> cmp);
+	public int length();
+	public LuanTable subList(int from,int to);
+	public LuanTable getMetatable();
+	public void setMetatable(LuanTable metatable);
 }
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/LuanTableImpl.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/LuanTableImpl.java	Thu Jul 17 07:49:26 2014 +0000
@@ -0,0 +1,390 @@
+package luan;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.regex.Pattern;
+
+
+public final class LuanTableImpl implements LuanTable, DeepCloneable<LuanTableImpl>, LuanRepr {
+	private Map<Object,Object> map = null;
+	private List<Object> list = null;
+	private LuanTable metatable = null;
+
+	public LuanTableImpl() {}
+/*
+	public LuanTableImpl(LuanTableImpl tbl) {
+		if( tbl.map != null )
+			this.map = new HashMap<Object,Object>(tbl.map);
+		if( tbl.list != null )
+			this.list = new ArrayList<Object>(tbl.list);
+	}
+*/
+	LuanTableImpl(List<Object> list) {
+		this.list = list;
+		this.map = new HashMap<Object,Object>();
+		map.put("n",list.size());
+		for( int i=0; i<list.size(); i++ ) {
+			if( list.get(i) == null ) {
+				listToMap(i);
+				break;
+			}
+		}
+	}
+
+	LuanTableImpl(Map<Object,Object> map) {
+		map.remove(null);
+		for( Iterator<Object> i=map.values().iterator(); i.hasNext(); ) {
+			if( i.next() == null )
+				i.remove();
+		}
+		this.map = map;
+	}
+
+	LuanTableImpl(Set<Object> set) {
+		map = new HashMap<Object,Object>();
+		for( Object obj : set ) {
+			if( obj != null )
+				map.put(obj,Boolean.TRUE);
+		}
+	}
+
+	@Override public LuanTableImpl shallowClone() {
+		return new LuanTableImpl();
+	}
+
+	@Override public void deepenClone(LuanTableImpl clone,DeepCloner cloner) {
+		if( map != null ) {
+			clone.map = new HashMap<Object,Object>();
+			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
+				clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) );
+			}
+		}
+		if( list != null ) {
+			clone.list = new ArrayList<Object>();
+			for( Object obj : list ) {
+				clone.list.add( cloner.get(obj) );
+			}
+		}
+		if( metatable != null )
+			clone.metatable = cloner.get(metatable);
+	}
+
+	public boolean isList() {
+		return map==null || map.isEmpty();
+	}
+
+	@Override public List<Object> asList() {
+		return list!=null ? list : Collections.emptyList();
+	}
+
+	@Override public Map<Object,Object> asMap() {
+		if( list == null || list.isEmpty() )
+			return map!=null ? map : Collections.emptyMap();
+		Map<Object,Object> rtn = map!=null ? new HashMap<Object,Object>(map) : new HashMap<Object,Object>();
+		for( ListIterator iter = list.listIterator(); iter.hasNext(); ) {
+			int i = iter.nextIndex();
+			rtn.put(i+1,iter.next());
+		}
+		return rtn;
+	}
+
+	public boolean isSet() {
+		if( list != null ) {
+			for( Object obj : list ) {
+				if( obj!=null && !obj.equals(Boolean.TRUE) )
+					return false;
+			}
+		}
+		if( map != null ) {
+			for( Object obj : map.values() ) {
+				if( !obj.equals(Boolean.TRUE) )
+					return false;
+			}
+		}
+		return true;
+	}
+
+	public Set<Object> asSet() {
+		if( list == null || list.isEmpty() )
+			return map!=null ? map.keySet() : Collections.emptySet();
+		Set<Object> rtn = map!=null ? new HashSet<Object>(map.keySet()) : new HashSet<Object>();
+		for( int i=1; i<=list.size(); i++ ) {
+			rtn.add(i);
+		}
+		return rtn;
+	}
+
+	@Override public String toString() {
+		return "table: " + Integer.toHexString(hashCode());
+	}
+
+	@Override public String repr() {
+		return repr( Collections.newSetFromMap(new IdentityHashMap<LuanTableImpl,Boolean>()) );
+	}
+
+	private String repr(Set<LuanTableImpl> set) {
+		if( !set.add(this) ) {
+			return "\"<circular reference>\"";
+		}
+		StringBuilder sb = new StringBuilder();
+		sb.append('{');
+		boolean isFirst = true;
+		if( list != null ) {
+			boolean gotNull = false;
+			for( int i=0; i<list.size(); i++ ) {
+				Object obj = list.get(i);
+				if( obj==null ) {
+					gotNull = true;
+				} else {
+					if( isFirst ) {
+						isFirst = false;
+					} else {
+						sb.append(", ");
+					}
+					if( gotNull )
+						sb.append(i+1).append('=');
+					sb.append(repr(set,obj));
+				}
+			}
+		}
+		if( map != null ) {
+			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
+				if( isFirst ) {
+					isFirst = false;
+				} else {
+					sb.append(", ");
+				}
+				sb.append(reprKey(set,entry.getKey())).append('=').append(repr(set,entry.getValue()));
+			}
+		}
+		sb.append('}');
+		return sb.toString();
+	}
+
+	private static final Pattern namePtn = Pattern.compile("[a-zA-Z_][a-zA-Z_0-9]*");
+
+	private static String reprKey(Set<LuanTableImpl> set,Object obj) {
+		if( obj instanceof String ) {
+			String s = (String)obj;
+			if( namePtn.matcher(s).matches() )
+				return s;
+		}
+		return "[" + repr(set,obj) + "]";
+	}
+
+	private static String repr(Set<LuanTableImpl> set,Object obj) {
+		if( obj instanceof LuanTableImpl ) {
+			LuanTableImpl t = (LuanTableImpl)obj;
+			return t.repr(set);
+		} else {
+			String s = Luan.repr(obj);
+			if( s == null )
+				s = "<couldn't repr: " + Luan.stringEncode(Luan.toString(obj)) + ">";
+			return s;
+		}
+	}
+
+	@Override public Object get(Object key) {
+		if( list != null ) {
+			Integer iT = Luan.asInteger(key);
+			if( iT != null ) {
+				int i = iT - 1;
+				if( i>=0 && i<list.size() )
+					return list.get(i);
+			}
+		}
+		if( map==null )
+			return null;
+		return map.get(key);
+	}
+
+	@Override public Object put(Object key,Object val) {
+		Integer iT = Luan.asInteger(key);
+		if( iT != null ) {
+			int i = iT - 1;
+			if( list != null || i == 0 ) {
+				if( i == list().size() ) {
+					if( val != null ) {
+						list.add(val);
+						mapToList();
+					}
+					return null;
+				} else if( i>=0 && i<list.size() ) {
+					Object old = list.get(i);
+					list.set(i,val);
+					if( val == null ) {
+						listToMap(i);
+					}
+					return old;
+				}
+			}
+		}
+		if( map==null ) {
+			map = new HashMap<Object,Object>();
+		}
+		if( key instanceof Number && !(key instanceof Double) ) {
+			Number n = (Number)key;
+			key = Double.valueOf(n.doubleValue());
+		}
+		if( val == null ) {
+			return map.remove(key);
+		} else {
+			return map.put(key,val);
+		}
+	}
+
+	private void mapToList() {
+		if( map != null ) {
+			while(true) {
+				Object v = map.remove(Double.valueOf(list.size()+1));
+				if( v == null )
+					break;
+				list.add(v);
+			}
+		}
+	}
+
+	private void listToMap(int from) {
+		if( list != null ) {
+			while( list.size() > from ) {
+				int i = list.size() - 1;
+				Object v = list.remove(i);
+				if( v != null ) {
+					if( map==null )
+						map = new HashMap<Object,Object>();
+					map.put(i+1,v);
+				}
+			}
+		}
+	}
+
+	private List<Object> list() {
+		if( list == null ) {
+			list = new ArrayList<Object>();
+			mapToList();
+		}
+		return list;
+	}
+
+	@Override public void insert(int pos,Object value) {
+		if( value==null )
+			throw new UnsupportedOperationException();
+		list().add(pos-1,value);
+		mapToList();
+	}
+
+	@Override public void add(Object value) {
+		if( value==null )
+			throw new UnsupportedOperationException();
+		list().add(value);
+		mapToList();
+	}
+
+	@Override public Object remove(int pos) {
+		return list().remove(pos-1);
+	}
+
+	@Override public void sort(Comparator<Object> cmp) {
+		Collections.sort(list(),cmp);
+	}
+
+	@Override public int length() {
+		return list==null ? 0 : list.size();
+	}
+
+	@Override public Iterator<Map.Entry<Object,Object>> iterator() {
+		if( list == null ) {
+			if( map == null )
+				return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
+			return map.entrySet().iterator();
+		}
+		if( map == null )
+			return listIterator();
+		return new Iterator<Map.Entry<Object,Object>>() {
+			Iterator<Map.Entry<Object,Object>> iter = listIterator();
+			boolean isList = true;
+			public boolean hasNext() {
+				boolean b = iter.hasNext();
+				if( !b && isList ) {
+					iter = map.entrySet().iterator();
+					isList = false;
+					b = iter.hasNext();
+				}
+				return b;
+			}
+			public Map.Entry<Object,Object> next() {
+				return iter.next();
+			}
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+
+	private Iterator<Map.Entry<Object,Object>> listIterator() {
+		if( list == null )
+			return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
+		final ListIterator iter = list.listIterator();
+		return new Iterator<Map.Entry<Object,Object>>() {
+			public boolean hasNext() {
+				return iter.hasNext();
+			}
+			public Map.Entry<Object,Object> next() {
+				Double key = Double.valueOf(iter.nextIndex()+1);
+				return new MapEntry(key,iter.next());
+			}
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+
+	@Override public LuanTable subList(int from,int to) {
+		LuanTableImpl tbl = new LuanTableImpl();
+		tbl.list = new ArrayList<Object>(list().subList(from-1,to-1));
+		return tbl;
+	}
+
+	@Override public LuanTable getMetatable() {
+		return metatable;
+	}
+
+	@Override public void setMetatable(LuanTable metatable) {
+		this.metatable = metatable;
+	}
+
+	private static final class MapEntry implements Map.Entry<Object,Object> {
+		private final Object key;
+		private final Object value;
+
+		MapEntry(Object key,Object value) {
+			this.key = key;
+			this.value = value;
+		}
+
+		@Override public Object getKey() {
+			return key;
+		}
+
+		@Override public Object getValue() {
+			return value;
+		}
+
+		@Override public Object setValue(Object value) {
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	public boolean isEmpty() {
+		return (list==null || list.isEmpty()) && (map==null || map.isEmpty());
+	}
+}
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/impl/LuanCompiler.java
--- a/core/src/luan/impl/LuanCompiler.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/impl/LuanCompiler.java	Thu Jul 17 07:49:26 2014 +0000
@@ -6,6 +6,7 @@
 import luan.LuanSource;
 import luan.LuanElement;
 import luan.LuanTable;
+import luan.Luan;
 import java.util.Map;
 
 
@@ -15,7 +16,7 @@
 	public static LuanFunction compile(LuanState luan,LuanSource src,LuanTable env,boolean allowExpr) throws LuanException {
 		boolean passedEnv = env != null;
 		if( !passedEnv )
-			env = new LuanTable();
+			env = Luan.newTable();
 		UpValue.Getter envGetter = new UpValue.ValueGetter(env);
 		LuanParser parser = new LuanParser(src,envGetter);
 		for( Map.Entry<Object,Object> entry : luan.global() ) {
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/impl/TableExpr.java
--- a/core/src/luan/impl/TableExpr.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/impl/TableExpr.java	Thu Jul 17 07:49:26 2014 +0000
@@ -3,6 +3,7 @@
 import luan.LuanException;
 import luan.LuanTable;
 import luan.LuanSource;
+import luan.Luan;
 
 
 final class TableExpr extends CodeImpl implements Expr {
@@ -27,7 +28,7 @@
 	}
 
 	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		LuanTable table = new LuanTable();
+		LuanTable table = Luan.newTable();
 		for( Field field : fields ) {
 			table.put( field.key.eval(luan), field.value.eval(luan) );
 		}
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/BasicLuan.java
--- a/core/src/luan/modules/BasicLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/BasicLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -22,7 +22,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			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 );
@@ -85,8 +85,11 @@
 		return luan.call(fn);
 	}
 
-	private static LuanFunction pairs(final Iterator<Map.Entry<Object,Object>> iter) {
+	public static LuanFunction pairs(LuanState luan,final LuanTable t) throws LuanException {
+		Utils.checkNotNull(luan,t,"table");
 		return new LuanFunction() {
+			Iterator<Map.Entry<Object,Object>> iter = t.iterator();
+
 			@Override public Object[] call(LuanState luan,Object[] args) {
 				if( !iter.hasNext() )
 					return LuanFunction.NOTHING;
@@ -96,14 +99,20 @@
 		};
 	}
 
-	public static LuanFunction pairs(LuanState luan,LuanTable t) throws LuanException {
+	public static LuanFunction ipairs(LuanState luan,final LuanTable t) throws LuanException {
 		Utils.checkNotNull(luan,t,"table");
-		return pairs( t.iterator() );
-	}
+		return new LuanFunction() {
+			List<Object> list = t.asList();
+			int i = 0;
+			final int size = list.size();
 
-	public static LuanFunction ipairs(LuanState luan,LuanTable t) throws LuanException {
-		Utils.checkNotNull(luan,t,"table");
-		return pairs( t.listIterator() );
+			@Override public Object[] call(LuanState luan,Object[] args) {
+				if( i >= size )
+					return LuanFunction.NOTHING;
+				Object val = list.get(i++);
+				return new Object[]{i,val};
+			}
+		};
 	}
 
 	public static LuanTable get_metatable(LuanState luan,Object obj) {
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/BinaryLuan.java
--- a/core/src/luan/modules/BinaryLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/BinaryLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -1,5 +1,6 @@
 package luan.modules;
 
+import luan.Luan;
 import luan.LuanState;
 import luan.LuanTable;
 import luan.LuanFunction;
@@ -11,7 +12,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			try {
 				add( module, "to_string", new byte[0].getClass() );
 			} catch(NoSuchMethodException e) {
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/HtmlLuan.java
--- a/core/src/luan/modules/HtmlLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/HtmlLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -1,5 +1,6 @@
 package luan.modules;
 
+import luan.Luan;
 import luan.LuanState;
 import luan.LuanTable;
 import luan.LuanFunction;
@@ -11,7 +12,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			try {
 				add( module, "encode", String.class );
 			} catch(NoSuchMethodException e) {
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/IoLuan.java
--- a/core/src/luan/modules/IoLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/IoLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -24,6 +24,7 @@
 import java.net.MalformedURLException;
 import java.util.List;
 import java.util.ArrayList;
+import luan.Luan;
 import luan.LuanState;
 import luan.LuanTable;
 import luan.LuanFunction;
@@ -35,12 +36,12 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			try {
 				add( module, "File", LuanState.class, String.class );
 				add( module, "read_console_line", String.class );
 
-				LuanTable stdin = new LuanTable();
+				LuanTable stdin = Luan.newTable();
 				stdin.put( "read_text", new LuanJavaFunction(
 					IoLuan.class.getMethod( "stdin_read_text" ), null
 				) );
@@ -133,7 +134,7 @@
 	}
 
 	private static LuanTable writer(LuanWriter luanWriter) {
-		LuanTable writer = new LuanTable();
+		LuanTable writer = Luan.newTable();
 		try {
 			writer.put( "write", new LuanJavaFunction(
 				LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter
@@ -149,7 +150,7 @@
 
 
 	public static LuanTable binaryWriter(final OutputStream out) {
-		LuanTable writer = new LuanTable();
+		LuanTable writer = Luan.newTable();
 		try {
 			writer.put( "write", new LuanJavaFunction(
 				OutputStream.class.getMethod( "write", new byte[0].getClass() ), out
@@ -238,7 +239,7 @@
 		}
 
 		LuanTable table() {
-			LuanTable tbl = new LuanTable();
+			LuanTable tbl = Luan.newTable();
 			try {
 				tbl.put( "to_string", new LuanJavaFunction(
 					LuanIn.class.getMethod( "to_string" ), this
@@ -358,7 +359,7 @@
 			File[] files = file.listFiles();
 			if( files==null )
 				return null;
-			LuanTable list = new LuanTable();
+			LuanTable list = Luan.newTable();
 			for( File f : files ) {
 				list.add(new LuanFile(luan,f).table());
 			}
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/JavaLuan.java
--- a/core/src/luan/modules/JavaLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/JavaLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -29,7 +29,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			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 );
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/MathLuan.java
--- a/core/src/luan/modules/MathLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/MathLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -1,5 +1,6 @@
 package luan.modules;
 
+import luan.Luan;
 import luan.LuanState;
 import luan.LuanTable;
 import luan.LuanFunction;
@@ -11,7 +12,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			try {
 				add( module, "abs", Double.TYPE );
 				add( module, "acos", Double.TYPE );
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/PackageLuan.java
--- a/core/src/luan/modules/PackageLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/PackageLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -19,9 +19,9 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			module.put( "loaded", loaded(luan) );
-			module.put( "preload", new LuanTable() );
+			module.put( "preload", Luan.newTable() );
 			module.put( "path", "?.luan;java:luan/modules/?.luan" );
 			module.put( "jpath", jpath );
 			try {
@@ -70,7 +70,7 @@
 		String key = "Package.searchers";
 		LuanTable tbl = (LuanTable)luan.registry().get(key);
 		if( tbl == null ) {
-			tbl = new LuanTable();
+			tbl = Luan.newTable();
 			tbl.add(preloadSearcher);
 			tbl.add(fileSearcher);
 			tbl.add(javaSearcher);
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/PickleClient.java
--- a/core/src/luan/modules/PickleClient.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/PickleClient.java	Thu Jul 17 07:49:26 2014 +0000
@@ -64,7 +64,7 @@
 	}
 
 	LuanTable table() {
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = Luan.newTable();
 		try {
 			tbl.put( "pickle", new LuanJavaFunction(
 				PickleCon.class.getMethod( "pickle", Object.class ), con
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/PickleCon.java
--- a/core/src/luan/modules/PickleCon.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/PickleCon.java	Thu Jul 17 07:49:26 2014 +0000
@@ -26,7 +26,7 @@
 	private final DataOutputStream out;
 	private final List<byte[]> binaries = new ArrayList<byte[]>();
 	String src;
-	final LuanTable env = new LuanTable();
+	final LuanTable env = Luan.newTable();
 
 	PickleCon(LuanState luan,DataInputStream in,DataOutputStream out) {
 		this.in = in;
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/StringLuan.java
--- a/core/src/luan/modules/StringLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/StringLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -15,7 +15,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			try {
 				add( module, "to_binary", String.class );
 				add( module, "to_integers", String.class );
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/TableLuan.java
--- a/core/src/luan/modules/TableLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/TableLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -18,7 +18,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			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 );
@@ -111,7 +111,20 @@
 	}
 
 	public static LuanTable pack(Object... args) {
-		return new LuanTable(new ArrayList<Object>(Arrays.asList(args)));
+		LuanTable tbl = Luan.newTable();
+		boolean hasNull = false;
+		for( int i=0; i<args.length; i++ ) {
+			Object v = args[i];
+			if( v==null ) {
+				hasNull = true;
+			} else if( !hasNull ) {
+				tbl.add(v);
+			} else {
+				tbl.put(i+1,v);
+			}
+		}
+		tbl.put( "n", args.length );
+		return tbl;
 	}
 
 	public static Object[] unpack(LuanTable tbl,Integer iFrom,Integer iTo) {
diff -r 61afe2a1ce96 -r ec016471c6eb core/src/luan/modules/ThreadLuan.java
--- a/core/src/luan/modules/ThreadLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/core/src/luan/modules/ThreadLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -2,6 +2,7 @@
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
+import luan.Luan;
 import luan.LuanState;
 import luan.LuanFunction;
 import luan.LuanTable;
@@ -14,7 +15,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			try {
 				add( module, "fork", LuanState.class, LuanFunction.class, new Object[0].getClass() );
 			} catch(NoSuchMethodException e) {
diff -r 61afe2a1ce96 -r ec016471c6eb dist/luan-core-trunk.jar
Binary file dist/luan-core-trunk.jar has changed
diff -r 61afe2a1ce96 -r ec016471c6eb dist/luan-logging-trunk.jar
Binary file dist/luan-logging-trunk.jar has changed
diff -r 61afe2a1ce96 -r ec016471c6eb dist/luan-web-trunk.jar
Binary file dist/luan-web-trunk.jar has changed
diff -r 61afe2a1ce96 -r ec016471c6eb logging/src/luan/modules/logging/LuanLogger.java
--- a/logging/src/luan/modules/logging/LuanLogger.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/logging/src/luan/modules/logging/LuanLogger.java	Thu Jul 17 07:49:26 2014 +0000
@@ -1,6 +1,7 @@
 package luan.modules.logging;
 
 import org.apache.log4j.Logger;
+import luan.Luan;
 import luan.LuanState;
 import luan.LuanException;
 import luan.LuanTable;
@@ -31,7 +32,7 @@
 	}
 
 	public LuanTable table() {
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = Luan.newTable();
 		try {
 			tbl.put( "error", new LuanJavaFunction(
 				LuanLogger.class.getMethod( "error", LuanState.class, Object.class ), this
diff -r 61afe2a1ce96 -r ec016471c6eb web/src/luan/modules/web/HttpLuan.java
--- a/web/src/luan/modules/web/HttpLuan.java	Wed Jul 16 04:59:45 2014 +0000
+++ b/web/src/luan/modules/web/HttpLuan.java	Thu Jul 17 07:49:26 2014 +0000
@@ -28,7 +28,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
+			LuanTable module = Luan.newTable();
 			try {
 				addStatic( module, "new_luan_handler", LuanState.class );
 			} catch(NoSuchMethodException e) {
@@ -114,7 +114,7 @@
 	}
 
 	private LuanTable requestTable() throws NoSuchMethodException {
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = Luan.newTable();
 		tbl.put("java",request);
 		add( tbl, "get_parameter", String.class );
 		tbl.put( "get_header", new LuanJavaFunction(
@@ -137,7 +137,7 @@
 	}
 
 	private LuanTable responseTable() throws NoSuchMethodException {
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = Luan.newTable();
 		tbl.put("java",response);
 		add( tbl, "send_redirect", String.class );
 		add( tbl, "send_error", Integer.TYPE, String.class );
@@ -158,7 +158,7 @@
 	}
 
 	private LuanTable cookieTable() throws NoSuchMethodException {
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = Luan.newTable();
 		tbl.put( "get", new LuanJavaFunction(
 			HttpLuan.class.getMethod("get_cookie",String.class), this
 		) );
@@ -172,7 +172,7 @@
 	}
 
 	private LuanTable sessionTable() throws NoSuchMethodException {
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = Luan.newTable();
 		tbl.put( "get_attribute", new LuanJavaFunction(
 			HttpLuan.class.getMethod("get_session_attribute",String.class), this
 		) );