Various changes to bring pm.lua test closer to passing:
* New and complete character class support * string.gsub implemented * rawset implemented * lua_call added (based on lua_pcall) * lua_pop added * newCall removed; luaGetTable and luaSetTable in LFunction updated to use lua_call instead. * StandardTest changed to avoid an ArrayIndexOutOfBoundsException Unfortunately I discovered a problem where execute() will loop around too many times and call exec() with a call frame that has already completed. This might be due to the lack of the "tailcalls" field in our CallInfo class that the C version maintains?
This commit is contained in:
50
src/addon/java/lua/addon/luacompat/LBuffer.java
Normal file
50
src/addon/java/lua/addon/luacompat/LBuffer.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package lua.addon.luacompat;
|
||||
|
||||
import lua.value.LString;
|
||||
|
||||
public class LBuffer {
|
||||
|
||||
private byte[] bytes;
|
||||
private int length;
|
||||
|
||||
public LBuffer( int initialCapacity ) {
|
||||
bytes = new byte[ initialCapacity ];
|
||||
length = 0;
|
||||
}
|
||||
|
||||
public void append( byte b ) {
|
||||
ensureCapacity( length + 1 );
|
||||
bytes[ length++ ] = b;
|
||||
}
|
||||
|
||||
public void append( LString str ) {
|
||||
final int alen = str.length();
|
||||
ensureCapacity( length + alen );
|
||||
str.copyInto( 0, bytes, length, alen );
|
||||
length += alen;
|
||||
}
|
||||
|
||||
public void setLength( int length ) {
|
||||
ensureCapacity( length );
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public LString toLuaString() {
|
||||
return new LString( realloc( bytes, length ) );
|
||||
}
|
||||
|
||||
public void ensureCapacity( int minSize ) {
|
||||
if ( minSize > bytes.length )
|
||||
realloc( minSize );
|
||||
}
|
||||
|
||||
private void realloc( int minSize ) {
|
||||
bytes = realloc( bytes, Math.max( bytes.length * 2, minSize ) );
|
||||
}
|
||||
|
||||
private static byte[] realloc( byte[] b, int newSize ) {
|
||||
byte[] newBytes = new byte[ newSize ];
|
||||
System.arraycopy( b, 0, newBytes, 0, Math.min( b.length, newSize ) );
|
||||
return newBytes;
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ public class LuaCompat extends LFunction {
|
||||
"loadfile",
|
||||
"tonumber",
|
||||
"rawget",
|
||||
"rawset",
|
||||
"setfenv",
|
||||
"select",
|
||||
"collectgarbage",
|
||||
@@ -125,17 +126,18 @@ public class LuaCompat extends LFunction {
|
||||
private static final int LOADFILE = GLOBALS_BASE + 1;
|
||||
private static final int TONUMBER = GLOBALS_BASE + 2;
|
||||
private static final int RAWGET = GLOBALS_BASE + 3;
|
||||
private static final int SETFENV = GLOBALS_BASE + 4;
|
||||
private static final int SELECT = GLOBALS_BASE + 5;
|
||||
private static final int COLLECTGARBAGE = GLOBALS_BASE + 6;
|
||||
private static final int DOFILE = GLOBALS_BASE + 7;
|
||||
private static final int LOADSTRING = GLOBALS_BASE + 8;
|
||||
private static final int LOAD = GLOBALS_BASE + 9;
|
||||
private static final int TOSTRING = GLOBALS_BASE + 10;
|
||||
private static final int UNPACK = GLOBALS_BASE + 11;
|
||||
private static final int NEXT = GLOBALS_BASE + 12;
|
||||
private static final int MODULE = GLOBALS_BASE + 13;
|
||||
private static final int REQUIRE = GLOBALS_BASE + 14;
|
||||
private static final int RAWSET = GLOBALS_BASE + 4;
|
||||
private static final int SETFENV = GLOBALS_BASE + 5;
|
||||
private static final int SELECT = GLOBALS_BASE + 6;
|
||||
private static final int COLLECTGARBAGE = GLOBALS_BASE + 7;
|
||||
private static final int DOFILE = GLOBALS_BASE + 8;
|
||||
private static final int LOADSTRING = GLOBALS_BASE + 9;
|
||||
private static final int LOAD = GLOBALS_BASE + 10;
|
||||
private static final int TOSTRING = GLOBALS_BASE + 11;
|
||||
private static final int UNPACK = GLOBALS_BASE + 12;
|
||||
private static final int NEXT = GLOBALS_BASE + 13;
|
||||
private static final int MODULE = GLOBALS_BASE + 14;
|
||||
private static final int REQUIRE = GLOBALS_BASE + 15;
|
||||
|
||||
|
||||
private static final int MATH_BASE = 20;
|
||||
@@ -201,7 +203,7 @@ public class LuaCompat extends LFunction {
|
||||
vm.setResult( toNumber( vm ) );
|
||||
break;
|
||||
case RAWGET: {
|
||||
LValue t = vm.getArg(0);;
|
||||
LValue t = vm.getArg(0);
|
||||
LValue k = vm.getArg(1);
|
||||
LValue result = LNil.NIL;
|
||||
if ( t instanceof LTable ) {
|
||||
@@ -209,6 +211,17 @@ public class LuaCompat extends LFunction {
|
||||
}
|
||||
vm.setResult( result );
|
||||
} break;
|
||||
case RAWSET: {
|
||||
LValue t = vm.getArg(0);
|
||||
LValue k = vm.getArg(1);
|
||||
LValue v = vm.getArg(2);
|
||||
vm.setResult();
|
||||
if ( t instanceof LTable ) {
|
||||
( (LTable) t ).put( k, v );
|
||||
} else {
|
||||
vm.lua_error( "expected table" );
|
||||
}
|
||||
} break;
|
||||
case SETFENV:
|
||||
setfenv( (StackState) vm );
|
||||
break;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package lua.addon.luacompat;
|
||||
|
||||
import lua.VM;
|
||||
import lua.value.LFunction;
|
||||
import lua.value.LInteger;
|
||||
import lua.value.LNil;
|
||||
import lua.value.LNumber;
|
||||
import lua.value.LString;
|
||||
import lua.value.LTable;
|
||||
import lua.value.LValue;
|
||||
|
||||
public class StrLib {
|
||||
/**
|
||||
@@ -185,9 +189,41 @@ public class StrLib {
|
||||
* x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
|
||||
* --> x="lua-5.1.tar.gz"
|
||||
*/
|
||||
static void gsub( VM vm ) {
|
||||
static void gsub( VM vm ) {
|
||||
LString src = vm.getArgAsLuaString( 0 );
|
||||
final int srclen = src.length();
|
||||
LString p = vm.getArgAsLuaString( 1 );
|
||||
LValue repl = vm.getArg( 2 );
|
||||
int max_s = ( vm.getArgCount() > 3 ? vm.getArgAsInt( 3 ) : srclen + 1 );
|
||||
final boolean anchor = p.length() > 0 && p.charAt( 0 ) == '^';
|
||||
|
||||
LBuffer lbuf = new LBuffer( srclen );
|
||||
MatchState ms = new MatchState( vm, src, p );
|
||||
|
||||
int soffset = 0;
|
||||
int n = 0;
|
||||
while ( n < max_s ) {
|
||||
ms.reset();
|
||||
int res = ms.match( soffset, anchor ? 1 : 0 );
|
||||
if ( res != -1 ) {
|
||||
n++;
|
||||
ms.add_value( lbuf, soffset, res, repl );
|
||||
}
|
||||
if ( res != -1 && res > soffset )
|
||||
soffset = res;
|
||||
else if ( soffset < srclen )
|
||||
lbuf.append( (byte) src.luaByte( soffset++ ) );
|
||||
else
|
||||
break;
|
||||
if ( anchor )
|
||||
break;
|
||||
}
|
||||
lbuf.append( src.substring( soffset, srclen ) );
|
||||
vm.setResult();
|
||||
vm.push( lbuf.toLuaString() );
|
||||
vm.push( new LInteger( n ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* string.len (s)
|
||||
*
|
||||
@@ -367,6 +403,45 @@ public class StrLib {
|
||||
private static final int CAP_UNFINISHED = -1;
|
||||
private static final int CAP_POSITION = -2;
|
||||
|
||||
private static final byte MASK_ALPHA = 0x01;
|
||||
private static final byte MASK_LOWERCASE = 0x02;
|
||||
private static final byte MASK_UPPERCASE = 0x04;
|
||||
private static final byte MASK_DIGIT = 0x08;
|
||||
private static final byte MASK_PUNCT = 0x10;
|
||||
private static final byte MASK_SPACE = 0x20;
|
||||
private static final byte MASK_CONTROL = 0x40;
|
||||
private static final byte MASK_HEXDIGIT = (byte)0x80;
|
||||
|
||||
private static final byte[] CHAR_TABLE;
|
||||
|
||||
static {
|
||||
CHAR_TABLE = new byte[256];
|
||||
|
||||
for ( int i = 0; i < 256; ++i ) {
|
||||
final char c = (char) i;
|
||||
CHAR_TABLE[i] = (byte)( ( Character.isDigit( c ) ? MASK_DIGIT : 0 ) |
|
||||
( Character.isLowerCase( c ) ? MASK_LOWERCASE : 0 ) |
|
||||
( Character.isUpperCase( c ) ? MASK_UPPERCASE : 0 ) |
|
||||
( ( c < ' ' || c == 0x7F ) ? MASK_CONTROL : 0 ) );
|
||||
if ( ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) || ( c >= '0' && c <= '9' ) ) {
|
||||
CHAR_TABLE[i] |= MASK_HEXDIGIT;
|
||||
}
|
||||
if ( ( c >= '!' && c <= '/' ) || ( c >= ':' && c <= '@' ) ) {
|
||||
CHAR_TABLE[i] |= MASK_PUNCT;
|
||||
}
|
||||
if ( ( CHAR_TABLE[i] & ( MASK_LOWERCASE | MASK_UPPERCASE ) ) != 0 ) {
|
||||
CHAR_TABLE[i] |= MASK_ALPHA;
|
||||
}
|
||||
}
|
||||
|
||||
CHAR_TABLE[' '] = MASK_SPACE;
|
||||
CHAR_TABLE['\r'] |= MASK_SPACE;
|
||||
CHAR_TABLE['\n'] |= MASK_SPACE;
|
||||
CHAR_TABLE['\t'] |= MASK_SPACE;
|
||||
CHAR_TABLE[0x0C /* '\v' */ ] |= MASK_SPACE;
|
||||
CHAR_TABLE['\f'] |= MASK_SPACE;
|
||||
};
|
||||
|
||||
private static class MatchState {
|
||||
final LString s;
|
||||
final LString p;
|
||||
@@ -388,17 +463,71 @@ public class StrLib {
|
||||
level = 0;
|
||||
}
|
||||
|
||||
void push_captures( boolean wholeMatch, int soff, int end ) {
|
||||
private void add_s( LBuffer lbuf, LString news, int soff, int e ) {
|
||||
int l = news.length();
|
||||
for ( int i = 0; i < l; ++i ) {
|
||||
byte b = (byte) news.luaByte( i );
|
||||
if ( b != L_ESC ) {
|
||||
lbuf.append( (byte) b );
|
||||
} else {
|
||||
++i; // skip ESC
|
||||
b = (byte) news.luaByte( i );
|
||||
if ( !Character.isDigit( (char) b ) ) {
|
||||
lbuf.append( b );
|
||||
} else if ( b == '0' ) {
|
||||
lbuf.append( s.substring( soff, e ) );
|
||||
} else {
|
||||
push_onecapture( b - '1', soff, e );
|
||||
lbuf.append( vm.lua_tolvalue( -1 ).luaAsString() );
|
||||
vm.lua_pop( 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void add_value( LBuffer lbuf, int soffset, int end, LValue repl ) {
|
||||
if ( repl instanceof LString || repl instanceof LNumber ) {
|
||||
add_s( lbuf, repl.luaAsString(), soffset, end );
|
||||
return;
|
||||
} else if ( repl instanceof LFunction ) {
|
||||
vm.push( repl );
|
||||
int n = push_captures( true, soffset, end );
|
||||
vm.lua_call( n, 1 );
|
||||
} else if ( repl instanceof LTable ) {
|
||||
// Need to call push_onecapture here for the error checking
|
||||
push_onecapture( 0, soffset, end );
|
||||
LValue k = vm.lua_tolvalue( -1 );
|
||||
vm.lua_pop( 1 );
|
||||
((LTable) repl).luaGetTable( vm, repl, k );
|
||||
} else {
|
||||
vm.lua_error( "string/function/table expected" );
|
||||
return;
|
||||
}
|
||||
|
||||
repl = vm.lua_tolvalue( -1 );
|
||||
if ( !repl.luaAsBoolean() ) {
|
||||
repl = s.substring( soffset, end );
|
||||
} else if ( ! ( repl instanceof LString || repl instanceof LNumber ) ) {
|
||||
vm.lua_error( "invalid replacement value (a "+repl.luaGetType()+")" );
|
||||
}
|
||||
vm.lua_pop( 1 );
|
||||
lbuf.append( repl.luaAsString() );
|
||||
}
|
||||
|
||||
int push_captures( boolean wholeMatch, int soff, int end ) {
|
||||
int nlevels = ( this.level == 0 && wholeMatch ) ? 1 : this.level;
|
||||
for ( int i = 0; i < nlevels; ++i ) {
|
||||
push_onecapture( i, soff, end );
|
||||
}
|
||||
return nlevels;
|
||||
}
|
||||
|
||||
private void push_onecapture( int i, int soff, int end ) {
|
||||
if ( i >= this.level ) {
|
||||
if ( i == 0 ) {
|
||||
vm.push( s.substring( soff, end ) );
|
||||
} else {
|
||||
vm.lua_error( "invalid capture index" );
|
||||
}
|
||||
} else {
|
||||
int l = clen[i];
|
||||
@@ -454,27 +583,25 @@ public class StrLib {
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isalpha( int c ) {
|
||||
return ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' );
|
||||
}
|
||||
|
||||
static boolean match_class( int c, int cl ) {
|
||||
final char lcl = Character.toLowerCase( (char) cl );
|
||||
int cdata = CHAR_TABLE[c];
|
||||
|
||||
boolean res;
|
||||
switch ( Character.toLowerCase( (char) c ) ) {
|
||||
case 'a': res = isalpha( c ); break;
|
||||
case 'd': res = Character.isDigit( (char) c ); break;
|
||||
case 'l': res = Character.isLowerCase( (char) c ); break;
|
||||
case 'u': res = Character.isUpperCase( (char) c ); break;
|
||||
switch ( lcl ) {
|
||||
case 'a': res = ( cdata & MASK_ALPHA ) != 0; break;
|
||||
case 'd': res = ( cdata & MASK_DIGIT ) != 0; break;
|
||||
case 'l': res = ( cdata & MASK_LOWERCASE ) != 0; break;
|
||||
case 'u': res = ( cdata & MASK_UPPERCASE ) != 0; break;
|
||||
case 'c': res = ( cdata & MASK_CONTROL ) != 0; break;
|
||||
case 'p': res = ( cdata & MASK_PUNCT ) != 0; break;
|
||||
case 's': res = ( cdata & MASK_SPACE ) != 0; break;
|
||||
case 'w': res = ( cdata & ( MASK_ALPHA | MASK_DIGIT ) ) != 0; break;
|
||||
case 'x': res = ( cdata & MASK_HEXDIGIT ) != 0; break;
|
||||
case 'z': res = ( c == 0 ); break;
|
||||
case 'c':
|
||||
case 'p':
|
||||
case 's':
|
||||
case 'w':
|
||||
case 'x':
|
||||
throw new RuntimeException("match: unimplemented: %" + (char)cl );
|
||||
default: return cl == c;
|
||||
}
|
||||
return ( Character.isLowerCase( (char) cl ) ? res : !res );
|
||||
return ( lcl == cl ) ? res : !res;
|
||||
}
|
||||
|
||||
boolean matchbracketclass( int c, int poff, int ec ) {
|
||||
@@ -521,10 +648,10 @@ public class StrLib {
|
||||
return soffset;
|
||||
switch ( p.luaByte( poffset ) ) {
|
||||
case '(':
|
||||
if ( p.luaByte( poffset + 1 ) == ')' )
|
||||
return start_capture( soffset, poffset + 2, CAP_POSITION );
|
||||
if ( ++poffset < p.length() && p.luaByte( poffset ) == ')' )
|
||||
return start_capture( soffset, poffset + 1, CAP_POSITION );
|
||||
else
|
||||
return start_capture( soffset, poffset + 1, CAP_UNFINISHED );
|
||||
return start_capture( soffset, poffset, CAP_UNFINISHED );
|
||||
case ')':
|
||||
return end_capture( soffset, poffset + 1 );
|
||||
case L_ESC:
|
||||
|
||||
@@ -11,10 +11,6 @@ public interface VM {
|
||||
|
||||
// ================ interfaces for performing calls
|
||||
|
||||
/** Prepare the VM stack for a new call with arguments to be pushed
|
||||
*/
|
||||
public void newCall();
|
||||
|
||||
/** Push an argument or return value onto the stack
|
||||
*/
|
||||
public void push( LValue value );
|
||||
@@ -163,6 +159,13 @@ public interface VM {
|
||||
*/
|
||||
public void lua_error(String message);
|
||||
|
||||
/**
|
||||
* Run the method on the stack, propagating any error that occurs.
|
||||
* @param nArgs number of arguments on the stack
|
||||
* @param nResults number of results on the stack
|
||||
*/
|
||||
public void lua_call(int nArgs, int nResults);
|
||||
|
||||
/**
|
||||
* Run the method on the stack in protected mode.
|
||||
* @param nArgs number of arguments on the stack
|
||||
@@ -186,4 +189,9 @@ public interface VM {
|
||||
* @return
|
||||
*/
|
||||
public LValue lua_tolvalue(int i);
|
||||
|
||||
/**
|
||||
* Pop some number of items off the stack.
|
||||
*/
|
||||
public void lua_pop(int n);
|
||||
}
|
||||
|
||||
@@ -12,27 +12,18 @@ public class LFunction extends LValue {
|
||||
}
|
||||
|
||||
public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
|
||||
vm.newCall();
|
||||
vm.push( this );
|
||||
vm.push( table );
|
||||
vm.push( key );
|
||||
vm.push( val );
|
||||
vm.setExpectedResultCount( 0 );
|
||||
if ( this.luaStackCall( vm ) )
|
||||
vm.execute();
|
||||
else
|
||||
vm.adjustResults();
|
||||
vm.lua_call( 3, 0 );
|
||||
}
|
||||
|
||||
public void luaGetTable(VM vm, LValue table, LValue key) {
|
||||
vm.newCall();
|
||||
vm.push( this );
|
||||
vm.push( table );
|
||||
vm.push( key );
|
||||
vm.setExpectedResultCount( 1 );
|
||||
if ( this.luaStackCall( vm ) )
|
||||
vm.execute();
|
||||
vm.adjustResults();
|
||||
vm.lua_call( 2, 1 );
|
||||
}
|
||||
|
||||
public LString luaGetType() {
|
||||
|
||||
@@ -15,6 +15,7 @@ import junit.framework.TestSuite;
|
||||
import lua.Builtin;
|
||||
import lua.StackState;
|
||||
import lua.addon.luacompat.LuaCompat;
|
||||
import lua.debug.DebugStackState;
|
||||
import lua.io.Closure;
|
||||
import lua.io.LoadState;
|
||||
import lua.io.Proto;
|
||||
@@ -79,7 +80,7 @@ public class StandardTest extends TestCase {
|
||||
// the garbage collector. Until we implement that, remove the
|
||||
// built-in collectgarbage function.
|
||||
GlobalState.getGlobalsTable().put( "collectgarbage", LNil.NIL );
|
||||
StackState state = new StackState();
|
||||
StackState state = new DebugStackState();
|
||||
Closure c = new Closure( state, code );
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
@@ -88,13 +89,15 @@ public class StandardTest extends TestCase {
|
||||
try {
|
||||
state.doCall( c, new LValue[0] );
|
||||
} catch ( RuntimeException exn ) {
|
||||
StackTraceElement[] stackTrace = new StackTraceElement[state.cc+1];
|
||||
for ( int i = 0; i <= state.cc; ++i ) {
|
||||
final int ncalls = Math.min( state.calls.length, state.cc+1 );
|
||||
StackTraceElement[] stackTrace = new StackTraceElement[ncalls];
|
||||
|
||||
for ( int i = 0; i < ncalls; ++i ) {
|
||||
CallInfo call = state.calls[i];
|
||||
Proto p = call.closure.p;
|
||||
int line = p.lineinfo[call.pc-1];
|
||||
String func = call.closure.luaAsString().toJavaString();
|
||||
stackTrace[state.cc - i] = new StackTraceElement(getName(), func, getName()+".lua", line );
|
||||
stackTrace[ncalls - i - 1] = new StackTraceElement(getName(), func, getName()+".lua", line );
|
||||
}
|
||||
|
||||
RuntimeException newExn = new RuntimeException( exn );
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
print( string.find( 'alo alx 123 b\0o b\0o', '(..*) %1' ) )
|
||||
print( string.find( 'aloALO', '%l*' ) )
|
||||
print( string.find( ' \n isto <20> assim', '%S%S*' ) )
|
||||
|
||||
print( string.find( "", "" ) )
|
||||
print( string.find( "ababaabbaba", "abb" ) )
|
||||
print( string.find( "ababaabbaba", "abb", 7 ) )
|
||||
@@ -19,3 +23,23 @@ print( string.byte("hi", -3) )
|
||||
print( tostring(1234567890123) )
|
||||
print( tostring(1234567890124) )
|
||||
print( tostring(1234567890125) )
|
||||
|
||||
function f1(s, p)
|
||||
print(p)
|
||||
p = string.gsub(p, "%%([0-9])", function (s) return "%" .. (s+1) end)
|
||||
print(p)
|
||||
p = string.gsub(p, "^(^?)", "%1()", 1)
|
||||
print(p)
|
||||
p = string.gsub(p, "($?)$", "()%1", 1)
|
||||
print(p)
|
||||
local t = {string.match(s, p)}
|
||||
return string.sub(s, t[1], t[#t] - 1)
|
||||
end
|
||||
|
||||
print( f1('alo alx 123 b\0o b\0o', '(..*) %1') )
|
||||
|
||||
local function badpat()
|
||||
print( string.gsub( "alo", "(.)", "%2" ) )
|
||||
end
|
||||
|
||||
print( pcall( badpat ) )
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user