Many built-ins: ipairs, table.insert, require, pcall

This commit is contained in:
James Roseborough
2007-09-21 00:34:14 +00:00
parent d45bd96536
commit 2db26b0844
7 changed files with 240 additions and 62 deletions

View File

@@ -13,8 +13,6 @@ import lua.Lua;
import lua.StackState; import lua.StackState;
import lua.VM; import lua.VM;
import lua.io.Closure; import lua.io.Closure;
import lua.io.LoadState;
import lua.io.Proto;
import lua.value.LBoolean; import lua.value.LBoolean;
import lua.value.LDouble; import lua.value.LDouble;
import lua.value.LFunction; import lua.value.LFunction;
@@ -52,6 +50,11 @@ public class LuaCompat extends LFunction {
installNames( pckg, PACKAGE_NAMES, PACKAGES_BASE ); installNames( pckg, PACKAGE_NAMES, PACKAGES_BASE );
globals.put( "package", pckg ); globals.put( "package", pckg );
pckg.put( "loaded", LOADED ); pckg.put( "loaded", LOADED );
// table lib
LTable table = new LTable();
installNames( pckg, TABLE_NAMES, TABLES_BASE );
globals.put( "table", pckg );
} }
private static void installNames( LTable table, String[] names, int indexBase ) { private static void installNames( LTable table, String[] names, int indexBase ) {
@@ -107,6 +110,14 @@ public class LuaCompat extends LFunction {
"seeall", "seeall",
}; };
public static final String[] TABLE_NAMES = {
"concat",
"insert",
"maxn",
"remove",
"sort",
};
private static final int GLOBALS_BASE = 0; private static final int GLOBALS_BASE = 0;
private static final int ASSERT = GLOBALS_BASE + 0; private static final int ASSERT = GLOBALS_BASE + 0;
private static final int LOADFILE = GLOBALS_BASE + 1; private static final int LOADFILE = GLOBALS_BASE + 1;
@@ -152,6 +163,13 @@ public class LuaCompat extends LFunction {
private static final int LOADLIB = PACKAGES_BASE + 0; private static final int LOADLIB = PACKAGES_BASE + 0;
private static final int SEEALL = PACKAGES_BASE + 1; private static final int SEEALL = PACKAGES_BASE + 1;
private static final int TABLES_BASE = 60;
private static final int CONCAT = TABLES_BASE + 0;
private static final int INSERT = TABLES_BASE + 1;
private static final int MAXN = TABLES_BASE + 2;
private static final int REMOVE = TABLES_BASE + 3;
private static final int SORT = TABLES_BASE + 4;
private final int id; private final int id;
private LuaCompat( int id ) { private LuaCompat( int id ) {
@@ -198,7 +216,8 @@ public class LuaCompat extends LFunction {
vm.setResult(); vm.setResult();
break; break;
case DOFILE: case DOFILE:
return dofile(vm, vm.getArgAsString(0)); dofile(vm);
break;
case LOADSTRING: case LOADSTRING:
loadstring(vm, vm.getArg(0), vm.getArgAsString(1)); loadstring(vm, vm.getArg(0), vm.getArgAsString(1));
break; break;
@@ -209,8 +228,7 @@ public class LuaCompat extends LFunction {
vm.setResult( tostring(vm, vm.getArg(0)) ); vm.setResult( tostring(vm, vm.getArg(0)) );
break; break;
case UNPACK: case UNPACK:
vm.setResult(); unpack(vm);
unpack(vm, vm.getArg(0), vm.getArgAsInt(1), vm.getArgAsInt(2));
break; break;
case NEXT: case NEXT:
vm.setResult( next(vm, vm.getArg(0), vm.getArgAsInt(1)) ); vm.setResult( next(vm, vm.getArg(0), vm.getArgAsInt(1)) );
@@ -290,12 +308,30 @@ public class LuaCompat extends LFunction {
case SEEALL: case SEEALL:
seeall(vm); seeall(vm);
break; break;
// table library
case CONCAT:
concat(vm);
break;
case INSERT:
insert(vm);
break;
case MAXN:
maxn(vm);
break;
case REMOVE:
remove(vm);
break;
case SORT:
sort(vm);
break;
default: default:
luaUnsupportedOperation(); luaUnsupportedOperation();
} }
return false; return false;
} }
private void select( VM vm ) { private void select( VM vm ) {
LValue arg = vm.getArg( 0 ); LValue arg = vm.getArg( 0 );
if ( arg instanceof LNumber ) { if ( arg instanceof LNumber ) {
@@ -451,13 +487,14 @@ public class LuaCompat extends LFunction {
return loadis( vm, is, script ); return loadis( vm, is, script );
} }
// return true if call placed on the stack & ready to execute, false otherwise // if load succeeds, return 0 for success, 1 for error (as per lua spec)
private boolean dofile( VM vm, String fileName ) { private void dofile( VM vm ) {
if ( loadfile( vm, fileName ) ) { String filename = vm.getArgAsString(0);
vm.prepStackCall(); // TODO: is this necessary? if ( loadfile( vm, filename ) ) {
return true; int s = vm.lua_pcall(1, 0);
vm.setResult( new LInteger( s!=0? 1: 0 ) );
} else { } else {
return false; vm.lua_error("cannot open "+filename);
} }
} }
@@ -508,25 +545,26 @@ public class LuaCompat extends LFunction {
return arg.luaAsString(); return arg.luaAsString();
} }
private static LTable toTable(VM vm, LValue list) { /** unpack (list [, i [, j]])
if ( ! (list instanceof LTable) ) { *
vm.lua_error("not a list: "+list); * Returns the elements from the given table. This function is equivalent to
return null; * return list[i], list[i+1], ···, list[j]
} *
return (LTable) list; * except that the above code can be written only for a fixed number of elements.
} * By default, i is 1 and j is the length of the list, as defined by the length operator (see §2.5.5).
*/
private void unpack(VM vm, LValue list, int i, int j) { private void unpack(VM vm) {
LTable t = toTable(vm, list); LValue v = vm.getArg(0);
if ( t == null ) int i = vm.getArgAsInt(1);
return; int j = vm.getArgAsInt(2);
LTable list = (LTable) v;
if ( i == 0 ) if ( i == 0 )
i = 1; i = 1;
if ( j == 0 ) if ( j == 0 )
j = t.size(); j = list.luaLength().luaAsInt();
LValue[] keys = t.getKeys(); vm.setResult();
for ( int k=i; k<=j; k++ ) for ( int k=i; k<=j; k++ )
vm.push( t.get(keys[k-1]) ); vm.push( list.get(k) );
} }
private LValue next(VM vm, LValue table, int index) { private LValue next(VM vm, LValue table, int index) {
@@ -575,13 +613,12 @@ public class LuaCompat extends LFunction {
if ( ! loadfile(vm, s+".luac") && ! loadfile(vm, s+".lua") ) if ( ! loadfile(vm, s+".luac") && ! loadfile(vm, s+".lua") )
vm.lua_error( "not found: "+s ); vm.lua_error( "not found: "+s );
else if ( 0 == vm.lua_pcall(0, 1) ) { else if ( 0 == vm.lua_pcall(0, 1) ) {
LValue result = vm.getArg(0); LValue result = vm.lua_tolvalue( -1 );
if ( result != LNil.NIL ) if ( result != LNil.NIL )
LOADED.put(modname, result); LOADED.put(modname, result);
else if ( ! LOADED.containsKey(modname) ) { else if ( ! LOADED.containsKey(modname) )
LOADED.put(modname, LBoolean.TRUE); LOADED.put(modname, result = LBoolean.TRUE);
vm.setResult( LBoolean.TRUE ); vm.setResult( result );
}
} }
} }
} }
@@ -594,5 +631,88 @@ public class LuaCompat extends LFunction {
vm.lua_error( "seeall not implemented" ); vm.lua_error( "seeall not implemented" );
} }
// ============= tables support =============
/** table.concat (table [, sep [, i [, j]]])
*
* Given an array where all elements are strings or numbers, returns table[i]..sep..table[i+1] ··· sep..table[j].
* The default value for sep is the empty string, the default for i is 1, and the default for j is the length of the table.
* If i is greater than j, returns the empty string.
*/
private void concat(VM vm) {
LTable table = (LTable) vm.getArg(0);
LString sep = vm.getArgAsLuaString(1);
int i = vm.getArgAsInt(2);
int j = vm.getArgAsInt(3);
LValue[] keys = table.getKeys();
if ( i == 0 )
i = 1;
if ( j == 0 )
j = keys.length;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
for ( int k=i; k<=j; k++ ) {
LValue v = table.get(keys[k-1]);
v.luaAsString().write(baos);
if ( k<j )
sep.write( baos );
}
vm.setResult( new LString( baos.toByteArray() ) );
} catch (IOException e) {
vm.lua_error(e.getMessage());
}
}
/** table.insert (table, [pos,] value)
*
* Inserts element value at position pos in table, shifting up other elements to open space, if necessary.
* The default value for pos is n+1, where n is the length of the table (see §2.5.5), so that a call
* table.insert(t,x) inserts x at the end of table t.
*/
private void insert(VM vm) {
int n = vm.getArgCount();
LTable table = (LTable) vm.getArg(0);
int pos = (n>2? vm.getArgAsInt(1): 0);
LValue value = vm.getArg(n-1);
table.luaInsertPos( pos, value );
}
/** table.maxn (table)
*
* Returns the largest positive numerical index of the given table, or zero if the table has no positive numerical
* indices. (To do its job this function does a linear traversal of the whole table.)
*/
private void maxn(VM vm) {
LTable table = (LTable) vm.getArg(0);
vm.setResult( new LInteger( table.luaMaxN() ) );
}
/** table.remove (table [, pos])
*
* Removes from table the element at position pos, shifting down other elements to close the space, if necessary.
* Returns the value of the removed element. The default value for pos is n, where n is the length of the table,
* so that a call table.remove(t) removes the last element of table t.
*/
private void remove(VM vm) {
int n = vm.getArgCount();
LTable table = (LTable) vm.getArg(0);
int pos = (n>1? vm.getArgAsInt(1): 0);
table.luaRemovePos( pos );
}
/** table.sort (table [, comp])
*
* Sorts table elements in a given order, in-place, from table[1] to table[n], where n is the length of the table.
* If comp is given, then it must be a function that receives two table elements, and returns true when the first
* is less than the second (so that not comp(a[i+1],a[i]) will be true after the sort). If comp is not given,
* then the standard Lua operator &lt; is used instead.
*
* The sort algorithm is not stable; that is, elements considered equal by the given order may have their relative positions changed by the sort.
*/
private void sort(VM vm) {
LTable table = (LTable) vm.getArg(0);
table.luaSort();
}
} }

View File

@@ -25,13 +25,16 @@ final class Builtin extends LFunction {
"getmetatable", "getmetatable",
"setmetatable", "setmetatable",
"type", "type",
"pcall" }; "pcall",
"ipairs",
};
private static final int PRINT = 0; private static final int PRINT = 0;
private static final int PAIRS = 1; private static final int PAIRS = 1;
private static final int GETMETATABLE = 2; private static final int GETMETATABLE = 2;
private static final int SETMETATABLE = 3; private static final int SETMETATABLE = 3;
private static final int TYPE = 4; private static final int TYPE = 4;
private static final int PCALL = 5; private static final int PCALL = 5;
private static final int IPAIRS = 6;
private static PrintStream stdout = System.out; private static PrintStream stdout = System.out;
@@ -47,18 +50,20 @@ final class Builtin extends LFunction {
// perform a lua call // perform a lua call
public boolean luaStackCall(VM vm) { public boolean luaStackCall(VM vm) {
switch ( id ) { switch ( id ) {
case PRINT: case PRINT: {
int n = vm.getArgCount(); int n = vm.getArgCount();
for ( int i=0; i<n; i++ ) { for ( int i=0; i<n; i++ ) {
if ( i > 0 ) if ( i > 0 )
stdout.print( "\t" ); stdout.print( "\t" );
stdout.print( vm.getArg(i).luaAsString() ); stdout.print( vm.getArg(i).luaAsString() );
}
stdout.println();
vm.setResult();
} }
stdout.println();
vm.setResult();
break; break;
case PAIRS: case PAIRS:
vm.setResult( vm.getArg(0).luaPairs() ); case IPAIRS:
vm.setResult( vm.getArg(0).luaPairs(id==PAIRS) );
break; break;
case GETMETATABLE: case GETMETATABLE:
vm.setResult( vm.getArg(0).luaGetMetatable() ); vm.setResult( vm.getArg(0).luaGetMetatable() );
@@ -71,13 +76,16 @@ final class Builtin extends LFunction {
case TYPE: case TYPE:
vm.setResult( vm.getArg(0).luaGetType() ); vm.setResult( vm.getArg(0).luaGetType() );
break; break;
case PCALL: case PCALL: {
if ( 0 != vm.lua_pcall( vm.getArgCount()-1, Lua.LUA_MULTRET ) ) { int n = vm.getArgCount();
LValue v = vm.getArg(0); int s = vm.lua_pcall( n-1, Lua.LUA_MULTRET );
vm.setResult( LBoolean.FALSE ); if ( s != 0 ) {
vm.push( v ); LValue v = vm.lua_tolvalue(-1);
} else { vm.setResult( LBoolean.FALSE );
vm.setResult( LBoolean.TRUE ); vm.push( v );
} else {
vm.setResult( LBoolean.TRUE );
}
} }
break; break;
default: default:

View File

@@ -179,4 +179,11 @@ public interface VM {
* @return 0 if successful, LUA_ERRMEM if no memory, LUA_ERRSYNTAX for i/o or any other errors * @return 0 if successful, LUA_ERRMEM if no memory, LUA_ERRSYNTAX for i/o or any other errors
*/ */
public int lua_load( InputStream is, String chunkname ); public int lua_load( InputStream is, String chunkname );
/**
* Get a value on the stack, relative to the top or bottom
* @param i index from bottom if 0 or greater, index from top if less than 0
* @return
*/
public LValue lua_tolvalue(int i);
} }

View File

@@ -5,7 +5,6 @@ import java.util.Map;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import lua.CallInfo; import lua.CallInfo;
import lua.Print;
import lua.StackState; import lua.StackState;
import lua.io.LocVars; import lua.io.LocVars;
import lua.io.Proto; import lua.io.Proto;
@@ -38,22 +37,34 @@ public class DebugStackState extends StackState implements DebugRequestListener
return source+":"+line+"("+func+")"; return source+":"+line+"("+func+")";
} }
private String addLineInfo( String message ) {
return getFileLine(cc)+": "+message;
}
private void printLuaTrace(String message) { private void printLuaTrace(String message) {
System.out.println( "Lua error: "+message ); System.out.println( "Lua error: "+addLineInfo( message ) );
for ( int cindex=cc-1; cindex>=0; cindex-- ) for ( int cindex=cc-1; cindex>=0; cindex-- )
System.out.println( "\tcalled by "+getFileLine( cindex ) ); System.out.println( "\tcalled by "+getFileLine( cindex ) );
} }
protected void debugPcallError(Throwable t) {
System.out.println(addLineInfo("(caught in pcall) "+t.getMessage()));
System.out.flush();
}
public void lua_error(String message) {
throw new RuntimeException( message );
}
// intercept exceptions and fill in line numbers // intercept exceptions and fill in line numbers
public void exec() { public void exec() {
try { try {
super.exec(); super.exec();
} catch ( RuntimeException t ) { } catch ( RuntimeException t ) {
String message = getFileLine(cc)+": "+t.getMessage();
t.printStackTrace(); t.printStackTrace();
printLuaTrace(message); printLuaTrace(t.getMessage());
System.out.flush(); System.out.flush();
throw new RuntimeException( message, t ); throw t;
} }
} }
@@ -63,6 +74,9 @@ public class DebugStackState extends StackState implements DebugRequestListener
if ( exiting ) if ( exiting )
throw new java.lang.RuntimeException("exiting"); throw new java.lang.RuntimeException("exiting");
// make sure line numbers are current in any stack traces
calls[cc].pc = pc;
synchronized ( this ) { synchronized ( this ) {
// anytime the line doesn't change we keep going // anytime the line doesn't change we keep going

View File

@@ -1,6 +1,7 @@
package lua.value; package lua.value;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@@ -185,6 +186,10 @@ public class LString extends LValue {
os.write( m_bytes, m_offset+offset, len ); os.write( m_bytes, m_offset+offset, len );
} }
public void write(OutputStream os) throws IOException {
write(os, 0, m_length);
}
/** /**
* Copy the bytes of the string into the given byte array. * Copy the bytes of the string into the given byte array.

View File

@@ -267,18 +267,20 @@ public class LTable extends LValue {
} }
/** Valid for tables */ /** Valid for tables */
public LValue luaPairs() { public LValue luaPairs(boolean isPairs) {
return new LTableIterator(); return new LTableIterator(isPairs);
} }
/** Iterator for tables */ /** Iterator for tables */
private final class LTableIterator extends LFunction { private final class LTableIterator extends LFunction {
private int arrayIndex; private int arrayIndex;
private int hashIndex; private int hashIndex;
private final boolean isPairs;
private LTableIterator() { private LTableIterator(boolean isPairs) {
this.arrayIndex = 0; this.arrayIndex = 0;
this.hashIndex = 0; this.hashIndex = 0;
this.isPairs = isPairs;
} }
// perform a lua call // perform a lua call
@@ -292,7 +294,7 @@ public class LTable extends LValue {
return false; return false;
} }
} }
if ( m_hashKeys != null ) { if ( isPairs && (m_hashKeys != null) ) {
while ( ( i = hashIndex++ ) < m_hashKeys.length ) { while ( ( i = hashIndex++ ) < m_hashKeys.length ) {
if ( m_hashKeys[i] != null ) { if ( m_hashKeys[i] != null ) {
vm.push( m_hashKeys[i] ); vm.push( m_hashKeys[i] );
@@ -513,5 +515,26 @@ public class LTable extends LValue {
int getArrayCapacity() { int getArrayCapacity() {
return m_vector.length; return m_vector.length;
} }
/*
* @pos index to insert at, or 0 to insert at end.
*/
public void luaInsertPos(int pos, LValue value) {
if ( pos != 0 )
throw new RuntimeException("luaInsertPos() not implemented");
put( m_arrayEntries + m_hashEntries + 1, value );
}
public void luaSort() {
throw new RuntimeException("luaSort() not implemented");
}
public void luaRemovePos(int pos) {
throw new RuntimeException("luaRemovePos() not implemented");
}
public int luaMaxN() {
throw new RuntimeException("luaMaxN() not implemented");
}
} }

View File

@@ -151,8 +151,9 @@ public class LValue {
return luaUnsupportedOperation(); return luaUnsupportedOperation();
} }
/** Valid for tables */ /** Valid for tables
public LValue luaPairs() { * @param isPairs true to iterate over non-integers as well */
public LValue luaPairs(boolean isPairs) {
return luaUnsupportedOperation(); return luaUnsupportedOperation();
} }