From 2db26b0844d62d2990f580e90a9304c53f150aaf Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Fri, 21 Sep 2007 00:34:14 +0000 Subject: [PATCH] Many built-ins: ipairs, table.insert, require, pcall --- .../java/lua/addon/luacompat/LuaCompat.java | 186 ++++++++++++++---- src/main/java/lua/Builtin.java | 42 ++-- src/main/java/lua/VM.java | 7 + src/main/java/lua/debug/DebugStackState.java | 24 ++- src/main/java/lua/value/LString.java | 5 + src/main/java/lua/value/LTable.java | 33 +++- src/main/java/lua/value/LValue.java | 5 +- 7 files changed, 240 insertions(+), 62 deletions(-) diff --git a/src/addon/java/lua/addon/luacompat/LuaCompat.java b/src/addon/java/lua/addon/luacompat/LuaCompat.java index 664b0e81..054ad67d 100644 --- a/src/addon/java/lua/addon/luacompat/LuaCompat.java +++ b/src/addon/java/lua/addon/luacompat/LuaCompat.java @@ -13,8 +13,6 @@ import lua.Lua; import lua.StackState; import lua.VM; import lua.io.Closure; -import lua.io.LoadState; -import lua.io.Proto; import lua.value.LBoolean; import lua.value.LDouble; import lua.value.LFunction; @@ -52,6 +50,11 @@ public class LuaCompat extends LFunction { installNames( pckg, PACKAGE_NAMES, PACKAGES_BASE ); globals.put( "package", pckg ); 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 ) { @@ -107,6 +110,14 @@ public class LuaCompat extends LFunction { "seeall", }; + public static final String[] TABLE_NAMES = { + "concat", + "insert", + "maxn", + "remove", + "sort", + }; + private static final int GLOBALS_BASE = 0; private static final int ASSERT = GLOBALS_BASE + 0; 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 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 LuaCompat( int id ) { @@ -198,7 +216,8 @@ public class LuaCompat extends LFunction { vm.setResult(); break; case DOFILE: - return dofile(vm, vm.getArgAsString(0)); + dofile(vm); + break; case LOADSTRING: loadstring(vm, vm.getArg(0), vm.getArgAsString(1)); break; @@ -209,8 +228,7 @@ public class LuaCompat extends LFunction { vm.setResult( tostring(vm, vm.getArg(0)) ); break; case UNPACK: - vm.setResult(); - unpack(vm, vm.getArg(0), vm.getArgAsInt(1), vm.getArgAsInt(2)); + unpack(vm); break; case NEXT: vm.setResult( next(vm, vm.getArg(0), vm.getArgAsInt(1)) ); @@ -290,12 +308,30 @@ public class LuaCompat extends LFunction { case SEEALL: seeall(vm); 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: luaUnsupportedOperation(); } return false; } + private void select( VM vm ) { LValue arg = vm.getArg( 0 ); if ( arg instanceof LNumber ) { @@ -451,13 +487,14 @@ public class LuaCompat extends LFunction { return loadis( vm, is, script ); } - // return true if call placed on the stack & ready to execute, false otherwise - private boolean dofile( VM vm, String fileName ) { - if ( loadfile( vm, fileName ) ) { - vm.prepStackCall(); // TODO: is this necessary? - return true; + // if load succeeds, return 0 for success, 1 for error (as per lua spec) + private void dofile( VM vm ) { + String filename = vm.getArgAsString(0); + if ( loadfile( vm, filename ) ) { + int s = vm.lua_pcall(1, 0); + vm.setResult( new LInteger( s!=0? 1: 0 ) ); } else { - return false; + vm.lua_error("cannot open "+filename); } } @@ -508,25 +545,26 @@ public class LuaCompat extends LFunction { return arg.luaAsString(); } - private static LTable toTable(VM vm, LValue list) { - if ( ! (list instanceof LTable) ) { - vm.lua_error("not a list: "+list); - return null; - } - return (LTable) list; - } - - private void unpack(VM vm, LValue list, int i, int j) { - LTable t = toTable(vm, list); - if ( t == null ) - return; + /** unpack (list [, i [, j]]) + * + * Returns the elements from the given table. This function is equivalent to + * return list[i], list[i+1], ···, list[j] + * + * 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 v = vm.getArg(0); + int i = vm.getArgAsInt(1); + int j = vm.getArgAsInt(2); + LTable list = (LTable) v; if ( i == 0 ) i = 1; if ( j == 0 ) - j = t.size(); - LValue[] keys = t.getKeys(); - for ( int k=i; k<=j; k++ ) - vm.push( t.get(keys[k-1]) ); + j = list.luaLength().luaAsInt(); + vm.setResult(); + for ( int k=i; k<=j; k++ ) + vm.push( list.get(k) ); } 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") ) vm.lua_error( "not found: "+s ); else if ( 0 == vm.lua_pcall(0, 1) ) { - LValue result = vm.getArg(0); + LValue result = vm.lua_tolvalue( -1 ); if ( result != LNil.NIL ) LOADED.put(modname, result); - else if ( ! LOADED.containsKey(modname) ) { - LOADED.put(modname, LBoolean.TRUE); - vm.setResult( LBoolean.TRUE ); - } + else if ( ! LOADED.containsKey(modname) ) + LOADED.put(modname, result = LBoolean.TRUE); + vm.setResult( result ); } } } @@ -594,5 +631,88 @@ public class LuaCompat extends LFunction { 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 ( k2? 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 < 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(); + } } diff --git a/src/main/java/lua/Builtin.java b/src/main/java/lua/Builtin.java index e3d76504..4a6eefde 100644 --- a/src/main/java/lua/Builtin.java +++ b/src/main/java/lua/Builtin.java @@ -25,13 +25,16 @@ final class Builtin extends LFunction { "getmetatable", "setmetatable", "type", - "pcall" }; + "pcall", + "ipairs", + }; private static final int PRINT = 0; private static final int PAIRS = 1; private static final int GETMETATABLE = 2; private static final int SETMETATABLE = 3; private static final int TYPE = 4; private static final int PCALL = 5; + private static final int IPAIRS = 6; private static PrintStream stdout = System.out; @@ -47,18 +50,20 @@ final class Builtin extends LFunction { // perform a lua call public boolean luaStackCall(VM vm) { switch ( id ) { - case PRINT: - int n = vm.getArgCount(); - for ( int i=0; i 0 ) - stdout.print( "\t" ); - stdout.print( vm.getArg(i).luaAsString() ); + case PRINT: { + int n = vm.getArgCount(); + for ( int i=0; i 0 ) + stdout.print( "\t" ); + stdout.print( vm.getArg(i).luaAsString() ); + } + stdout.println(); + vm.setResult(); } - stdout.println(); - vm.setResult(); break; case PAIRS: - vm.setResult( vm.getArg(0).luaPairs() ); + case IPAIRS: + vm.setResult( vm.getArg(0).luaPairs(id==PAIRS) ); break; case GETMETATABLE: vm.setResult( vm.getArg(0).luaGetMetatable() ); @@ -71,13 +76,16 @@ final class Builtin extends LFunction { case TYPE: vm.setResult( vm.getArg(0).luaGetType() ); break; - case PCALL: - if ( 0 != vm.lua_pcall( vm.getArgCount()-1, Lua.LUA_MULTRET ) ) { - LValue v = vm.getArg(0); - vm.setResult( LBoolean.FALSE ); - vm.push( v ); - } else { - vm.setResult( LBoolean.TRUE ); + case PCALL: { + int n = vm.getArgCount(); + int s = vm.lua_pcall( n-1, Lua.LUA_MULTRET ); + if ( s != 0 ) { + LValue v = vm.lua_tolvalue(-1); + vm.setResult( LBoolean.FALSE ); + vm.push( v ); + } else { + vm.setResult( LBoolean.TRUE ); + } } break; default: diff --git a/src/main/java/lua/VM.java b/src/main/java/lua/VM.java index 4891d2c7..4bd32360 100644 --- a/src/main/java/lua/VM.java +++ b/src/main/java/lua/VM.java @@ -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 */ 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); } diff --git a/src/main/java/lua/debug/DebugStackState.java b/src/main/java/lua/debug/DebugStackState.java index d6f1ad1f..e19c7169 100644 --- a/src/main/java/lua/debug/DebugStackState.java +++ b/src/main/java/lua/debug/DebugStackState.java @@ -5,7 +5,6 @@ import java.util.Map; import java.util.StringTokenizer; import lua.CallInfo; -import lua.Print; import lua.StackState; import lua.io.LocVars; import lua.io.Proto; @@ -38,22 +37,34 @@ public class DebugStackState extends StackState implements DebugRequestListener return source+":"+line+"("+func+")"; } + private String addLineInfo( String message ) { + return getFileLine(cc)+": "+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-- ) 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 public void exec() { try { super.exec(); } catch ( RuntimeException t ) { - String message = getFileLine(cc)+": "+t.getMessage(); t.printStackTrace(); - printLuaTrace(message); + printLuaTrace(t.getMessage()); System.out.flush(); - throw new RuntimeException( message, t ); + throw t; } } @@ -63,6 +74,9 @@ public class DebugStackState extends StackState implements DebugRequestListener if ( exiting ) throw new java.lang.RuntimeException("exiting"); + // make sure line numbers are current in any stack traces + calls[cc].pc = pc; + synchronized ( this ) { // anytime the line doesn't change we keep going diff --git a/src/main/java/lua/value/LString.java b/src/main/java/lua/value/LString.java index 9254b127..b65020ee 100644 --- a/src/main/java/lua/value/LString.java +++ b/src/main/java/lua/value/LString.java @@ -1,6 +1,7 @@ package lua.value; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; @@ -185,6 +186,10 @@ public class LString extends LValue { 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. diff --git a/src/main/java/lua/value/LTable.java b/src/main/java/lua/value/LTable.java index 9edcdbaf..900e6235 100644 --- a/src/main/java/lua/value/LTable.java +++ b/src/main/java/lua/value/LTable.java @@ -267,18 +267,20 @@ public class LTable extends LValue { } /** Valid for tables */ - public LValue luaPairs() { - return new LTableIterator(); + public LValue luaPairs(boolean isPairs) { + return new LTableIterator(isPairs); } /** Iterator for tables */ private final class LTableIterator extends LFunction { private int arrayIndex; private int hashIndex; + private final boolean isPairs; - private LTableIterator() { + private LTableIterator(boolean isPairs) { this.arrayIndex = 0; this.hashIndex = 0; + this.isPairs = isPairs; } // perform a lua call @@ -292,7 +294,7 @@ public class LTable extends LValue { return false; } } - if ( m_hashKeys != null ) { + if ( isPairs && (m_hashKeys != null) ) { while ( ( i = hashIndex++ ) < m_hashKeys.length ) { if ( m_hashKeys[i] != null ) { vm.push( m_hashKeys[i] ); @@ -513,5 +515,26 @@ public class LTable extends LValue { int getArrayCapacity() { 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"); + } + } diff --git a/src/main/java/lua/value/LValue.java b/src/main/java/lua/value/LValue.java index 9a7dfe70..d419ac59 100644 --- a/src/main/java/lua/value/LValue.java +++ b/src/main/java/lua/value/LValue.java @@ -151,8 +151,9 @@ public class LValue { return luaUnsupportedOperation(); } - /** Valid for tables */ - public LValue luaPairs() { + /** Valid for tables + * @param isPairs true to iterate over non-integers as well */ + public LValue luaPairs(boolean isPairs) { return luaUnsupportedOperation(); }