changeset 88:6ca02b188dba

add LuanBit to clean up code; add repr(); git-svn-id: https://luan-java.googlecode.com/svn/trunk@89 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Wed, 27 Feb 2013 23:50:32 +0000
parents eaf37cfa30c2
children 8ef2d6701541
files src/luan/Luan.java src/luan/LuanBit.java src/luan/LuanException.java src/luan/LuanJavaFunction.java src/luan/LuanRepr.java src/luan/LuanState.java src/luan/LuanTable.java src/luan/interp/BinaryOpExpr.java src/luan/interp/ConcatExpr.java src/luan/interp/EqExpr.java src/luan/interp/FnCall.java src/luan/interp/GenericForStmt.java src/luan/interp/IndexExpr.java src/luan/interp/LeExpr.java src/luan/interp/LenExpr.java src/luan/interp/LtExpr.java src/luan/interp/LuanCompiler.java src/luan/interp/LuanStateImpl.java src/luan/interp/NumericForStmt.java src/luan/interp/OutputStmt.java src/luan/interp/ReturnStmt.java src/luan/interp/SetTableEntry.java src/luan/interp/UnmExpr.java src/luan/lib/BasicLib.java src/luan/lib/HttpLib.java src/luan/lib/JavaLib.java src/luan/lib/PackageLib.java src/luan/lib/StringLib.java src/luan/lib/TableLib.java src/luan/lib/Utils.java src/luan/tools/CmdLine.java src/luan/tools/WebShell.java
diffstat 32 files changed, 260 insertions(+), 161 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/Luan.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/Luan.java	Wed Feb 27 23:50:32 2013 +0000
@@ -94,5 +94,30 @@
 		return obj.toString();
 	}
 
+	public static String stringEncode(String s) {
+		s = s.replace("\\","\\\\");
+		s = s.replace("\u0007","\\a");
+		s = s.replace("\b","\\b");
+		s = s.replace("\f","\\f");
+		s = s.replace("\n","\\n");
+		s = s.replace("\r","\\r");
+		s = s.replace("\t","\\t");
+		s = s.replace("\u000b","\\v");
+		s = s.replace("\"","\\\"");
+		return s;
+	}
+
+	public static String repr(Object obj) {
+		if( obj == null )
+			return "nil";
+		if( obj instanceof Number )
+			return Luan.toString((Number)obj);
+		if( obj instanceof String )
+			return "\"" + stringEncode((String)obj) + "\"";
+		if( obj instanceof LuanRepr )
+			return ((LuanRepr)obj).repr();
+		return null;
+	}
+
 	private Luan() {}  // never
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/LuanBit.java	Wed Feb 27 23:50:32 2013 +0000
@@ -0,0 +1,105 @@
+package luan;
+
+import java.util.List;
+
+
+public final class LuanBit {
+	public final LuanState luan;
+	public final LuanElement el;
+
+	LuanBit(LuanState luan,LuanElement el) {
+		this.luan = luan;
+		this.el = el;
+	}
+
+	public LuanException exception(Object msg) {
+		return new LuanException(this,msg);
+	}
+
+	public Object[] call(LuanFunction fn,String fnName,Object... args) throws LuanException {
+		List<StackTraceElement> stackTrace = luan.stackTrace;
+		stackTrace.add( new StackTraceElement(el,fnName) );
+		try {
+			return fn.call(luan,args);
+		} finally {
+			stackTrace.remove(stackTrace.size()-1);
+		}
+	}
+
+	public String checkString(Object obj) throws LuanException {
+		String s = Luan.asString(obj);
+		if( s == null )
+			throw exception( "attempt to use a " + Luan.type(obj) + " as a string" );
+		return s;
+	}
+
+	public Number checkNumber(Object obj) throws LuanException {
+		Number n = Luan.toNumber(obj);
+		if( n == null )
+			throw exception( "attempt to perform arithmetic on a " + Luan.type(obj) + " value" );
+		return n;
+	}
+
+	public LuanFunction checkFunction(Object obj) throws LuanException {
+		if( obj instanceof LuanFunction )
+			return (LuanFunction)obj;
+		throw exception( "attempt to call a " + Luan.type(obj) + " value" );
+	}
+
+	public String toString(Object obj) throws LuanException {
+		LuanFunction fn = getHandlerFunction("__tostring",obj);
+		if( fn != null )
+			return checkString( Luan.first( call(fn,"__tostring",obj) ) );
+		return Luan.toString(obj);
+	}
+
+	public String repr(Object obj) throws LuanException {
+		LuanFunction fn = getHandlerFunction("__repr",obj);
+		if( fn != null )
+			return checkString( Luan.first( call(fn,"__repr",obj) ) );
+		String repr = Luan.repr(obj);
+		if( repr==null )
+			throw exception( "value '" + obj + "' doesn't support repr()" );
+		return repr;
+	}
+
+	public LuanFunction getHandlerFunction(String op,Object obj) throws LuanException {
+		Object f = luan.getHandler(op,obj);
+		if( f == null )
+			return null;
+		return checkFunction(f);
+	}
+
+	public LuanFunction getBinHandler(String op,Object o1,Object o2) throws LuanException {
+		LuanFunction f1 = getHandlerFunction(op,o1);
+		if( f1 != null )
+			return f1;
+		return getHandlerFunction(op,o2);
+	}
+
+	public boolean isLessThan(Object o1,Object o2) throws LuanException {
+		if( o1 instanceof Number && o2 instanceof Number ) {
+			Number n1 = (Number)o1;
+			Number n2 = (Number)o2;
+			return n1.doubleValue() < n2.doubleValue();
+		}
+		if( o1 instanceof String && o2 instanceof String ) {
+			String s1 = (String)o1;
+			String s2 = (String)o2;
+			return s1.compareTo(s2) < 0;
+		}
+		LuanFunction fn = getBinHandler("__lt",o1,o2);
+		if( fn != null )
+			return Luan.toBoolean( Luan.first(call(fn,"__lt",o1,o2)) );
+		throw exception( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) );
+	}
+
+	public Object arithmetic(String op,Object o1,Object o2) throws LuanException {
+		LuanFunction fn = getBinHandler(op,o1,o2);
+		if( fn != null )
+			return Luan.first(call(fn,op,o1,o2));
+		String type = Luan.toNumber(o1)==null ? Luan.type(o1) : Luan.type(o2);
+		throw exception("attempt to perform arithmetic on a "+type+" value");
+	}
+
+}
--- a/src/luan/LuanException.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/LuanException.java	Wed Feb 27 23:50:32 2013 +0000
@@ -4,9 +4,9 @@
 public class LuanException extends Exception {
 	private final String stackTrace;
 
-	public LuanException(LuanState luan,LuanElement el,Object msg) {
+	LuanException(LuanBit bit,Object msg) {
 		super(message(msg),cause(msg));
-		stackTrace = stackTrace(luan,el,msg);
+		stackTrace = stackTrace(bit.luan,bit.el,msg);
 	}
 
 	@Override public String getMessage() {
--- a/src/luan/LuanJavaFunction.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/LuanJavaFunction.java	Wed Feb 27 23:50:32 2013 +0000
@@ -74,7 +74,7 @@
 			if( cause instanceof Error )
 				throw (Error)cause;
 			if( cause instanceof RuntimeException )
-				throw new LuanException(luan,LuanElement.JAVA,cause);
+				throw luan.JAVA.exception(cause);
 			if( cause instanceof LuanException )
 				throw (LuanException)cause;
 			throw new RuntimeException(e);
@@ -94,10 +94,10 @@
 				String expected = paramType.getSimpleName();
 				if( arg==null ) {
 					if( paramType.isPrimitive() )
-						throw new LuanException(luan,LuanElement.JAVA,"bad argument #"+(i+1-start)+" ("+expected+" expected, got nil)");
+						throw luan.JAVA.exception("bad argument #"+(i+1-start)+" ("+expected+" expected, got nil)");
 				} else {
 					String got = arg.getClass().getSimpleName();
-					throw new LuanException(luan,LuanElement.JAVA,"bad argument #"+(i+1-start)+" ("+expected+" expected, got "+got+")");
+					throw luan.JAVA.exception("bad argument #"+(i+1-start)+" ("+expected+" expected, got "+got+")");
 				}
 			}
 		}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/LuanRepr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -0,0 +1,6 @@
+package luan;
+
+
+public interface LuanRepr {
+	public String repr();
+}
--- a/src/luan/LuanState.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/LuanState.java	Wed Feb 27 23:50:32 2013 +0000
@@ -19,6 +19,9 @@
 public abstract class LuanState implements DeepCloneable<LuanState> {
 	public static final String _G = "_G";
 
+	public final LuanBit JAVA = bit(LuanElement.JAVA);
+	public final LuanBit COMPILER = bit(LuanElement.COMPILER);
+
 	private LuanTable loaded;
 	private LuanTable preload;
 	private final List<String> defaultMods;
@@ -124,7 +127,7 @@
 
 	public final Object[] eval(String cmd,String sourceName,LuanTable env) throws LuanException {
 		LuanFunction fn = BasicLib.load(this,cmd,sourceName,env);
-		return call(fn,null,null);
+		return JAVA.call(fn,null);
 	}
 
 
@@ -145,47 +148,8 @@
 		mtGetters.add(mg);
 	}
 
-	public final Object[] call(LuanFunction fn,LuanElement calledFrom,String fnName,Object... args) throws LuanException {
-		stackTrace.add( new StackTraceElement(calledFrom,fnName) );
-		try {
-			return fn.call(this,args);
-		} finally {
-			stackTrace.remove(stackTrace.size()-1);
-		}
-	}
-
-	public final String checkString(LuanElement el,Object obj) throws LuanException {
-		String s = Luan.asString(obj);
-		if( s == null )
-			throw new LuanException( this, el, "attempt to use a " + Luan.type(obj) + " as a string" );
-		return s;
-	}
-
-	public final Number checkNumber(LuanElement el,Object obj) throws LuanException {
-		Number n = Luan.toNumber(obj);
-		if( n == null )
-			throw new LuanException( this, el, "attempt to perform arithmetic on a " + Luan.type(obj) + " value" );
-		return n;
-	}
-
-	public final LuanFunction checkFunction(LuanElement el,Object obj) throws LuanException {
-		if( obj instanceof LuanFunction )
-			return (LuanFunction)obj;
-		throw new LuanException( this, el, "attempt to call a " + Luan.type(obj) + " value" );
-	}
-
-	public final String toString(LuanElement el,Object obj) throws LuanException {
-		LuanFunction fn = getHandlerFunction(el,"__tostring",obj);
-		if( fn != null )
-			return checkString( el, Luan.first( call(fn,el,"__tostring",obj) ) );
-		return Luan.toString(obj);
-	}
-
-	public final LuanFunction getHandlerFunction(LuanElement el,String op,Object obj) throws LuanException {
-		Object f = getHandler(op,obj);
-		if( f == null )
-			return null;
-		return checkFunction(el,f);
+	public final LuanBit bit(LuanElement el) {
+		return new LuanBit(this,el);
 	}
 
 	public final Object getHandler(String op,Object obj) {
@@ -193,28 +157,4 @@
 		return t==null ? null : t.get(op);
 	}
 
-
-	public final LuanFunction getBinHandler(LuanElement el,String op,Object o1,Object o2) throws LuanException {
-		LuanFunction f1 = getHandlerFunction(el,op,o1);
-		if( f1 != null )
-			return f1;
-		return getHandlerFunction(el,op,o2);
-	}
-
-	public final boolean isLessThan(LuanElement el,Object o1,Object o2) throws LuanException {
-		if( o1 instanceof Number && o2 instanceof Number ) {
-			Number n1 = (Number)o1;
-			Number n2 = (Number)o2;
-			return n1.doubleValue() < n2.doubleValue();
-		}
-		if( o1 instanceof String && o2 instanceof String ) {
-			String s1 = (String)o1;
-			String s2 = (String)o2;
-			return s1.compareTo(s2) < 0;
-		}
-		LuanFunction fn = getBinHandler(el,"__lt",o1,o2);
-		if( fn != null )
-			return Luan.toBoolean( Luan.first(call(fn,el,"__lt",o1,o2)) );
-		throw new LuanException( this, el, "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) );
-	}
 }
--- a/src/luan/LuanTable.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/LuanTable.java	Wed Feb 27 23:50:32 2013 +0000
@@ -11,9 +11,10 @@
 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>> {
+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;
@@ -105,13 +106,13 @@
 		return "table: " + Integer.toHexString(hashCode());
 	}
 
-	public String show() {
-		return show( Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
+	public String repr() {
+		return repr( Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
 	}
 
-	private String show(Set<LuanTable> set) {
+	private String repr(Set<LuanTable> set) {
 		if( !set.add(this) ) {
-			return "...";
+			return "\"<circular reference>\"";
 		}
 		StringBuilder sb = new StringBuilder();
 		sb.append('{');
@@ -130,7 +131,7 @@
 					}
 					if( gotNull )
 						sb.append(i+1).append('=');
-					sb.append(show(set,obj));
+					sb.append(repr(set,obj));
 				}
 			}
 		}
@@ -141,19 +142,33 @@
 				} else {
 					sb.append(", ");
 				}
-				sb.append(show(set,entry.getKey())).append('=').append(show(set,entry.getValue()));
+				sb.append(reprKey(set,entry.getKey())).append('=').append(repr(set,entry.getValue()));
 			}
 		}
 		sb.append('}');
 		return sb.toString();
 	}
 
-	private static String show(Set<LuanTable> set,Object obj) {
+	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.show(set);
+			return t.repr(set);
 		} else {
-			return Luan.toString(obj);
+			String s = Luan.repr(obj);
+			if( s == null )
+				s = "\"<couldn't repr: " + Luan.stringEncode(Luan.toString(obj)) + ">\"";
+			return s;
 		}
 	}
 
--- a/src/luan/interp/BinaryOpExpr.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/BinaryOpExpr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -18,7 +18,7 @@
 	}
 
 	Object arithmetic(LuanStateImpl luan,String op,Object o1,Object o2) throws LuanException {
-		return luan.arithmetic(se(),"__mod",o1,o2);
+		return luan.bit(se()).arithmetic("__mod",o1,o2);
 	}
 
 }
--- a/src/luan/interp/ConcatExpr.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/ConcatExpr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -15,8 +15,8 @@
 	@Override public Object eval(LuanStateImpl luan) throws LuanException {
 		Object o1 = op1.eval(luan);
 		Object o2 = op2.eval(luan);
-		String s1 = luan.toString(op1.se(),o1);
-		String s2 = luan.toString(op2.se(),o2);
+		String s1 = luan.bit(op1.se()).toString(o1);
+		String s2 = luan.bit(op2.se()).toString(o2);
 /*
 		if( s1 != null && s2 != null )
 			return s1 + s2;
--- a/src/luan/interp/EqExpr.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/EqExpr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -5,6 +5,7 @@
 import luan.LuanTable;
 import luan.LuanException;
 import luan.LuanSource;
+import luan.LuanBit;
 
 
 final class EqExpr extends BinaryOpExpr {
@@ -32,7 +33,8 @@
 		Object f = mt1.get("__eq");
 		if( f == null || !f.equals(mt2.get("__eq")) )
 			return null;
-		LuanFunction fn = luan.checkFunction(se,f);
-		return Luan.toBoolean( Luan.first(luan.call(fn,se,"__eq",o1,o2)) );
+		LuanBit bit = luan.bit(se);
+		LuanFunction fn = bit.checkFunction(f);
+		return Luan.toBoolean( Luan.first(bit.call(fn,"__eq",o1,o2)) );
 	}
 }
--- a/src/luan/interp/FnCall.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/FnCall.java	Wed Feb 27 23:50:32 2013 +0000
@@ -25,11 +25,11 @@
 	private Object[] call(LuanStateImpl luan,Object o) throws LuanException {
 		if( o instanceof LuanFunction ) {
 			LuanFunction fn = (LuanFunction)o;
-			return luan.call( fn, se, fnName, args.eval(luan) );
+			return luan.bit(se).call( fn, fnName, args.eval(luan) );
 		}
 		Object h = luan.getHandler("__call",o);
 		if( h != null )
 			return call(luan,h);
-		throw new LuanException( luan, fnExpr.se(), "attempt to call '"+fnExpr.se().text()+"' (a " + Luan.type(o) + " value)" );
+		throw luan.bit(fnExpr.se()).exception( "attempt to call '"+fnExpr.se().text()+"' (a " + Luan.type(o) + " value)" );
 	}
 }
--- a/src/luan/interp/GenericForStmt.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/GenericForStmt.java	Wed Feb 27 23:50:32 2013 +0000
@@ -21,10 +21,10 @@
 	}
 
 	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		LuanFunction iter = luan.checkFunction( se, iterExpr.eval(luan) );
+		LuanFunction iter = luan.bit(se).checkFunction( iterExpr.eval(luan) );
 		try {
 			while(true) {
-				Object[] vals = luan.call(iter,iterExpr.se(),iterExpr.se().text());
+				Object[] vals = luan.bit(iterExpr.se()).call(iter,iterExpr.se().text());
 				if( vals.length==0 || vals[0]==null )
 					break;
 				for( int i=0; i<nVars; i++ ) {
--- a/src/luan/interp/IndexExpr.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/IndexExpr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -30,11 +30,11 @@
 		} else {
 			h = luan.getHandler("__index",t);
 			if( h==null )
-				throw new LuanException( luan, op1.se(), "attempt to index '"+op1.se().text()+"' (a " + Luan.type(t) + " value)" );
+				throw luan.bit(op1.se()).exception( "attempt to index '"+op1.se().text()+"' (a " + Luan.type(t) + " value)" );
 		}
 		if( h instanceof LuanFunction ) {
 			LuanFunction fn = (LuanFunction)h;
-			return Luan.first(luan.call(fn,se,"__index",t,key));
+			return Luan.first(luan.bit(se).call(fn,"__index",t,key));
 		}
 		return index(luan,h,key);
 	}
--- a/src/luan/interp/LeExpr.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/LeExpr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -4,6 +4,7 @@
 import luan.LuanFunction;
 import luan.LuanException;
 import luan.LuanSource;
+import luan.LuanBit;
 
 
 final class LeExpr extends BinaryOpExpr {
@@ -25,12 +26,13 @@
 			String s2 = (String)o2;
 			return s1.compareTo(s2) <= 0;
 		}
-		LuanFunction fn = luan.getBinHandler(se,"__le",o1,o2);
+		LuanBit bit = luan.bit(se);
+		LuanFunction fn = bit.getBinHandler("__le",o1,o2);
 		if( fn != null )
-			return Luan.toBoolean( Luan.first(luan.call(fn,se,"__le",o1,o2)) );
-		fn = luan.getBinHandler(se,"__lt",o1,o2);
+			return Luan.toBoolean( Luan.first(bit.call(fn,"__le",o1,o2)) );
+		fn = bit.getBinHandler("__lt",o1,o2);
 		if( fn != null )
-			return !Luan.toBoolean( Luan.first(luan.call(fn,se,"__lt",o2,o1)) );
-		throw new LuanException( luan, se, "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) );
+			return !Luan.toBoolean( Luan.first(bit.call(fn,"__lt",o2,o1)) );
+		throw bit.exception( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) );
 	}
 }
--- a/src/luan/interp/LenExpr.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/LenExpr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -5,6 +5,7 @@
 import luan.LuanFunction;
 import luan.LuanException;
 import luan.LuanSource;
+import luan.LuanBit;
 
 
 final class LenExpr extends UnaryOpExpr {
@@ -19,13 +20,14 @@
 			String s = (String)o;
 			return s.length();
 		}
-		LuanFunction fn = luan.getHandlerFunction(se,"__len",o);
+		LuanBit bit = luan.bit(se);
+		LuanFunction fn = bit.getHandlerFunction("__len",o);
 		if( fn != null )
-			return Luan.first(luan.call(fn,se,"__len",o));
+			return Luan.first(bit.call(fn,"__len",o));
 		if( o instanceof LuanTable ) {
 			LuanTable t = (LuanTable)o;
 			return t.length();
 		}
-		throw new LuanException( luan, se, "attempt to get length of a " + Luan.type(o) + " value" );
+		throw bit.exception( "attempt to get length of a " + Luan.type(o) + " value" );
 	}
 }
--- a/src/luan/interp/LtExpr.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/LtExpr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -15,6 +15,6 @@
 	@Override public Object eval(LuanStateImpl luan) throws LuanException {
 		Object o1 = op1.eval(luan);
 		Object o2 = op2.eval(luan);
-		return luan.isLessThan(se,o1,o2);
+		return luan.bit(se).isLessThan(o1,o2);
 	}
 }
--- a/src/luan/interp/LuanCompiler.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/LuanCompiler.java	Wed Feb 27 23:50:32 2013 +0000
@@ -22,7 +22,7 @@
 		ParsingResult<?> result = new ReportingParseRunner(parser.Target()).run(src.text);
 //		ParsingResult<?> result = new TracingParseRunner(parser.Target()).run(src);
 		if( result.hasErrors() )
-			throw new LuanException( luan, LuanElement.COMPILER, ErrorUtils.printParseErrors(result) );
+			throw luan.COMPILER.exception( ErrorUtils.printParseErrors(result) );
 		FnDef fnDef = (FnDef)result.resultValue;
 		return new Closure((LuanStateImpl)luan,fnDef);
 	}
--- a/src/luan/interp/LuanStateImpl.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/LuanStateImpl.java	Wed Feb 27 23:50:32 2013 +0000
@@ -132,13 +132,4 @@
 		return (LuanTable)frame.closure.upValues()[0].get();
 	}
 
-
-	final Object arithmetic(LuanElement el,String op,Object o1,Object o2) throws LuanException {
-		LuanFunction fn = getBinHandler(el,op,o1,o2);
-		if( fn != null )
-			return Luan.first(call(fn,el,op,o1,o2));
-		String type = Luan.toNumber(o1)==null ? Luan.type(o1) : Luan.type(o2);
-		throw new LuanException(this,el,"attempt to perform arithmetic on a "+type+" value");
-	}
-
 }
--- a/src/luan/interp/NumericForStmt.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/NumericForStmt.java	Wed Feb 27 23:50:32 2013 +0000
@@ -3,6 +3,7 @@
 import luan.Luan;
 import luan.LuanException;
 import luan.LuanSource;
+import luan.LuanBit;
 
 
 final class NumericForStmt extends CodeImpl implements Stmt {
@@ -22,9 +23,10 @@
 	}
 
 	@Override public void eval(LuanStateImpl luan) throws LuanException {
-		double v = luan.checkNumber( se, fromExpr.eval(luan) ).doubleValue();
-		double limit = luan.checkNumber( se, toExpr.eval(luan) ).doubleValue();
-		double step = luan.checkNumber( se, stepExpr.eval(luan) ).doubleValue();
+		LuanBit bit = luan.bit(se);
+		double v = bit.checkNumber( fromExpr.eval(luan) ).doubleValue();
+		double limit = bit.checkNumber( toExpr.eval(luan) ).doubleValue();
+		double step = bit.checkNumber( stepExpr.eval(luan) ).doubleValue();
 		try {
 			while( step > 0.0 && v <= limit || step < 0.0 && v >= limit ) {
 				luan.stackSet( iVar, v );
--- a/src/luan/interp/OutputStmt.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/OutputStmt.java	Wed Feb 27 23:50:32 2013 +0000
@@ -3,6 +3,7 @@
 import luan.Luan;
 import luan.LuanSource;
 import luan.LuanException;
+import luan.LuanBit;
 
 
 final class OutputStmt extends CodeImpl implements Stmt {
@@ -14,8 +15,9 @@
 	}
 
 	@Override public void eval(LuanStateImpl luan) throws LuanException {
+		LuanBit bit = luan.bit(se);
 		for( Object obj : expressions.eval(luan) ) {
-			luan.out.print( luan.toString(se,obj) );
+			luan.out.print( bit.toString(obj) );
 		}
 	}
 
--- a/src/luan/interp/ReturnStmt.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/ReturnStmt.java	Wed Feb 27 23:50:32 2013 +0000
@@ -26,11 +26,11 @@
 	@Override public void eval(LuanStateImpl luan) throws LuanException {
 		luan.returnValues = expressions.eval(luan);
 		if( tailFnExpr != null ) {
-			LuanFunction tailFn = luan.checkFunction( se, tailFnExpr.eval(luan) );
+			LuanFunction tailFn = luan.bit(se).checkFunction( tailFnExpr.eval(luan) );
 			if( tailFn instanceof Closure ) {
 				luan.tailFn = (Closure)tailFn;
 			} else {
-				luan.returnValues =  luan.call(tailFn,tailFnExpr.se(),tailFnExpr.se().text(),luan.returnValues);
+				luan.returnValues =  luan.bit(tailFnExpr.se()).call(tailFn,tailFnExpr.se().text(),luan.returnValues);
 			}
 		}
 		if( throwReturnException )
--- a/src/luan/interp/SetTableEntry.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/SetTableEntry.java	Wed Feb 27 23:50:32 2013 +0000
@@ -35,11 +35,11 @@
 		} else {
 			h = luan.getHandler("__newindex",t);
 			if( h==null )
-				throw new LuanException( luan, se, "attempt to index a " + Luan.type(t) + " value" );
+				throw luan.bit(se).exception( "attempt to index a " + Luan.type(t) + " value" );
 		}
 		if( h instanceof LuanFunction ) {
 			LuanFunction fn = (LuanFunction)h;
-			luan.call(fn,se,"__newindex",t,key,value);
+			luan.bit(se).call(fn,"__newindex",t,key,value);
 			return;
 		}
 		newindex(luan,h,key,value);
--- a/src/luan/interp/UnmExpr.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/interp/UnmExpr.java	Wed Feb 27 23:50:32 2013 +0000
@@ -4,6 +4,7 @@
 import luan.LuanFunction;
 import luan.LuanException;
 import luan.LuanSource;
+import luan.LuanBit;
 
 
 // unary minus
@@ -18,10 +19,11 @@
 		Number n = Luan.toNumber(o);
 		if( n != null )
 			return -n.doubleValue();
-		LuanFunction fn = luan.getHandlerFunction(se,"__unm",o);
+		LuanBit bit = luan.bit(se);
+		LuanFunction fn = bit.getHandlerFunction("__unm",o);
 		if( fn != null ) {
-			return Luan.first(luan.call(fn,se,"__unm",o));
+			return Luan.first(bit.call(fn,"__unm",o));
 		}
-		throw new LuanException(luan,se,"attempt to perform arithmetic on a "+Luan.type(o)+" value");
+		throw bit.exception("attempt to perform arithmetic on a "+Luan.type(o)+" value");
 	}
 }
--- a/src/luan/lib/BasicLib.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/lib/BasicLib.java	Wed Feb 27 23:50:32 2013 +0000
@@ -46,6 +46,7 @@
 				add( global, "raw_get", LuanTable.class, Object.class );
 				add( global, "raw_len", LuanState.class, Object.class );
 				add( global, "raw_set", LuanTable.class, Object.class, Object.class );
+				add( global, "repr", LuanState.class, Object.class );
 				add( global, "set_metatable", LuanTable.class, LuanTable.class );
 				add( global, "to_number", Object.class, Integer.class );
 				add( global, "to_string", LuanState.class, Object.class );
@@ -66,7 +67,7 @@
 		for( int i=0; i<args.length; i++ ) {
 			if( i > 0 )
 				luan.out.print('\t');
-			luan.out.print( luan.toString(LuanElement.JAVA,args[i]) );
+			luan.out.print( luan.JAVA.toString(args[i]) );
 		}
 		luan.out.println();
 	}
@@ -85,13 +86,13 @@
 			String src = fileName==null ? Utils.readAll(new InputStreamReader(System.in)) : Utils.read(new File(fileName));
 			return load(luan,src,fileName,env);
 		} catch(IOException e) {
-			throw new LuanException(luan,LuanElement.JAVA,e);
+			throw luan.JAVA.exception(e);
 		}
 	}
 
 	public static Object[] do_file(LuanState luan,String fileName,LuanTable env) throws LuanException {
 		LuanFunction fn = load_file(luan,fileName,env);
-		return luan.call(fn,LuanElement.JAVA,null);
+		return luan.JAVA.call(fn,null);
 	}
 
 	private static LuanFunction pairs(final Iterator<Map.Entry<Object,Object>> iter) {
@@ -146,7 +147,7 @@
 			LuanTable t = (LuanTable)v;
 			return t.length();
 		}
-		throw new LuanException( luan, LuanElement.JAVA, "bad argument #1 to 'raw_len' (table or string expected)" );
+		throw luan.JAVA.exception( "bad argument #1 to 'raw_len' (table or string expected)" );
 	}
 
 	public static Number to_number(Object e,Integer base) {
@@ -154,11 +155,11 @@
 	}
 
 	public static String to_string(LuanState luan,Object v) throws LuanException {
-		return luan.toString(LuanElement.JAVA,v);
+		return luan.JAVA.toString(v);
 	}
 
 	public static void error(LuanState luan,Object msg) throws LuanException {
-		throw new LuanException(luan,LuanElement.JAVA,msg);
+		throw luan.JAVA.exception(msg);
 	}
 
 	public static Object assert_(LuanState luan,Object v,String msg) throws LuanException {
@@ -166,7 +167,7 @@
 			return v;
 		if( msg == null )
 			msg = "assertion failed!";
-		throw new LuanException( luan, LuanElement.JAVA, msg );
+		throw luan.JAVA.exception( msg );
 	}
 
 	public static String assert_string(LuanState luan,String v) throws LuanException {
@@ -190,8 +191,12 @@
 
 	public static Object assert_nil(LuanState luan,Object v) throws LuanException {
 		if( v != null )
-			throw new LuanException(luan,LuanElement.JAVA,"bad argument #1 (nil expected, got "+Luan.type(v)+")");
+			throw luan.JAVA.exception("bad argument #1 (nil expected, got "+Luan.type(v)+")");
 		return v;
 	}
 
+	public static String repr(LuanState luan,Object v) throws LuanException {
+		return luan.JAVA.repr(v);
+	}
+
 }
--- a/src/luan/lib/HttpLib.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/lib/HttpLib.java	Wed Feb 27 23:50:32 2013 +0000
@@ -24,7 +24,7 @@
 		PackageLib.require(luan,NAME);
 		Object fn = luan.get(HttpLib.FN_NAME);
 		if( !(fn instanceof LuanFunction) )
-			throw new LuanException( luan, LuanElement.JAVA, "function '"+HttpLib.FN_NAME+"' not defined" );
+			throw luan.JAVA.exception( "function '"+HttpLib.FN_NAME+"' not defined" );
 	}
 
 	public static void service(LuanState luan,HttpServletRequest request,HttpServletResponse response)
@@ -49,7 +49,7 @@
 		module.put("parameters",parameters);
 		module.put("parameter_lists",parameter_lists);
 
-		luan.call(fn,LuanElement.JAVA,FN_NAME);
+		luan.JAVA.call(fn,FN_NAME);
 	}
 /*
 	private final HttpServletRequest request;
--- a/src/luan/lib/JavaLib.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/lib/JavaLib.java	Wed Feb 27 23:50:32 2013 +0000
@@ -99,7 +99,7 @@
 					}
 				}
 			}
-			throw new LuanException(luan,LuanElement.JAVA,"invalid member '"+key+"' for: "+obj);
+			throw luan.JAVA.exception("invalid member '"+key+"' for: "+obj);
 		}
 		Class cls = obj.getClass();
 		if( cls.isArray() ) {
@@ -110,7 +110,7 @@
 			if( i != null ) {
 				return Array.get(obj,i);
 			}
-			throw new LuanException(luan,LuanElement.JAVA,"invalid member '"+key+"' for java array: "+obj);
+			throw luan.JAVA.exception("invalid member '"+key+"' for java array: "+obj);
 		}
 		if( key instanceof String ) {
 			String name = (String)key;
@@ -123,7 +123,7 @@
 				}
 			}
 		}
-		throw new LuanException(luan,LuanElement.JAVA,"invalid member '"+key+"' for java object: "+obj);
+		throw luan.JAVA.exception("invalid member '"+key+"' for java object: "+obj);
 	}
 
 	private static Object member(Object obj,List<Member> members) throws LuanException {
@@ -166,7 +166,7 @@
 					return;
 				}
 			}
-			throw new LuanException(luan,LuanElement.JAVA,"invalid member '"+key+"' for: "+obj);
+			throw luan.JAVA.exception("invalid member '"+key+"' for: "+obj);
 		}
 		Class cls = obj.getClass();
 		if( cls.isArray() ) {
@@ -175,7 +175,7 @@
 				Array.set(obj,i,value);
 				return;
 			}
-			throw new LuanException(luan,LuanElement.JAVA,"invalid member '"+key+"' for java array: "+obj);
+			throw luan.JAVA.exception("invalid member '"+key+"' for java array: "+obj);
 		}
 		if( key instanceof String ) {
 			String name = (String)key;
@@ -187,7 +187,7 @@
 				return;
 			}
 		}
-		throw new LuanException(luan,LuanElement.JAVA,"invalid member '"+key+"' for java object: "+obj);
+		throw luan.JAVA.exception("invalid member '"+key+"' for java object: "+obj);
 	}
 
 	private static void setMember(Object obj,List<Member> members,Object value) {
@@ -293,7 +293,7 @@
 			try {
 				cls = Thread.currentThread().getContextClassLoader().loadClass(name);
 			} catch(ClassNotFoundException e2) {
-				throw new LuanException(luan,LuanElement.JAVA,e);
+				throw luan.JAVA.exception(e);
 			}
 		}
 		return new Static(cls);
@@ -324,7 +324,7 @@
 					return fn.rawCall(luan,args);
 				} catch(IllegalArgumentException e) {}
 			}
-			throw new LuanException(luan,LuanElement.JAVA,"no method matched args");
+			throw luan.JAVA.exception("no method matched args");
 		}
 	}
 
@@ -361,7 +361,7 @@
 			if( !cls.isInstance(v) ) {
 				String got = v.getClass().getSimpleName();
 				String expected = cls.getSimpleName();
-				throw new LuanException(luan,LuanElement.JAVA,"bad argument #1 ("+expected+" expected, got "+got+")");
+				throw luan.JAVA.exception("bad argument #1 ("+expected+" expected, got "+got+")");
 			}
 			return v;
 		}
@@ -391,8 +391,8 @@
 					Object fnObj = t.get(name);
 					if( fnObj==null && base!=null )
 						return method.invoke(base,args);
-					LuanFunction fn = luan.checkFunction(LuanElement.JAVA,fnObj);
-					return Luan.first(luan.call(fn,LuanElement.JAVA,name,args));
+					LuanFunction fn = luan.JAVA.checkFunction(fnObj);
+					return Luan.first(luan.JAVA.call(fn,name,args));
 				}
 			}
 		);
--- a/src/luan/lib/PackageLib.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/lib/PackageLib.java	Wed Feb 27 23:50:32 2013 +0000
@@ -61,18 +61,18 @@
 				searchers = new LuanTable(Collections.<Object>singletonList(preloadSearcher));
 			for( Object s : searchers.asList() ) {
 				LuanFunction searcher = (LuanFunction)s;
-				Object[] a = luan.call(searcher,LuanElement.JAVA,"<searcher>",modName);
+				Object[] a = luan.JAVA.call(searcher,"<searcher>",modName);
 				if( a.length >= 1 && a[0] instanceof LuanFunction ) {
 					LuanFunction loader = (LuanFunction)a[0];
-					luan.call(loader,LuanElement.JAVA,"<loader>");
+					luan.JAVA.call(loader,"<loader>");
 					mod = (LuanTable)luan.loaded().get(modName);
 					if( mod==null )
-						throw new LuanException( luan, LuanElement.JAVA, "module '"+modName+"' didn't define its module" );
+						throw luan.JAVA.exception( "module '"+modName+"' didn't define its module" );
 					break;
 				}
 			}
 			if( mod == null )
-				throw new LuanException( luan, LuanElement.JAVA, "module '"+modName+"' not found" );
+				throw luan.JAVA.exception( "module '"+modName+"' not found" );
 		}
 		if( env != null )
 			env.put(modName,mod);
@@ -135,7 +135,7 @@
 				LuanFunction fn = BasicLib.load(luan,src,url.toString(),null);
 				fn.call(luan,EMPTY);
 			} catch(IOException e) {
-				throw new LuanException(luan,LuanElement.JAVA,e);
+				throw luan.JAVA.exception(e);
 			}
 		}
 	};
--- a/src/luan/lib/StringLib.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/lib/StringLib.java	Wed Feb 27 23:50:32 2013 +0000
@@ -171,7 +171,7 @@
 				if( Luan.toBoolean(val) ) {
 					String replacement = Luan.asString(val);
 					if( replacement==null )
-						throw new LuanException( luan, LuanElement.JAVA, "invalid replacement value (a "+Luan.type(val)+")" );
+						throw luan.JAVA.exception( "invalid replacement value (a "+Luan.type(val)+")" );
 					m.appendReplacement(sb,replacement);
 				}
 				i++;
@@ -194,11 +194,11 @@
 						args[j] = m.group(j);
 					}
 				}
-				Object val = Luan.first( luan.call(fn,LuanElement.JAVA,"repl-arg",args) );
+				Object val = Luan.first( luan.JAVA.call(fn,"repl-arg",args) );
 				if( Luan.toBoolean(val) ) {
 					String replacement = Luan.asString(val);
 					if( replacement==null )
-						throw new LuanException( luan, LuanElement.JAVA, "invalid replacement value (a "+Luan.type(val)+")" );
+						throw luan.JAVA.exception( "invalid replacement value (a "+Luan.type(val)+")" );
 					m.appendReplacement(sb,replacement);
 				}
 				i++;
@@ -206,7 +206,7 @@
 			m.appendTail(sb);
 			return new Object[]{ sb.toString(), i };
 		}
-		throw new LuanException( luan, LuanElement.JAVA, "bad argument #3 to 'gsub' (string/function/table expected)" );
+		throw luan.JAVA.exception( "bad argument #3 to 'gsub' (string/function/table expected)" );
 	}
 
 	// note - String.format() is too stupid to convert between ints and floats.
--- a/src/luan/lib/TableLib.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/lib/TableLib.java	Wed Feb 27 23:50:32 2013 +0000
@@ -52,7 +52,7 @@
 				buf.append(sep);
 			String s = Luan.asString(val);
 			if( s==null )
-				throw new LuanException( luan, LuanElement.JAVA, "invalid value ("+Luan.type(val)+") at index "+k+" in table for 'concat'" );
+				throw luan.JAVA.exception( "invalid value ("+Luan.type(val)+") at index "+k+" in table for 'concat'" );
 			buf.append(val);
 		}
 		return buf.toString();
@@ -62,7 +62,7 @@
 		try {
 			list.insert(pos,value);
 		} catch(IndexOutOfBoundsException e) {
-			throw new LuanException( luan, LuanElement.JAVA, e);
+			throw luan.JAVA.exception(e);
 		}
 	}
 
@@ -70,7 +70,7 @@
 		try {
 			return list.remove(pos);
 		} catch(IndexOutOfBoundsException e) {
-			throw new LuanException( luan, LuanElement.JAVA, e);
+			throw luan.JAVA.exception(e);
 		}
 	}
 
@@ -84,7 +84,7 @@
 			lt = new LessThan() {
 				public boolean isLessThan(Object o1,Object o2) {
 					try {
-						return luan.isLessThan(LuanElement.JAVA,o1,o2);
+						return luan.JAVA.isLessThan(o1,o2);
 					} catch(LuanException e) {
 						throw new LuanRuntimeException(e);
 					}
@@ -94,7 +94,7 @@
 			lt = new LessThan() {
 				public boolean isLessThan(Object o1,Object o2) {
 					try {
-						return Luan.toBoolean(Luan.first(luan.call(comp,LuanElement.JAVA,"comp-arg",o1,o2)));
+						return Luan.toBoolean(Luan.first(luan.JAVA.call(comp,"comp-arg",o1,o2)));
 					} catch(LuanException e) {
 						throw new LuanRuntimeException(e);
 					}
--- a/src/luan/lib/Utils.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/lib/Utils.java	Wed Feb 27 23:50:32 2013 +0000
@@ -16,7 +16,7 @@
 
 	public static void checkNotNull(LuanState luan,Object v,String expected) throws LuanException {
 		if( v == null )
-			throw new LuanException(luan,LuanElement.JAVA,"bad argument #1 ("+expected+" expected, got nil)");
+			throw luan.JAVA.exception("bad argument #1 ("+expected+" expected, got nil)");
 	}
 
 	public static String readAll(Reader in)
--- a/src/luan/tools/CmdLine.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/tools/CmdLine.java	Wed Feb 27 23:50:32 2013 +0000
@@ -43,7 +43,7 @@
 					String cmd = args[i];
 					try {
 						LuanFunction fn = BasicLib.load(luan,cmd,"(command line)",env);
-						luan.call(fn,null,null);
+						luan.JAVA.call(fn,null);
 					} catch(LuanException e) {
 						System.err.println("command line error: "+e.getMessage());
 						System.exit(-1);
@@ -75,7 +75,7 @@
 			env.put("arg",argsTable);
 			try {
 				LuanFunction fn = BasicLib.load_file(luan,file,env);
-				luan.call(fn,null,null,varArgs);
+				luan.JAVA.call(fn,null,varArgs);
 			} catch(LuanException e) {
 //				System.err.println("error: "+e.getMessage());
 				e.printStackTrace();
--- a/src/luan/tools/WebShell.java	Wed Feb 27 19:42:09 2013 +0000
+++ b/src/luan/tools/WebShell.java	Wed Feb 27 23:50:32 2013 +0000
@@ -75,7 +75,7 @@
 						for( int i=0; i<result.length; i++ ) {
 							if( i > 0 )
 								writer.write("  ");
-							writer.write(HtmlLib.encode(luan.toString(null,result[i])));
+							writer.write(HtmlLib.encode(luan.JAVA.toString(result[i])));
 						}
 						writer.write("\r\n");
 					}