Add arithmetic metatag processing.
This commit is contained in:
@@ -28,6 +28,7 @@ import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.luaj.vm2.lib.MathLib;
|
||||
import org.luaj.vm2.lib.StringLib;
|
||||
|
||||
public class LuaString extends LuaValue {
|
||||
@@ -108,24 +109,33 @@ public class LuaString extends LuaValue {
|
||||
}
|
||||
|
||||
// unary operators
|
||||
public LuaValue neg() { return checkarith().neg(); }
|
||||
public LuaValue neg() { return valueOf(-checkarith()); }
|
||||
|
||||
// basic binary arithmetic
|
||||
public LuaValue add( LuaValue rhs ) { return checkarith().add(rhs); }
|
||||
public LuaValue add( double lhs ) { return checkarith().add(lhs); }
|
||||
public LuaValue sub( LuaValue rhs ) { return checkarith().sub(rhs); }
|
||||
public LuaValue subFrom( double lhs ) { return checkarith().subFrom(lhs); }
|
||||
public LuaValue mul( LuaValue rhs ) { return checkarith().mul(rhs); }
|
||||
public LuaValue mul( double lhs ) { return checkarith().mul(lhs); }
|
||||
public LuaValue mul( int lhs ) { return checkarith().mul(lhs); }
|
||||
public LuaValue pow( LuaValue rhs ) { return checkarith().pow(rhs); }
|
||||
public LuaValue powWith( double lhs ) { return checkarith().powWith(lhs); }
|
||||
public LuaValue powWith( int lhs ) { return checkarith().powWith(lhs); }
|
||||
public LuaValue div( LuaValue rhs ) { return checkarith().div(rhs); }
|
||||
public LuaValue divInto( double lhs ) { return checkarith().divInto(lhs); }
|
||||
public LuaValue mod( LuaValue rhs ) { return checkarith().mod(rhs); }
|
||||
public LuaValue modFrom( double lhs ) { return checkarith().modFrom(lhs); }
|
||||
|
||||
public LuaValue add( LuaValue rhs ) { double d = scannumber(10); return Double.isNaN(d)? arithmt(ADD,rhs): rhs.add(d); }
|
||||
public LuaValue add( double rhs ) { return valueOf( checkarith() + rhs ); }
|
||||
public LuaValue add( int rhs ) { return valueOf( checkarith() + rhs ); }
|
||||
public LuaValue sub( LuaValue rhs ) { double d = scannumber(10); return Double.isNaN(d)? arithmt(SUB,rhs): rhs.subFrom(d); }
|
||||
public LuaValue sub( double rhs ) { return valueOf( checkarith() - rhs ); }
|
||||
public LuaValue sub( int rhs ) { return valueOf( checkarith() - rhs ); }
|
||||
public LuaValue subFrom( double lhs ) { return valueOf( lhs - checkarith() ); }
|
||||
public LuaValue mul( LuaValue rhs ) { double d = scannumber(10); return Double.isNaN(d)? arithmt(MUL,rhs): rhs.mul(d); }
|
||||
public LuaValue mul( double rhs ) { return valueOf( checkarith() * rhs ); }
|
||||
public LuaValue mul( int rhs ) { return valueOf( checkarith() * rhs ); }
|
||||
public LuaValue pow( LuaValue rhs ) { double d = scannumber(10); return Double.isNaN(d)? arithmt(POW,rhs): rhs.powWith(d); }
|
||||
public LuaValue pow( double rhs ) { return MathLib.dpow(checkarith(),rhs); }
|
||||
public LuaValue pow( int rhs ) { return MathLib.dpow(checkarith(),rhs); }
|
||||
public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs, checkarith()); }
|
||||
public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs, checkarith()); }
|
||||
public LuaValue div( LuaValue rhs ) { double d = scannumber(10); return Double.isNaN(d)? arithmt(DIV,rhs): rhs.divInto(d); }
|
||||
public LuaValue div( double rhs ) { return LuaDouble.ddiv(checkarith(),rhs); }
|
||||
public LuaValue div( int rhs ) { return LuaDouble.ddiv(checkarith(),rhs); }
|
||||
public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs, checkarith()); }
|
||||
public LuaValue mod( LuaValue rhs ) { double d = scannumber(10); return Double.isNaN(d)? arithmt(MOD,rhs): rhs.modFrom(d); }
|
||||
public LuaValue mod( double rhs ) { return LuaDouble.dmod(checkarith(), rhs); }
|
||||
public LuaValue mod( int rhs ) { return LuaDouble.dmod(checkarith(), rhs); }
|
||||
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs, checkarith()); }
|
||||
|
||||
// relational operators, these only work with other strings
|
||||
public LuaValue lt( LuaValue rhs ) { return rhs.strcmp(this)>0? LuaValue.TRUE: FALSE; }
|
||||
public boolean lt_b( LuaValue rhs ) { return rhs.strcmp(this)>0; }
|
||||
@@ -160,56 +170,67 @@ public class LuaString extends LuaValue {
|
||||
}
|
||||
|
||||
/** Check for number in arithmetic, or throw aritherror */
|
||||
private LuaValue checkarith() {
|
||||
LuaValue v = tonumber(10);
|
||||
return v.isnil()? aritherror(): v;
|
||||
private double checkarith() {
|
||||
double d = scannumber(10);
|
||||
if ( Double.isNaN(d) )
|
||||
aritherror();
|
||||
return d;
|
||||
}
|
||||
|
||||
public int checkint() {
|
||||
return checknumber().toint();
|
||||
return (int) (long) checkdouble();
|
||||
}
|
||||
public LuaInteger checkinteger() {
|
||||
return checknumber().checkinteger();
|
||||
return valueOf(checkint());
|
||||
}
|
||||
public long checklong() {
|
||||
return checknumber().tolong();
|
||||
return (long) checkdouble();
|
||||
}
|
||||
public double checkdouble() {
|
||||
return checknumber().todouble();
|
||||
double d = scannumber(10);
|
||||
if ( Double.isNaN(d) )
|
||||
argerror("number");
|
||||
return d;
|
||||
}
|
||||
public LuaNumber checknumber() {
|
||||
LuaValue n = tonumber(10);
|
||||
if ( ! n.isnumber() )
|
||||
argerror("number");
|
||||
return (LuaNumber) n;
|
||||
return valueOf(checkdouble());
|
||||
}
|
||||
public LuaNumber checknumber(String msg) {
|
||||
LuaValue n = tonumber(10);
|
||||
if ( ! n.isnumber() )
|
||||
argerror(msg);
|
||||
return (LuaNumber) n;
|
||||
double d = scannumber(10);
|
||||
if ( Double.isNaN(d) )
|
||||
argerror("number");
|
||||
return valueOf(d);
|
||||
}
|
||||
public LuaValue tonumber() {
|
||||
return tonumber(10);
|
||||
}
|
||||
|
||||
public boolean isnumber() {
|
||||
return ! tonumber(10).isnil();
|
||||
double d = scannumber(10);
|
||||
return ! Double.isNaN(d);
|
||||
}
|
||||
|
||||
public boolean isint() {
|
||||
return tonumber(10).isint();
|
||||
double d = scannumber(10);
|
||||
if ( Double.isNaN(d) )
|
||||
return false;
|
||||
int i = (int) d;
|
||||
return i == d;
|
||||
}
|
||||
|
||||
public boolean islong() {
|
||||
return tonumber(10).islong();
|
||||
double d = scannumber(10);
|
||||
if ( Double.isNaN(d) )
|
||||
return false;
|
||||
long l = (long) d;
|
||||
return l == d;
|
||||
}
|
||||
|
||||
public byte tobyte() { return (byte) toint(); }
|
||||
public char tochar() { return (char) toint(); }
|
||||
public double todouble() { LuaValue n=tonumber(10); return n.isnil()? 0: n.todouble(); }
|
||||
public double todouble() { double d=scannumber(10); return Double.isNaN(d)? 0: d; }
|
||||
public float tofloat() { return (float) todouble(); }
|
||||
public int toint() { LuaValue n=tonumber(10); return n.isnil()? 0: n.toint(); }
|
||||
public int toint() { return (int) tolong(); }
|
||||
public long tolong() { return (long) todouble(); }
|
||||
public short toshort() { return (short) toint(); }
|
||||
|
||||
@@ -467,44 +488,53 @@ public class LuaString extends LuaValue {
|
||||
* @return IntValue, DoubleValue, or NIL depending on the content of the string.
|
||||
*/
|
||||
public LuaValue tonumber( int base ) {
|
||||
double d = scannumber( base );
|
||||
return Double.isNaN(d)? NIL: valueOf(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to a number in a base, or return Double.NaN if not a number.
|
||||
*/
|
||||
public double scannumber( int base ) {
|
||||
if ( base >= 2 && base <= 36 ) {
|
||||
int i=m_offset,j=m_offset+m_length;
|
||||
while ( i<j && m_bytes[i]==' ' ) ++i;
|
||||
while ( i<j && m_bytes[j-1]==' ' ) --j;
|
||||
if ( i>=j ) return FALSE;
|
||||
if ( i>=j )
|
||||
return Double.NaN;
|
||||
if ( ( base == 10 || base == 16 ) && ( m_bytes[i]=='0' && i+1<j && (m_bytes[i+1]=='x'||m_bytes[i+1]=='X') ) ) {
|
||||
base = 16;
|
||||
i+=2;
|
||||
}
|
||||
LuaValue l = scanlong( base, i, j );
|
||||
return l!=NIL? l: base==10? scandouble(i,j): NIL;
|
||||
double l = scanlong( base, i, j );
|
||||
return Double.isNaN(l) && base==10? scandouble(i,j): l;
|
||||
}
|
||||
|
||||
return NIL;
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan and convert a long value, or return NIL if not found.
|
||||
* @return DoubleValue, IntValue, or NIL depending on what is found.
|
||||
* Scan and convert a long value, or return Double.NaN if not found.
|
||||
* @return DoubleValue, IntValue, or Double.NaN depending on what is found.
|
||||
*/
|
||||
private LuaValue scanlong( int base, int start, int end ) {
|
||||
private double scanlong( int base, int start, int end ) {
|
||||
long x = 0;
|
||||
boolean neg = (m_bytes[start] == '-');
|
||||
for ( int i=(neg?start+1:start); i<end; i++ ) {
|
||||
int digit = m_bytes[i] - (base<=10||(m_bytes[i]>='0'&&m_bytes[i]<='9')? '0':
|
||||
m_bytes[i]>='A'&&m_bytes[i]<='Z'? ('A'-10): ('a'-10));
|
||||
if ( digit < 0 || digit >= base )
|
||||
return NIL;
|
||||
return Double.NaN;
|
||||
x = x * base + digit;
|
||||
}
|
||||
return valueOf(neg? -x: x);
|
||||
return neg? -x: x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan and convert a double value, or return NIL if not a double.
|
||||
* @return DoubleValue, IntValue, or NIL depending on what is found.
|
||||
* Scan and convert a double value, or return Double.NaN if not a double.
|
||||
* @return DoubleValue, IntValue, or Double.NaN depending on what is found.
|
||||
*/
|
||||
private LuaValue scandouble(int start, int end) {
|
||||
private double scandouble(int start, int end) {
|
||||
if ( end>start+64 ) end=start+64;
|
||||
for ( int i=start; i<end; i++ ) {
|
||||
switch ( m_bytes[i] ) {
|
||||
@@ -516,17 +546,17 @@ public class LuaString extends LuaValue {
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
break;
|
||||
default:
|
||||
return NIL;
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
char [] c = new char[end-start];
|
||||
for ( int i=start; i<end; i++ )
|
||||
c[i-start] = (char) m_bytes[i];
|
||||
try {
|
||||
return valueOf( Double.parseDouble(new String(c)));
|
||||
return Double.parseDouble(new String(c));
|
||||
} catch ( Exception e ) {
|
||||
return Double.NaN;
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,6 +66,12 @@ public class LuaValue extends Varargs {
|
||||
public static final LuaString CALL = valueOf("__call");
|
||||
public static final LuaString MODE = valueOf("__mode");
|
||||
public static final LuaString METATABLE = valueOf("__metatable");
|
||||
public static final LuaString ADD = valueOf("__add");
|
||||
public static final LuaString SUB = valueOf("__sub");
|
||||
public static final LuaString DIV = valueOf("__div");
|
||||
public static final LuaString MUL = valueOf("__mul");
|
||||
public static final LuaString POW = valueOf("__pow");
|
||||
public static final LuaString MOD = valueOf("__mod");
|
||||
public static final LuaString EMPTYSTRING = valueOf("");
|
||||
|
||||
private static int MAXSTACK = 250;
|
||||
@@ -261,30 +267,36 @@ public class LuaValue extends Varargs {
|
||||
public boolean neq_b( int val ) { return ! eq_b(val); }
|
||||
|
||||
// arithmetic operators
|
||||
public LuaValue add( LuaValue rhs ) { return aritherror("add"); }
|
||||
public LuaValue add( LuaValue rhs ) { return arithmt(ADD,rhs); }
|
||||
public LuaValue add(double rhs) { return aritherror("add"); }
|
||||
public LuaValue add(int rhs) { return add((double)rhs); }
|
||||
public LuaValue sub( LuaValue rhs ) { return aritherror("sub"); }
|
||||
public LuaValue sub( LuaValue rhs ) { return arithmt(SUB,rhs); }
|
||||
public LuaValue sub( double rhs ) { return aritherror("sub"); }
|
||||
public LuaValue sub( int rhs ) { return aritherror("sub"); }
|
||||
public LuaValue subFrom(double lhs) { return aritherror("sub"); }
|
||||
public LuaValue subFrom(int lhs) { return subFrom((double)lhs); }
|
||||
public LuaValue mul( LuaValue rhs ) { return aritherror("mul"); }
|
||||
public LuaValue mul( LuaValue rhs ) { return arithmt(MUL,rhs); }
|
||||
public LuaValue mul(double rhs) { return aritherror("mul"); }
|
||||
public LuaValue mul(int rhs) { return mul((double)rhs); }
|
||||
public LuaValue pow( LuaValue rhs ) { return aritherror("pow"); }
|
||||
public LuaValue pow( LuaValue rhs ) { return arithmt(POW,rhs); }
|
||||
public LuaValue pow( double rhs ) { return aritherror("pow"); }
|
||||
public LuaValue pow( int rhs ) { return aritherror("pow"); }
|
||||
public LuaValue powWith(double lhs) { return aritherror("mul"); }
|
||||
public LuaValue powWith(int lhs) { return powWith((double)lhs); }
|
||||
public LuaValue div( LuaValue rhs ) { return aritherror("div"); }
|
||||
public LuaValue div( LuaValue rhs ) { return arithmt(DIV,rhs); }
|
||||
public LuaValue div( double rhs ) { return aritherror("div"); }
|
||||
public LuaValue div( int rhs ) { return aritherror("div"); }
|
||||
public LuaValue divInto(double lhs) { return aritherror("divInto"); }
|
||||
public LuaValue mod( LuaValue rhs ) { return aritherror("mod"); }
|
||||
public LuaValue mod( LuaValue rhs ) { return arithmt(MOD,rhs); }
|
||||
public LuaValue mod( double rhs ) { return aritherror("mod"); }
|
||||
public LuaValue mod( int rhs ) { return aritherror("mod"); }
|
||||
public LuaValue modFrom(double lhs) { return aritherror("modFrom"); }
|
||||
protected LuaValue arithmt(LuaValue tag, LuaValue op2) {
|
||||
LuaValue h = this.metatag(tag);
|
||||
if ( h.isnil() )
|
||||
h = op2.checkmetatag(tag, "attempt to perform arithmetic on ");
|
||||
return h.call( this, op2 );
|
||||
}
|
||||
|
||||
// relational operators
|
||||
public LuaValue lt( LuaValue rhs ) { return compareerror(rhs); }
|
||||
|
||||
@@ -1,14 +1,36 @@
|
||||
print( '---- initial metatables' )
|
||||
local values = { 1, false, coroutine.create( function() end ) }
|
||||
local anumber = 111
|
||||
local aboolean = false
|
||||
local afunction = function() end
|
||||
local athread = coroutine.create( afunction )
|
||||
local values = { athread, aboolean, afunction, athread }
|
||||
for i=1,#values do
|
||||
print( debug.getmetatable( values[i] ) )
|
||||
end
|
||||
local ts = tostring
|
||||
tostring = function(o)
|
||||
local t = type(o)
|
||||
return (t=='thread' or t=='function') and t or ts(o)
|
||||
end
|
||||
|
||||
local buildbin = function(name)
|
||||
return function(a,b)
|
||||
print( 'mt.__'..name..'()', type(a), type(b), a, b )
|
||||
return '__'..name..'-result'
|
||||
end
|
||||
end
|
||||
|
||||
local mt = {
|
||||
__call=function(a,b,c)
|
||||
print( 'mt.__call()', type(a), type(b), type(c), b, c )
|
||||
return '__call-result'
|
||||
end,
|
||||
__add=buildbin('add'),
|
||||
__sub=buildbin('sub'),
|
||||
__mul=buildbin('mul'),
|
||||
__div=buildbin('div'),
|
||||
__pow=buildbin('pow'),
|
||||
__mod=buildbin('mod'),
|
||||
}
|
||||
|
||||
-- pcall a function and check for a pattern in the error string
|
||||
@@ -30,6 +52,34 @@ for i=1,#values do
|
||||
print( debug.setmetatable( values[i], nil ) )
|
||||
end
|
||||
|
||||
print( '---- __add, __sub, __mul, __div, __pow, __mod' )
|
||||
local groups = { {aboolean, aboolean}, {aboolean, athread}, {aboolean, afunction}, {aboolean, "abc"} }
|
||||
for i=1,#groups do
|
||||
local a,b = groups[i][1], groups[i][2]
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return a+b end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return b+a end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return a-b end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return b-a end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return a*b end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return b*a end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return a^b end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return b^a end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return a%b end ) )
|
||||
print( type(values[i]), 'before', ecall( 'attempt to perform arithmetic', function() return b%a end ) )
|
||||
print( debug.setmetatable( a, mt ) )
|
||||
print( type(values[i]), 'after', pcall( function() return a+b end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return b+a end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return a-b end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return b-a end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return a*b end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return b*a end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return a^b end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return b^a end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return a%b end ) )
|
||||
print( type(values[i]), 'after', pcall( function() return b%a end ) )
|
||||
print( debug.setmetatable( a, nil ) )
|
||||
end
|
||||
|
||||
|
||||
|
||||
print( '---- final metatables' )
|
||||
|
||||
Reference in New Issue
Block a user