changeset 35:e51906de0f11

implement metatables git-svn-id: https://luan-java.googlecode.com/svn/trunk@36 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Tue, 18 Dec 2012 07:05:58 +0000
parents 0cdc1da466ee
children 2a35154aec14
files src/luan/CmdLine.java src/luan/Lua.java src/luan/LuaTable.java src/luan/interp/AddExpr.java src/luan/interp/BinaryOpExpr.java src/luan/interp/ConcatExpr.java src/luan/interp/DivExpr.java src/luan/interp/EqExpr.java src/luan/interp/ExpressionsExpr.java src/luan/interp/FnCall.java src/luan/interp/GetExpr.java src/luan/interp/IndexExpr.java src/luan/interp/LeExpr.java src/luan/interp/LenExpr.java src/luan/interp/LtExpr.java src/luan/interp/LuaParser.java src/luan/interp/ModExpr.java src/luan/interp/MulExpr.java src/luan/interp/PowExpr.java src/luan/interp/SetTableEntry.java src/luan/interp/SubExpr.java src/luan/interp/TableExpr.java src/luan/interp/UnmExpr.java src/luan/interp/Utils.java src/luan/lib/BasicLib.java
diffstat 25 files changed, 289 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/CmdLine.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/CmdLine.java	Tue Dec 18 07:05:58 2012 +0000
@@ -8,7 +8,6 @@
 
 
 public class CmdLine {
-	static final String version = "Luan 0.0";
 
 	public static void main(String[] args) throws Exception {
 		LuaState lua = LuaCompiler.newLuaState();
@@ -56,16 +55,16 @@
 			}
 		}
 		if( showVersion )
-			System.out.println(version);
+			System.out.println(Lua.version);
 		if( i < args.length ) {
 			String file = args[i++];
 			Object[] varArgs = new Object[args.length-1];
 			System.arraycopy(args,1,varArgs,0,varArgs.length);
 			LuaTable argsTable = new LuaTable();
 			for( int j=0; j<args.length; j++ ) {
-				argsTable.set( new LuaNumber(j), args[j] );
+				argsTable.put( new LuaNumber(j), args[j] );
 			}
-			lua.global().set("arg",argsTable);
+			lua.global().put("arg",argsTable);
 			try {
 				LuaFunction fn = BasicLib.loadfile(lua,file);
 				fn.call(lua,varArgs);
--- a/src/luan/Lua.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/Lua.java	Tue Dec 18 07:05:58 2012 +0000
@@ -2,6 +2,7 @@
 
 
 public class Lua {
+	public static final String version = "Luan 0.0";
 
 	public static String type(Object obj) {
 		if( obj == null )
@@ -63,4 +64,11 @@
 		throw new LuaException( "attempt to call a " + type(obj) + " value" );
 	}
 
+	public static LuaTable getMetatable(Object obj) {
+		if( !(obj instanceof LuaTable) )
+			return null;
+		LuaTable table = (LuaTable)obj;
+		return table.getMetatable();
+	}
+
 }
--- a/src/luan/LuaTable.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/LuaTable.java	Tue Dec 18 07:05:58 2012 +0000
@@ -7,6 +7,7 @@
 
 public class LuaTable {
 	private final Map<Object,Object> map = new HashMap<Object,Object>();
+	private LuaTable metatable;
 
 	@Override public String toString() {
 		return "table: " + Integer.toHexString(hashCode());
@@ -16,11 +17,11 @@
 		return map.get(key);
 	}
 
-	public void set(Object key,Object val) {
+	public Object put(Object key,Object val) {
 		if( val == null ) {
-			map.remove(key);
+			return map.remove(key);
 		} else {
-			map.put(key,val);
+			return map.put(key,val);
 		}
 	}
 
@@ -36,4 +37,11 @@
 		return map.entrySet().iterator();
 	}
 
+	public LuaTable getMetatable() {
+		return metatable;
+	}
+
+	public void setMetatable(LuaTable metatable) {
+		this.metatable = metatable;
+	}
 }
--- a/src/luan/interp/AddExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/AddExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -12,8 +12,12 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		double n1 = Lua.checkNumber(op1.eval(lua)).value();
-		double n2 = Lua.checkNumber(op2.eval(lua)).value();
-		return new LuaNumber( n1 + n2 );
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		LuaNumber n1 = Lua.toNumber(o1);
+		LuaNumber n2 = Lua.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return new LuaNumber( n1.value() + n2.value() );
+		return arithmetic(lua,"__add",o1,o2);
 	}
 }
--- a/src/luan/interp/BinaryOpExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/BinaryOpExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -1,5 +1,10 @@
 package luan.interp;
 
+import luan.Lua;
+import luan.LuaTable;
+import luan.LuaFunction;
+import luan.LuaException;
+
 
 abstract class BinaryOpExpr implements Expr {
 	final Expr op1;
@@ -9,4 +14,19 @@
 		this.op1 = op1;
 		this.op2 = op2;
 	}
+
+	static final LuaFunction getBinHandler(String op,Object o1,Object o2) throws LuaException {
+		LuaFunction f1 = Utils.getHandler(op,o1);
+		if( f1 != null )
+			return f1;
+		return Utils.getHandler(op,o2);
+	}
+
+	static final Object arithmetic(LuaStateImpl lua,String op,Object o1,Object o2) throws LuaException {
+		LuaFunction fn = getBinHandler(op,o1,o2);
+		if( fn != null )
+			return Utils.first(fn.call(lua,o1,o2));
+		String type = Lua.toNumber(o1)==null ? Lua.type(o1) : Lua.type(o2);
+		throw new LuaException("attempt to perform arithmetic on a "+type+" value");
+	}
 }
--- a/src/luan/interp/ConcatExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/ConcatExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -1,7 +1,7 @@
 package luan.interp;
 
 import luan.Lua;
-import luan.LuaNumber;
+import luan.LuaFunction;
 import luan.LuaException;
 
 
@@ -12,13 +12,16 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		return toString(op1.eval(lua)) + toString(op2.eval(lua));
-	}
-
-	private static String toString(Object v) throws LuaException {
-		String s = Lua.asString(v);
-		if( s==null )
-			throw new LuaException( "attempt to concatenate a " + Lua.type(v) + " value" );
-		return s;
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		String s1 = Lua.asString(o1);
+		String s2 = Lua.asString(o2);
+		if( s1 != null && s2 != null )
+			return s1 + s2;
+		LuaFunction fn = getBinHandler("__concat",o1,o2);
+		if( fn != null )
+			return Utils.first(fn.call(lua,o1,o2));
+		String type = s1==null ? Lua.type(o1) : Lua.type(o2);
+		throw new LuaException( "attempt to concatenate a " + type + " value" );
 	}
 }
--- a/src/luan/interp/DivExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/DivExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -12,8 +12,12 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		double n1 = Lua.checkNumber(op1.eval(lua)).value();
-		double n2 = Lua.checkNumber(op2.eval(lua)).value();
-		return new LuaNumber( n1 / n2 );
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		LuaNumber n1 = Lua.toNumber(o1);
+		LuaNumber n2 = Lua.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return new LuaNumber( n1.value() / n2.value() );
+		return arithmetic(lua,"__div",o1,o2);
 	}
 }
--- a/src/luan/interp/EqExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/EqExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -2,6 +2,8 @@
 
 import luan.Lua;
 import luan.LuaNumber;
+import luan.LuaFunction;
+import luan.LuaTable;
 import luan.LuaException;
 
 
@@ -12,8 +14,20 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		Object v1 = op1.eval(lua);
-		Object v2 = op2.eval(lua);
-		return v1 == v2 || v1 != null && v1.equals(v2);
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		if( o1 == o2 || o1 != null && o1.equals(o2) )
+			return true;
+		if( !o1.getClass().equals(o2.getClass()) )
+			return false;
+		LuaTable mt1 = Lua.getMetatable(o1);
+		LuaTable mt2 = Lua.getMetatable(o2);
+		if( mt1==null || mt2==null )
+			return false;
+		Object f = mt1.get("__eq");
+		if( f == null || !f.equals(mt2.get("__eq")) )
+			return null;
+		LuaFunction fn = Lua.checkFunction(f);
+		return Lua.toBoolean( Utils.first(fn.call(lua,o1,o2)) );
 	}
 }
--- a/src/luan/interp/ExpressionsExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/ExpressionsExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -13,7 +13,6 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		Object[] a = expressions.eval(lua);
-		return a.length==0 ? null : a[0];
+		return Utils.first( expressions.eval(lua) );
 	}
 }
--- a/src/luan/interp/FnCall.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/FnCall.java	Tue Dec 18 07:05:58 2012 +0000
@@ -15,7 +15,17 @@
 	}
 
 	@Override public Object[] eval(LuaStateImpl lua) throws LuaException {
-		LuaFunction fn = Lua.checkFunction( fnExpr.eval(lua) );
-		return fn.call( lua, args.eval(lua) );
+		return call( lua, fnExpr.eval(lua) );
+	}
+
+	private Object[] call(LuaStateImpl lua,Object o) throws LuaException {
+		if( o instanceof LuaFunction ) {
+			LuaFunction fn = (LuaFunction)o;
+			return fn.call( lua, args.eval(lua) );
+		}
+		Object h = Utils.getHandlerObject("__call",o);
+		if( h != null )
+			return call(lua,h);
+		throw new LuaException( "attempt to call a " + Lua.type(o) + " value" );
 	}
 }
--- a/src/luan/interp/GetExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package luan.interp;
-
-import luan.Lua;
-import luan.LuaException;
-import luan.LuaTable;
-
-
-final class GetExpr extends BinaryOpExpr {
-
-	GetExpr(Expr op1,Expr op2) {
-		super(op1,op2);
-	}
-
-	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		Object t = op1.eval(lua);
-		if( t instanceof LuaTable ) {
-			LuaTable tbl = (LuaTable)t;
-			Object key = op2.eval(lua);
-			return tbl.get(key);
-		}
-		throw new LuaException( "attempt to index a " + Lua.type(t) + " value" );
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/IndexExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -0,0 +1,40 @@
+package luan.interp;
+
+import luan.Lua;
+import luan.LuaException;
+import luan.LuaTable;
+import luan.LuaFunction;
+
+
+final class IndexExpr extends BinaryOpExpr {
+
+	IndexExpr(Expr op1,Expr op2) {
+		super(op1,op2);
+	}
+
+	@Override public Object eval(LuaStateImpl lua) throws LuaException {
+		return index(lua,op1.eval(lua),op2.eval(lua));
+	}
+
+	private static Object index(LuaStateImpl lua,Object t,Object key) throws LuaException {
+		Object h;
+		if( t instanceof LuaTable ) {
+			LuaTable tbl = (LuaTable)t;
+			Object value = tbl.get(key);
+			if( value != null )
+				return value;
+			h = Utils.getHandlerObject("__index",t);
+			if( h==null )
+				return null;
+		} else {
+			h = Utils.getHandlerObject("__index",t);
+			if( h==null )
+				throw new LuaException( "attempt to index a " + Lua.type(t) + " value" );
+		}
+		if( h instanceof LuaFunction ) {
+			LuaFunction fn = (LuaFunction)h;
+			return Utils.first(fn.call(lua,t,key));
+		}
+		return index(lua,h,key);
+	}
+}
--- a/src/luan/interp/LeExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/LeExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -2,6 +2,7 @@
 
 import luan.Lua;
 import luan.LuaNumber;
+import luan.LuaFunction;
 import luan.LuaException;
 
 
@@ -12,18 +13,24 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		Object v1 = op1.eval(lua);
-		Object v2 = op2.eval(lua);
-		if( v1 instanceof LuaNumber && v2 instanceof LuaNumber ) {
-			LuaNumber n1 = (LuaNumber)v1;
-			LuaNumber n2 = (LuaNumber)v2;
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		if( o1 instanceof LuaNumber && o2 instanceof LuaNumber ) {
+			LuaNumber n1 = (LuaNumber)o1;
+			LuaNumber n2 = (LuaNumber)o2;
 			return n1.compareTo(n2) <= 0;
 		}
-		if( v1 instanceof String && v2 instanceof String ) {
-			String s1 = (String)v1;
-			String s2 = (String)v2;
+		if( o1 instanceof String && o2 instanceof String ) {
+			String s1 = (String)o1;
+			String s2 = (String)o2;
 			return s1.compareTo(s2) <= 0;
 		}
-		throw new LuaException( "attempt to compare " + Lua.type(v1) + " with " + Lua.type(v2) );
+		LuaFunction fn = getBinHandler("__le",o1,o2);
+		if( fn != null )
+			return Lua.toBoolean( Utils.first(fn.call(lua,o1,o2)) );
+		fn = getBinHandler("__lt",o1,o2);
+		if( fn != null )
+			return !Lua.toBoolean( Utils.first(fn.call(lua,o2,o1)) );
+		throw new LuaException( "attempt to compare " + Lua.type(o1) + " with " + Lua.type(o2) );
 	}
 }
--- a/src/luan/interp/LenExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/LenExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -3,6 +3,7 @@
 import luan.Lua;
 import luan.LuaNumber;
 import luan.LuaTable;
+import luan.LuaFunction;
 import luan.LuaException;
 
 
@@ -13,18 +14,18 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		return new LuaNumber( length(op.eval(lua)) );
-	}
-
-	private static int length(Object obj) throws LuaException {
-		if( obj instanceof String ) {
-			String s = (String)obj;
-			return s.length();
+		Object o = op.eval(lua);
+		if( o instanceof String ) {
+			String s = (String)o;
+			return new LuaNumber( s.length() );
 		}
-		if( obj instanceof LuaTable ) {
-			LuaTable t = (LuaTable)obj;
+		LuaFunction fn = Utils.getHandler("__len",o);
+		if( fn != null )
+			return Utils.first(fn.call(lua,o));
+		if( o instanceof LuaTable ) {
+			LuaTable t = (LuaTable)o;
 			return t.length();
 		}
-		throw new LuaException( "attempt to get length of a " + Lua.type(obj) + " value" );
+		throw new LuaException( "attempt to get length of a " + Lua.type(o) + " value" );
 	}
 }
--- a/src/luan/interp/LtExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/LtExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -2,6 +2,7 @@
 
 import luan.Lua;
 import luan.LuaNumber;
+import luan.LuaFunction;
 import luan.LuaException;
 
 
@@ -12,18 +13,21 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		Object v1 = op1.eval(lua);
-		Object v2 = op2.eval(lua);
-		if( v1 instanceof LuaNumber && v2 instanceof LuaNumber ) {
-			LuaNumber n1 = (LuaNumber)v1;
-			LuaNumber n2 = (LuaNumber)v2;
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		if( o1 instanceof LuaNumber && o2 instanceof LuaNumber ) {
+			LuaNumber n1 = (LuaNumber)o1;
+			LuaNumber n2 = (LuaNumber)o2;
 			return n1.compareTo(n2) < 0;
 		}
-		if( v1 instanceof String && v2 instanceof String ) {
-			String s1 = (String)v1;
-			String s2 = (String)v2;
+		if( o1 instanceof String && o2 instanceof String ) {
+			String s1 = (String)o1;
+			String s2 = (String)o2;
 			return s1.compareTo(s2) < 0;
 		}
-		throw new LuaException( "attempt to compare " + Lua.type(v1) + " with " + Lua.type(v2) );
+		LuaFunction fn = getBinHandler("__lt",o1,o2);
+		if( fn != null )
+			return Lua.toBoolean( Utils.first(fn.call(lua,o1,o2)) );
+		throw new LuaException( "attempt to compare " + Lua.type(o1) + " with " + Lua.type(o2) );
 	}
 }
--- a/src/luan/interp/LuaParser.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/LuaParser.java	Tue Dec 18 07:05:58 2012 +0000
@@ -23,7 +23,6 @@
 
 class LuaParser extends BaseParser<Object> {
 
-	static final String _G = "_G";
 	static final String _ENV = "_ENV";
 
 	static final class Frame {
@@ -37,8 +36,6 @@
 
 		Frame() {
 			this.parent = null;
-			upValueSymbols.add(_G);
-			upValueGetters.add(UpValue.globalGetter);
 			upValueSymbols.add(_ENV);
 			upValueGetters.add(UpValue.globalGetter);
 		}
@@ -673,7 +670,7 @@
 			return true;
 		Object obj1 = pop();
 		if( obj1 != null )
-			return push( new GetExpr( expr(obj1), expr(obj2) ) );
+			return push( new IndexExpr( expr(obj1), expr(obj2) ) );
 		String name = (String)obj2;
 		int index = stackIndex(name);
 		if( index != -1 )
@@ -681,7 +678,7 @@
 		index = upValueIndex(name);
 		if( index != -1 )
 			return push( new GetUpVar(index) );
-		return push( new GetExpr( env(), new ConstExpr(name) ) );
+		return push( new IndexExpr( env(), new ConstExpr(name) ) );
 	}
 
 	// function should be on top of the stack
--- a/src/luan/interp/ModExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/ModExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -12,8 +12,12 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		double n1 = Lua.checkNumber(op1.eval(lua)).value();
-		double n2 = Lua.checkNumber(op2.eval(lua)).value();
-		return new LuaNumber( n1 % n2 );
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		LuaNumber n1 = Lua.toNumber(o1);
+		LuaNumber n2 = Lua.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return new LuaNumber( n1.value() % n2.value() );
+		return arithmetic(lua,"__mod",o1,o2);
 	}
 }
--- a/src/luan/interp/MulExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/MulExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -12,8 +12,12 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		double n1 = Lua.checkNumber(op1.eval(lua)).value();
-		double n2 = Lua.checkNumber(op2.eval(lua)).value();
-		return new LuaNumber( n1 * n2 );
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		LuaNumber n1 = Lua.toNumber(o1);
+		LuaNumber n2 = Lua.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return new LuaNumber( n1.value() * n2.value() );
+		return arithmetic(lua,"__mul",o1,o2);
 	}
 }
--- a/src/luan/interp/PowExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/PowExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -12,8 +12,12 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		double n1 = Lua.checkNumber(op1.eval(lua)).value();
-		double n2 = Lua.checkNumber(op2.eval(lua)).value();
-		return new LuaNumber( Math.pow(n1,n2) );
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		LuaNumber n1 = Lua.toNumber(o1);
+		LuaNumber n2 = Lua.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return new LuaNumber( Math.pow( n1.value(), n2.value() ) );
+		return arithmetic(lua,"__pow",o1,o2);
 	}
 }
--- a/src/luan/interp/SetTableEntry.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/SetTableEntry.java	Tue Dec 18 07:05:58 2012 +0000
@@ -3,6 +3,7 @@
 import luan.LuaException;
 import luan.LuaTable;
 import luan.Lua;
+import luan.LuaFunction;
 
 
 final class SetTableEntry implements Settable {
@@ -15,12 +16,30 @@
 	}
 
 	@Override public void set(LuaStateImpl lua,Object value) throws LuaException {
-		Object t = tableExpr.eval(lua);
-		if( !(t instanceof LuaTable) )
-			throw new LuaException( "attempt to index a " + Lua.type(t) + " value" );
-		LuaTable table = (LuaTable)t;
-		Object key = keyExpr.eval(lua);
-		table.set(key,value);
+		newindex( lua, tableExpr.eval(lua), keyExpr.eval(lua), value );
+	}
+
+	private static void newindex(LuaStateImpl lua,Object t,Object key,Object value) throws LuaException {
+		Object h;
+		if( t instanceof LuaTable ) {
+			LuaTable table = (LuaTable)t;
+			Object old = table.put(key,value);
+			if( old != null )
+				return;
+			h = Utils.getHandlerObject("__newindex",t);
+			if( h==null )
+				return;
+			table.put(key,old);
+		} else {
+			h = Utils.getHandlerObject("__newindex",t);
+			if( h==null )
+				throw new LuaException( "attempt to index a " + Lua.type(t) + " value" );
+		}
+		if( h instanceof LuaFunction ) {
+			LuaFunction fn = (LuaFunction)h;
+			fn.call(lua,t,key,value);
+		}
+		newindex(lua,h,key,value);
 	}
 
 }
--- a/src/luan/interp/SubExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/SubExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -12,8 +12,12 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		double n1 = Lua.checkNumber(op1.eval(lua)).value();
-		double n2 = Lua.checkNumber(op2.eval(lua)).value();
-		return new LuaNumber( n1 - n2 );
+		Object o1 = op1.eval(lua);
+		Object o2 = op2.eval(lua);
+		LuaNumber n1 = Lua.toNumber(o1);
+		LuaNumber n2 = Lua.toNumber(o2);
+		if( n1 != null && n2 != null )
+			return new LuaNumber( n1.value() - n2.value() );
+		return arithmetic(lua,"__sub",o1,o2);
 	}
 }
--- a/src/luan/interp/TableExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/TableExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -28,11 +28,11 @@
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
 		LuaTable table = new LuaTable();
 		for( Field field : fields ) {
-			table.set( field.key.eval(lua), field.value.eval(lua) );
+			table.put( field.key.eval(lua), field.value.eval(lua) );
 		}
 		Object[] a = expressions.eval(lua);
 		for( int i=0; i<a.length; i++ ) {
-			table.set( new LuaNumber(i+1), a[i] );
+			table.put( new LuaNumber(i+1), a[i] );
 		}
 		return table;
 	}
--- a/src/luan/interp/UnmExpr.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/interp/UnmExpr.java	Tue Dec 18 07:05:58 2012 +0000
@@ -2,6 +2,7 @@
 
 import luan.Lua;
 import luan.LuaNumber;
+import luan.LuaFunction;
 import luan.LuaException;
 
 
@@ -13,7 +14,13 @@
 	}
 
 	@Override public Object eval(LuaStateImpl lua) throws LuaException {
-		double n = Lua.checkNumber(op.eval(lua)).value();
-		return new LuaNumber( -n );
+		Object o = op.eval(lua);
+		LuaNumber n = Lua.toNumber(o);
+		if( n != null )
+			return new LuaNumber( -n.value() );
+		LuaFunction fn = Utils.getHandler("__unm",o);
+		if( fn != null )
+			return Utils.first(fn.call(lua,o));
+		throw new LuaException("attempt to perform arithmetic on a "+Lua.type(o)+" value");
 	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/Utils.java	Tue Dec 18 07:05:58 2012 +0000
@@ -0,0 +1,28 @@
+package luan.interp;
+
+import luan.Lua;
+import luan.LuaFunction;
+import luan.LuaTable;
+import luan.LuaException;
+
+
+final class Utils {
+	private Utils() {}  // never
+
+	static Object first(Object[] a) {
+		return a.length==0 ? null : a[0];
+	}
+
+	static final Object getHandlerObject(String op,Object obj) throws LuaException {
+		LuaTable t = Lua.getMetatable(obj);
+		return t==null ? null : t.get(op);
+	}
+
+	static final LuaFunction getHandler(String op,Object obj) throws LuaException {
+		Object f = getHandlerObject(op,obj);
+		if( f == null )
+			return null;
+		return Lua.checkFunction(f);
+	}
+
+}
--- a/src/luan/lib/BasicLib.java	Sun Dec 16 09:23:56 2012 +0000
+++ b/src/luan/lib/BasicLib.java	Tue Dec 18 07:05:58 2012 +0000
@@ -21,17 +21,21 @@
 
 	public static void register(LuaState lua) {
 		LuaTable t = lua.global();
-		add( t, "print", new Object[0].getClass() );
-		add( t, "type", Object.class );
+		t.put( "_G", t );
+		add( t, "getmetatable", Object.class );
+		add( t, "ipairs", LuaTable.class );
 		add( t, "load", LuaState.class, String.class );
 		add( t, "loadfile", LuaState.class, String.class );
 		add( t, "pairs", LuaTable.class );
-		add( t, "ipairs", LuaTable.class );
+		add( t, "print", new Object[0].getClass() );
+		add( t, "setmetatable", LuaTable.class, LuaTable.class );
+		add( t, "type", Object.class );
+		t.put( "_VERSION", Lua.version );
 	}
 
 	private static void add(LuaTable t,String method,Class<?>... parameterTypes) {
 		try {
-			t.set( method, new LuaJavaFunction(BasicLib.class.getMethod(method,parameterTypes),null) );
+			t.put( method, new LuaJavaFunction(BasicLib.class.getMethod(method,parameterTypes),null) );
 		} catch(NoSuchMethodException e) {
 			throw new RuntimeException(e);
 		}
@@ -131,4 +135,13 @@
 			throw new RuntimeException(e);
 		}
 	}
+
+	public static LuaTable getmetatable(Object obj) {
+		return Lua.getMetatable(obj);
+	}
+
+	public static LuaTable setmetatable(LuaTable table,LuaTable metatable) {
+		table.setMetatable(metatable);
+		return table;
+	}
 }