changeset 43:80b67b1a653c

implement string lib git-svn-id: https://luan-java.googlecode.com/svn/trunk@44 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Tue, 25 Dec 2012 03:42:42 +0000
parents 786699c78837
children 57054fa43189
files src/luan/LuaException.java src/luan/LuaJavaFunction.java src/luan/lib/BasicLib.java src/luan/lib/StringLib.java src/luan/tools/CmdLine.java
diffstat 5 files changed, 307 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/LuaException.java	Sun Dec 23 06:36:56 2012 +0000
+++ b/src/luan/LuaException.java	Tue Dec 25 03:42:42 2012 +0000
@@ -35,12 +35,11 @@
 
 	private static String stackTrace(LuaState lua,LuaElement el,Object msg) {
 		StringBuilder buf = new StringBuilder();
-		int i = lua.stackTrace.size() - 1;
-		do {
+		for( int i  = lua.stackTrace.size() - 1; i>=0; i-- ) {
 			StackTraceElement stackTraceElement = lua.stackTrace.get(i);
 			buf.append( "\n\t" ).append( el.toString(stackTraceElement.fnName) );
 			el = stackTraceElement.call;
-		} while( --i >= 0 );
+		}
 		if( msg instanceof LuaException ) {
 			LuaException le = (LuaException)msg;
 			buf.append( "\ncaused by:" ).append( le.stackTrace );
--- a/src/luan/LuaJavaFunction.java	Sun Dec 23 06:36:56 2012 +0000
+++ b/src/luan/LuaJavaFunction.java	Tue Dec 25 03:42:42 2012 +0000
@@ -81,10 +81,11 @@
 				if( args.length < argConverters.length ) {
 					rtn[rtn.length-1] = Array.newInstance(varArgCls,0);
 				} else {
-					Object[] varArgs = (Object[])Array.newInstance(varArgCls,args.length-n);
+					int len = args.length - n;
+					Object varArgs = Array.newInstance(varArgCls,len);
 					ArgConverter ac = argConverters[n];
-					for( int i=0; i<varArgs.length; i++ ) {
-						varArgs[i] = ac.convert(args[n+i]);
+					for( int i=0; i<len; i++ ) {
+						Array.set( varArgs, i, ac.convert(args[n+i]) );
 					}
 					rtn[rtn.length-1] = varArgs;
 				}
@@ -110,6 +111,8 @@
 
 	private static final RtnConverter RTN_ARRAY = new RtnConverter() {
 		public Object[] convert(Object obj) {
+			if( obj == null )
+				return NULL_RTN;
 			return (Object[])obj;
 		}
 	};
@@ -120,31 +123,56 @@
 		}
 	};
 
+	private static final Object[] NULL_RTN = new Object[1];
+
 	private static final RtnConverter RTN_NUMBER = new RtnConverter() {
 		public Object[] convert(Object obj) {
 			if( obj == null )
-				return new Object[1];
+				return NULL_RTN;
 			Number n = (Number)obj;
 			LuaNumber ln = new LuaNumber(n.doubleValue());
 			return new Object[]{ln};
 		}
 	};
 
+	private static final RtnConverter RTN_NUMBER_ARRAY = new RtnConverter() {
+		public Object[] convert(Object obj) {
+			if( obj == null )
+				return NULL_RTN;
+			Object[] rtn = new Object[Array.getLength(obj)];
+			for( int i=0; i<rtn.length; i++ ) {
+				Number n = (Number)Array.get(obj,i);
+				if( n != null )
+					rtn[i] = new LuaNumber(n.doubleValue());
+			}
+			return rtn;
+		}
+	};
+
 	private static RtnConverter getRtnConverter(JavaMethod m) {
 		Class<?> rtnType = m.getReturnType();
 		if( rtnType == Void.TYPE )
 			return RTN_EMPTY;
-		if( Number.class.isAssignableFrom(rtnType)
+		if( isNumber(rtnType) )
+			return RTN_NUMBER;
+		if( rtnType.isArray() ) {
+			rtnType = rtnType.getComponentType();
+			if( isNumber(rtnType) )
+				return RTN_NUMBER_ARRAY;
+			return RTN_ARRAY;
+		}
+		return RTN_ONE;
+	}
+
+	private static boolean isNumber(Class<?> rtnType) {
+		return Number.class.isAssignableFrom(rtnType)
+			|| rtnType == Byte.TYPE
 			|| rtnType == Short.TYPE
 			|| rtnType == Integer.TYPE
 			|| rtnType == Long.TYPE
 			|| rtnType == Float.TYPE
 			|| rtnType == Double.TYPE
-		)
-			return RTN_NUMBER;
-		if( rtnType.isArray() )
-			return RTN_ARRAY;
-		return RTN_ONE;
+		;
 	}
 
 
@@ -244,6 +272,24 @@
 		}
 	};
 
+	private static final ArgConverter ARG_BYTE = new ArgConverter() {
+		public Object convert(Object obj) {
+			if( obj instanceof LuaNumber ) {
+				LuaNumber ln = (LuaNumber)obj;
+				byte i = (byte)ln.n;
+				if( i == ln.n )
+					return Byte.valueOf(i);
+			}
+			else if( obj instanceof String ) {
+				String s = (String)obj;
+				try {
+					return Byte.valueOf(s);
+				} catch(NumberFormatException e) {}
+			}
+			return obj;
+		}
+	};
+
 	private static boolean takesLuaState(JavaMethod m) {
 		Class<?>[] paramTypes = m.getParameterTypes();
 		return paramTypes.length > 0 && paramTypes[0].equals(LuaState.class);
@@ -278,6 +324,8 @@
 			return ARG_INTEGER;
 		if( cls == Short.TYPE || cls.equals(Short.class) )
 			return ARG_SHORT;
+		if( cls == Byte.TYPE || cls.equals(Byte.class) )
+			return ARG_BYTE;
 		return ARG_SAME;
 	}
 
@@ -292,39 +340,45 @@
 	
 		static JavaMethod of(final Method m) {
 			return new JavaMethod() {
-				boolean isVarArgs() {
+				@Override boolean isVarArgs() {
 					return m.isVarArgs();
 				}
-				Class<?>[] getParameterTypes() {
+				@Override Class<?>[] getParameterTypes() {
 					return m.getParameterTypes();
 				}
-				Object invoke(Object obj,Object... args)
+				@Override Object invoke(Object obj,Object... args)
 					throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
 				{
 					return m.invoke(obj,args);
 				}
-				Class<?> getReturnType() {
+				@Override Class<?> getReturnType() {
 					return m.getReturnType();
 				}
+				@Override public String toString() {
+					return m.toString();
+				}
 			};
 		}
 	
 		static JavaMethod of(final Constructor c) {
 			return new JavaMethod() {
-				boolean isVarArgs() {
+				@Override boolean isVarArgs() {
 					return c.isVarArgs();
 				}
-				Class<?>[] getParameterTypes() {
+				@Override Class<?>[] getParameterTypes() {
 					return c.getParameterTypes();
 				}
-				Object invoke(Object obj,Object... args)
+				@Override Object invoke(Object obj,Object... args)
 					throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException
 				{
 					return c.newInstance(args);
 				}
-				Class<?> getReturnType() {
+				@Override Class<?> getReturnType() {
 					return c.getDeclaringClass();
 				}
+				@Override public String toString() {
+					return c.toString();
+				}
 			};
 		}
 	
--- a/src/luan/lib/BasicLib.java	Sun Dec 23 06:36:56 2012 +0000
+++ b/src/luan/lib/BasicLib.java	Tue Dec 25 03:42:42 2012 +0000
@@ -121,66 +121,27 @@
 		return lua.call(fn,LuaElement.JAVA,null);
 	}
 
-	private static class TableIter {
-		private final Iterator<Map.Entry<Object,Object>> iter;
-
-		TableIter(Iterator<Map.Entry<Object,Object>> iter) {
-			this.iter = iter;
-		}
-
-		public Object[] next() {
-			if( !iter.hasNext() )
-				return LuaFunction.EMPTY_RTN;
-			Map.Entry<Object,Object> entry = iter.next();
-			return new Object[]{entry.getKey(),entry.getValue()};
-		}
-	}
-	private static final Method nextTableIter;
-	static {
-		try {
-			nextTableIter = TableIter.class.getMethod("next");
-			nextTableIter.setAccessible(true);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	static LuaFunction pairs(Iterator<Map.Entry<Object,Object>> iter) {
-		TableIter ti = new TableIter(iter);
-		return new LuaJavaFunction(nextTableIter,ti);
+	public static LuaFunction pairs(LuaTable t) {
+		final Iterator<Map.Entry<Object,Object>> iter = t.iterator();
+		return new LuaFunction() {
+			public Object[] call(LuaState lua,Object[] args) {
+				if( !iter.hasNext() )
+					return LuaFunction.EMPTY_RTN;
+				Map.Entry<Object,Object> entry = iter.next();
+				return new Object[]{entry.getKey(),entry.getValue()};
+			}
+		};
 	}
 
-	public static LuaFunction pairs(LuaTable t) {
-		return pairs(t.iterator());
-	}
-
-	private static class ArrayIter {
-		private final LuaTable t;
-		private double i = 0.0;
-
-		ArrayIter(LuaTable t) {
-			this.t = t;
-		}
-
-		public Object[] next() {
-			LuaNumber n = new LuaNumber(++i);
-			Object val = t.get(n);
-			return val==null ? LuaFunction.EMPTY_RTN : new Object[]{n,val};
-		}
-	}
-	private static final Method nextArrayIter;
-	static {
-		try {
-			nextArrayIter = ArrayIter.class.getMethod("next");
-			nextArrayIter.setAccessible(true);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public static LuaFunction ipairs(LuaTable t) {
-		ArrayIter ai = new ArrayIter(t);
-		return new LuaJavaFunction(nextArrayIter,ai);
+	public static LuaFunction ipairs(final LuaTable t) {
+		return new LuaFunction() {
+			private double i = 0.0;
+			public Object[] call(LuaState lua,Object[] args) {
+				LuaNumber n = new LuaNumber(++i);
+				Object val = t.get(n);
+				return val==null ? LuaFunction.EMPTY_RTN : new Object[]{n,val};
+			}
+		};
 	}
 
 	public static LuaTable get_metatable(LuaState lua,Object obj) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/lib/StringLib.java	Tue Dec 25 03:42:42 2012 +0000
@@ -0,0 +1,210 @@
+package luan.lib;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import luan.Lua;
+import luan.LuaState;
+import luan.LuaTable;
+import luan.LuaFunction;
+import luan.LuaJavaFunction;
+import luan.LuaNumber;
+import luan.LuaElement;
+import luan.LuaException;
+
+
+public final class StringLib {
+
+	public static void register(LuaState lua) {
+		LuaTable module = new LuaTable();
+		LuaTable global = lua.global();
+		global.put("string",module);
+		try {
+			module.put( "byte", new LuaJavaFunction(StringLib.class.getMethod("byte_",String.class,Integer.class,Integer.class),null) );
+			module.put( "char", new LuaJavaFunction(StringLib.class.getMethod("char_",new byte[0].getClass()),null) );
+			add( module, "find", String.class, String.class, Integer.class, Boolean.class );
+			add( module, "gmatch", String.class, String.class );
+			add( module, "gsub", LuaState.class, String.class, String.class, Object.class, Integer.class );
+			add( module, "len", String.class );
+			add( module, "lower", String.class );
+			add( module, "match", String.class, String.class, Integer.class );
+			add( module, "rep", String.class, Integer.TYPE, String.class );
+			add( module, "reverse", String.class );
+			add( module, "sub", String.class, Integer.TYPE, Integer.class );
+			add( module, "upper", String.class );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private static void add(LuaTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuaJavaFunction(StringLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	static int start(String s,int i) {
+		return i==0 ? 0 : i > 0 ? i - 1 : s.length() + i;
+	}
+
+	static int start(String s,Integer i,int dflt) {
+		return i==null ? dflt : start(s,i);
+	}
+
+	static int end(String s,int i) {
+		return i==0 ? 0 : i > 0 ? i : s.length() + i + 1;
+	}
+
+	static int end(String s,Integer i,int dflt) {
+		return i==null ? dflt : end(s,i);
+	}
+
+	public static byte[] byte_(String s,Integer i,Integer j) {
+		int start = start(s,i,0);
+		int end = end(s,j,start+1);
+		return s.substring(start,end).getBytes();
+	}
+
+	public static String char_(byte... bytes) {
+		return new String(bytes);
+	}
+
+	// format is hard because String.format() is too stupid to convert ints to floats.
+
+	public static int len(String s) {
+		return s.length();
+	}
+
+	public static String lower(String s) {
+		return s.toLowerCase();
+	}
+
+	public static String upper(String s) {
+		return s.toUpperCase();
+	}
+
+	public static String reverse(String s) {
+		return new StringBuilder(s).reverse().toString();
+	}
+
+	public static String rep(String s,int n,String sep) {
+		if( n < 1 )
+			return "";
+		StringBuilder buf = new StringBuilder(s);
+		while( --n > 0 ) {
+			if( sep != null )
+				buf.append(sep);
+			buf.append(s);
+		}
+		return buf.toString();
+	}
+
+	public static String sub(String s,int i,Integer j) {
+		int start = start(s,i);
+		int end = end(s,j,s.length());
+		return s.substring(start,end);
+	}
+
+	public static int[] find(String s,String pattern,Integer init,Boolean plain) {
+		int start = start(s,init,0);
+		if( Boolean.TRUE.equals(plain) ) {
+			int i = s.indexOf(pattern,start);
+			return i == -1 ? null : new int[]{i+1,i+pattern.length()};
+		}
+		Matcher m = Pattern.compile(pattern).matcher(s);
+		return m.find(start) ? new int[]{m.start()+1,m.end()} : null;
+	}
+
+	public static String[] match(String s,String pattern,Integer init) {
+		int start = start(s,init,0);
+		Matcher m = Pattern.compile(pattern).matcher(s);
+		if( !m.find(start) )
+			return null;
+		final int n = m.groupCount();
+		if( n == 0 )
+			return new String[]{m.group()};
+		String[] rtn = new String[n];
+		for( int i=0; i<n; i++ ) {
+			rtn[i] = m.group(i);
+		}
+		return rtn;
+	}
+
+	public static LuaFunction gmatch(String s,String pattern) {
+		final Matcher m = Pattern.compile(pattern).matcher(s);
+		return new LuaFunction() {
+			public Object[] call(LuaState lua,Object[] args) {
+				if( !m.find() )
+					return LuaFunction.EMPTY_RTN;
+				final int n = m.groupCount();
+				if( n == 0 )
+					return new String[]{m.group()};
+				String[] rtn = new String[n];
+				for( int i=0; i<n; i++ ) {
+					rtn[i] = m.group(i);
+				}
+				return rtn;
+			}
+		};
+	}
+
+	public static Object[] gsub(LuaState lua,String s,String pattern,Object repl,Integer n) throws LuaException {
+		int max = n==null ? Integer.MAX_VALUE : n;
+		final Matcher m = Pattern.compile(pattern).matcher(s);
+		if( repl instanceof String ) {
+			String replacement = (String)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				m.appendReplacement(sb,replacement);
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), new LuaNumber(i) };
+		}
+		if( repl instanceof LuaTable ) {
+			LuaTable t = (LuaTable)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				String match = m.groupCount()==0 ? m.group() : m.group(0);
+				Object val = t.get(match);
+				if( Lua.toBoolean(val) ) {
+					String replacement = Lua.asString(val);
+					if( replacement==null )
+						throw new LuaException( lua, LuaElement.JAVA, "invalid replacement value (a "+Lua.type(val)+")" );
+					m.appendReplacement(sb,replacement);
+				}
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), new LuaNumber(i) };
+		}
+		if( repl instanceof LuaFunction ) {
+			LuaFunction fn = (LuaFunction)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				Object[] args;
+				final int count = m.groupCount();
+				if( count == 0 ) {
+					args = new Object[]{m.group()};
+				} else {
+					args = new Object[count];
+					for( int j=0; j<count; j++ ) {
+						args[j] = m.group(j);
+					}
+				}
+				Object val = Lua.first( lua.call(fn,LuaElement.JAVA,"repl-arg",args) );
+				if( Lua.toBoolean(val) ) {
+					String replacement = Lua.asString(val);
+					if( replacement==null )
+						throw new LuaException( lua, LuaElement.JAVA, "invalid replacement value (a "+Lua.type(val)+")" );
+					m.appendReplacement(sb,replacement);
+				}
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), new LuaNumber(i) };
+		}
+		throw new LuaException( lua, LuaElement.JAVA, "bad argument #3 to 'gsub' (string/function/table expected)" );
+	}
+
+}
--- a/src/luan/tools/CmdLine.java	Sun Dec 23 06:36:56 2012 +0000
+++ b/src/luan/tools/CmdLine.java	Tue Dec 25 03:42:42 2012 +0000
@@ -4,6 +4,7 @@
 import java.util.Scanner;
 import luan.lib.BasicLib;
 import luan.lib.JavaLib;
+import luan.lib.StringLib;
 import luan.Lua;
 import luan.LuaState;
 import luan.LuaFunction;
@@ -19,6 +20,7 @@
 		LuaState lua = LuaCompiler.newLuaState();
 		BasicLib.register(lua);
 		JavaLib.register(lua);
+		StringLib.register(lua);
 		BasicLib.make_standard(lua);
 		boolean interactive = false;
 		boolean showVersion = false;
@@ -75,8 +77,8 @@
 				LuaFunction fn = BasicLib.load_file(lua,file);
 				lua.call(fn,null,null,varArgs);
 			} catch(LuaException e) {
-				System.err.println("error: "+e.getMessage());
-//				e.printStackTrace();
+//				System.err.println("error: "+e.getMessage());
+				e.printStackTrace();
 				System.exit(-1);
 			}
 		}