Mercurial Hosting > luan
changeset 775:1a68fc55a80c
simplify dir structure
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 26 Aug 2016 14:36:40 -0600 |
parents | 3e30cf310e56 |
children | 815c119dac7a |
files | core/src/luan/DeepCloneable.java core/src/luan/DeepCloner.java core/src/luan/Luan.java core/src/luan/LuanException.java core/src/luan/LuanFunction.java core/src/luan/LuanJava.java core/src/luan/LuanJavaFunction.java core/src/luan/LuanMeta.java core/src/luan/LuanMethod.java core/src/luan/LuanPropertyMeta.java core/src/luan/LuanRuntimeException.java core/src/luan/LuanState.java core/src/luan/LuanTable.java core/src/luan/cmd_line.luan core/src/luan/impl/Closure.java core/src/luan/impl/LuanCompiler.java core/src/luan/impl/LuanImpl.java core/src/luan/impl/LuanJavaCompiler.java core/src/luan/impl/LuanParser.java core/src/luan/impl/ParseException.java core/src/luan/impl/Parser.java core/src/luan/impl/Pointer.java core/src/luan/impl/TableField.java core/src/luan/modules/BasicLuan.java core/src/luan/modules/Binary.luan core/src/luan/modules/BinaryLuan.java core/src/luan/modules/Html.luan core/src/luan/modules/HtmlLuan.java core/src/luan/modules/Io.luan core/src/luan/modules/IoLuan.java core/src/luan/modules/JavaLuan.java core/src/luan/modules/Luan.luan core/src/luan/modules/Math.luan core/src/luan/modules/MathLuan.java core/src/luan/modules/Number.luan core/src/luan/modules/Package.luan core/src/luan/modules/PackageLuan.java core/src/luan/modules/Parsers.luan core/src/luan/modules/Rpc.luan core/src/luan/modules/RpcLuan.java core/src/luan/modules/String.luan core/src/luan/modules/StringLuan.java core/src/luan/modules/Table.luan core/src/luan/modules/TableLuan.java core/src/luan/modules/Thread.luan core/src/luan/modules/ThreadLuan.java core/src/luan/modules/Time.luan core/src/luan/modules/Utils.java core/src/luan/modules/Which_mod.luan core/src/luan/modules/host/Hosting.luan core/src/luan/modules/host/backup.luan core/src/luan/modules/host/delete.luan core/src/luan/modules/host/push.luan core/src/luan/modules/host/restore.luan core/src/luan/modules/luan_to_java.luan core/src/luan/modules/mmake.luan core/src/luan/modules/parsers/BBCode.java core/src/luan/modules/parsers/Csv.java core/src/luan/modules/parsers/Html.java core/src/luan/modules/parsers/Json.java core/src/luan/modules/parsers/ParseException.java core/src/luan/modules/parsers/Parser.java core/src/luan/modules/parsers/Theme.java core/src/luan/modules/theme_to_luan.luan core/src/luan/modules/url/LuanUrl.java core/src/luan/modules/url/MultiPartOutputStream.java core/src/luan/modules/url/MultipartClient.java core/src/luan/modules/url/UrlCall.java core/src/luan/modules/which.luan http/ext/jetty-continuation-8.1.15.v20140411.jar http/ext/jetty-http-8.1.15.v20140411.jar http/ext/jetty-io-8.1.15.v20140411.jar http/ext/jetty-server-8.1.15.v20140411.jar http/ext/jetty-util-8.1.15.v20140411.jar http/ext/servlet-api-3.0.jar http/src/luan/modules/http/AuthenticationHandler.java http/src/luan/modules/http/Dump_mod.luan http/src/luan/modules/http/Http.luan http/src/luan/modules/http/HttpServicer.java http/src/luan/modules/http/Http_test.luan http/src/luan/modules/http/LuanHandler.java http/src/luan/modules/http/NotFound.java http/src/luan/modules/http/Server.luan http/src/luan/modules/http/Shell_mod.luan http/src/luan/modules/http/dump.luan http/src/luan/modules/http/run.luan http/src/luan/modules/http/serve.luan http/src/luan/modules/http/shell.luan http/src/luan/modules/http/test.luan lib/javax.mail.jar lib/jetty-continuation-8.1.15.v20140411.jar lib/jetty-http-8.1.15.v20140411.jar lib/jetty-io-8.1.15.v20140411.jar lib/jetty-server-8.1.15.v20140411.jar lib/jetty-util-8.1.15.v20140411.jar lib/log4j-1.2.17.jar lib/lucene-analyzers-common-4.9.0.jar lib/lucene-core-4.9.0.jar lib/lucene-highlighter-4.9.0.jar lib/lucene-memory-4.9.0.jar lib/lucene-queries-4.9.0.jar lib/servlet-api-3.0.jar lib/slf4j-api-1.6.4.jar lib/slf4j-log4j12-1.6.4.jar logging/ext/log4j-1.2.17.jar logging/ext/slf4j-api-1.6.4.jar logging/ext/slf4j-log4j12-1.6.4.jar logging/src/luan/modules/logging/Logging.luan logging/src/luan/modules/logging/LuanLogger.java logging/src/luan/modules/logging/init.luan lucene/ext/lucene-analyzers-common-4.9.0.jar lucene/ext/lucene-core-4.9.0.jar lucene/ext/lucene-highlighter-4.9.0.jar lucene/ext/lucene-memory-4.9.0.jar lucene/ext/lucene-queries-4.9.0.jar lucene/src/luan/modules/lucene/Ab_testing.luan lucene/src/luan/modules/lucene/Lucene.luan lucene/src/luan/modules/lucene/LuceneIndex.java lucene/src/luan/modules/lucene/Versioning.luan lucene/src/luan/modules/lucene/Web_search.luan lucene/src/luan/modules/lucene/queryparser/FieldParser.java lucene/src/luan/modules/lucene/queryparser/MultiFieldParser.java lucene/src/luan/modules/lucene/queryparser/NumberFieldParser.java lucene/src/luan/modules/lucene/queryparser/ParseException.java lucene/src/luan/modules/lucene/queryparser/Parser.java lucene/src/luan/modules/lucene/queryparser/SaneQueryParser.java lucene/src/luan/modules/lucene/queryparser/StringFieldParser.java lucene/src/luan/modules/lucene/queryparser/SynonymParser.java mail/ext/javax.mail.jar mail/src/luan/modules/mail/Mail.luan mail/src/luan/modules/mail/SmtpCon.java scripts/build-luan.sh scripts/cp-luan src/luan/DeepCloneable.java src/luan/DeepCloner.java src/luan/Luan.java src/luan/LuanException.java src/luan/LuanFunction.java src/luan/LuanJava.java src/luan/LuanJavaFunction.java src/luan/LuanMeta.java src/luan/LuanMethod.java src/luan/LuanPropertyMeta.java src/luan/LuanRuntimeException.java src/luan/LuanState.java src/luan/LuanTable.java src/luan/cmd_line.luan src/luan/impl/Closure.java src/luan/impl/LuanCompiler.java src/luan/impl/LuanImpl.java src/luan/impl/LuanJavaCompiler.java src/luan/impl/LuanParser.java src/luan/impl/ParseException.java src/luan/impl/Parser.java src/luan/impl/Pointer.java src/luan/impl/TableField.java src/luan/modules/BasicLuan.java src/luan/modules/Binary.luan src/luan/modules/BinaryLuan.java src/luan/modules/Html.luan src/luan/modules/HtmlLuan.java src/luan/modules/Io.luan src/luan/modules/IoLuan.java src/luan/modules/JavaLuan.java src/luan/modules/Luan.luan src/luan/modules/Math.luan src/luan/modules/MathLuan.java src/luan/modules/Number.luan src/luan/modules/Package.luan src/luan/modules/PackageLuan.java src/luan/modules/Parsers.luan src/luan/modules/Rpc.luan src/luan/modules/RpcLuan.java src/luan/modules/String.luan src/luan/modules/StringLuan.java src/luan/modules/Table.luan src/luan/modules/TableLuan.java src/luan/modules/Thread.luan src/luan/modules/ThreadLuan.java src/luan/modules/Time.luan src/luan/modules/Utils.java src/luan/modules/Which_mod.luan src/luan/modules/host/Hosting.luan src/luan/modules/host/backup.luan src/luan/modules/host/delete.luan src/luan/modules/host/push.luan src/luan/modules/host/restore.luan src/luan/modules/http/AuthenticationHandler.java src/luan/modules/http/Dump_mod.luan src/luan/modules/http/Http.luan src/luan/modules/http/HttpServicer.java src/luan/modules/http/Http_test.luan src/luan/modules/http/LuanHandler.java src/luan/modules/http/NotFound.java src/luan/modules/http/Server.luan src/luan/modules/http/Shell_mod.luan src/luan/modules/http/dump.luan src/luan/modules/http/run.luan src/luan/modules/http/serve.luan src/luan/modules/http/shell.luan src/luan/modules/http/test.luan src/luan/modules/logging/Logging.luan src/luan/modules/logging/LuanLogger.java src/luan/modules/logging/init.luan src/luan/modules/luan_to_java.luan src/luan/modules/lucene/Ab_testing.luan src/luan/modules/lucene/Lucene.luan src/luan/modules/lucene/LuceneIndex.java src/luan/modules/lucene/Versioning.luan src/luan/modules/lucene/Web_search.luan src/luan/modules/lucene/queryparser/FieldParser.java src/luan/modules/lucene/queryparser/MultiFieldParser.java src/luan/modules/lucene/queryparser/NumberFieldParser.java src/luan/modules/lucene/queryparser/ParseException.java src/luan/modules/lucene/queryparser/Parser.java src/luan/modules/lucene/queryparser/SaneQueryParser.java src/luan/modules/lucene/queryparser/StringFieldParser.java src/luan/modules/lucene/queryparser/SynonymParser.java src/luan/modules/mail/Mail.luan src/luan/modules/mail/SmtpCon.java src/luan/modules/mmake.luan src/luan/modules/parsers/BBCode.java src/luan/modules/parsers/Csv.java src/luan/modules/parsers/Html.java src/luan/modules/parsers/Json.java src/luan/modules/parsers/ParseException.java src/luan/modules/parsers/Parser.java src/luan/modules/parsers/Theme.java src/luan/modules/theme_to_luan.luan src/luan/modules/url/LuanUrl.java src/luan/modules/url/MultiPartOutputStream.java src/luan/modules/url/MultipartClient.java src/luan/modules/url/UrlCall.java src/luan/modules/which.luan |
diffstat | 234 files changed, 13958 insertions(+), 14007 deletions(-) [+] |
line wrap: on
line diff
--- a/core/src/luan/DeepCloneable.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -package luan; - - -public interface DeepCloneable { - public DeepCloneable shallowClone(); - public void deepenClone(DeepCloneable clone,DeepCloner cloner); -}
--- a/core/src/luan/DeepCloner.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -package luan; - -import java.util.Map; -import java.util.HashMap; -import java.util.IdentityHashMap; - - -public final class DeepCloner { - private final Map cloned = new IdentityHashMap(); - - public DeepCloneable deepClone(DeepCloneable obj) { - if( obj==null ) - return null; - DeepCloneable rtn = (DeepCloneable)cloned.get(obj); - if( rtn == null ) { - rtn = obj.shallowClone(); - cloned.put(obj,rtn); - obj.deepenClone(rtn,this); - } - return rtn; - } - - public Object[] deepClone(Object[] obj) { - if( obj.length == 0 ) - return obj; - Object[] rtn = (Object[])cloned.get(obj); - if( rtn == null ) { - rtn = obj.clone(); - cloned.put(obj,rtn); - for( int i=0; i<rtn.length; i++ ) { - rtn[i] = get(rtn[i]); - } - } - return rtn; - } - - public Map deepClone(Map obj) { - if( !obj.getClass().equals(HashMap.class) ) - throw new RuntimeException("can only clone HashMap"); - Map rtn = (Map)cloned.get(obj); - if( rtn == null ) { - rtn = new HashMap(); - for( Object stupid : obj.entrySet() ) { - Map.Entry entry = (Map.Entry)stupid; - rtn.put( get(entry.getKey()), get(entry.getValue()) ); - } - } - return rtn; - } - - public Object get(Object obj) { - if( obj instanceof DeepCloneable ) - return deepClone((DeepCloneable)obj); - if( obj instanceof Object[] ) - return deepClone((Object[])obj); - if( obj instanceof Map ) - return deepClone((Map)obj); - return obj; - } -}
--- a/core/src/luan/Luan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,157 +0,0 @@ -package luan; - -import java.util.List; -import luan.modules.BasicLuan; -import luan.impl.LuanCompiler; - - -public final class Luan { - - public static void main(String[] args) throws LuanException { - LuanState luan = new LuanState(); - LuanFunction standalone = (LuanFunction)BasicLuan.load_file(luan,"classpath:luan/cmd_line.luan"); - standalone.call(luan,args); - } - - public static Object first(Object obj) { - if( !(obj instanceof Object[]) ) - return obj; - Object[] a = (Object[])obj; - return a.length==0 ? null : a[0]; - } - - public static Object[] array(Object obj) { - return obj instanceof Object[] ? (Object[])obj : new Object[]{obj}; - } - - public static String type(Object obj) { - if( obj == null ) - return "nil"; - if( obj instanceof String ) - return "string"; - if( obj instanceof Boolean ) - return "boolean"; - if( obj instanceof Number ) - return "number"; - if( obj instanceof LuanTable ) - return "table"; - if( obj instanceof LuanFunction ) - return "function"; - if( obj instanceof byte[] ) - return "binary"; - return "java"; - } - - public static String toString(Number n) { - if( n instanceof Integer ) - return n.toString(); - int i = n.intValue(); - if( i == n.doubleValue() ) - return Integer.toString(i); - String s = n.toString(); - int iE = s.indexOf('E'); - String ending = null; - if( iE != -1 ) { - ending = s.substring(iE); - s = s.substring(0,iE); - } - if( s.endsWith(".0") ) - s = s.substring(0,s.length()-2); - if( ending != null ) - s += ending; - return s; - } - - public static Integer asInteger(Object obj) { - if( obj instanceof Integer ) - return (Integer)obj; - if( !(obj instanceof Number) ) - return null; - Number n = (Number)obj; - int i = n.intValue(); - return i==n.doubleValue() ? Integer.valueOf(i) : null; - } - - public static String stringEncode(String s) { - s = s.replace("\\","\\\\"); - s = s.replace("\u0007","\\a"); - s = s.replace("\b","\\b"); - s = s.replace("\f","\\f"); - s = s.replace("\n","\\n"); - s = s.replace("\r","\\r"); - s = s.replace("\t","\\t"); - s = s.replace("\u000b","\\v"); - s = s.replace("\"","\\\""); - s = s.replace("\'","\\'"); - return s; - } - - - // from LuanState - - public static Boolean checkBoolean(Object obj) throws LuanException { - if( obj instanceof Boolean ) - return (Boolean)obj; - throw new LuanException("attempt to use a " + Luan.type(obj) + " value as a boolean" ); - } - - public static String checkString(Object obj) throws LuanException { - if( obj instanceof String ) - return (String)obj; - throw new LuanException("attempt to use a " + Luan.type(obj) + " value as a string" ); - } - - public static LuanFunction checkFunction(Object obj) throws LuanException { - if( obj instanceof LuanFunction ) - return (LuanFunction)obj; - throw new LuanException("attempt to call a " + Luan.type(obj) + " value" ); - } - - public static boolean isLessThan(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 instanceof Number && o2 instanceof Number ) { - Number n1 = (Number)o1; - Number n2 = (Number)o2; - return n1.doubleValue() < n2.doubleValue(); - } - if( o1 instanceof String && o2 instanceof String ) { - String s1 = (String)o1; - String s2 = (String)o2; - return s1.compareTo(s2) < 0; - } - LuanFunction fn = getBinHandler("__lt",o1,o2); - if( fn != null ) - return checkBoolean( Luan.first(fn.call(luan,new Object[]{o1,o2})) ); - throw new LuanException( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) ); - } - - public static LuanFunction getBinHandler(String op,Object o1,Object o2) throws LuanException { - if( o1 instanceof LuanTable ) { - LuanFunction f1 = getHandlerFunction(op,(LuanTable)o1); - if( f1 != null ) - return f1; - } - return o2 instanceof LuanTable ? getHandlerFunction(op,(LuanTable)o2) : null; - } - - public static LuanFunction getHandlerFunction(String op,LuanTable t) throws LuanException { - Object f = t.getHandler(op); - if( f == null ) - return null; - return checkFunction(f); - } - - public static LuanFunction load(String text,String sourceName,LuanTable env) - throws LuanException - { - return LuanCompiler.compile(text,sourceName,env); - } - - public static LuanFunction load(String text,String sourceName) - throws LuanException - { - return load(text,sourceName,null); - } - - - private Luan() {} // never -}
--- a/core/src/luan/LuanException.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,151 +0,0 @@ -package luan; - -import java.io.StringWriter; -import java.io.PrintWriter; -import java.util.List; -import java.util.ArrayList; - - -public final class LuanException extends Exception implements DeepCloneable { - private LuanTable table; - - public LuanException(String msg,Throwable cause) { - super(msg,cause); - initTable(); - } - - public LuanException(String msg) { - super(msg); - initTable(); - } - - public LuanException(Throwable cause) { - super(cause); - initTable(); - } - - private LuanException(String msg,Throwable cause,int nonsense) { - super(msg,cause); - } - - @Override public LuanException shallowClone() { - return new LuanException(getMessage(),getCause(),99); - } - - @Override public void deepenClone(DeepCloneable dc,DeepCloner cloner) { - LuanException clone = (LuanException)dc; - clone.table = (LuanTable)cloner.get(table); - } - - public LuanTable table() { - return table; - } - - private void initTable() { - table = new LuanTable(); - table.rawPut( "java", this ); - LuanTable metatable = new LuanTable(); - table.setMetatable(metatable); - try { - table.rawPut( "get_message", new LuanJavaFunction( - LuanException.class.getMethod( "getMessage" ), this - ) ); - table.rawPut( "throw", new LuanJavaFunction( - LuanException.class.getMethod( "throwThis" ), this - ) ); - table.rawPut( "get_java_stack_trace_string", new LuanJavaFunction( - LuanException.class.getMethod( "getJavaStackTraceString" ), this - ) ); - metatable.rawPut( "__to_string", new LuanJavaFunction( - LuanException.class.getMethod( "getFullMessage" ), this - ) ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - public void throwThis() throws LuanException { - throw this; - } - - public String getFullMessage() { - return getLuanStackTraceString(); -// return getLuanStackTraceString()+"\n"+getJavaStackTraceString(); -/* - StringBuilder buf = new StringBuilder(); - - Object msg = table.rawGet("message"); - String msgStr = (String)table.rawGet("message_string"); - buf.append( msgStr ); - - for( int i = table.rawLength(); i>=1; i-- ) { - LuanTable tbl = (LuanTable)table.rawGet(i); - buf.append( "\n\t" ).append( tbl.rawGet("source") ).append( " line " ).append( tbl.rawGet("line") ); - Object callTo = tbl.rawGet("call_to"); - if( callTo != null ) - buf.append( " in call to '" ).append( callTo ).append( "'" ); - } - - if( msg instanceof Throwable ) { - buf.append( "\nCaused by: " ); - Throwable cause = (Throwable)msg; - StringWriter sw = new StringWriter(); - cause.printStackTrace(new PrintWriter(sw)); - buf.append( sw ); - } - - return buf.toString(); -*/ - } - - public String getJavaStackTraceString() { - return getJavaStackTraceString(this); - } - - private static String getJavaStackTraceString(Throwable th) { - StringWriter sw = new StringWriter(); - th.printStackTrace(new PrintWriter(sw)); - return sw.toString(); - } - - public static List<StackTraceElement> justLuan(StackTraceElement[] orig) { - List<StackTraceElement> list = new ArrayList<StackTraceElement>(); - for( int i=0; i<orig.length; i++ ) { - StackTraceElement ste = orig[i]; - if( !ste.getClassName().startsWith("luan.impl.EXP") ) - continue; - list.add(ste); - if( !ste.getMethodName().equals("doCall") ) - i++; - } - return list; - } - - public static String toString(StackTraceElement ste) { - StringBuilder sb = new StringBuilder(); - sb.append( ste.getFileName() ).append( " line " ).append( ste.getLineNumber() ); - String method = ste.getMethodName(); - if( !method.equals("doCall") ) - sb.append( " in function '" ).append( method.substring(1) ).append( "'" ); - return sb.toString(); - } - - public String getLuanStackTraceString() { - StringBuilder sb = new StringBuilder(); - sb.append( getMessage() ); - for( StackTraceElement ste : justLuan(getStackTrace()) ) { - sb.append( "\n\t" ).append( toString(ste) ); - } - Throwable cause = getCause(); - if( cause != null ) - sb.append( "\nCaused by: " ).append( getJavaStackTraceString(cause) ); - return sb.toString(); - } - - public static String currentSource() { - LuanException ex = new LuanException("currentSource"); - List<StackTraceElement> st = ex.justLuan(ex.getStackTrace()); - return st.isEmpty() ? null : st.get(0).getFileName(); - } - -}
--- a/core/src/luan/LuanFunction.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -package luan; - - -public abstract class LuanFunction { - - public abstract Object call(LuanState luan,Object[] args) throws LuanException; - - public static final Object[] NOTHING = new Object[0]; - - public final Object call(LuanState luan) throws LuanException { - return call(luan,NOTHING); - } - - @Override public String toString() { - return "function: " + Integer.toHexString(hashCode()); - } - -}
--- a/core/src/luan/LuanJava.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -package luan; - -import luan.DeepCloneable; -import luan.DeepCloner; - - -public final class LuanJava implements DeepCloneable { - public boolean ok = false; - - @Override public LuanJava shallowClone() { - LuanJava java = new LuanJava(); - java.ok = ok; - return java; - } - - @Override public void deepenClone(DeepCloneable clone,DeepCloner cloner) {} -}
--- a/core/src/luan/LuanJavaFunction.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,572 +0,0 @@ -package luan; - -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; -import java.util.Set; -import java.util.Arrays; -import java.util.Collection; - - -public final class LuanJavaFunction extends LuanFunction { - private final JavaMethod method; - private Object obj; - private final RtnConverter rtnConverter; - private final boolean takesLuaState; - private final ArgConverter[] argConverters; - private final Class varArgCls; - - public LuanJavaFunction(Method method,Object obj) { - this( JavaMethod.of(method), obj ); - } - - public LuanJavaFunction(Constructor constr,Object obj) { - this( JavaMethod.of(constr), obj ); - } - - private LuanJavaFunction(JavaMethod method,Object obj) { - this.method = method; - this.obj = obj; - this.rtnConverter = getRtnConverter(method); - this.takesLuaState = takesLuaState(method); - this.argConverters = getArgConverters(takesLuaState,method); - if( method.isVarArgs() ) { - Class[] paramTypes = method.getParameterTypes(); - this.varArgCls = paramTypes[paramTypes.length-1].getComponentType(); - } else { - this.varArgCls = null; - } - } -/* - private LuanJavaFunction(LuanJavaFunction f) { - this.method = f.method; - this.rtnConverter = f.rtnConverter; - this.takesLuaState = f.takesLuaState; - this.argConverters = f.argConverters; - this.varArgCls = f.varArgCls; - } - - @Override public LuanJavaFunction shallowClone() { - return obj==null ? this : new LuanJavaFunction(this); - } - - @Override public void deepenClone(LuanJavaFunction clone,DeepCloner cloner) { - clone.obj = cloner.get(obj); - } -*/ - @Override public String toString() { - return "java-function: " + method; - } - - public Class[] getParameterTypes() { - return method.getParameterTypes(); - } - - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - try { - args = fixArgs(luan,args); - return doCall(args); - } catch(IllegalArgumentException e) { - checkArgs(args); - throw e; - } - } - - public Object rawCall(LuanState luan,Object[] args) throws LuanException { - args = fixArgs(luan,args); - return doCall(args); - } - - private Object doCall(Object[] args) throws LuanException { - Object rtn; - try { - rtn = method.invoke(obj,args); - } catch(IllegalAccessException e) { - throw new RuntimeException("method = "+method,e); - } catch(InvocationTargetException e) { - Throwable cause = e.getCause(); - if( cause instanceof Error ) - throw (Error)cause; - if( cause instanceof LuanException ) - throw (LuanException)cause; - throw new LuanException(cause); - } catch(InstantiationException e) { - throw new RuntimeException(e); - } - return rtnConverter.convert(rtn); - } - - private static final Map primitiveMap = new HashMap(); - static { - primitiveMap.put(Boolean.TYPE,Boolean.class); - primitiveMap.put(Character.TYPE,Character.class); - primitiveMap.put(Byte.TYPE,Byte.class); - primitiveMap.put(Short.TYPE,Short.class); - primitiveMap.put(Integer.TYPE,Integer.class); - primitiveMap.put(Long.TYPE,Long.class); - primitiveMap.put(Float.TYPE,Float.class); - primitiveMap.put(Double.TYPE,Double.class); - primitiveMap.put(Void.TYPE,Void.class); - } - - private void checkArgs(Object[] args) throws LuanException { - Class[] a = getParameterTypes(); - int start = takesLuaState ? 1 : 0; - for( int i=start; i<a.length; i++ ) { - Class paramType = a[i]; - Class type = paramType; - if( type.isPrimitive() ) - type = (Class)primitiveMap.get(type); - Object arg = args[i]; - if( !type.isInstance(arg) ) { - String expected; - if( i==a.length-1 && method.isVarArgs() ) - expected = fixType(paramType.getComponentType().getSimpleName())+"..."; - else - expected = fixType(paramType.getSimpleName()); - if( arg==null ) { - if( paramType.isPrimitive() ) - throw new LuanException("bad argument #"+(i+1-start)+" ("+expected+" expected, got nil)"); - } else { - String got = fixType(arg.getClass().getSimpleName()); - throw new LuanException("bad argument #"+(i+1-start)+" ("+expected+" expected, got "+got+")"); - } - } - } - } - - private static String fixType(String type) { - if( type.equals("byte[]") ) - return "binary"; - if( type.equals("Double") ) - return "number"; - if( type.equals("LuanTable") ) - return "table"; - if( type.equals("Boolean") ) - return "boolean"; - if( type.equals("String") ) - return "string"; - if( type.equals("Closure") ) - return "function"; - if( type.equals("LuanJavaFunction") ) - return "function"; - return type; - } - - private Object[] fixArgs(LuanState luan,Object[] args) throws LuanException { - int n = argConverters.length; - Object[] rtn; - int start = 0; - if( !takesLuaState && varArgCls==null && args.length == n ) { - rtn = args; - } else { - if( takesLuaState ) - n++; - rtn = new Object[n]; - if( takesLuaState ) { - rtn[start++] = luan; - } - n = argConverters.length; - if( varArgCls != null ) { - n--; - if( args.length < argConverters.length ) { - rtn[rtn.length-1] = Array.newInstance(varArgCls,0); - } else { - int len = args.length - n; - Object varArgs = Array.newInstance(varArgCls,len); - ArgConverter ac = argConverters[n]; - for( int i=0; i<len; i++ ) { - Array.set( varArgs, i, ac.convert(luan,args[n+i]) ); - } - rtn[rtn.length-1] = varArgs; - } - } - System.arraycopy(args,0,rtn,start,Math.min(args.length,n)); - } - for( int i=0; i<n; i++ ) { - rtn[start+i] = argConverters[i].convert(luan,rtn[start+i]); - } - return rtn; - } - - - private interface RtnConverter { - public Object convert(Object obj); - } - - private static final RtnConverter RTN_NOTHING = new RtnConverter() { - @Override public Object[] convert(Object obj) { - return NOTHING; - } - }; - - private static final RtnConverter RTN_SAME = new RtnConverter() { - @Override public Object convert(Object obj) { - return obj; - } - }; - - private static final RtnConverter RTN_ARRAY = new RtnConverter() { - @Override public Object convert(Object obj) { - if( obj == null ) - return null; - Object[] a = new Object[Array.getLength(obj)]; - for( int i=0; i<a.length; i++ ) { - a[i] = Array.get(obj,i); - } - return new LuanTable(new ArrayList<Object>(Arrays.asList(a))); - } - }; - - private static RtnConverter getRtnConverter(JavaMethod m) { - Class rtnType = m.getReturnType(); - if( rtnType == Void.TYPE ) - return RTN_NOTHING; - if( !m.isLuan() && rtnType.isArray() && !rtnType.getComponentType().isPrimitive() ) { -//System.out.println("qqqqqq "+m); - return RTN_ARRAY; - } - return RTN_SAME; - } - - private static boolean isNumber(Class rtnType) { - return rtnType == Short.TYPE - || rtnType == Integer.TYPE - || rtnType == Long.TYPE - || rtnType == Float.TYPE - || rtnType == Double.TYPE - ; - } - - private interface ArgConverter { - public Object convert(LuanState luan,Object obj) throws LuanException; - } - - private static final ArgConverter ARG_SAME = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - return obj; - } - @Override public String toString() { - return "ARG_SAME"; - } - }; - - private static final ArgConverter ARG_DOUBLE = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - if( obj instanceof Double ) - return obj; - if( obj instanceof Number ) { - Number n = (Number)obj; - return n.doubleValue(); - } - return obj; - } - @Override public String toString() { - return "ARG_DOUBLE"; - } - }; - - private static final ArgConverter ARG_FLOAT = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - if( obj instanceof Float ) - return obj; - if( obj instanceof Number ) { - Number n = (Number)obj; - return n.floatValue(); - } - return obj; - } - @Override public String toString() { - return "ARG_FLOAT"; - } - }; - - private static final ArgConverter ARG_LONG = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - if( obj instanceof Long ) - return obj; - if( obj instanceof Number ) { - Number n = (Number)obj; - long r = n.longValue(); - if( r==n.doubleValue() ) - return r; - } - return obj; - } - @Override public String toString() { - return "ARG_LONG"; - } - }; - - private static final ArgConverter ARG_INTEGER = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - if( obj instanceof Integer ) - return obj; - if( obj instanceof Number ) { - Number n = (Number)obj; - int r = n.intValue(); - if( r==n.doubleValue() ) - return r; - } - return obj; - } - @Override public String toString() { - return "ARG_INTEGER"; - } - }; - - private static final ArgConverter ARG_SHORT = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - if( obj instanceof Short ) - return obj; - if( obj instanceof Number ) { - Number n = (Number)obj; - short r = n.shortValue(); - if( r==n.doubleValue() ) - return r; - } - return obj; - } - @Override public String toString() { - return "ARG_SHORT"; - } - }; - - private static final ArgConverter ARG_BYTE = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - if( obj instanceof Byte ) - return obj; - if( obj instanceof Number ) { - Number n = (Number)obj; - byte r = n.byteValue(); - if( r==n.doubleValue() ) - return r; - } - return obj; - } - @Override public String toString() { - return "ARG_BYTE"; - } - }; - - private static final ArgConverter ARG_TABLE = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - if( obj == null ) - return null; - if( obj instanceof List ) { - return new LuanTable((List)obj); - } - if( obj instanceof Map ) { - return new LuanTable((Map)obj); - } - if( obj instanceof Set ) { - return new LuanTable((Set)obj); - } - Class cls = obj.getClass(); - if( cls.isArray() && !cls.getComponentType().isPrimitive() ) { - Object[] a = (Object[])obj; - return new LuanTable(Arrays.asList(a)); - } - return obj; - } - @Override public String toString() { - return "ARG_TABLE"; - } - }; - - private static final ArgConverter ARG_MAP = new ArgConverter() { - public Object convert(LuanState luan,Object obj) throws LuanException { - if( obj instanceof LuanTable ) { - LuanTable t = (LuanTable)obj; - return t.asMap(luan); - } - return obj; - } - @Override public String toString() { - return "ARG_MAP"; - } - }; - - private static final ArgConverter ARG_LIST = new ArgConverter() { - public Object convert(LuanState luan,Object obj) { - if( obj instanceof LuanTable ) { - LuanTable t = (LuanTable)obj; - if( t.isList() ) - return t.asList(); - } - return obj; - } - @Override public String toString() { - return "ARG_LIST"; - } - }; - - private static final ArgConverter ARG_SET = new ArgConverter() { - public Object convert(LuanState luan,Object obj) throws LuanException { - if( obj instanceof LuanTable ) { - LuanTable t = (LuanTable)obj; - if( t.isSet(luan) ) - return t.asSet(luan); - } - return obj; - } - @Override public String toString() { - return "ARG_SET"; - } - }; - - private static final ArgConverter ARG_COLLECTION = new ArgConverter() { - public Object convert(LuanState luan,Object obj) throws LuanException { - if( obj instanceof LuanTable ) { - LuanTable t = (LuanTable)obj; - if( t.isList() ) - return t.asList(); - if( t.isSet(luan) ) - return t.asSet(luan); - } - return obj; - } - @Override public String toString() { - return "ARG_COLLECTION"; - } - }; - - private static class ArgArray implements ArgConverter { - private final Object[] a; - - ArgArray(Class cls) { - a = (Object[])Array.newInstance(cls.getComponentType(),0); - } - - public Object convert(LuanState luan,Object obj) { - if( obj instanceof LuanTable ) { - LuanTable t = (LuanTable)obj; - if( t.isList() ) { - try { - return t.asList().toArray(a); - } catch(ArrayStoreException e) {} - } - } - return obj; - } - } - - private static boolean takesLuaState(JavaMethod m) { - Class[] paramTypes = m.getParameterTypes(); - return paramTypes.length > 0 && paramTypes[0].equals(LuanState.class); - } - - private static ArgConverter[] getArgConverters(boolean takesLuaState,JavaMethod m) { - final boolean isVarArgs = m.isVarArgs(); - Class[] paramTypes = m.getParameterTypes(); - if( takesLuaState ) { - Class[] t = new Class[paramTypes.length-1]; - System.arraycopy(paramTypes,1,t,0,t.length); - paramTypes = t; - } - ArgConverter[] a = new ArgConverter[paramTypes.length]; - for( int i=0; i<a.length; i++ ) { - Class paramType = paramTypes[i]; - if( isVarArgs && i == a.length-1 ) - paramType = paramType.getComponentType(); - a[i] = getArgConverter(paramType); - } - return a; - } - - private static ArgConverter getArgConverter(Class cls) { - if( cls == Double.TYPE || cls.equals(Double.class) ) - return ARG_DOUBLE; - if( cls == Float.TYPE || cls.equals(Float.class) ) - return ARG_FLOAT; - if( cls == Long.TYPE || cls.equals(Long.class) ) - return ARG_LONG; - if( cls == Integer.TYPE || cls.equals(Integer.class) ) - 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; - if( cls.equals(LuanTable.class) ) - return ARG_TABLE; - if( cls.equals(Map.class) ) - return ARG_MAP; - if( cls.equals(List.class) ) - return ARG_LIST; - if( cls.equals(Set.class) ) - return ARG_SET; - if( cls.equals(Collection.class) ) - return ARG_COLLECTION; - if( cls.isArray() && !cls.getComponentType().isPrimitive() ) - return new ArgArray(cls); - return ARG_SAME; - } - - - - private static abstract class JavaMethod { - abstract boolean isVarArgs(); - abstract Class[] getParameterTypes(); - abstract Object invoke(Object obj,Object... args) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException; - abstract Class getReturnType(); - abstract boolean isLuan(); - - static JavaMethod of(final Method m) { - return new JavaMethod() { - @Override boolean isVarArgs() { - return m.isVarArgs(); - } - @Override Class[] getParameterTypes() { - return m.getParameterTypes(); - } - @Override Object invoke(Object obj,Object... args) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException - { - return m.invoke(obj,args); - } - @Override Class getReturnType() { - return m.getReturnType(); - } - @Override boolean isLuan() { - return m.getAnnotation(LuanMethod.class) != null; - } - @Override public String toString() { - return m.toString(); - } - }; - } - - static JavaMethod of(final Constructor c) { - return new JavaMethod() { - @Override boolean isVarArgs() { - return c.isVarArgs(); - } - @Override Class[] getParameterTypes() { - return c.getParameterTypes(); - } - @Override Object invoke(Object obj,Object... args) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException - { - return c.newInstance(args); - } - @Override Class getReturnType() { - return c.getDeclaringClass(); - } - @Override boolean isLuan() { - return false; - } - @Override public String toString() { - return c.toString(); - } - }; - } - - } - -}
--- a/core/src/luan/LuanMeta.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -package luan; - -import java.util.Map; -import java.util.Iterator; -import java.util.Set; -import java.util.HashSet; - - -public abstract class LuanMeta { - - public abstract Object __index(LuanState luan,LuanTable tbl,Object key) throws LuanException; - - protected abstract Iterator keys(LuanTable tbl); - - public LuanFunction __pairs(final LuanState luan,final LuanTable tbl) { - return new LuanFunction() { - final Iterator<Map.Entry<Object,Object>> iter1 = tbl.rawIterator(); - final Iterator<Object> iter2 = keys(tbl); - final Set<Object> set = new HashSet<Object>(); - - @Override public Object[] call(LuanState luan,Object[] args) throws LuanException { - if( iter1.hasNext() ) { - Map.Entry<Object,Object> entry = iter1.next(); - Object key = entry.getKey(); - set.add(key); - return new Object[]{key,entry.getValue()}; - } - while( iter2.hasNext() ) { - Object key = iter2.next(); - if( set.add(key) ) { - Object value = __index(luan,tbl,key); - return new Object[]{key,value}; - } - } - return LuanFunction.NOTHING; - } - }; - } - - public boolean canNewindex() { - return false; - } - - public void __new_index(LuanState luan,LuanTable tbl,Object key,Object value) throws LuanException { - throw new UnsupportedOperationException(); - } - - protected abstract String type(LuanTable tbl); - - public String __to_string(LuanState luan,LuanTable tbl) throws LuanException { - return type(tbl) + "-" + tbl.rawToString(); - } - - public LuanTable newMetatable() { - LuanTable mt = new LuanTable(); - mt.rawPut( "__index", this ); - mt.rawPut( "__pairs", this ); - mt.rawPut( "__to_string", this ); - if( canNewindex() ) - mt.rawPut( "__new_index", this ); - return mt; - } - - public LuanTable newTable() { - LuanTable tbl = new LuanTable(); - tbl.setMetatable( newMetatable() ); - return tbl; - } -}
--- a/core/src/luan/LuanMethod.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -package luan; - -import java.lang.annotation.*; - - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface LuanMethod {}
--- a/core/src/luan/LuanPropertyMeta.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -package luan; - -import java.util.Map; -import java.util.Iterator; - - -public final class LuanPropertyMeta extends LuanMeta { - public static final LuanPropertyMeta INSTANCE = new LuanPropertyMeta(); - - private LuanPropertyMeta() {} - - public LuanTable getters(LuanTable tbl) { - return (LuanTable)tbl.getMetatable().rawGet("get"); - } - - public LuanTable setters(LuanTable tbl) { - return (LuanTable)tbl.getMetatable().rawGet("set"); - } - - protected String type(LuanTable tbl) { - return (String)tbl.getMetatable().rawGet("type"); - } - - @Override public Object __index(LuanState luan,LuanTable tbl,Object key) throws LuanException { - Object obj = getters(tbl).rawGet(key); - if( obj == null ) - return null; - if( !(obj instanceof LuanFunction) ) - throw new LuanException("get for '"+key+"' isn't a function"); - LuanFunction fn = (LuanFunction)obj; - return fn.call(luan); - } - - @Override protected Iterator keys(final LuanTable tbl) { - return new Iterator() { - final Iterator<Map.Entry<Object,Object>> iter = getters(tbl).rawIterator(); - - @Override public boolean hasNext() { - return iter.hasNext(); - } - @Override public Object next() { - return iter.next().getKey(); - } - @Override public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - - @Override public boolean canNewindex() { - return true; - } - - @Override public void __new_index(LuanState luan,LuanTable tbl,Object key,Object value) throws LuanException { - Object obj = setters(tbl).rawGet(key); - if( obj == null ) { - tbl.rawPut(key,value); - return; - } - if( !(obj instanceof LuanFunction) ) - throw new LuanException("set for '"+key+"' isn't a function"); - LuanFunction fn = (LuanFunction)obj; - fn.call(luan,new Object[]{value}); - } - - @Override public LuanTable newMetatable() { - LuanTable mt = super.newMetatable(); - mt.rawPut( "get", new LuanTable() ); - mt.rawPut( "set", new LuanTable() ); - mt.rawPut( "type", "property" ); - return mt; - } - -}
--- a/core/src/luan/LuanRuntimeException.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -package luan; - - -public final class LuanRuntimeException extends RuntimeException { - public LuanRuntimeException(LuanException e) { - super(e); - } -}
--- a/core/src/luan/LuanState.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -package luan; - -import java.io.Closeable; -import java.io.IOException; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; -import luan.impl.LuanCompiler; -import luan.modules.BasicLuan; -import luan.modules.JavaLuan; - - -public final class LuanState implements DeepCloneable { - - public LuanJava java; - private Map registry; - private final List<Reference<Closeable>> onClose = new ArrayList<Reference<Closeable>>(); - - public LuanState() { - java = new LuanJava(); - registry = new HashMap(); - } - - private LuanState(LuanState luan) {} - - @Override public LuanState shallowClone() { - return new LuanState(this); - } - - @Override public void deepenClone(DeepCloneable dc,DeepCloner cloner) { - LuanState clone = (LuanState)dc; - clone.registry = cloner.deepClone(registry); - clone.java = (LuanJava)cloner.deepClone(java); - } - - public final Map registry() { - return registry; - } - - public void onClose(Closeable c) { - onClose.add(new WeakReference<Closeable>(c)); - } - - public void close() throws IOException { - for( Reference<Closeable> ref : onClose ) { - Closeable c = ref.get(); - if( c != null ) - c.close(); - } - onClose.clear(); - } -/* - public final Object eval(String cmd) throws LuanException { - return eval(cmd,new LuanTable()); - } - - public final Object eval(String cmd,LuanTable env) throws LuanException { - LuanFunction fn = BasicLuan.load(this,cmd,"eval",env,true); - return fn.call(this); - } -*/ - - public String toString(Object obj) throws LuanException { - if( obj instanceof LuanTable ) { - LuanTable tbl = (LuanTable)obj; - return tbl.toString(this); - } - if( obj == null ) - return "nil"; - if( obj instanceof Number ) - return Luan.toString((Number)obj); - if( obj instanceof byte[] ) - return "binary: " + Integer.toHexString(obj.hashCode()); - return obj.toString(); - } - - public Object index(Object obj,Object key) throws LuanException { - if( obj instanceof LuanTable ) { - LuanTable tbl = (LuanTable)obj; - return tbl.get(this,key); - } - if( obj != null && java.ok ) - return JavaLuan.__index(this,obj,key,false); - throw new LuanException("attempt to index a " + Luan.type(obj) + " value" ); - } - -/* - public Number checkNumber(Object obj) throws LuanException { - if( obj instanceof Number ) - return (Number)obj; - throw new LuanException( "attempt to perform arithmetic on '"+context()+"' (a " + Luan.type(obj) + " value)" ); - } -*/ -}
--- a/core/src/luan/LuanTable.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,451 +0,0 @@ -package luan; - -import java.util.Iterator; -import java.util.ListIterator; -import java.util.Map; -import java.util.AbstractMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Set; -import java.util.HashSet; - - -public final class LuanTable implements DeepCloneable { - private Map map = null; - private List list = null; - private LuanTable metatable = null; - public LuanJava java; - - public LuanTable() {} - - public LuanTable(List list) { - int n = list.size(); - for( int i=0; i<n; i++ ) { - Object val = list.get(i); - if( val != null ) - rawPut(i+1,val); - } - } - - public LuanTable(Map map) { - for( Object stupid : map.entrySet() ) { - Map.Entry entry = (Map.Entry)stupid; - Object key = entry.getKey(); - Object value = entry.getValue(); - if( key != null && value != null ) - rawPut(key,value); - } - } - - public LuanTable(Set set) { - for( Object el : set ) { - if( el != null ) - rawPut(el,Boolean.TRUE); - } - } - - public LuanTable(LuanTable tbl) { - if( tbl.map != null && !tbl.map.isEmpty() ) - this.map = new LinkedHashMap<Object,Object>(tbl.map); - if( tbl.rawLength() > 0 ) - this.list = new ArrayList<Object>(tbl.list); - this.metatable = tbl.metatable; - } - - @Override public LuanTable shallowClone() { - return new LuanTable(); - } - - @Override public void deepenClone(DeepCloneable dc,DeepCloner cloner) { - LuanTable clone = (LuanTable)dc; - if( map != null ) { - clone.map = newMap(); - for( Object stupid : map.entrySet() ) { - Map.Entry entry = (Map.Entry)stupid; - clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) ); - } - } - if( list != null ) { - clone.list = new ArrayList<Object>(); - for( Object obj : list ) { - clone.list.add( cloner.get(obj) ); - } - } - if( metatable != null ) - clone.metatable = (LuanTable)cloner.get(metatable); - clone.java = (LuanJava)cloner.deepClone(java); - } - - public boolean isList() { - return map==null || map.isEmpty(); - } - - public List<Object> asList() { - return list!=null ? list : Collections.emptyList(); - } - - public String toString(LuanState luan) throws LuanException { - Object h = getHandler("__to_string"); - if( h == null ) - return rawToString(); - if( h instanceof LuanMeta ) { - LuanMeta meta = (LuanMeta)h; - return meta.__to_string(luan,this); - } - LuanFunction fn = Luan.checkFunction(h); - return Luan.checkString( Luan.first( fn.call(luan,new Object[]{this}) ) ); - } - - public String rawToString() { - return "table: " + Integer.toHexString(hashCode()); - } - - public Object get(LuanState luan,Object key) throws LuanException { - Object value = rawGet(key); - if( value != null ) - return value; - Object h = getHandler("__index"); - if( h==null ) - return null; - if( h instanceof LuanFunction ) { - LuanFunction fn = (LuanFunction)h; - return Luan.first(fn.call(luan,new Object[]{this,key})); - } - if( h instanceof LuanMeta ) { - LuanMeta meta = (LuanMeta)h; - return meta.__index(luan,this,key); - } - return luan.index(h,key); - } - - public Object rawGet(Object key) { - if( list != null ) { - Integer iT = Luan.asInteger(key); - if( iT != null ) { - int i = iT - 1; - if( i>=0 && i<list.size() ) - return list.get(i); - } - } - if( map==null ) - return null; - if( key instanceof Number && !(key instanceof Double) ) { - Number n = (Number)key; - key = Double.valueOf(n.doubleValue()); - } - return map.get(key); - } - - public void put(LuanState luan,Object key,Object value) throws LuanException { - Object h = getHandler("__new_index"); - if( h==null || rawGet(key)!=null ) { - rawPut(key,value); - return; - } - if( h instanceof LuanFunction ) { - LuanFunction fn = (LuanFunction)h; - fn.call(luan,new Object[]{this,key,value}); - return; - } - if( h instanceof LuanMeta ) { - LuanMeta meta = (LuanMeta)h; - meta.__new_index(luan,this,key,value); - return; - } - if( h instanceof LuanTable ) { - LuanTable tbl = (LuanTable)h; - tbl.put(luan,key,value); - return; - } - throw new LuanException("invalid type "+Luan.type(h)+" for metamethod __new_index"); - } - - public void rawPut(Object key,Object val) { - Integer iT = Luan.asInteger(key); - if( iT != null ) { - int i = iT - 1; - if( list != null || i == 0 ) { - if( i == list().size() ) { - if( val != null ) { - list.add(val); - mapToList(); - } - return; - } else if( i>=0 && i<list.size() ) { - list.set(i,val); - if( val == null ) { - listToMap(i); - } - return; - } - } - } - if( map==null ) - map = newMap(); - if( key instanceof Number && !(key instanceof Double) ) { - Number n = (Number)key; - key = Double.valueOf(n.doubleValue()); - } - if( val == null ) { - map.remove(key); - } else { - map.put(key,val); - } - } - - private void mapToList() { - if( map != null ) { - while(true) { - Object v = map.remove(Double.valueOf(list.size()+1)); - if( v == null ) - break; - list.add(v); - } - } - } - - private void listToMap(int from) { - if( list != null ) { - while( list.size() > from ) { - int i = list.size() - 1; - Object v = list.remove(i); - if( v != null ) { - if( map==null ) - map = newMap(); - map.put(i+1,v); - } - } - } - } - - private List<Object> list() { - if( list == null ) { - list = new ArrayList<Object>(); - mapToList(); - } - return list; - } - - public void rawInsert(int pos,Object value) { - if( value==null ) - throw new IllegalArgumentException("can't insert a nil value"); - list().add(pos-1,value); - mapToList(); - } - - public Object rawRemove(int pos) { - return list().remove(pos-1); - } - - public void rawSort(Comparator<Object> cmp) { - Collections.sort(list(),cmp); - } - - public int length(LuanState luan) throws LuanException { - Object h = getHandler("__len"); - if( h != null ) { - LuanFunction fn = Luan.checkFunction(h); - return (Integer)Luan.first(fn.call(luan,new Object[]{this})); - } - return rawLength(); - } - - public int rawLength() { - return list==null ? 0 : list.size(); - } - - public Iterable<Map.Entry<Object,Object>> iterable(LuanState luan) throws LuanException { - final Iterator<Map.Entry<Object,Object>> iter = iterator(luan); - return new Iterable<Map.Entry<Object,Object>>() { - public Iterator<Map.Entry<Object,Object>> iterator() { - return iter; - } - }; - } - - public Iterable<Map.Entry<Object,Object>> rawIterable() throws LuanException { - final Iterator<Map.Entry<Object,Object>> iter = rawIterator(); - return new Iterable<Map.Entry<Object,Object>>() { - public Iterator<Map.Entry<Object,Object>> iterator() { - return iter; - } - }; - } - - public Iterator<Map.Entry<Object,Object>> iterator(final LuanState luan) throws LuanException { - if( getHandler("__pairs") == null ) - return rawIterator(); - final LuanFunction fn = pairs(luan); - return new Iterator<Map.Entry<Object,Object>>() { - private Map.Entry<Object,Object> next = getNext(); - - private Map.Entry<Object,Object> getNext() { - try { - Object obj = fn.call(luan); - if( obj==null ) - return null; - Object[] a = (Object[])obj; - if( a.length == 0 || a[0]==null ) - return null; - return new AbstractMap.SimpleEntry<Object,Object>(a[0],a[1]); - } catch(LuanException e) { - throw new LuanRuntimeException(e); - } - } - - public boolean hasNext() { - return next != null; - } - - public Map.Entry<Object,Object> next() { - Map.Entry<Object,Object> rtn = next; - next = getNext(); - return rtn; - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - public LuanFunction pairs(LuanState luan) throws LuanException { - Object h = getHandler("__pairs"); - if( h != null ) { - if( h instanceof LuanFunction ) { - LuanFunction fn = (LuanFunction)h; - Object obj = Luan.first(fn.call(luan,new Object[]{this})); - if( !(obj instanceof LuanFunction) ) - throw new LuanException( "metamethod __pairs should return function but returned " + Luan.type(obj) ); - return (LuanFunction)obj; - } - if( h instanceof LuanMeta ) { - LuanMeta meta = (LuanMeta)h; - return meta.__pairs(luan,this); - } - throw new LuanException( "invalid type of metamethod __pairs: " + Luan.type(h) ); - } - return rawPairs(); - } - - private LuanFunction rawPairs() { - return new LuanFunction() { - final Iterator<Map.Entry<Object,Object>> iter = rawIterator(); - - @Override public Object[] call(LuanState luan,Object[] args) { - if( !iter.hasNext() ) - return LuanFunction.NOTHING; - Map.Entry<Object,Object> entry = iter.next(); - return new Object[]{entry.getKey(),entry.getValue()}; - } - }; - } - - public Iterator<Map.Entry<Object,Object>> rawIterator() { - if( list == null ) { - if( map == null ) - return Collections.<Map.Entry<Object,Object>>emptyList().iterator(); - return map.entrySet().iterator(); - } - if( map == null ) - return listIterator(); - return new Iterator<Map.Entry<Object,Object>>() { - Iterator<Map.Entry<Object,Object>> iter = listIterator(); - boolean isList = true; - - public boolean hasNext() { - boolean b = iter.hasNext(); - if( !b && isList ) { - iter = map.entrySet().iterator(); - isList = false; - b = iter.hasNext(); - } - return b; - } - - public Map.Entry<Object,Object> next() { - return iter.next(); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - private Iterator<Map.Entry<Object,Object>> listIterator() { - if( list == null ) - return Collections.<Map.Entry<Object,Object>>emptyList().iterator(); - final ListIterator iter = list.listIterator(); - return new Iterator<Map.Entry<Object,Object>>() { - - public boolean hasNext() { - return iter.hasNext(); - } - - public Map.Entry<Object,Object> next() { - Integer key = iter.nextIndex()+1; - return new AbstractMap.SimpleEntry<Object,Object>(key,iter.next()); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - public LuanTable rawSubList(int from,int to) { - LuanTable tbl = shallowClone(); - tbl.list = new ArrayList<Object>(list().subList(from-1,to-1)); - return tbl; - } - - public LuanTable getMetatable() { - return metatable; - } - - public void setMetatable(LuanTable metatable) { - this.metatable = metatable; - } - - public Object getHandler(String op) { - return metatable==null ? null : metatable.rawGet(op); - } - - private Map<Object,Object> newMap() { - return new LinkedHashMap<Object,Object>(); - } - - public boolean isSet(LuanState luan) throws LuanException { - for( Map.Entry<Object,Object> entry : iterable(luan) ) { - if( !entry.getValue().equals(Boolean.TRUE) ) - return false; - } - return true; - } - - public Set<Object> asSet(LuanState luan) throws LuanException { - Set<Object> set = new HashSet<Object>(); - for( Map.Entry<Object,Object> entry : iterable(luan) ) { - set.add(entry.getKey()); - } - return set; - } - - public Map<Object,Object> asMap(LuanState luan) throws LuanException { - Map<Object,Object> map = newMap(); - for( Map.Entry<Object,Object> entry : iterable(luan) ) { - map.put(entry.getKey(),entry.getValue()); - } - return map; - } - - public void rawClear() { - map = null; - list = null; - } - -}
--- a/core/src/luan/cmd_line.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local ipairs = Luan.ipairs or error() -local load_file = Luan.load_file or error() -local try = Luan.try or error() -local Table = require "luan:Table.luan" -local Io = require "luan:Io.luan" -local print = Io.print or error() - - -local args = {...} -if #args == 0 then - print("Luan "..Luan.VERSION) - Io.debug("> ") -else - local file = args[1] - Luan.arg = {} - for j,v in ipairs(args) do - Luan.arg[j-1] = v - end - try { - function() - local main_file = load_file(file) - print( main_file( Table.unpack(Luan.arg) ) ) - end; - catch = function(e) --- java(); e.java.printStackTrace(); - Io.print_to(Io.stderr, e ) - end; - } -end
--- a/core/src/luan/impl/Closure.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -package luan.impl; - -import luan.Luan; -import luan.LuanFunction; -import luan.LuanState; -import luan.LuanException; -import luan.DeepCloner; -import luan.DeepCloneable; -import luan.LuanJava; - - -public abstract class Closure extends LuanFunction implements DeepCloneable, Cloneable { - public Pointer[] upValues; - public LuanJava java; - - public Closure(int nUpValues,LuanJava java) throws LuanException { - this.upValues = new Pointer[nUpValues]; - this.java = java; - } - - @Override public Closure shallowClone() { - try { - return (Closure)clone(); - } catch(CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - @Override public void deepenClone(DeepCloneable dc,DeepCloner cloner) { - Closure clone = (Closure)dc; - clone.upValues = (Pointer[])cloner.deepClone(upValues); - clone.java = (LuanJava)cloner.deepClone(java); - } - - @Override public final Object call(LuanState luan,Object[] args) throws LuanException { - LuanJava old = luan.java; - luan.java = java; - try { - return doCall(luan,args); - } catch(StackOverflowError e) { - throw new LuanException( "stack overflow" ); - } finally { - luan.java = old; - } - } - - public abstract Object doCall(LuanState luan,Object[] args) throws LuanException; -}
--- a/core/src/luan/impl/LuanCompiler.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -package luan.impl; - -import java.lang.ref.WeakReference; -import java.lang.reflect.InvocationTargetException; -import java.util.Map; -import java.util.HashMap; -import luan.LuanFunction; -import luan.LuanState; -import luan.LuanException; -import luan.LuanTable; -import luan.LuanJava; -import luan.modules.JavaLuan; -import luan.modules.PackageLuan; - - -public final class LuanCompiler { - private static final Map<String,WeakReference<Class>> map = new HashMap<String,WeakReference<Class>>(); - - public static LuanFunction compile(String sourceText,String sourceName,LuanTable env) throws LuanException { - Class fnClass = env==null ? getClass(sourceText,sourceName) : getClass(sourceText,sourceName,env); - LuanJava java; - if( env == null ) { - java = new LuanJava(); - } else { - java = env.java; - if( java == null ) { - java = new LuanJava(); - env.java = java; - } - } - Closure closure; - try { - closure = (Closure)fnClass.getConstructor(LuanJava.class).newInstance(java); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } catch(InstantiationException e) { - throw new RuntimeException(e); - } catch(IllegalAccessException e) { - throw new RuntimeException(e); - } catch(InvocationTargetException e) { - throw new RuntimeException(e); - } - closure.upValues[0].o = JavaLuan.javaFn; - closure.upValues[1].o = PackageLuan.requireFn; - if( env != null ) closure.upValues[2].o = env; - return closure; - } - - private static synchronized Class getClass(String sourceText,String sourceName) throws LuanException { - String key = sourceName + "~~~" + sourceText; - WeakReference<Class> ref = map.get(key); - if( ref != null ) { - Class cls = ref.get(); - if( cls != null ) - return cls; - } - Class cls = getClass(sourceText,sourceName,null); - map.put(key,new WeakReference<Class>(cls)); - return cls; - } - - private static Class getClass(String sourceText,String sourceName,LuanTable env) throws LuanException { - LuanParser parser = new LuanParser(sourceText,sourceName); - parser.addVar( "java" ); - parser.addVar( "require" ); - if( env != null ) parser.addVar( "_ENV" ); - try { - return parser.RequiredModule(); - } catch(ParseException e) { -//e.printStackTrace(); - throw new LuanException( e.getFancyMessage() ); - } - } - - public static String toJava(String sourceText,String sourceName) throws LuanException { - LuanParser parser = new LuanParser(sourceText,sourceName); - parser.addVar( "java" ); - parser.addVar( "require" ); - try { - return parser.RequiredModuleSource(); - } catch(ParseException e) { - throw new LuanException( e.getFancyMessage() ); - } - } - - private LuanCompiler() {} // never -}
--- a/core/src/luan/impl/LuanImpl.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,250 +0,0 @@ -package luan.impl; - -import java.util.Arrays; -import java.util.List; -import java.util.ArrayList; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanException; -import luan.modules.JavaLuan; - - -public final class LuanImpl { - private LuanImpl() {} // never - - public static int len(LuanState luan,Object o) throws LuanException { - if( o instanceof String ) { - String s = (String)o; - return s.length(); - } - if( o instanceof byte[] ) { - byte[] a = (byte[])o; - return a.length; - } - if( o instanceof LuanTable ) { - LuanTable t = (LuanTable)o; - return t.length(luan); - } - throw new LuanException( "attempt to get length of a " + Luan.type(o) + " value" ); - } - - public static Object unm(LuanState luan,Object o) throws LuanException { - if( o instanceof Number ) - return -((Number)o).doubleValue(); - if( o instanceof LuanTable ) { - LuanFunction fn = Luan.getHandlerFunction("__unm",(LuanTable)o); - if( fn != null ) { - return Luan.first(fn.call(luan,new Object[]{o})); - } - } - throw new LuanException("attempt to perform arithmetic on a "+Luan.type(o)+" value"); - } - - private static Object arithmetic(LuanState luan,String op,Object o1,Object o2) throws LuanException { - LuanFunction fn = Luan.getBinHandler(op,o1,o2); - if( fn != null ) - return Luan.first(fn.call(luan,new Object[]{o1,o2})); - String type = !(o1 instanceof Number) ? Luan.type(o1) : Luan.type(o2); - throw new LuanException("attempt to perform arithmetic on a "+type+" value"); - } - - public static Object pow(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 instanceof Number && o2 instanceof Number ) - return Math.pow( ((Number)o1).doubleValue(), ((Number)o2).doubleValue() ); - return arithmetic(luan,"__pow",o1,o2); - } - - public static Object mul(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 instanceof Number && o2 instanceof Number ) - return ((Number)o1).doubleValue() * ((Number)o2).doubleValue(); - return arithmetic(luan,"__mul",o1,o2); - } - - public static Object div(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 instanceof Number && o2 instanceof Number ) - return ((Number)o1).doubleValue() / ((Number)o2).doubleValue(); - return arithmetic(luan,"__div",o1,o2); - } - - public static Object mod(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 instanceof Number && o2 instanceof Number ) { - double d1 = ((Number)o1).doubleValue(); - double d2 = ((Number)o2).doubleValue(); - return d1 - Math.floor(d1/d2)*d2; - } - return arithmetic(luan,"__mod",o1,o2); - } - - public static Object add(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 instanceof Number && o2 instanceof Number ) - return ((Number)o1).doubleValue() + ((Number)o2).doubleValue(); - return arithmetic(luan,"__add",o1,o2); - } - - public static Object sub(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 instanceof Number && o2 instanceof Number ) - return ((Number)o1).doubleValue() - ((Number)o2).doubleValue(); - return arithmetic(luan,"__sub",o1,o2); - } - - public static Object concat(LuanState luan,Object o1,Object o2) throws LuanException { - LuanFunction fn = Luan.getBinHandler("__concat",o1,o2); - if( fn != null ) - return Luan.first(fn.call(luan,new Object[]{o1,o2})); - String s1 = luan.toString(o1); - String s2 = luan.toString(o2); - return s1 + s2; - } - - public static boolean eq(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 == o2 || o1 != null && o1.equals(o2) ) - return true; - if( o1 instanceof Number && o2 instanceof Number ) { - Number n1 = (Number)o1; - Number n2 = (Number)o2; - return n1.doubleValue() == n2.doubleValue(); - } - if( o1 instanceof byte[] && o2 instanceof byte[] ) { - byte[] b1 = (byte[])o1; - byte[] b2 = (byte[])o2; - return Arrays.equals(b1,b2); - } - if( !(o1 instanceof LuanTable && o2 instanceof LuanTable) ) - return false; - LuanTable t1 = (LuanTable)o1; - LuanTable t2 = (LuanTable)o2; - LuanTable mt1 = t1.getMetatable(); - LuanTable mt2 = t2.getMetatable(); - if( mt1==null || mt2==null ) - return false; - Object f = mt1.rawGet("__eq"); - if( f == null || !f.equals(mt2.rawGet("__eq")) ) - return false; - LuanFunction fn = Luan.checkFunction(f); - return Luan.checkBoolean( Luan.first(fn.call(luan,new Object[]{o1,o2})) ); - } - - public static boolean le(LuanState luan,Object o1,Object o2) throws LuanException { - if( o1 instanceof Number && o2 instanceof Number ) { - Number n1 = (Number)o1; - Number n2 = (Number)o2; - return n1.doubleValue() <= n2.doubleValue(); - } - if( o1 instanceof String && o2 instanceof String ) { - String s1 = (String)o1; - String s2 = (String)o2; - return s1.compareTo(s2) <= 0; - } - LuanFunction fn = Luan.getBinHandler("__le",o1,o2); - if( fn != null ) - return Luan.checkBoolean( Luan.first(fn.call(luan,new Object[]{o1,o2})) ); - fn = Luan.getBinHandler("__lt",o1,o2); - if( fn != null ) - return !Luan.checkBoolean( Luan.first(fn.call(luan,new Object[]{o2,o1})) ); - throw new LuanException( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) ); - } - - public static boolean lt(LuanState luan,Object o1,Object o2) throws LuanException { - return Luan.isLessThan(luan,o1,o2); - } - - public static boolean cnd(Object o) throws LuanException { - return !(o == null || Boolean.FALSE.equals(o)); - } - - public static void nop(Object o) {} - - public static void put(LuanState luan,Object t,Object key,Object value) throws LuanException { - if( t instanceof LuanTable ) { - LuanTable tbl = (LuanTable)t; - tbl.put(luan,key,value); - return; - } - if( t != null && luan.java.ok ) - JavaLuan.__new_index(luan,t,key,value); - else - throw new LuanException( "attempt to index a " + Luan.type(t) + " value" ); - } - - public static Object pick(Object o,int i) { - if( i < 1 ) - throw new RuntimeException(); - if( !(o instanceof Object[]) ) - return null; - Object[] a = (Object[])o; - return i<a.length ? a[i] : null; - } - - public static Object[] varArgs(Object[] a,int i) { - if( i >= a.length ) - return LuanFunction.NOTHING; - Object[] rtn = new Object[a.length - i]; - System.arraycopy(a,i,rtn,0,rtn.length); - return rtn; - } - - public static Object[] concatArgs(Object o1,Object o2) { - if( o1 instanceof Object[] ) { - Object[] a1 = (Object[])o1; - if( o2 instanceof Object[] ) { - Object[] a2 = (Object[])o2; - Object[] rtn = new Object[a1.length+a2.length]; - System.arraycopy(a1,0,rtn,0,a1.length); - System.arraycopy(a2,0,rtn,a1.length,a2.length); - return rtn; - } else { - Object[] rtn = new Object[a1.length+1]; - System.arraycopy(a1,0,rtn,0,a1.length); - rtn[a1.length] = o2; - return rtn; - } - } else { - if( o2 instanceof Object[] ) { - Object[] a2 = (Object[])o2; - Object[] rtn = new Object[1+a2.length]; - rtn[0] = o1; - System.arraycopy(a2,0,rtn,1,a2.length); - return rtn; - } else { - Object[] rtn = new Object[2]; - rtn[0] = o1; - rtn[2] = o2; - return rtn; - } - } - } - - public static LuanTable table(Object[] a) { - LuanTable table = new LuanTable(); - int i = 0; - for( Object fld : a ) { - if( fld instanceof TableField ) { - TableField tblFld = (TableField)fld; - Object key = tblFld.key; - Object value = tblFld.value; - if( key != null && value != null ) - table.rawPut(key,value); - } else { - i++; - if( fld != null ) - table.rawPut(i,fld); - } - } - return table; - } - - public static Object first(Object[] a) { - return a.length==0 ? null : a[0]; - } - - public static String strconcat(String... a) { - StringBuilder sb = new StringBuilder(); - for( String s : a ) { - sb.append(s); - } - return sb.toString(); - } - -}
--- a/core/src/luan/impl/LuanJavaCompiler.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -package luan.impl; - -import java.io.OutputStream; -import java.io.ByteArrayOutputStream; -import java.io.StringWriter; -import java.io.IOException; -import java.net.URI; -import java.util.Collections; -import java.util.Map; -import java.util.HashMap; -import javax.tools.FileObject; -import javax.tools.JavaFileObject; -import javax.tools.SimpleJavaFileObject; -import javax.tools.JavaCompiler; -import javax.tools.ToolProvider; -import javax.tools.JavaFileManager; -import javax.tools.StandardJavaFileManager; -import javax.tools.ForwardingJavaFileManager; - - -public final class LuanJavaCompiler { - private LuanJavaCompiler() {} // never - - private static class MyJavaFileObject extends SimpleJavaFileObject { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - MyJavaFileObject() { - super(URI.create("whatever"),JavaFileObject.Kind.CLASS); - } - - @Override public OutputStream openOutputStream() { - return baos; - } - - byte[] byteCode(String sourceName) { - byte[] byteCode = baos.toByteArray(); - final int len = sourceName.length(); - int max = byteCode.length-len-3; - outer: - for( int i=0; true; i++ ) { - if( i > max ) - throw new RuntimeException("len="+len); - if( byteCode[i]==1 && (byteCode[i+1] << 8 | 0xFF & byteCode[i+2]) == len ) { - for( int j=i+3; j<i+3+len; j++ ) { - if( byteCode[j] != '$' ) - continue outer; - } - System.arraycopy(sourceName.getBytes(),0,byteCode,i+3,len); - break; - } - } - return byteCode; - } - } - - public static Class compile(final String className,final String sourceName,final String code) throws ClassNotFoundException { - final int len = sourceName.length(); - StringBuilder sb = new StringBuilder(sourceName); - for( int i=0; i<len; i++ ) - sb.setCharAt(i,'$'); - JavaFileObject sourceFile = new SimpleJavaFileObject(URI.create(sb.toString()),JavaFileObject.Kind.SOURCE) { - @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { - return code; - } - @Override public String getName() { - return sourceName; - } - @Override public boolean isNameCompatible(String simpleName,JavaFileObject.Kind kind) { - return true; - } - }; - final Map<String,MyJavaFileObject> map = new HashMap<String,MyJavaFileObject>(); - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - StandardJavaFileManager sjfm = compiler.getStandardFileManager(null,null,null); - ForwardingJavaFileManager fjfm = new ForwardingJavaFileManager(sjfm) { - @Override public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { - if( map.containsKey(className) ) - throw new RuntimeException(className); - MyJavaFileObject classFile = new MyJavaFileObject(); - map.put(className,classFile); - return classFile; - } - }; - StringWriter out = new StringWriter(); - boolean b = compiler.getTask(out, fjfm, null, null, null, Collections.singletonList(sourceFile)).call(); - if( !b ) - throw new RuntimeException("\n"+out+"\ncode:\n"+code+"\n"); - ClassLoader cl = new ClassLoader() { - @Override protected Class<?> findClass(String name) throws ClassNotFoundException { - MyJavaFileObject jfo = map.get(name); - if( jfo != null ) { - byte[] byteCode = jfo.byteCode(sourceName); - return defineClass(name, byteCode, 0, byteCode.length); - } - return super.findClass(name); - } - }; - return cl.loadClass(className); - } -}
--- a/core/src/luan/impl/LuanParser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2004 +0,0 @@ -package luan.impl; - -//import java.io.StringWriter; -//import java.io.PrintWriter; -import java.util.Set; -import java.util.HashSet; -import java.util.Arrays; -import java.util.List; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.modules.PackageLuan; - - -final class LuanParser { - - private interface Sym { - public Expr exp(); - } - - private int symCounter = 0; - - private class LocalSym implements Sym { - final String name; - final String javaName; - boolean isPointer = false; - - LocalSym(String name) { - this.name = name; - this.javaName = name + "_" + (++symCounter); - } - - Stmts declaration(Expr value) { - Stmts stmt = new Stmts(); - if( value==null ) { - stmt.add( new Object() { - @Override public String toString() { - if( !isPointer ) - return "Object " + javaName + "; "; - else - return "final Pointer " + javaName + " = new Pointer(); "; - } - } ); - } else { - if( value.valType != Val.SINGLE ) throw new RuntimeException(); - stmt.add( new Object() { - @Override public String toString() { - if( !isPointer ) - return "Object " + javaName + " = "; - else - return "final Pointer " + javaName + " = new Pointer("; - } - } ); - stmt.addAll(value); - stmt.add( new Object() { - @Override public String toString() { - if( !isPointer ) - return "; "; - else - return "); "; - } - } ); - } - return stmt; - } - - @Override public Expr exp() { - Expr exp = new Expr(Val.SINGLE,false); - exp.add( new Object() { - @Override public String toString() { - if( !isPointer ) - return javaName; - else - return javaName + ".o"; - } - } ); - return exp; - } - } - - private class UpSym implements Sym { - final String name; - final int i; - final String value; - - UpSym(String name,int i,String value) { - this.name = name; - this.i = i; - this.value = value; - } - - String init() { - return "upValues[" + i + "] = " + value + "; "; - } - - @Override public Expr exp() { - Expr exp = new Expr(Val.SINGLE,false); - exp.add( new Object() { - @Override public String toString() { - return "upValues[" + i + "].o"; - } - } ); - return exp; - } - } - - private final class Frame { - final Frame parent; - final List<LocalSym> symbols = new ArrayList<LocalSym>(); - int loops = 0; - boolean isVarArg = false; - final List<UpSym> upValueSymbols = new ArrayList<UpSym>(); - - Frame() { - this.parent = null; - } - - Frame(Frame parent) { - this.parent = parent; - } - - LocalSym addLocalSym(String name) { - LocalSym sym = new LocalSym(name); - symbols.add(sym); - return sym; - } - - UpSym addUpSym(String name,String value) { - UpSym sym = new UpSym( name, upValueSymbols.size(), value ); - upValueSymbols.add(sym); - return sym; - } - - LocalSym getLocalSym(String name) { - int i = symbols.size(); - while( --i >= 0 ) { - LocalSym sym = symbols.get(i); - if( sym.name.equals(name) ) - return sym; - } - return null; - } - - UpSym getUpSym(String name) { - for( UpSym upSym : upValueSymbols ) { - if( upSym.name.equals(name) ) - return upSym; - } - if( parent != null ) { - LocalSym sym = parent.getLocalSym(name); - if( sym != null ) { - sym.isPointer = true; - return addUpSym(name,sym.javaName); - } - UpSym upSym = parent.getUpSym(name); - if( upSym != null ) { - return addUpSym(name,"parentUpValues["+upSym.i+"]"); - } - } - return null; - } - - Sym getSym(String name) { - Sym sym = getLocalSym(name); - return sym != null ? sym : getUpSym(name); - } - - } - - private static class In { - static final In NOTHING = new In(false); - - final boolean template; - - private In(boolean template) { - this.template = template; - } - - In template() { - return template ? this : new In(true); - } - } - - private Frame frame; - private final Parser parser; - private final Stmts top; - - LuanParser(String sourceText,String sourceName) { - this.frame = new Frame(); - this.parser = new Parser(sourceText,sourceName); - this.top = new Stmts(); - } - - void addVar(String name) { - UpSym upSym = frame.addUpSym( "-ADDED-" ,"new Pointer()"); - LocalSym sym = frame.addLocalSym( name ); - sym.isPointer = true; - top.add( "final Pointer " + sym.javaName + " = upValues[" + upSym.i + "]; " ); - } - - private int symbolsSize() { - return frame.symbols.size(); - } - - private Stmts addSymbol(String name,Expr value) { - final LocalSym sym = frame.addLocalSym(name); - return sym.declaration(value); - } - - private Sym getSym(String name) { - return frame.getSym(name); - } - - private void popSymbols(int n) { - List<LocalSym> symbols = frame.symbols; - while( n-- > 0 ) { - symbols.remove(symbols.size()-1); - } - } - - private void incLoops() { - frame.loops++; - } - - private void decLoops() { - frame.loops--; - } - - private <T> T required(T t) throws ParseException { - if( t==null ) - throw parser.exception(); - return t; - } - - private <T> T required(T t,String msg) throws ParseException { - if( t==null ) - throw parser.exception(msg); - return t; - } - - private Class newFnClass(Stmts stmt) { - return toFnClass( stmt, frame.upValueSymbols ); - } - - private Expr newFnExp(Stmts stmt,String name) { - return toFnExp( stmt, frame.upValueSymbols, name ); - } -/* - Class Expression() throws ParseException { - Spaces(); - parser.begin(); - Expr expr = ExprZ(In.NOTHING); - if( expr != null && parser.endOfInput() ) { - top.add( "return " ); - top.addAll( expr ); - top.add( "; " ); - top.hasReturn = true; - return parser.success(newFnClass(top)); - } - return parser.failure(null); - } -*/ - Class RequiredModule() throws ParseException { - GetRequiredModule(); - return newFnClass(top); - } - - String RequiredModuleSource() throws ParseException { - GetRequiredModule(); - return toFnString( top, frame.upValueSymbols ); - } - - void GetRequiredModule() throws ParseException { - Spaces(); - parser.begin(); - frame.isVarArg = true; - top.add( "final Object[] varArgs = LuanImpl.varArgs(args,0); " ); - Stmts block = RequiredBlock(); - top.addAll( block ); - top.hasReturn = block.hasReturn; - if( !parser.endOfInput() ) - throw parser.exception(); - parser.success(); - } - - private Stmts RequiredBlock() throws ParseException { - Stmts stmts = new Stmts(); - int stackStart = symbolsSize(); - do { - Spaces(); - stmts.addNewLines(); - Stmts stmt = Stmt(); - if( stmt != null ) { - stmts.addAll(stmt); - stmts.hasReturn = stmt.hasReturn; - } - } while( !stmts.hasReturn && (StmtSep() || TemplateSep(stmts)) ); - Spaces(); - while( StmtSep() ) - Spaces(); - stmts.addNewLines(); - int stackEnd = symbolsSize(); - popSymbols( stackEnd - stackStart ); - return stmts; - } - - private boolean StmtSep() throws ParseException { - return parser.match( ';' ) || EndOfLine(); - } - - private boolean TemplateSep(Stmts stmts) throws ParseException { - Stmts stmt = TemplateStmt(); - if( stmt != null ) { - stmts.addAll(stmt); - return true; - } - return false; - } - - private boolean EndOfLine() { - if( MatchEndOfLine() ) { - parser.sb().append('\n'); - return true; - } else { - return false; - } - } - - private boolean MatchEndOfLine() { - return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' ); - } - - private Stmts Stmt() throws ParseException { - Stmts stmt; - if( (stmt=ReturnStmt()) != null - || (stmt=FunctionStmt()) != null - || (stmt=LocalStmt()) != null - || (stmt=LocalFunctionStmt()) != null - || (stmt=BreakStmt()) != null - || (stmt=ForStmt()) != null - || (stmt=DoStmt()) != null - || (stmt=WhileStmt()) != null - || (stmt=RepeatStmt()) != null - || (stmt=IfStmt()) != null - || (stmt=SetStmt()) != null - || (stmt=ExpressionsStmt()) != null - ) { - return stmt; - } - return null; - } - - private Expr indexExpStr(Expr exp1,Expr exp2) { - Expr exp = new Expr(Val.SINGLE,false); - exp.add( "luan.index(" ); - exp.addAll( exp1.single() ); - exp.add( "," ); - exp.addAll( exp2.single() ); - exp.add( ")" ); - return exp; - } - - private Expr callExpStr(Expr fn,Expr args) { - Expr exp = new Expr(null,true); - exp.add( "Luan.checkFunction(" ); - exp.addAll( fn.single() ); - exp.add( ").call(luan," ); - exp.addAll( args.array() ); - exp.add( ")" ); - return exp; - } - - private Stmts TemplateStmt() throws ParseException { - Expr exprs = TemplateExpressions(In.NOTHING); - if( exprs == null ) - return null; - Stmts stmt = new Stmts(); - stmt.add( "Luan.checkFunction(luan.index(PackageLuan.require(luan,\"luan:Io.luan\"),\"template_write\")).call(luan," ); - stmt.addAll( exprs.array() ); - stmt.add( "); " ); - return stmt; - } - - private Expr TemplateExpressions(In in) throws ParseException { - if( in.template ) - return null; - int start = parser.begin(); - if( !parser.match( "%>" ) ) - return parser.failure(null); - EndOfLine(); - In inTemplate = in.template(); - List<Expr> builder = new ArrayList<Expr>(); - while(true) { - if( parser.match( "<%=" ) ) { - Spaces(); - Expr exp = new Expr(Val.SINGLE,false); - exp.addAll( RequiredExpr(inTemplate).single() ); - builder.add(exp); - RequiredMatch( "%>" ); - } else if( parser.match( "<%" ) ) { - Spaces(); - return parser.success(expString(builder)); - } else { - Expr exp = new Expr(Val.SINGLE,false); - int i = parser.currentIndex(); - do { - if( parser.match( "%>" ) ) - throw parser.exception("'%>' unexpected"); - if( !(EndOfLine() || parser.anyChar()) ) - throw parser.exception("Unclosed template expression"); - } while( !parser.test( "<%" ) ); - String match = parser.textFrom(i); - String rtns = parser.sb().toString(); - parser.sb().setLength(0); - exp.addAll( constExpStr(match) ); - if( rtns.length() > 0 ) - exp.add(rtns); - builder.add(exp); - } - } - } - - private Stmts ReturnStmt() throws ParseException { - parser.begin(); - if( !Keyword("return") ) - return parser.failure(null); - Expr exprs = ExpStringList(In.NOTHING); - Stmts stmt = new Stmts(); - stmt.add( "return " ); - if( exprs != null ) - stmt.addAll( exprs ); - else - stmt.add( "LuanFunction.NOTHING" ); - stmt.add( "; " ); - stmt.hasReturn = true; - return parser.success( stmt ); - } - - private Stmts FunctionStmt() throws ParseException { - parser.begin(); - if( !Keyword("function") ) - return parser.failure(null); - - parser.currentIndex(); - String name = RequiredName(); - Var var = nameVar(name); - while( parser.match( '.' ) ) { - Spaces(); -// Expr exp = NameExpr(); - name = Name(); - if( name==null ) - return parser.failure(null); - var = indexVar( var.exp(), constExpStr(name) ); - } - - Expr fnDef = RequiredFunction(name); - return parser.success( var.set(fnDef) ); - } - - private Stmts LocalFunctionStmt() throws ParseException { - parser.begin(); - if( !(Keyword("local") && Keyword("function")) ) - return parser.failure(null); - Stmts stmt = new Stmts(); - String name = RequiredName(); - stmt.addAll( addSymbol(name,null) ); - Expr fnDef = RequiredFunction(name); - stmt.addAll( nameVar(name).set(fnDef) ); - return parser.success( stmt ); - } - - private Stmts BreakStmt() throws ParseException { - parser.begin(); - if( !Keyword("break") ) - return parser.failure(null); - if( frame.loops <= 0 ) - throw parser.exception("'break' outside of loop"); - Stmts stmt = new Stmts(); - stmt.add( "break; " ); - return parser.success( stmt ); - } - - int forCounter = 0; - - private Stmts ForStmt() throws ParseException { - parser.begin(); - int stackStart = symbolsSize(); - if( !Keyword("for") ) - return parser.failure(null); - List<String> names = RequiredNameList(); - if( !Keyword("in") ) - return parser.failure(null); - Expr expr = RequiredExpr(In.NOTHING).single(); - RequiredKeyword("do"); - - String fnVar = "fn"+ ++forCounter; - Expr fnExp = new Expr(null,false); - fnExp.add( fnVar + ".call(luan)" ); - Stmts stmt = new Stmts(); - stmt.add( "" - +"LuanFunction "+fnVar+" = Luan.checkFunction(" - ); - stmt.addAll( expr ); - stmt.add( "); " ); - stmt.add( "while(true) { " ); - stmt.addAll( makeLocalSetStmt(names,fnExp) ); - stmt.add( "if( " ); - stmt.addAll( nameVar(names.get(0)).exp() ); - stmt.add( "==null ) break; " ); - Stmts loop = RequiredLoopBlock(); - RequiredKeyword("end"); - stmt.addAll( loop ); - stmt.add( "} " ); - popSymbols( symbolsSize() - stackStart ); - return parser.success(stmt); - } - - private Stmts DoStmt() throws ParseException { - parser.begin(); - if( !Keyword("do") ) - return parser.failure(null); - Stmts stmt = RequiredBlock(); - RequiredKeyword("end"); - return parser.success(stmt); - } - - private Stmts LocalStmt() throws ParseException { - parser.begin(); - if( !Keyword("local") ) - return parser.failure(null); - List<String> names = NameList(); - if( names==null ) { - if( Keyword("function") ) - return parser.failure(null); // handled later - throw parser.exception("Invalid local statement"); - } - Stmts stmt = new Stmts(); - if( parser.match( '=' ) ) { - Spaces(); - Expr values = ExpStringList(In.NOTHING); - if( values==null ) - throw parser.exception("Expressions expected"); - stmt.addAll( makeLocalSetStmt(names,values) ); - } else { - Expr value = new Expr(Val.SINGLE,false); - value.add( "null" ); - for( String name : names ) { - stmt.addAll( addSymbol(name,value) ); - } - } - return parser.success(stmt); - } - - private List<String> RequiredNameList() throws ParseException { - parser.begin(); - List<String> names = NameList(); - if( names==null ) - throw parser.exception("Name expected"); - return parser.success(names); - } - - private List<String> NameList() throws ParseException { - String name = Name(); - if( name==null ) - return null; - List<String> names = new ArrayList<String>(); - names.add(name); - while( (name=anotherName()) != null ) { - names.add(name); - } - return names; - } - - private String anotherName() throws ParseException { - parser.begin(); - if( !parser.match( ',' ) ) - return parser.failure(null); - Spaces(); - String name = Name(); - if( name==null ) - return parser.failure(null); - return parser.success(name); - } - - private Stmts WhileStmt() throws ParseException { - parser.begin(); - if( !Keyword("while") ) - return parser.failure(null); - Expr cnd = RequiredExpr(In.NOTHING).single(); - RequiredKeyword("do"); - Stmts loop = RequiredLoopBlock(); - RequiredKeyword("end"); - Stmts stmt = new Stmts(); - stmt.add( "while( Luan.checkBoolean(" ); - stmt.addAll( cnd ); - stmt.add( ") ) { " ); - stmt.addAll( loop ); - stmt.add( "} " ); - return parser.success( stmt ); - } - - private Stmts RepeatStmt() throws ParseException { - parser.begin(); - if( !Keyword("repeat") ) - return parser.failure(null); - Stmts loop =RequiredLoopBlock(); - RequiredKeyword("until"); - Expr cnd = RequiredExpr(In.NOTHING).single(); - Stmts stmt = new Stmts(); - stmt.add( "do { " ); - stmt.addAll( loop ); - stmt.add( "} while( !Luan.checkBoolean(" ); - stmt.addAll( cnd ); - stmt.add( ") ); " ); - return parser.success( stmt ); - } - - private Stmts RequiredLoopBlock() throws ParseException { - incLoops(); - Stmts stmt = RequiredBlock(); - decLoops(); - return stmt; - } - - private Stmts IfStmt() throws ParseException { - parser.begin(); - if( !Keyword("if") ) - return parser.failure(null); - Stmts stmt = new Stmts(); - Expr cnd; - Stmts block; - boolean hasReturn = true; - cnd = RequiredExpr(In.NOTHING).single(); - RequiredKeyword("then"); - block = RequiredBlock(); - stmt.add( "if( Luan.checkBoolean(" ); - stmt.addAll( cnd ); - stmt.add( ") ) { " ); - stmt.addAll( block ); - if( !block.hasReturn ) - hasReturn = false; - while( Keyword("elseif") ) { - cnd = RequiredExpr(In.NOTHING).single(); - RequiredKeyword("then"); - block = RequiredBlock(); - stmt.add( "} else if( Luan.checkBoolean(" ); - stmt.addAll( cnd ); - stmt.add( ") ) { " ); - stmt.addAll( block ); - if( !block.hasReturn ) - hasReturn = false; - } - if( Keyword("else") ) { - block = RequiredBlock(); - stmt.add( "} else { " ); - stmt.addAll( block ); - if( !block.hasReturn ) - hasReturn = false; - } else { - hasReturn = false; - } - RequiredKeyword("end"); - stmt.add( "} " ); - stmt.hasReturn = hasReturn; - return parser.success( stmt ); - } - - private Stmts SetStmt() throws ParseException { - parser.begin(); - List<Var> vars = new ArrayList<Var>(); - Var v = SettableVar(); - if( v == null ) - return parser.failure(null); - vars.add(v); - while( parser.match( ',' ) ) { - Spaces(); - v = SettableVar(); - if( v == null ) - return parser.failure(null); - vars.add(v); - } - if( !parser.match( '=' ) ) - return parser.failure(null); - Spaces(); - Expr values = ExpStringList(In.NOTHING); - if( values==null ) -// throw parser.exception("Expressions expected"); - return parser.failure(null); - return parser.success( makeSetStmt(vars,values) ); - } - - private Stmts makeSetStmt(List<Var> vars,Expr values) throws ParseException { - int n = vars.size(); - if( n == 1 ) - return vars.get(0).set(values); - Stmts stmt = new Stmts(); - String varName = values.valType==Val.ARRAY ? "a" : "t"; - stmt.add( varName + " = " ); - stmt.addAll( values ); - stmt.add( "; " ); - Expr t = new Expr(values.valType,false); - t.add( varName ); - t = t.single(); - stmt.addAll( vars.get(0).set(t) ); - for( int i=1; i<n; i++ ) { - t.clear(); - t.add( "LuanImpl.pick(" + varName + ","+i+")" ); - stmt.addAll( vars.get(i).set(t) ); - } - return stmt; - } - - private Stmts makeLocalSetStmt(List<String> names,Expr values) throws ParseException { - int n = names.size(); - if( n == 1 ) - return addSymbol(names.get(0),values.single()); - Stmts stmt = new Stmts(); - String varName = values.valType==Val.ARRAY ? "a" : "t"; - stmt.add( varName + " = " ); - stmt.addAll( values ); - stmt.add( "; " ); - Expr t = new Expr(values.valType,false); - t.add( varName ); - t = t.single(); - stmt.addAll( addSymbol(names.get(0),t) ); - for( int i=1; i<n; i++ ) { - t.clear(); - t.add( "LuanImpl.pick(" + varName + ","+i+")" ); - stmt.addAll( addSymbol(names.get(i),t) ); - } - return stmt; - } - - private Stmts ExpressionsStmt() throws ParseException { - parser.begin(); - Expr exp = ExprZ(In.NOTHING); - if( exp != null && exp.isStmt ) { - Stmts stmt = new Stmts(); - if( exp.valType==Val.SINGLE ) { - stmt.add( "LuanImpl.nop(" ); - stmt.addAll( exp ); - stmt.add( ")" ); - } else { - stmt.addAll( exp ); - } - stmt.add( "; " ); - return parser.success( stmt ); - } - return parser.failure(null); - } - - private Var SettableVar() throws ParseException { - int start = parser.begin(); - Var var = VarZ(In.NOTHING); - if( var==null || !var.isSettable() ) - return parser.failure(null); - return parser.success( var ); - } - - private Expr RequiredExpr(In in) throws ParseException { - parser.begin(); - return parser.success(required(ExprZ(in),"Bad expression")); - } - - private Expr ExprZ(In in) throws ParseException { - return OrExpr(in); - } - - private Expr OrExpr(In in) throws ParseException { - parser.begin(); - Expr exp = AndExpr(in); - if( exp==null ) - return parser.failure(null); - while( Keyword("or") ) { - exp = exp.single(); - Expr exp2 = required(AndExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,true); - newExp.add( "(LuanImpl.cnd(t = " ); - newExp.addAll( exp ); - newExp.add( ") ? t : (" ); - newExp.addAll( exp2 ); - newExp.add( "))" ); - exp = newExp; - } - return parser.success(exp); - } - - private Expr AndExpr(In in) throws ParseException { - parser.begin(); - Expr exp = RelExpr(in); - if( exp==null ) - return parser.failure(null); - while( Keyword("and") ) { - exp = exp.single(); - Expr exp2 = required(RelExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,true); - newExp.add( "(LuanImpl.cnd(t = " ); - newExp.addAll( exp ); - newExp.add( ") ? (" ); - newExp.addAll( exp2 ); - newExp.add( ") : t)" ); - exp = newExp; - } - return parser.success(exp); - } - - private Expr RelExpr(In in) throws ParseException { - parser.begin(); - Expr exp = ConcatExpr(in); - if( exp==null ) - return parser.failure(null); - while(true) { - if( parser.match("==") ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(ConcatExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.eq(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else if( parser.match("~=") ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(ConcatExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "!LuanImpl.eq(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else if( parser.match("<=") ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(ConcatExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.le(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else if( parser.match(">=") ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(ConcatExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.le(luan," ); - newExp.addAll( exp2 ); - newExp.add( "," ); - newExp.addAll( exp ); - newExp.add( ")" ); - exp = newExp; - } else if( parser.match("<") ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(ConcatExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.lt(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else if( parser.match(">") ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(ConcatExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.lt(luan," ); - newExp.addAll( exp2 ); - newExp.add( "," ); - newExp.addAll( exp ); - newExp.add( ")" ); - exp = newExp; - } else - break; - } - return parser.success(exp); - } - - private Expr ConcatExpr(In in) throws ParseException { - parser.begin(); - Expr exp = SumExpr(in); - if( exp==null ) - return parser.failure(null); - if( parser.match("..") ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(ConcatExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.concat(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } - return parser.success(exp); - } - - private Expr SumExpr(In in) throws ParseException { - parser.begin(); - Expr exp = TermExpr(in); - if( exp==null ) - return parser.failure(null); - while(true) { - if( parser.match('+') ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(TermExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.add(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else if( Minus() ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(TermExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.sub(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else - break; - } - return parser.success(exp); - } - - private boolean Minus() { - parser.begin(); - return parser.match('-') && !parser.match('-') ? parser.success() : parser.failure(); - } - - private Expr TermExpr(In in) throws ParseException { - parser.begin(); - Expr exp = UnaryExpr(in); - if( exp==null ) - return parser.failure(null); - while(true) { - if( parser.match('*') ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(UnaryExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.mul(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else if( parser.match('/') ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(UnaryExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.div(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else if( Mod() ) { - Spaces(); - exp = exp.single(); - Expr exp2 = required(UnaryExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.mod(luan," ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( exp2 ); - newExp.add( ")" ); - exp = newExp; - } else - break; - } - return parser.success(exp); - } - - private boolean Mod() { - parser.begin(); - return parser.match('%') && !parser.match('>') ? parser.success() : parser.failure(); - } - - private Expr UnaryExpr(In in) throws ParseException { - parser.begin(); - if( parser.match('#') ) { - Spaces(); - Expr exp = required(UnaryExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.len(luan," ); - newExp.addAll( exp ); - newExp.add( ")" ); - return parser.success(newExp); - } - if( Minus() ) { - Spaces(); - Expr exp = required(UnaryExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.unm(luan," ); - newExp.addAll( exp ); - newExp.add( ")" ); - return parser.success(newExp); - } - if( Keyword("not") ) { - Spaces(); - Expr exp = required(UnaryExpr(in)).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "!Luan.checkBoolean(" ); - newExp.addAll( exp ); - newExp.add( ")" ); - return parser.success(newExp); - } - Expr exp = PowExpr(in); - if( exp==null ) - return parser.failure(null); - return parser.success(exp); - } - - private Expr PowExpr(In in) throws ParseException { - parser.begin(); - Expr exp1 = SingleExpr(in); - if( exp1==null ) - return parser.failure(null); - if( parser.match('^') ) { - Spaces(); - Expr exp2 = required(PowExpr(in)); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "LuanImpl.pow(luan," ); - newExp.addAll( exp1.single() ); - newExp.add( "," ); - newExp.addAll( exp2.single() ); - newExp.add( ")" ); - exp1 = newExp; - } - return parser.success(exp1); - } - - private Expr SingleExpr(In in) throws ParseException { - parser.begin(); - Expr exp = FunctionExpr(); - if( exp != null ) - return parser.success(exp); - exp = VarExp(in); - if( exp != null ) - return parser.success(exp); - exp = VarArgs(); - if( exp != null ) - return parser.success(exp); - return parser.failure(null); - } - - private Expr FunctionExpr() throws ParseException { - if( !Keyword("function") ) - return null; - return RequiredFunction(null); - } - - private Expr RequiredFunction(String name) throws ParseException { - parser.begin(); - RequiredMatch('('); - Spaces(); - frame = new Frame(frame); - Stmts stmt = new Stmts(); - List<String> names = NameList(); - if( names != null ) { - Expr args = new Expr(Val.ARRAY,false); - args.add( "args" ); - stmt.addAll( makeLocalSetStmt(names,args) ); - if( parser.match(',') ) { - Spaces(); - if( !parser.match("...") ) - throw parser.exception(); - Spaces(); - frame.isVarArg = true; - stmt.add( "final Object[] varArgs = LuanImpl.varArgs(args," + names.size() + "); " ); - } - } else if( parser.match("...") ) { - Spaces(); - frame.isVarArg = true; - stmt.add( "final Object[] varArgs = LuanImpl.varArgs(args,0); " ); - } - RequiredMatch(')'); - Spaces(); - Stmts block = RequiredBlock(); - stmt.addAll( block ); - stmt.hasReturn = block.hasReturn; - Expr fnDef = newFnExp(stmt,name); - RequiredKeyword("end"); - frame = frame.parent; - return parser.success(fnDef); - } - - private Expr VarArgs() throws ParseException { - parser.begin(); - if( !frame.isVarArg || !parser.match("...") ) - return parser.failure(null); - Spaces(); - Expr exp = new Expr(Val.ARRAY,false); - exp.add("varArgs"); - return parser.success(exp); - } - - private Expr TableExpr() throws ParseException { - parser.begin(); - if( !parser.match('{') ) - return parser.failure(null); - Expr tblExp = new Expr(Val.SINGLE,false); - tblExp.add( "LuanImpl.table(" ); - Expr lastExp = tblExp; - List<Expr> builder = new ArrayList<Expr>(); -/* - Spaces(); - Field(builder); - while( FieldSep() ) { - Spaces(); - Field(builder); - } -*/ - do { - Spaces(); lastExp.addNewLines(); - Expr exp = Field(); - if( exp != null ) { - builder.add(exp); - lastExp = exp; - Spaces(); lastExp.addNewLines(); - } - } while( FieldSep() ); - Expr exp = TemplateExpressions(In.NOTHING); - if( exp != null ) - builder.add(exp); - if( !parser.match('}') ) - throw parser.exception("Expected table element or '}'"); - tblExp.addAll( expString(builder).array() ); - tblExp.add( ")" ); - Spaces(); - tblExp.addNewLines(); - return parser.success( tblExp ); - } - - private boolean FieldSep() throws ParseException { - return parser.anyOf(",;") || EndOfLine(); - } - - private Expr Field() throws ParseException { - parser.begin(); - Expr exp = SubExpr(In.NOTHING); - if( exp==null ) - exp = NameExpr(); - if( exp!=null && parser.match('=') ) { - Spaces(); - Expr val = RequiredExpr(In.NOTHING).single(); - Expr newExp = new Expr(Val.SINGLE,false); - newExp.add( "new TableField(" ); - newExp.addAll( exp ); - newExp.add( "," ); - newExp.addAll( val ); - newExp.add( ")" ); - return parser.success(newExp); - } - parser.rollback(); - Expr exprs = ExprZ(In.NOTHING); - if( exprs != null ) { - return parser.success(exprs); - } - return parser.failure(null); - } - - private Expr VarExp(In in) throws ParseException { - Var var = VarZ(in); - return var==null ? null : var.exp(); - } - - private Var VarZ(In in) throws ParseException { - parser.begin(); - Var var = VarStart(in); - if( var==null ) - return parser.failure(null); - Var var2; - while( (var2=Var2(in,var.exp())) != null ) { - var = var2; - } - return parser.success(var); - } - - private Var VarStart(In in) throws ParseException { - if( parser.match('(') ) { - Spaces(); - Expr exp = RequiredExpr(in).single(); - RequiredMatch(')'); - Spaces(); - return exprVar(exp); - } - String name = Name(); - if( name != null ) - return nameVar(name); - Expr exp; - exp = TableExpr(); - if( exp != null ) - return exprVar(exp); - exp = Literal(); - if( exp != null ) - return exprVar(exp); - return null; - } - - private Var Var2(In in,Expr exp1) throws ParseException { - parser.begin(); - Expr exp2 = SubExpr(in); - if( exp2 != null ) - return parser.success(indexVar(exp1,exp2)); - if( parser.match('.') ) { - Spaces(); - exp2 = NameExpr(); - if( exp2!=null ) - return parser.success(indexVar(exp1,exp2)); - return parser.failure(null); - } - Expr fnCall = Args( in, exp1, new ArrayList<Expr>() ); - if( fnCall != null ) - return parser.success(exprVar(fnCall)); - return parser.failure(null); - } - - private interface Var { - public Expr exp() throws ParseException; -// public Settable settable() throws ParseException; - public boolean isSettable(); - public Stmts set(Expr val) throws ParseException; - } - - private Expr env() { - Sym sym = getSym("_ENV"); - if( sym != null ) - return sym.exp(); - return null; - } - - private Var nameVar(final String name) { - return new Var() { - - public Expr exp() throws ParseException { - Sym sym = getSym(name); - if( sym != null ) - return sym.exp(); - Expr envExpr = env(); - if( envExpr != null ) - return indexExpStr( envExpr, constExpStr(name) ); - parser.failure(null); - throw parser.exception("name '"+name+"' not defined"); - } - - public boolean isSettable() { - return true; - } - - public Stmts set(Expr val) throws ParseException { - Sym sym = getSym(name); - if( sym != null ) { - Stmts stmt = new Stmts(); - stmt.addAll( sym.exp() ); - stmt.add( " = " ); - stmt.addAll( val.single() ); - stmt.add( "; " ); - return stmt; - } - Expr envExpr = env(); - if( envExpr != null ) - return indexVar( envExpr, constExpStr(name) ).set(val); - parser.failure(null); - throw parser.exception("name '"+name+"' not defined"); - } - }; - } - - private Var exprVar(final Expr expr) { - return new Var() { - - public Expr exp() { - return expr; - } - - public boolean isSettable() { - return false; - } - - public Stmts set(Expr val) { - throw new RuntimeException(); - } - }; - } - - private Var indexVar(final Expr table,final Expr key) { - return new Var() { - - public Expr exp() { - return indexExpStr( table, key ); - } - - public boolean isSettable() { - return true; - } - - public Stmts set(Expr val) { - Stmts stmt = new Stmts(); - stmt.add( "LuanImpl.put(luan," ); - stmt.addAll( table.single() ); - stmt.add( "," ); - stmt.addAll( key.single() ); - stmt.add( "," ); - stmt.addAll( val.single() ); - stmt.add( "); " ); - return stmt; - } - }; - } - - private Expr Args(In in,Expr fn,List<Expr> builder) throws ParseException { - parser.begin(); - return args(in,builder) - ? parser.success( callExpStr( fn, expString(builder) ) ) - : parser.failure((Expr)null); - } - - private boolean args(In in,List<Expr> builder) throws ParseException { - parser.begin(); - if( parser.match('(') ) { - Spaces(); - ExpList(in,builder); // optional - if( !parser.match(')') ) - throw parser.exception("Expression or ')' expected"); - Spaces(); - return parser.success(); - } - Expr exp = TableExpr(); - if( exp != null ) { - builder.add(exp); - return parser.success(); - } - exp = StringLiteral(); - if( exp != null ) { - builder.add(exp); - return parser.success(); - } - return parser.failure(); - } - - private Expr ExpStringList(In in) throws ParseException { - List<Expr> builder = new ArrayList<Expr>(); - return ExpList(in,builder) ? expString(builder) : null; - } - - private boolean ExpList(In in,List<Expr> builder) throws ParseException { - parser.begin(); - Expr exp = TemplateExpressions(in); - if( exp != null ) { - builder.add(exp); - return parser.success(); - } - exp = ExprZ(in); - if( exp==null ) - return parser.failure(); - exp.addNewLines(); - builder.add(exp); - while( parser.match(',') ) { - Spaces(); - exp = TemplateExpressions(in); - if( exp != null ) { - builder.add(exp); - return parser.success(); - } - exp = RequiredExpr(in); - exp.addNewLines(); - builder.add(exp); - } - return parser.success(); - } - - private Expr SubExpr(In in) throws ParseException { - parser.begin(); - if( !parser.match('[') || parser.test("[") || parser.test("=") ) - return parser.failure(null); - Spaces(); - Expr exp = RequiredExpr(In.NOTHING).single(); - RequiredMatch(']'); - Spaces(); - return parser.success(exp); - } - - private Expr NameExpr() throws ParseException { - parser.begin(); - String name = Name(); - if( name==null ) - return parser.failure(null); - return parser.success(constExpStr(name)); - } - - private String RequiredName() throws ParseException { - parser.begin(); - String name = Name(); - if( name==null ) - throw parser.exception("Name expected"); - return parser.success(name); - } - - private String Name() throws ParseException { - int start = parser.begin(); - if( !NameFirstChar() ) - return parser.failure(null); - while( NameChar() ); - String match = parser.textFrom(start); - if( keywords.contains(match) ) - return parser.failure(null); - Spaces(); - return parser.success(match); - } - - private boolean NameChar() { - return NameFirstChar() || Digit(); - } - - private boolean NameFirstChar() { - return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_'); - } - - private void RequiredMatch(char c) throws ParseException { - if( !parser.match(c) ) - throw parser.exception("'"+c+"' expected"); - } - - private void RequiredMatch(String s) throws ParseException { - if( !parser.match(s) ) - throw parser.exception("'"+s+"' expected"); - } - - private void RequiredKeyword(String keyword) throws ParseException { - if( !Keyword(keyword) ) - throw parser.exception("'"+keyword+"' expected"); - } - - private boolean Keyword(String keyword) throws ParseException { - parser.begin(); - if( !parser.match(keyword) || NameChar() ) - return parser.failure(); - Spaces(); - return parser.success(); - } - - private static final Set<String> keywords = new HashSet<String>(Arrays.asList( - "and", - "break", - "do", - "else", - "elseif", - "end", - "false", - "for", - "function", - "goto", - "if", - "in", - "local", - "nil", - "not", - "or", - "repeat", - "return", - "then", - "true", - "until", - "while" - )); - - private Expr Literal() throws ParseException { - parser.begin(); - if( NilLiteral() ) { - Expr exp = new Expr(Val.SINGLE,false); - exp.add( "null" ); - return parser.success(exp); - } - Boolean b = BooleanLiteral(); - if( b != null ) { - Expr exp = new Expr(Val.SINGLE,false); - exp.add( b.toString() ); - return parser.success(exp); - } - Number n = NumberLiteral(); - if( n != null ) { - String s = n.toString(); - if( n instanceof Long ) - s += "L"; - Expr exp = new Expr(Val.SINGLE,false); - exp.add( s ); - return parser.success(exp); - } - Expr s = StringLiteral(); - if( s != null ) - return parser.success(s); - return parser.failure(null); - } - - private static int STR_LIM = 65000; - - private Expr constExpStr(String s) { - s = s - .replace("\\","\\\\") - .replace("\"","\\\"") - .replace("\n","\\n") - .replace("\r","\\r") - .replace("\t","\\t") - .replace("\b","\\b") - ; - if( s.length() > STR_LIM ) { - int len = s.length(); - StringBuilder sb = new StringBuilder(); - sb.append( "LuanImpl.strconcat(" ); - int start = 0; - while(true) { - int end = start + STR_LIM; - if( end >= len ) - break; - sb.append( "\"" ).append( s.substring(start,end) ).append( "\"," ); - start = end; - } - sb.append( "\"" ).append( s.substring(start) ).append( "\")" ); - s = sb.toString(); - } else - s = "\"" + s + "\""; - Expr exp = new Expr(Val.SINGLE,false); - exp.add( s ); - return exp; - } - - private boolean NilLiteral() throws ParseException { - return Keyword("nil"); - } - - private Boolean BooleanLiteral() throws ParseException { - if( Keyword("true") ) - return true; - if( Keyword("false") ) - return false; - return null; - } - - private Number NumberLiteral() throws ParseException { - parser.begin(); - Number n; - if( parser.matchIgnoreCase("0x") ) { - n = HexNumber(); - } else { - n = DecNumber(); - } - if( n==null || NameChar() ) - return parser.failure(null); - Spaces(); - return parser.success(n); - } - - private Number DecNumber() { - int start = parser.begin(); - boolean isInt = true; - if( Int() ) { - if( parser.match('.') ) { - isInt = false; - Int(); // optional - } - } else if( parser.match('.') && Int() ) { - // ok - isInt = false; - } else - return parser.failure(null); - if( Exponent() ) // optional - isInt = false; - String s = parser.textFrom(start); - if( isInt ) { - try { - return parser.success(Integer.valueOf(s)); - } catch(NumberFormatException e) {} - try { - return parser.success(Long.valueOf(s)); - } catch(NumberFormatException e) {} - } - return parser.success(Double.valueOf(s)); - } - - private boolean Exponent() { - parser.begin(); - if( !parser.matchIgnoreCase("e") ) - return parser.failure(); - parser.anyOf("+-"); // optional - if( !Int() ) - return parser.failure(); - return parser.success(); - } - - private boolean Int() { - if( !Digit() ) - return false; - while( Digit() ); - return true; - } - - private boolean Digit() { - return parser.inCharRange('0', '9'); - } - - private Number HexNumber() { - int start = parser.begin(); - long nLong = 0; - double n; - if( HexInt() ) { - nLong = Long.parseLong(parser.textFrom(start),16); - n = (double)nLong; - if( parser.match('.') ) { - start = parser.currentIndex(); - if( HexInt() ) { - String dec = parser.textFrom(start); - n += (double)Long.parseLong(dec,16) / Math.pow(16,dec.length()); - } - } - } else if( parser.match('.') && HexInt() ) { - String dec = parser.textFrom(start+1); - n = (double)Long.parseLong(dec,16) / Math.pow(16,dec.length()); - } else { - return parser.failure(null); - } - if( parser.matchIgnoreCase("p") ) { - parser.anyOf("+-"); // optional - start = parser.currentIndex(); - if( !HexInt() ) - return parser.failure(null); - n *= Math.pow(2,(double)Long.parseLong(parser.textFrom(start))); - } - if( nLong == n ) { - int nInt = (int)nLong; - if( nInt == nLong ) - return parser.success(Integer.valueOf(nInt)); - return parser.success(Long.valueOf(nLong)); - } - return parser.success(Double.valueOf(n)); - } - - private boolean HexInt() { - if( !HexDigit() ) - return false; - while( HexDigit() ); - return true; - } - - - private boolean HexDigit() { - return Digit() || parser.anyOf("abcdefABCDEF"); - } - - private Expr StringLiteral() throws ParseException { - Expr s; - if( (s=QuotedString('"'))==null - && (s=QuotedString('\''))==null - && (s=LongString())==null - ) - return null; - Spaces(); - return s; - } - - private Expr LongString() throws ParseException { - parser.begin(); - if( !parser.match('[') ) - return parser.failure(null); - int start = parser.currentIndex(); - while( parser.match('=') ); - int nEquals = parser.currentIndex() - start; - if( !parser.match('[') ) - return parser.failure(null); - EndOfLine(); - start = parser.currentIndex(); - while( !LongBracketsEnd(nEquals) ) { - if( !(EndOfLine() || parser.anyChar()) ) - throw parser.exception("Unclosed long string"); - } - String s = parser.text.substring( start, parser.currentIndex() - nEquals - 2 ); - String rtns = parser.sb().toString(); - parser.sb().setLength(0); - Expr exp = constExpStr(s); - if( rtns.length() > 0 ) - exp.add(rtns); - return parser.success(exp); - } - - private Expr QuotedString(char quote) throws ParseException { - parser.begin(); - if( !parser.match(quote) ) - return parser.failure(null); - StringBuilder buf = new StringBuilder(); - while( !parser.match(quote) ) { - Character c = EscSeq(); - if( c != null ) { - buf.append(c); - } else { - if( parser.test('\r') || parser.test('\n') || !parser.anyChar() ) - throw parser.exception("Unclosed string"); - buf.append(parser.lastChar()); - } - } - return parser.success(constExpStr(buf.toString())); - } - - private Character EscSeq() { - parser.begin(); - if( !parser.match('\\') ) - return parser.failure(null); - if( parser.match('a') ) return parser.success('\u0007'); - if( parser.match('b') ) return parser.success('\b'); - if( parser.match('f') ) return parser.success('\f'); - if( parser.match('n') ) return parser.success('\n'); - if( parser.match('r') ) return parser.success('\r'); - if( parser.match('t') ) return parser.success('\t'); - if( parser.match('v') ) return parser.success('\u000b'); - if( parser.match('\\') ) return parser.success('\\'); - if( parser.match('"') ) return parser.success('"'); - if( parser.match('\'') ) return parser.success('\''); - int start = parser.currentIndex(); - if( parser.match('x') && HexDigit() && HexDigit() ) - return parser.success((char)Integer.parseInt(parser.textFrom(start+1),16)); - if( parser.match('u') && HexDigit() && HexDigit() && HexDigit() && HexDigit() ) - return parser.success((char)Integer.parseInt(parser.textFrom(start+1),16)); - if( Digit() ) { - if( Digit() ) Digit(); // optional - return parser.success((char)Integer.parseInt(parser.textFrom(start))); - } - if( MatchEndOfLine() ) { - return parser.success('\n'); - } - return parser.failure(null); - } - - private void Spaces() throws ParseException { - while( parser.anyOf(" \t") || Comment() || ContinueOnNextLine() ); - } - - private boolean ContinueOnNextLine() { - parser.begin(); - if( parser.match('\\') && EndOfLine() ) { - parser.upSb(); - return parser.success(); - } else - return parser.failure(); - } - - private boolean Comment() throws ParseException { - if( LongComment() ) - return true; - if( parser.match("--") ) { - while( parser.noneOf("\r\n") ); - return true; - } - return false; - } - - private boolean LongComment() throws ParseException { - parser.begin(); - if( !parser.match("--[") ) - return parser.failure(); - int start = parser.currentIndex(); - while( parser.match('=') ); - int nEquals = parser.currentIndex() - start; - if( !parser.match('[') ) - return parser.failure(); - while( !LongBracketsEnd(nEquals) ) { - if( !(EndOfLine() || parser.anyChar()) ) - throw parser.exception("Unclosed comment"); - } - parser.upSb(); - return parser.success(); - } - - private boolean LongBracketsEnd(int nEquals) { - parser.begin(); - if( !parser.match(']') ) - return parser.failure(); - while( nEquals-- > 0 ) { - if( !parser.match('=') ) - return parser.failure(); - } - if( !parser.match(']') ) - return parser.failure(); - return parser.success(); - } - - - - private class ParseList extends ArrayList { - - void addNewLines() { - if( parser.sb().length() > 0 ) { - add( parser.sb().toString() ); - parser.sb().setLength(0); -/* -if( parser.sourceName.equals("stdin") ) { - StringWriter sw = new StringWriter(); - new Throwable().printStackTrace(new PrintWriter(sw,true)); -// add(sw.toString()); -} -*/ - } - } - - ParseList() { - addNewLines(); - } - - @Override public boolean add(Object obj) { - if( obj instanceof List ) throw new RuntimeException(); - return super.add(obj); - } - - @Override public void add(int index,Object obj) { - if( obj instanceof List ) throw new RuntimeException(); - super.add(index,obj); - } - - @Override public String toString() { - StringBuilder sb = new StringBuilder(); - for( Object o : this ) { - sb.append( o.toString() ); - } - return sb.toString(); - } - } - - - private static AtomicInteger classCounter = new AtomicInteger(); - - private enum Val { SINGLE, ARRAY } - - private class Expr extends ParseList { - final Val valType; - final boolean isStmt; - - Expr(Val valType,boolean isStmt) { - this.valType = valType; - this.isStmt = isStmt; - } - - Expr single() { - if( valType==Val.SINGLE ) - return this; - Expr exp = new Expr(Val.SINGLE,isStmt); - exp.add( valType==Val.ARRAY ? "LuanImpl.first(" : "Luan.first(" ); - exp.addAll( this ); - exp.add( ")" ); - return exp; - } - - Expr array() { - if( valType==Val.ARRAY ) - return this; - Expr exp = new Expr(Val.ARRAY,isStmt); - if( valType==Val.SINGLE ) { - exp.add( "new Object[]{" ); - exp.addAll( this ); - exp.add( "}" ); - } else { - exp.add( "Luan.array(" ); - exp.addAll( this ); - exp.add( ")" ); - } - return exp; - } - - } - - private Expr expString(List<Expr> list) { - Expr exp = new Expr(Val.ARRAY,false); - switch(list.size()) { - case 0: - exp.add("LuanFunction.NOTHING"); - return exp; - case 1: - return list.get(0); - default: - int lastI = list.size() - 1; - exp.add( "new Object[]{" ); - for( int i=0; i<lastI; i++ ) { - exp.addAll( list.get(i).single() ); - exp.add( "," ); - } - Expr last = list.get(lastI); - if( last.valType==Val.SINGLE ) { - exp.addAll( last ); - exp.add( "}" ); - } else { - exp.add( "}" ); - exp.add( 0, "LuanImpl.concatArgs(" ); - exp.add( "," ); - exp.addAll( last ); - exp.add( ")" ); - } - return exp; - } - } - - private class Stmts extends ParseList { - boolean hasReturn = false; - } - - private Class toFnClass(Stmts stmts,List<UpSym> upValueSymbols) { - String className = "EXP" + classCounter.incrementAndGet(); - String classCode = toFnString(stmts,upValueSymbols,className); - try { -//System.out.println(parser.sourceName); -//System.out.println(classCode); - return LuanJavaCompiler.compile("luan.impl."+className,parser.sourceName,classCode); - } catch(ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - private String toFnString(Stmts stmts,List<UpSym> upValueSymbols) { - String className = "EXP" + classCounter.incrementAndGet(); - return toFnString(stmts,upValueSymbols,className); - } - - private String toFnString(Stmts stmts,List<UpSym> upValueSymbols,String className) { - if( !stmts.hasReturn ) - stmts.add( "\nreturn LuanFunction.NOTHING;" ); - return "" - +"package luan.impl; " - +"import luan.Luan; " - +"import luan.LuanFunction; " - +"import luan.LuanState; " - +"import luan.LuanJava; " - +"import luan.LuanException; " - +"import luan.modules.PackageLuan; " - - +"public class " + className +" extends Closure { " - +"public "+className+"(LuanJava java) throws LuanException { " - +"super("+upValueSymbols.size()+",java); " - + init(upValueSymbols) - +"} " - - +"@Override public Object doCall(LuanState luan,Object[] args) throws LuanException { " - +"final Pointer[] parentUpValues = upValues; " - +"Object t; " - +"Object[] a; " - + stmts - +"\n} " - +"}\n" - ; - } - - private Expr toFnExp(Stmts stmt,List<UpSym> upValueSymbols,String name) { - stmt.addNewLines(); - if( !stmt.hasReturn ) - stmt.add( "return LuanFunction.NOTHING; " ); - Expr exp = new Expr(Val.SINGLE,false); - exp.add( "" - +"new Closure("+upValueSymbols.size()+",java) { " - +"{ " - + init(upValueSymbols) - +"} " - +"@Override public Object doCall(LuanState luan,Object[] args) throws LuanException { " - ); - if( name != null ) { - exp.add( "" - +"return _" + name + "(luan,args); " - +"} " - +"private Object _" + name + "(LuanState luan,Object[] args) throws LuanException { " - ); - } - exp.add( "" - +"final Pointer[] parentUpValues = upValues; " - +"Object t; " - +"Object[] a; " - ); - exp.addAll( stmt ); - exp.add( "" - +"} " - +"} " - ); - return exp; - } - - private static String init(List<UpSym> upValueSymbols) { - StringBuilder sb = new StringBuilder(); - for( UpSym upSym : upValueSymbols ) { - sb.append( upSym.init() ); - } - return sb.toString(); - } - -}
--- a/core/src/luan/impl/ParseException.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -package luan.impl; - - -public final class ParseException extends Exception { -// public final LuanSource src; - public final String sourceName; - public final String text; - public final int iCurrent; - public final int iHigh; - - ParseException(String msg,String text,String sourceName,int iCurrent,int iHigh) { - super(msg); -// this.src = src; - this.text = text; - this.sourceName = sourceName; - this.iCurrent = iCurrent; - this.iHigh = iHigh; -//System.out.println("iCurrent = "+iCurrent); -//System.out.println("iHigh = "+iHigh); - } - - private class Location { - final int line; - final int pos; - - Location(int index) { - int line = 0; - int i = -1; - while(true) { - int j = text.indexOf('\n',i+1); - if( j == -1 || j >= index ) - break; - i = j; - line++; - } - this.line = line; - this.pos = index - i - 1; - } - } - - private String[] lines() { - return text.split("\n",-1); - } - - public String getFancyMessage() { - Location loc = new Location(iCurrent); - String line = lines()[loc.line]; - String msg = getMessage() + " (line " + (loc.line+1) + ", pos " + (loc.pos+1) + ") in " + sourceName + "\n"; - StringBuilder sb = new StringBuilder(msg); - sb.append( line + "\n" ); - for( int i=0; i<loc.pos; i++ ) { - sb.append( line.charAt(i)=='\t' ? '\t' : ' ' ); - } - sb.append("^\n"); - return sb.toString(); - } -}
--- a/core/src/luan/impl/Parser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ -package luan.impl; - - -final class Parser { - - private static class Frame { - int i; - StringBuilder sb; - } - -// private final LuanSource src; - public final String text; - public final String sourceName; - private final int len; - private Frame[] stack = new Frame[256]; - private int frame = 0; - private int iHigh; - - public Parser(String text,String sourceName) { -// this.src = src; - this.text = text; - this.sourceName = sourceName; - this.len = text.length(); - stack[0] = new Frame(); - } - - private int i() { - return stack[frame].i; - } - - private void i(int i) { - Frame f = stack[frame]; - f.i += i; - if( iHigh < f.i ) - iHigh = f.i; - } - - public int begin() { - frame++; - if( frame == stack.length ) { - Frame[] a = new Frame[2*frame]; - System.arraycopy(stack,0,a,0,frame); - stack = a; - } - Frame f = new Frame(); - f.i = stack[frame-1].i; - stack[frame] = f; - return i(); - } - - public void rollback() { - Frame f = stack[frame]; - f.i = stack[frame-1].i; - f.sb = null; - } - - public <T> T success(T t) { - success(); - return t; - } - - public boolean success() { - Frame f = stack[frame]; - if( f.sb != null && f.sb.length() > 0 ) throw new RuntimeException("sb not emtpy"); - frame--; - stack[frame].i = f.i; - return true; - } - - public <T> T failure(T t) { - failure(); - return t; - } - - public boolean failure() { - frame--; - return false; - } - - public ParseException exception(String msg) { - return new ParseException(msg,text,sourceName,i(),iHigh); - } - - public ParseException exception() { - return exception("Invalid input"); - } - - public StringBuilder sb() { - Frame f = stack[frame]; - if( f.sb == null ) - f.sb = new StringBuilder(); - return f.sb; - } - - public void upSb() { - Frame f = stack[frame]; - StringBuilder sb = f.sb; - if( sb != null && sb.length() > 0 ) { - Frame fUp = stack[frame-1]; - if( fUp.sb == null ) - fUp.sb = sb; - else - fUp.sb.append(sb.toString()); - f.sb = null; - } - } - - public int currentIndex() { - return i(); - } - - public char lastChar() { - return text.charAt(i()-1); - } - - public char currentChar() { - return text.charAt(i()); - } - - public boolean endOfInput() { - return i() >= len; - } - - public boolean match(char c) { - if( endOfInput() || text.charAt(i()) != c ) - return false; - i(1); - return true; - } - - public boolean match(String s) { - int n = s.length(); - if( !text.regionMatches(i(),s,0,n) ) - return false; - i(n); - return true; - } - - public boolean matchIgnoreCase(String s) { - int n = s.length(); - if( !text.regionMatches(true,i(),s,0,n) ) - return false; - i(n); - return true; - } - - public boolean anyOf(String s) { - if( endOfInput() || s.indexOf(text.charAt(i())) == -1 ) - return false; - i(1); - return true; - } - - public boolean noneOf(String s) { - if( endOfInput() || s.indexOf(text.charAt(i())) != -1 ) - return false; - i(1); - return true; - } - - public boolean inCharRange(char cLow, char cHigh) { - if( endOfInput() ) - return false; - char c = text.charAt(i()); - if( !(cLow <= c && c <= cHigh) ) - return false; - i(1); - return true; - } - - public boolean anyChar() { - if( endOfInput() ) - return false; - i(1); - return true; - } - - public boolean test(char c) { - return !endOfInput() && text.charAt(i()) == c; - } - - public boolean test(String s) { - return text.regionMatches(i(),s,0,s.length()); - } - - public String textFrom(int start) { - return text.substring(start,i()); - } - -}
--- a/core/src/luan/impl/Pointer.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -package luan.impl; - -import luan.DeepCloneable; -import luan.DeepCloner; - - -public final class Pointer implements DeepCloneable { - public Object o; - - public Pointer() {} - - public Pointer(Object o) { - this.o = o; - } - - @Override public Pointer shallowClone() { - return new Pointer(); - } - - @Override public void deepenClone(DeepCloneable clone,DeepCloner cloner) { - ((Pointer)clone).o = cloner.get(o); - } -}
--- a/core/src/luan/impl/TableField.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -package luan.impl; - - -public final class TableField { - final Object key; - final Object value; - - public TableField(Object key,Object value) { - this.key = key; - this.value = value; - } -}
--- a/core/src/luan/modules/BasicLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,244 +0,0 @@ -package luan.modules; - -import java.io.InputStreamReader; -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.Iterator; -import java.util.Map; -import java.util.List; -import java.util.ArrayList; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanException; -import luan.LuanMethod; -import luan.LuanMeta; - - -public final class BasicLuan { - - public static String type(Object obj) { - return Luan.type(obj); - } - - public static LuanFunction load(String text,String sourceName,LuanTable env) - throws LuanException - { - Utils.checkNotNull(text); - Utils.checkNotNull(sourceName,1); - return Luan.load(text,sourceName,env); - } - - public static LuanFunction load_file(LuanState luan,String fileName) throws LuanException { - if( fileName == null ) { - fileName = "stdin:"; - } else if( fileName.indexOf(':') == -1 ) { - fileName = "file:" + fileName; - } - String src = PackageLuan.read(luan,fileName); - if( src == null ) - throw new LuanException("file '"+fileName+"' not found" ); - return load(src,fileName,null); - } - - public static LuanFunction pairs(final LuanState luan,final LuanTable t) throws LuanException { - Utils.checkNotNull(t); - return t.pairs(luan); - } - - public static LuanFunction ipairs(final LuanTable t) throws LuanException { - Utils.checkNotNull(t); - return new LuanFunction() { - List<Object> list = t.asList(); - int i = 0; - final int size = list.size(); - - @Override public Object[] call(LuanState luan,Object[] args) { - if( i >= size ) - return LuanFunction.NOTHING; - Object val = list.get(i++); - return new Object[]{i,val}; - } - }; - } - - public static Object get_metatable(LuanTable table) throws LuanException { - Utils.checkNotNull(table); - LuanTable metatable = table.getMetatable(); - if( metatable == null ) - return null; - Object obj = metatable.rawGet("__metatable"); - return obj!=null ? obj : metatable; - } - - public static void set_metatable(LuanTable table,LuanTable metatable) throws LuanException { - Utils.checkNotNull(table); - if( table.getHandler("__metatable") != null ) - throw new LuanException("cannot change a protected metatable"); - table.setMetatable(metatable); - } - - public static boolean raw_equal(Object v1,Object v2) { - return v1 == v2 || v1 != null && v1.equals(v2); - } - - public static Object raw_get(LuanTable table,Object index) { - return table.rawGet(index); - } - - public static void raw_set(LuanTable table,Object index,Object value) { - table.rawPut(index,value); - } - - public static int raw_len(Object v) throws LuanException { - if( v instanceof String ) { - String s = (String)v; - return s.length(); - } - if( v instanceof LuanTable ) { - LuanTable t = (LuanTable)v; - return t.rawLength(); - } - throw new LuanException( "bad argument #1 to 'raw_len' (table or string expected)" ); - } - - public static String to_string(LuanState luan,Object v) throws LuanException { - return luan.toString(v); - } - - public static LuanTable new_error(LuanState luan,Object msg) throws LuanException { - String s = luan.toString(msg); - LuanTable tbl = new LuanException(s).table(); - tbl.rawPut( "message", msg ); - return tbl; - } - - public static String assert_string(String v) throws LuanException { - Utils.checkNotNull(v); - return v; - } - - public static Number assert_number(Number v) throws LuanException { - Utils.checkNotNull(v); - return v; - } - - public static LuanTable assert_table(LuanTable v) throws LuanException { - Utils.checkNotNull(v); - return v; - } - - public static boolean assert_boolean(boolean v) throws LuanException { - return v; - } - - public static int assert_integer(int v) throws LuanException { - return v; - } - - public static long assert_long(long v) throws LuanException { - return v; - } - - public static double assert_double(double v) throws LuanException { - return v; - } - - @LuanMethod public static byte[] assert_binary(byte[] v) throws LuanException { - Utils.checkNotNull(v); - return v; - } - - public static LuanFunction assert_function(LuanFunction v) throws LuanException { - Utils.checkNotNull(v); - return v; - } - - public static LuanFunction range(final double from,final double to,Double stepV) throws LuanException { - final double step = stepV==null ? 1.0 : stepV; - if( step == 0.0 ) - throw new LuanException("bad argument #3 (step may not be zero)"); - return new LuanFunction() { - double v = from; - - @Override public Object call(LuanState luan,Object[] args) { - if( step > 0.0 && v > to || step < 0.0 && v < to ) - return LuanFunction.NOTHING; - double rtn = v; - v += step; - return rtn; - } - }; - } - - public static LuanFunction values(final Object... args) throws LuanException { - return new LuanFunction() { - int i = 0; - - @Override public Object call(LuanState luan,Object[] unused) { - if( i >= args.length ) - return LuanFunction.NOTHING; - return args[i++]; - } - }; - } - - private LuanFunction fn(Object obj) { - return obj instanceof LuanFunction ? (LuanFunction)obj : null; - } - - public static Object try_(LuanState luan,LuanTable blocks,Object... args) throws LuanException { - Utils.checkNotNull(blocks); - Object obj = blocks.get(luan,1); - if( obj == null ) - throw new LuanException("missing 'try' value"); - if( !(obj instanceof LuanFunction) ) - throw new LuanException("bad 'try' value (function expected, got "+Luan.type(obj)+")"); - LuanFunction tryFn = (LuanFunction)obj; - LuanFunction catchFn = null; - obj = blocks.get(luan,"catch"); - if( obj != null ) { - if( !(obj instanceof LuanFunction) ) - throw new LuanException("bad 'catch' value (function expected, got "+Luan.type(obj)+")"); - catchFn = (LuanFunction)obj; - } - LuanFunction finallyFn = null; - obj = blocks.get(luan,"finally"); - if( obj != null ) { - if( !(obj instanceof LuanFunction) ) - throw new LuanException("bad 'finally' value (function expected, got "+Luan.type(obj)+")"); - finallyFn = (LuanFunction)obj; - } - try { - return tryFn.call(luan,args); - } catch(LuanException e) { - if( catchFn == null ) - throw e; - return catchFn.call(luan,new Object[]{e.table()}); - } finally { - if( finallyFn != null ) - finallyFn.call(luan); - } - } - - @LuanMethod public static Object[] pcall(LuanState luan,LuanFunction f,Object... args) { - try { - Object[] r = Luan.array(f.call(luan,args)); - Object[] rtn = new Object[r.length+1]; - rtn[0] = true; - for( int i=0; i<r.length; i++ ) { - rtn[i+1] = r[i]; - } - return rtn; - } catch(LuanException e) { - return new Object[]{false,e.table()}; - } - } - - public static String number_type(Number v) throws LuanException { - Utils.checkNotNull(v); - return v.getClass().getSimpleName().toLowerCase(); - } - -}
--- a/core/src/luan/modules/Binary.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -java() -local BinaryLuan = require "java:luan.modules.BinaryLuan" - - -local M = {} - -M.binary = BinaryLuan.binary -M.byte = BinaryLuan.byte_ -M.to_string = BinaryLuan.to_string - -return M
--- a/core/src/luan/modules/BinaryLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -package luan.modules; - -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanException; -import luan.LuanMethod; - - -public final class BinaryLuan { - - static int start(byte[] binary,int i) { - int len = binary.length; - return i==0 ? 0 : i > 0 ? Math.min(i-1,len) : Math.max(len+i,0); - } - - static int start(byte[] binary,Integer i,int dflt) { - return i==null ? dflt : start(binary,i); - } - - static int end(byte[] binary,int i) { - int len = binary.length; - return i==0 ? 0 : i > 0 ? Math.min(i,len) : Math.max(len+i+1,0); - } - - static int end(byte[] binary,Integer i,int dflt) { - return i==null ? dflt : end(binary,i); - } - - @LuanMethod public static Byte[] byte_(byte[] binary,Integer i,Integer j) throws LuanException { - Utils.checkNotNull(binary); - int start = start(binary,i,1); - int end = end(binary,j,start+1); - Byte[] bytes = new Byte[end-start]; - for( int k=0; k<bytes.length; k++ ) { - bytes[k] = binary[start+k]; - } - return bytes; - } - - @LuanMethod public static byte[] binary(byte... bytes) { - return bytes; - } - - public static String to_string(byte[] binary) { - return new String(binary); - } - -}
--- a/core/src/luan/modules/Html.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -java() -local HtmlLuan = require "java:luan.modules.HtmlLuan" -local HtmlParser = require "java:luan.modules.parsers.Html" -local URLEncoder = require "java:java.net.URLEncoder" -local Luan = require "luan:Luan.luan" -local error = Luan.error -local ipairs = Luan.ipairs or error() -local pairs = Luan.pairs or error() -local type = Luan.type or error() -local Io = require "luan:Io.luan" -local output_of = Io.output_of or error() - - -local M = {} - -M.encode = HtmlLuan.encode - -local quote = HtmlLuan.quote -M.quote = quote - -function M.parse(text,container_tags) - text or error "text required" - container_tags = container_tags or {"script","style","textarea"} - return HtmlParser.toList(text,container_tags) -end - -function M.url_encode(s) - return URLEncoder.encode(s,"UTF-8") -end - -local function output_tag(tag) - %><<%= tag.name %><% - local attributes = tag.attributes - for name, value in pairs(attributes) do - %> <%= name %><% - if value ~= true then - %>=<%= quote(value) %><% - end - end - if tag.is_empty then - %>/<% - end - %>><% -end - -function M.to_string(list) - return output_of( function() - for _, obj in ipairs(list) do - local tp = type(obj) - if tp == "string" then - %><%= obj %><% - elseif tp == "table" then - tp = obj.type - if tp == nil then - error "no type in element of table for 'Html.to_string'" - elseif tp == "comment" then - %><!--<%= obj.text %>--><% - elseif tp == "cdata" then - %><![CDATA[<%= obj.text %>]]><% - elseif tp == "tag" then - output_tag(obj) - elseif tp == "container" then - local tag = obj.tag - output_tag(tag) - %><%= obj.text %></<%= tag.name %>><% - else - error "invalid element type for 'Html.to_string'" - end - else - error("invalid value ("..tp..") in list for 'Html.to_string'") - end - end - end ) -end - -return M
--- a/core/src/luan/modules/HtmlLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,348 +0,0 @@ -package luan.modules; - -import java.util.List; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Set; -import java.util.HashSet; -import java.util.Map; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanException; - - -public final class HtmlLuan { - - public static String encode(String s) throws LuanException { - Utils.checkNotNull(s); - char[] a = s.toCharArray(); - StringBuilder buf = new StringBuilder(); - for( int i=0; i<a.length; i++ ) { - char c = a[i]; - switch(c) { - case '&': - buf.append("&"); - break; - case '<': - buf.append("<"); - break; - case '>': - buf.append(">"); - break; - case '"': - buf.append("""); - break; - default: - buf.append(c); - } - } - return buf.toString(); - } - -/* -// public static final String TEXTAREA = "textarea"; - public static final String SCRIPT = "script"; - public static final String STYLE = "style"; - - public static Set<String> containerTags = new HashSet<String>(Arrays.asList(SCRIPT,STYLE)); -*/ -/* - public static LuanTable parse(LuanState luan,String text,LuanTable containerTagsTbl) - throws LuanException - { - Utils.checkNotNull(luan,text); - Utils.checkNotNull(luan,containerTagsTbl); - Set<String> containerTags = new HashSet<String>(); - for( Object v : containerTagsTbl.asList() ) { - containerTags.add((String)v); - } - List<Object> html = new ArrayList<Object>(); - int len = text.length(); - int i = 0; -outer: - while( i < len ) { - int i2 = text.indexOf('<',i); - while( i2 != -1 && i2+1 < len ) { - char c = text.charAt(i2+1); - if( Character.isLetter(c) || c=='/' || c=='!' ) - break; - i2 = text.indexOf('<',i2+1); - } - if( i2 == -1 ) { - html.add( text.substring(i) ); - break; - } - if( i < i2 ) - html.add( text.substring(i,i2) ); - if( text.startsWith("<!--",i2) ) { - i = text.indexOf("-->",i2+4); - if( i == -1 ) { - html.add( text.substring(i2) ); - break; - } - html.add( comment( text.substring(i2+4,i) ) ); - i += 3; - } else if( text.startsWith("<![CDATA[",i2) ) { - i = text.indexOf("]]>",i2+9); - if( i == -1 ) { - html.add( text.substring(i2) ); - break; - } - html.add( cdata( text.substring(i2+9,i) ) ); - i += 3; - } else { - i = text.indexOf('>',i2); - if( i == -1 ) { - html.add( text.substring(i2) ); - break; - } - String tagText = text.substring(i2+1,i); - try { - LuanTable tag = parseTag(tagText); - String tagName = (String)tag.rawGet("name"); - if( containerTags.contains(tagName) ) { - i2 = i; - String endTagName = '/' + tagName; - while(true) { - i2 = text.indexOf('<',i2+1); - if( i2 == -1 ) - break; - int i3 = text.indexOf('>',i2); - if( i3 == -1 ) - break; - int j = i2+1; - while( j<i3 && !Character.isWhitespace(text.charAt(j)) ) j++; - String s = text.substring(i2+1,j); - if( s.equalsIgnoreCase(endTagName) ) { - String text2 = text.substring(i+1,i2); - LuanTable textContainer = textContainer(tag,text2); - html.add( textContainer ); - i = i3 + 1; - continue outer; - } - } -// logger.warn("unclosed "+tagName); - } - i += 1; - html.add( tag ); - } catch(BadTag e) { -// logger.debug("bad tag",e); - i += 1; -// if( !removeBadTags ) { - html.add( "<" ); - html.add( encode(luan,tagText) ); - html.add( ">" ); -// } - } - } - } - return new LuanTable(html); - } - - static LuanTable comment(String text) { - LuanTable tbl = new LuanTable(); - tbl.rawPut("type","comment"); - tbl.rawPut("text",text); - return tbl; - } - - static LuanTable cdata(String text) { - LuanTable tbl = new LuanTable(); - tbl.rawPut("type","cdata"); - tbl.rawPut("text",text); - return tbl; - } - - static LuanTable textContainer(LuanTable tag,String text) { - LuanTable tbl = new LuanTable(); - tbl.rawPut("type","container"); - tbl.rawPut("tag",tag); - tbl.rawPut("text",text); - return tbl; - } - - - - static final class BadTag extends RuntimeException { - private BadTag(String msg) { - super(msg); - } - } - - static LuanTable parseTag(String text) { - LuanTable tbl = new LuanTable(); - tbl.rawPut("type","tag"); - if( text.endsWith("/") ) { - text = text.substring(0,text.length()-1); - tbl.rawPut("is_empty",true); - } else { - tbl.rawPut("is_empty",false); - } - int len = text.length(); - int i = 0; - int i2 = i; - if( i2<len && text.charAt(i2)=='/' ) - i2++; - while( i2<len ) { - char c = text.charAt(i2); - if( Character.isWhitespace(c) ) - break; - if( !( Character.isLetterOrDigit(c) || c=='_' || c=='.' || c=='-' || c==':' ) ) - throw new BadTag("invalid tag name for <"+text+">"); - i2++; - } - String name = text.substring(i,i2).toLowerCase(); - tbl.rawPut("name",name); - LuanTable attributes = new LuanTable(); - tbl.rawPut("attributes",attributes); - i = i2; - while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; - while( i<len ) { - i2 = toEndName(text,i,len); - String attrName = unquote(text.substring(i,i2).toLowerCase()); - if( attributes.rawGet(attrName) != null ) - throw new BadTag("duplicate attribute: "+attrName); - i = i2; - while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; - if( i<len && text.charAt(i) == '=' ) { - i++; - i2 = i; - while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; - i2 = toEndValue(text,i,len); - String attrValue = text.substring(i,i2); - if( attrValue.indexOf('<') != -1 || attrValue.indexOf('>') != -1 ) - throw new BadTag("invalid attribute value: "+attrValue); - attrValue = unquote(attrValue); - attributes.rawPut(attrName,attrValue); - i = i2; - while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; - } else { - attributes.rawPut(attrName,true); - } - } - return tbl; - } - - private static int toEndName(String text,int i,int len) { - if( i==len ) - return i; - char c = text.charAt(i); - switch(c) { - case '"': - case '\'': - i = text.indexOf(c,i+1); - return i==-1 ? len : i+1; - default: - if( Character.isWhitespace(c) ) { - throw new RuntimeException("text="+text+" i="+i); - } - do { - i++; - } while( i<len && (c=text.charAt(i))!='=' && !Character.isWhitespace(c) ); - return i; - } - } - - private static int toEndValue(String text,int i,int len) { - if( i==len ) - return i; - char c = text.charAt(i); - switch(c) { - case '"': - case '\'': - i = text.indexOf(c,i+1); - return i==-1 ? len : i+1; - default: - if( Character.isWhitespace(c) ) { - throw new RuntimeException("text="+text+" i="+i); - } - do { - i++; - } while( i<len && !Character.isWhitespace(text.charAt(i)) ); - return i; - } - } - - public static String unquote(String s) { - if( s==null || s.length()<=1 ) - return s; - char c = s.charAt(0); - return (c=='"' || c=='\'') && s.charAt(s.length()-1)==c - ? s.substring(1,s.length()-1) : s; - } -*/ - - -/* - public static String to_string(LuanState luan,LuanTable tbl) throws LuanException { - List<Object> html = tbl.asList(); - StringBuilder buf = new StringBuilder(); - for( Object o : html ) { - if( o instanceof String ) { - buf.append( o ); - } else if( o instanceof LuanTable ) { - LuanTable t = (LuanTable)o; - String type = (String)t.get(luan,"type"); - if( type==null ) - throw new LuanException(luan, "no type in element of table for 'Html.to_string'" ); - if( type.equals("comment") ) { - buf.append( "<!--" ).append( t.get(luan,"text") ).append( "-->" ); - } else if( type.equals("cdata") ) { - buf.append( "<![CDATA[" ).append( t.get(luan,"text") ).append( "]]" ); - } else if( type.equals("tag") ) { - buf.append( tagToString(luan,t) ); - } else if( type.equals("container") ) { - LuanTable tag = (LuanTable)t.get(luan,"tag"); - buf.append( tagToString(luan,tag) ); - buf.append( t.get(luan,"text") ); - buf.append( "</" ).append( tag.get(luan,"name") ).append( ">" ); - } else { - throw new LuanException(luan, "invalid element type for 'Html.to_string'" ); - } - } else - throw new LuanException(luan, "invalid value ("+Luan.type(o)+") in table for 'Html.to_string'" ); - } - return buf.toString(); - } - - private static String tagToString(LuanState luan,LuanTable tbl) throws LuanException { - StringBuilder buf = new StringBuilder(); - buf.append('<'); - buf.append(tbl.get(luan,"name")); - LuanTable attributes = (LuanTable)tbl.get(luan,"attributes"); - for( Map.Entry<Object,Object> attr : attributes.iterable(luan) ) { - buf.append( ' ' ); - buf.append( attr.getKey() ); - Object val = attr.getValue(); - if( !val.equals(Boolean.TRUE) ) { - buf.append( '=' ); - buf.append( quote((String)val) ); - } - } - if( tbl.get(luan,"is_empty").equals(Boolean.TRUE) ) - buf.append('/'); - buf.append('>'); - return buf.toString(); - } -*/ - public static String quote(String s) { - StringBuilder buf = new StringBuilder(); - buf.append('"'); - int i = 0; - while(true) { - int i2 = s.indexOf('"',i); - if( i2 == -1 ) { - buf.append(s.substring(i)); - break; - } else { - buf.append(s.substring(i,i2)); - buf.append("""); - i = i2 + 1; - } - } - buf.append('"'); - return buf.toString(); - } - -}
--- a/core/src/luan/modules/Io.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,185 +0,0 @@ -java() -local IoLuan = require "java:luan.modules.IoLuan" -local System = require "java:java.lang.System" - -local M = {} - -M.ip = IoLuan.ip -M.my_ips = IoLuan.my_ips -M.read_console_line = IoLuan.read_console_line -M.schemes = IoLuan.newSchemes() -M.uri = IoLuan.uri -M.stdin = IoLuan.defaultStdin.table() -M.socket_server = IoLuan.socket_server -M.stdout = IoLuan.textWriter(System.out) -M.stderr = IoLuan.textWriter(System.err) - --- used by http and rpc -M.password = "password" - -local Luan = require "luan:Luan.luan" -local error = Luan.error -local to_string = Luan.to_string or error() -local type = Luan.type or error() -local try = Luan.try or error() -local ipairs = Luan.ipairs or error() -local pairs = Luan.pairs or error() -local values = Luan.values or error() -local load = Luan.load or error() -local Table = require "luan:Table.luan" -local unpack = Table.unpack or error() -local String = require "luan:String.luan" -local encode = String.encode or error() -local matches = String.matches or error() - - --- do not change -function M.template_write(...) - return M.stdout.write(...) -end - - -function M.print_to(out,...) - local list = {} - for v in values(...) do - list[#list+1] = to_string(v) - list[#list+1] = '\t' - end - if #list > 0 then - list[#list] = '\n' - out.write( unpack(list) ) - end -end - -function M.print(...) - M.print_to(M.stdout,...) -end - - -function M.output_to(out,fn,...) - local old_out = M.stdout - return try( { - function(...) - M.stdout = out - fn(...) - end; - finally = function() - M.stdout = old_out - end; - }, ... ) -end - -local uri = M.uri -- make local - -function M.output_of(fn,...) - local string_uri = uri "string:" - local out = string_uri.text_writer() - M.output_to(out,fn,...) - out.close() - return string_uri.read_text() -end - - --- repr - -local function do_repr(out,obj,strict,done) - local tp = type(obj) - if tp == "table" then - if done[obj] == true then - strict and error "circular reference" - out.write "<circular reference>" - return - end - done[obj] = true - out.write( "{" ) - local is_first = true - local in_list = {} - for key, value in ipairs(obj) do - if is_first then is_first = false else out.write ", " end - do_repr(out,value,strict,done) - in_list[key] = true - end - for key, value in pairs(obj) do - if in_list[key] ~= true then - if is_first then is_first = false else out.write ", " end - if type(key) == "string" and matches(key,"^[a-zA-Z_][a-zA-Z_0-9]*$") ~= nil then - out.write( key ) - elseif type(key) == "table" then - out.write( "[<", key, ">]" ) - else - out.write "[" - do_repr(out,key,strict,done) - out.write "]" - end - out.write "=" - do_repr(out,value,strict,done) - end - end - out.write "}" - elseif tp == "string" then - out.write( '"', encode(obj), '"' ) - elseif tp == "nil" or tp == "boolean" or tp == "number" then - out.write( obj ) - else - strict and error("can't repr type '"..tp.."' of "..obj) - out.write( "<", obj, ">" ) - end -end - -function M.repr(obj,strict) - local string_uri = uri "string:" - local out = string_uri.text_writer() - do_repr(out,obj,strict or false,{}) - out.close() - return string_uri.read_text() -end - - --- useful for SimplyHTML responsiveness - -local NO = {} -M.NO = NO - -function M.dont_write_when_no(write_fn) - return function(...) - for v in values(...) do - if v == NO then - return - end - end - write_fn(...) - end -end - - --- debug - -function M.debug(prompt) - prompt = prompt or "luan_debug> " - local function console() - return M.read_console_line(prompt) - end - local env = {} - for line in console do - try { - function() - local fn - try { - function() - fn = load("return "..line,"stdin",env) - end - catch = function(e) - fn = load(line,"stdin",env) - end - } - M.print( fn() ) - end - catch = function(e) - M.print(e) - end - } - end -end - - -return M
--- a/core/src/luan/modules/IoLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,904 +0,0 @@ -package luan.modules; - -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.Reader; -import java.io.Writer; -import java.io.StringReader; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.StringWriter; -import java.io.IOException; -import java.io.FileNotFoundException; -import java.net.URL; -import java.net.Socket; -import java.net.ServerSocket; -import java.net.InetAddress; -import java.net.Inet4Address; -import java.net.NetworkInterface; -import java.net.MalformedURLException; -import java.net.UnknownHostException; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanJavaFunction; -import luan.LuanException; -import luan.modules.url.LuanUrl; - - -public final class IoLuan { - - private static void add(LuanTable t,String method,Class... parameterTypes) throws NoSuchMethodException { - t.rawPut( method, new LuanJavaFunction(IoLuan.class.getMethod(method,parameterTypes),null) ); - } - - public static String read_console_line(String prompt) throws IOException { - if( prompt==null ) - prompt = "> "; - return System.console().readLine(prompt); - } - - - public interface LuanWriter { - public void write(LuanState luan,Object... args) throws LuanException, IOException; - public void close() throws IOException; - } - - public static LuanTable textWriter(final PrintStream out) { - LuanWriter luanWriter = new LuanWriter() { - - public void write(LuanState luan,Object... args) throws LuanException { - for( Object obj : args ) { - out.print( luan.toString(obj) ); - } - } - - public void close() { - out.close(); - } - }; - return writer(luanWriter); - } - - public static LuanTable textWriter(final Writer out) { - LuanWriter luanWriter = new LuanWriter() { - - public void write(LuanState luan,Object... args) throws LuanException, IOException { - for( Object obj : args ) { - out.write( luan.toString(obj) ); - } - } - - public void close() throws IOException { - out.close(); - } - }; - return writer(luanWriter); - } - - private static LuanTable writer(LuanWriter luanWriter) { - LuanTable writer = new LuanTable(); - try { - writer.rawPut( "write", new LuanJavaFunction( - LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter - ) ); - writer.rawPut( "close", new LuanJavaFunction( - LuanWriter.class.getMethod( "close" ), luanWriter - ) ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - return writer; - } - - - public static LuanTable binaryWriter(final OutputStream out) { - LuanTable writer = new LuanTable(); - try { - writer.rawPut( "write", new LuanJavaFunction( - OutputStream.class.getMethod( "write", new byte[0].getClass() ), out - ) ); - writer.rawPut( "close", new LuanJavaFunction( - OutputStream.class.getMethod( "close" ), out - ) ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - return writer; - } - - static LuanFunction lines(final BufferedReader in) { - return new LuanFunction() { - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - try { - if( args.length > 0 ) { - if( args.length > 1 || !"close".equals(args[0]) ) - throw new LuanException( "the only argument allowed is 'close'" ); - in.close(); - return null; - } - String rtn = in.readLine(); - if( rtn==null ) - in.close(); - return rtn; - } catch(IOException e) { - throw new LuanException(e); - } - } - }; - } - - static LuanFunction blocks(final InputStream in,final int blockSize) { - return new LuanFunction() { - final byte[] a = new byte[blockSize]; - - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - try { - if( args.length > 0 ) { - if( args.length > 1 || !"close".equals(args[0]) ) - throw new LuanException( "the only argument allowed is 'close'" ); - in.close(); - return null; - } - if( in.read(a) == -1 ) { - in.close(); - return null; - } - return a; - } catch(IOException e) { - throw new LuanException(e); - } - } - }; - } - - - private static File objToFile(Object obj) { - if( obj instanceof String ) { - return new File((String)obj); - } - if( obj instanceof LuanTable ) { - LuanTable t = (LuanTable)obj; - Object java = t.rawGet("java"); - if( java instanceof LuanFile ) { - LuanFile luanFile = (LuanFile)java; - return luanFile.file; - } - } - return null; - } - - - public static abstract class LuanIn { - public abstract InputStream inputStream() throws IOException, LuanException; - public abstract String to_string(); - public abstract String to_uri_string(); - - public Reader reader() throws IOException, LuanException { - return new InputStreamReader(inputStream()); - } - - public String read_text() throws IOException, LuanException { - Reader in = reader(); - String s = Utils.readAll(in); - in.close(); - return s; - } - - public byte[] read_binary() throws IOException, LuanException { - InputStream in = inputStream(); - byte[] a = Utils.readAll(in); - in.close(); - return a; - } - - public LuanFunction read_lines() throws IOException, LuanException { - return lines(new BufferedReader(reader())); - } - - public LuanFunction read_blocks(Integer blockSize) throws IOException, LuanException { - int n = blockSize!=null ? blockSize : Utils.bufSize; - return blocks(inputStream(),n); - } - - public boolean exists() throws IOException, LuanException { - try { - inputStream().close(); - return true; - } catch(FileNotFoundException e) { - return false; - } - } - - public LuanTable table() { - LuanTable tbl = new LuanTable(); - try { - tbl.rawPut( "java", this ); - tbl.rawPut( "to_string", new LuanJavaFunction( - LuanIn.class.getMethod( "to_string" ), this - ) ); - tbl.rawPut( "to_uri_string", new LuanJavaFunction( - LuanIn.class.getMethod( "to_uri_string" ), this - ) ); - tbl.rawPut( "read_text", new LuanJavaFunction( - LuanIn.class.getMethod( "read_text" ), this - ) ); - tbl.rawPut( "read_binary", new LuanJavaFunction( - LuanIn.class.getMethod( "read_binary" ), this - ) ); - tbl.rawPut( "read_lines", new LuanJavaFunction( - LuanIn.class.getMethod( "read_lines" ), this - ) ); - tbl.rawPut( "read_blocks", new LuanJavaFunction( - LuanIn.class.getMethod( "read_blocks", Integer.class ), this - ) ); - tbl.rawPut( "exists", new LuanJavaFunction( - LuanIn.class.getMethod( "exists" ), this - ) ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - return tbl; - } - } - - public static final LuanIn defaultStdin = new LuanIn() { - - @Override public InputStream inputStream() { - return System.in; - } - - @Override public String to_string() { - return "<stdin>"; - } - - @Override public String to_uri_string() { - return "stdin:"; - } - - @Override public String read_text() throws IOException { - return Utils.readAll(new InputStreamReader(System.in)); - } - - @Override public byte[] read_binary() throws IOException { - return Utils.readAll(System.in); - } - - @Override public boolean exists() { - return true; - } - }; - - public static abstract class LuanIO extends LuanIn { - abstract OutputStream outputStream() throws IOException; - - public void write(Object obj) throws LuanException, IOException { - if( obj instanceof String ) { - String s = (String)obj; - Writer out = new OutputStreamWriter(outputStream()); - out.write(s); - out.close(); - return; - } - if( obj instanceof byte[] ) { - byte[] a = (byte[])obj; - OutputStream out = outputStream(); - Utils.copyAll(new ByteArrayInputStream(a),out); - out.close(); - return; - } - if( obj instanceof LuanTable ) { - LuanTable t = (LuanTable)obj; - Object java = t.rawGet("java"); - if( java instanceof LuanIn ) { - LuanIn luanIn = (LuanIn)java; - InputStream in = luanIn.inputStream(); - OutputStream out = outputStream(); - Utils.copyAll(in,out); - out.close(); - in.close(); - return; - } - } - throw new LuanException( "bad argument #1 to 'write' (string or binary or Io.uri expected)" ); - } - - public LuanTable text_writer() throws IOException { - return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream()))); - } - - public LuanTable binary_writer() throws IOException { - return binaryWriter(new BufferedOutputStream(outputStream())); - } - - @Override public LuanTable table() { - LuanTable tbl = super.table(); - try { - tbl.rawPut( "write", new LuanJavaFunction( - LuanIO.class.getMethod( "write", Object.class ), this - ) ); - tbl.rawPut( "text_writer", new LuanJavaFunction( - LuanIO.class.getMethod( "text_writer" ), this - ) ); - tbl.rawPut( "binary_writer", new LuanJavaFunction( - LuanIO.class.getMethod( "binary_writer" ), this - ) ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - return tbl; - } - } - - private static final LuanIO nullIO = new LuanIO() { - private final InputStream in = new InputStream() { - @Override public int read() { - return -1; - } - }; - private final OutputStream out = new OutputStream() { - @Override public void write(int b) {} - }; - - @Override public InputStream inputStream() { - return in; - } - - @Override OutputStream outputStream() { - return out; - } - - @Override public String to_string() { - return "<null>"; - } - - @Override public String to_uri_string() { - return "null:"; - } - - }; - - public static final class LuanString extends LuanIO { - private String s; - - private LuanString(String s) { - this.s = s; - } - - @Override public InputStream inputStream() { - throw new UnsupportedOperationException(); - } - - @Override OutputStream outputStream() { - throw new UnsupportedOperationException(); - } - - @Override public String to_string() { - return "<string>"; - } - - @Override public String to_uri_string() { - return "string:" + s; - } - - @Override public Reader reader() { - return new StringReader(s); - } - - @Override public String read_text() { - return s; - } - - @Override public boolean exists() { - return true; - } - - @Override public LuanTable text_writer() throws IOException { - LuanWriter luanWriter = new LuanWriter() { - private final Writer out = new StringWriter(); - - public void write(LuanState luan,Object... args) throws LuanException, IOException { - for( Object obj : args ) { - out.write( luan.toString(obj) ); - } - } - - public void close() throws IOException { - s = out.toString(); - } - }; - return writer(luanWriter); - } - } - - public static final class LuanFile extends LuanIO { - public final File file; - - private LuanFile(LuanState luan,File file) throws LuanException { - this(file); - check(luan,"file:"+file.toString()); - } - - private LuanFile(File file) { - this.file = file; - } - - @Override public InputStream inputStream() throws IOException { - return new FileInputStream(file); - } - - @Override OutputStream outputStream() throws IOException { - return new FileOutputStream(file); - } - - @Override public String to_string() { - return file.toString(); - } - - @Override public String to_uri_string() { - return "file:" + file.toString(); - } - - public LuanTable child(LuanState luan,String name) throws LuanException { - return new LuanFile(luan,new File(file,name)).table(); - } - - public LuanTable children(LuanState luan) throws LuanException { - File[] files = file.listFiles(); - if( files==null ) - return null; - LuanTable list = new LuanTable(); - for( File f : files ) { - list.rawPut(list.rawLength()+1,new LuanFile(luan,f).table()); - } - return list; - } - - public LuanTable parent(LuanState luan) throws LuanException, IOException { - File parent = file.getParentFile(); - if( parent==null ) - parent = file.getCanonicalFile().getParentFile(); - return new LuanFile(luan,parent).table(); - } - - @Override public boolean exists() { - return file.exists(); - } - - public void rename_to(Object destObj) throws LuanException { - File dest = objToFile(destObj); - if( dest==null ) - throw new LuanException( "bad argument #1 to 'objToFile' (string or file table expected)" ); - if( !file.renameTo(dest) ) - throw new LuanException("couldn't rename file "+file+" to "+dest); - } - - public LuanTable canonical(LuanState luan) throws LuanException, IOException { - return new LuanFile(luan,file.getCanonicalFile()).table(); - } - - public LuanTable create_temp_file(LuanState luan,String prefix,String suffix) throws LuanException, IOException { - File tmp = File.createTempFile(prefix,suffix,file); - return new LuanFile(luan,tmp).table(); - } - - public void delete() throws LuanException { - if( file.exists() ) - delete(file); - } - - private static void delete(File file) throws LuanException { - File[] children = file.listFiles(); - if( children != null ) { - for( File child : children ) { - delete(child); - } - } - if( !file.delete() ) - throw new LuanException("couldn't delete file "+file); - } - - public void mkdir() throws LuanException { - if( !file.isDirectory() ) { - if( !file.mkdirs() ) - throw new LuanException("couldn't make directory "+file); - } - } - - public void set_last_modified(long time) throws LuanException { - if( !file.setLastModified(time) ) - throw new LuanException("couldn't set_last_modified on "+file); - } - - @Override public LuanTable table() { - LuanTable tbl = super.table(); - try { - tbl.rawPut( "name", new LuanJavaFunction( - File.class.getMethod( "getName" ), file - ) ); - tbl.rawPut( "is_directory", new LuanJavaFunction( - File.class.getMethod( "isDirectory" ), file - ) ); - tbl.rawPut( "is_file", new LuanJavaFunction( - File.class.getMethod( "isFile" ), file - ) ); - tbl.rawPut( "delete", new LuanJavaFunction( - LuanFile.class.getMethod( "delete" ), this - ) ); - tbl.rawPut( "delete_on_exit", new LuanJavaFunction( - File.class.getMethod( "deleteOnExit" ), file - ) ); - tbl.rawPut( "mkdir", new LuanJavaFunction( - LuanFile.class.getMethod( "mkdir" ), this - ) ); - tbl.rawPut( "last_modified", new LuanJavaFunction( - File.class.getMethod( "lastModified" ), file - ) ); - tbl.rawPut( "set_last_modified", new LuanJavaFunction( - LuanFile.class.getMethod( "set_last_modified", Long.TYPE ), this - ) ); - tbl.rawPut( "length", new LuanJavaFunction( - File.class.getMethod( "length" ), file - ) ); - tbl.rawPut( "child", new LuanJavaFunction( - LuanFile.class.getMethod( "child", LuanState.class, String.class ), this - ) ); - tbl.rawPut( "children", new LuanJavaFunction( - LuanFile.class.getMethod( "children", LuanState.class ), this - ) ); - tbl.rawPut( "parent", new LuanJavaFunction( - LuanFile.class.getMethod( "parent", LuanState.class ), this - ) ); - tbl.rawPut( "rename_to", new LuanJavaFunction( - LuanFile.class.getMethod( "rename_to", Object.class ), this - ) ); - tbl.rawPut( "canonical", new LuanJavaFunction( - LuanFile.class.getMethod( "canonical", LuanState.class ), this - ) ); - tbl.rawPut( "create_temp_file", new LuanJavaFunction( - LuanFile.class.getMethod( "create_temp_file", LuanState.class, String.class, String.class ), this - ) ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - return tbl; - } - } - - public static LuanTable null_() { - return nullIO.table(); - } - - public static LuanTable string(String s) throws LuanException { - Utils.checkNotNull(s); - return new LuanString(s).table(); - } - - public static LuanTable file(LuanState luan,String name) throws LuanException { - File file = new File(name); - return new LuanFile(file).table(); - } - - public static LuanTable classpath(LuanState luan,String name) throws LuanException { - if( name.contains("//") ) - return null; - String path = name; - check(luan,"classpath:"+path); - URL url; - if( !path.contains("#") ) { - url = ClassLoader.getSystemResource(path); - } else { - String[] a = path.split("#"); - url = ClassLoader.getSystemResource(a[0]); - if( url==null ) { - for( int i=1; i<a.length; i++ ) { - url = ClassLoader.getSystemResource(a[0]+"/"+a[i]); - if( url != null ) { - try { - url = new URL(url,"."); - } catch(MalformedURLException e) { - throw new RuntimeException(e); - } - break; - } - } - } - } - if( url != null ) - return new LuanUrl(luan,url,null).table(); - - return null; - } - - private static LuanTable url(LuanState luan,String url,LuanTable options) throws IOException, LuanException { - return new LuanUrl(luan,new URL(url),options).table(); - } - - public static LuanTable http(LuanState luan,String path,LuanTable options) throws IOException, LuanException { - return url(luan,"http:"+path,options); - } - - public static LuanTable https(LuanState luan,String path,LuanTable options) throws IOException, LuanException { - return url(luan,"https:"+path,options); - } - - public static LuanTable luan(LuanState luan,String path) throws LuanException { - return classpath( luan, "luan/modules/" + path ); - } - - public static LuanTable stdin(LuanState luan) throws LuanException { - LuanTable io = (LuanTable)PackageLuan.require(luan,"luan:Io.luan"); - return (LuanTable)io.get(luan,"stdin"); - } - - public static LuanTable newSchemes() { - LuanTable schemes = new LuanTable(); - try { - schemes.rawPut( "null", new LuanJavaFunction(IoLuan.class.getMethod("null_"),null) ); - add( schemes, "string", String.class ); - add( schemes, "file", LuanState.class, String.class ); - add( schemes, "classpath", LuanState.class, String.class ); - add( schemes, "socket", String.class ); - add( schemes, "http", LuanState.class, String.class, LuanTable.class ); - add( schemes, "https", LuanState.class, String.class, LuanTable.class ); - add( schemes, "luan", LuanState.class, String.class ); - add( schemes, "stdin", LuanState.class ); - add( schemes, "os", LuanState.class, String.class, LuanTable.class ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - return schemes; - } - - private static LuanTable schemes(LuanState luan) throws LuanException { - LuanTable t = (LuanTable)PackageLuan.loaded(luan).rawGet("luan:Io.luan"); - if( t == null ) - return newSchemes(); - t = (LuanTable)t.get(luan,"schemes"); - if( t == null ) - return newSchemes(); - return t; - } - - public static LuanTable uri(LuanState luan,String name,LuanTable options) throws LuanException { - int i = name.indexOf(':'); - if( i == -1 ) - throw new LuanException( "invalid Io.uri name '"+name+"', missing scheme" ); - String scheme = name.substring(0,i); - String location = name.substring(i+1); - LuanTable schemes = schemes(luan); - LuanFunction opener = (LuanFunction)schemes.get(luan,scheme); - if( opener == null ) - throw new LuanException( "invalid scheme '"+scheme+"' in '"+name+"'" ); - return (LuanTable)Luan.first(opener.call(luan,new Object[]{location,options})); - } - - public static final class LuanSocket extends LuanIO { - public final Socket socket; - - private LuanSocket(String host,int port) throws LuanException { - try { - this.socket = new Socket(host,port); - } catch(IOException e) { - throw new LuanException(e.toString()); - } - } - - private LuanSocket(Socket socket) { - this.socket = socket; - } - - @Override public InputStream inputStream() throws IOException { - return socket.getInputStream(); - } - - @Override OutputStream outputStream() throws IOException { - return socket.getOutputStream(); - } - - @Override public String to_string() { - return socket.toString(); - } - - @Override public String to_uri_string() { - throw new UnsupportedOperationException(); - } - } - - public static LuanTable socket(String name) throws LuanException, IOException { - int i = name.indexOf(':'); - if( i == -1 ) - throw new LuanException( "invalid socket '"+name+"', format is: <host>:<port>" ); - String host = name.substring(0,i); - String portStr = name.substring(i+1); - int port = Integer.parseInt(portStr); - return new LuanSocket(host,port).table(); - } - - public static LuanFunction socket_server(int port) throws IOException { - final ServerSocket ss = new ServerSocket(port); - return new LuanFunction() { - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - try { - if( args.length > 0 ) { - if( args.length > 1 || !"close".equals(args[0]) ) - throw new LuanException( "the only argument allowed is 'close'" ); - ss.close(); - return null; - } - return new LuanSocket(ss.accept()).table(); - } catch(IOException e) { - throw new LuanException(e); - } - } - }; - } - - - public static final class LuanOs extends LuanIO { - private final String cmd; - private final Process proc; - - private LuanOs(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { - this.cmd = cmd; - File dir = null; - if( options != null ) { - Map map = options.asMap(luan); - Object obj = map.remove("dir"); - dir = objToFile(obj); - if( dir==null ) - throw new LuanException( "bad option 'dir' (string or file table expected)" ); - if( !map.isEmpty() ) - throw new LuanException( "unrecognized options: "+map ); - } - this.proc = Runtime.getRuntime().exec(cmd,null,dir); - } - - @Override public InputStream inputStream() throws IOException { - return proc.getInputStream(); - } - - @Override OutputStream outputStream() throws IOException { - return proc.getOutputStream(); - } - - @Override public String to_string() { - return proc.toString(); - } - - @Override public String to_uri_string() { - throw new UnsupportedOperationException(); - } - - @Override public boolean exists() { - return true; - } - - public void wait_for() - throws IOException, LuanException - { - try { - proc.waitFor(); - } catch(InterruptedException e) { - throw new RuntimeException(e); - } - int exitVal = proc.exitValue(); - if( exitVal != 0 ) { - Reader err = new InputStreamReader(proc.getErrorStream()); - String error = "error in: "+cmd+"\n"+Utils.readAll(err); - err.close(); - throw new LuanException(error); - } - } - - @Override public String read_text() throws IOException, LuanException { - String s = super.read_text(); - wait_for(); - return s; - } - - @Override public LuanTable table() { - LuanTable tbl = super.table(); - try { - tbl.rawPut( "wait_for", new LuanJavaFunction( - LuanOs.class.getMethod( "wait_for" ), this - ) ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - return tbl; - } - } - - public static LuanTable os(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { - return new LuanOs(luan,cmd,options).table(); - } - - - public static String ip(String domain) { - try { - return InetAddress.getByName(domain).getHostAddress(); - } catch(UnknownHostException e) { - return null; - } - } - - public static LuanTable my_ips() throws IOException { - LuanTable tbl = new LuanTable(); - for( Enumeration<NetworkInterface> e1 = NetworkInterface.getNetworkInterfaces(); e1.hasMoreElements(); ) { - NetworkInterface ni = e1.nextElement(); - for( Enumeration<InetAddress> e2 = ni.getInetAddresses(); e2.hasMoreElements(); ) { - InetAddress ia = e2.nextElement(); - if( ia instanceof Inet4Address ) - tbl.rawPut(ia.getHostAddress(),true); - } - } - return tbl; - } - -/* - // files maps zip name to uri - public static void zip(LuanState luan,String zipUri,LuanTable files) throws LuanException, IOException { - Object obj = uri(luan,zipUri,null).rawGet("java"); - if( !(obj instanceof LuanIO) ) - throw new LuanException("invalid uri for zip"); - LuanIO zipIo = (LuanIO)obj; - ZipOutputStream out = new ZipOutputStream(zipIo.outputStream()); - for( Map.Entry<Object,Object> entry : files.iterable(luan) ) { - obj = entry.getKey(); - if( !(obj instanceof String) ) - throw new LuanException("zip file table keys must be strings"); - String fileName = (String)obj; - obj = entry.getValue(); - if( !(obj instanceof String) ) - throw new LuanException("zip file table values must be strings"); - String uriStr = (String)obj; - out.putNextEntry(new ZipEntry(fileName)); - obj = uri(luan,uriStr,null).rawGet("java"); - if( !(obj instanceof LuanIn) ) - throw new LuanException("invalid uri for zip"); - LuanIn zipIn = (LuanIn)obj; - InputStream in = zipIn.inputStream(); - Utils.copyAll(in,out); - in.close(); - out.closeEntry(); - } - out.close(); - } -*/ - - // security - - public interface Security { - public void check(LuanState luan,String name) throws LuanException; - } - - private static String SECURITY_KEY = "Io.Security"; - - private static void check(LuanState luan,String name) throws LuanException { - Security s = (Security)luan.registry().get(SECURITY_KEY); - if( s!=null ) - s.check(luan,name); - } - - public static void setSecurity(LuanState luan,Security s) { - luan.registry().put(SECURITY_KEY,s); - } - - private void IoLuan() {} // never -}
--- a/core/src/luan/modules/JavaLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,475 +0,0 @@ -package luan.modules; - -import java.lang.reflect.Array; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Member; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; -import java.util.Map; -import java.util.HashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Collections; -import java.util.Arrays; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanException; -import luan.LuanFunction; -import luan.LuanJavaFunction; - - -public final class JavaLuan { - - public static void java(LuanState luan) throws LuanException { - check(luan,LuanException.currentSource()); - luan.java.ok = true; - } - - public static final LuanFunction javaFn; - static { - try { - javaFn = new LuanJavaFunction(JavaLuan.class.getMethod("java",LuanState.class),null); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - private static void checkJava(LuanState luan) throws LuanException { - if( !luan.java.ok ) - throw new LuanException("Java isn't allowed"); - } - - static final Object FAIL = new Object(); - - public static Object __index(LuanState luan,Object obj,Object key,boolean canReturnFail) throws LuanException { - checkJava(luan); - Class cls; - if( obj instanceof Static ) { - Static st = (Static)obj; - cls = st.cls; - if( key instanceof String ) { - String name = (String)key; - if( "class".equals(name) ) { - return cls; - } else if( "new".equals(name) ) { - Constructor[] constructors = cls.getConstructors(); - if( constructors.length > 0 ) { - if( constructors.length==1 ) { - return new LuanJavaFunction(constructors[0],null); - } else { - List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); - for( Constructor constructor : constructors ) { - fns.add(new LuanJavaFunction(constructor,null)); - } - return new AmbiguousJavaFunction(fns); - } - } -/* - } else if( "assert".equals(name) ) { - return new LuanJavaFunction(assertClass,new AssertClass(cls)); -*/ - } else if( "luan_proxy".equals(name) ) { - return new LuanJavaFunction(luan_proxyMethod,st); - } else { - List<Member> members = getStaticMembers(cls,name); - if( !members.isEmpty() ) { - return member(null,members); - } - } - } - } else { - cls = obj.getClass(); - if( cls.isArray() ) { - if( "length".equals(key) ) { - return Array.getLength(obj); - } - Integer i = Luan.asInteger(key); - if( i != null ) { - return Array.get(obj,i); - } -// throw new LuanException(luan,"invalid member '"+key+"' for java array: "+obj); - } else if( key instanceof String ) { - String name = (String)key; - if( "instanceof".equals(name) ) { - return new LuanJavaFunction(instanceOf,new InstanceOf(obj)); - } else { - List<Member> members = getMembers(cls,name); - if( !members.isEmpty() ) { - return member(obj,members); - } - } - } - } -//System.out.println("invalid member '"+key+"' for java object: "+obj); - if( canReturnFail ) - return FAIL; - throw new LuanException( "invalid index '"+key+"' for java "+cls ); - } - - private static Object member(Object obj,List<Member> members) throws LuanException { - try { - if( members.size()==1 ) { - Member member = members.get(0); - if( member instanceof Static ) { - return member; - } else if( member instanceof Field ) { - Field field = (Field)member; - Object rtn = field.get(obj); - return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn; - } else { - Method method = (Method)member; - return new LuanJavaFunction(method,obj); - } - } else { - List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); - for( Member member : members ) { - Method method = (Method)member; - fns.add(new LuanJavaFunction(method,obj)); - } - return new AmbiguousJavaFunction(fns); - } - } catch(IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public static void __new_index(LuanState luan,Object obj,Object key,Object value) throws LuanException { - checkJava(luan); - Class cls; - if( obj instanceof Static ) { - Static st = (Static)obj; - cls = st.cls; - if( key instanceof String ) { - String name = (String)key; - List<Member> members = getStaticMembers(cls,name); - if( !members.isEmpty() ) { - if( members.size() != 1 ) - throw new RuntimeException("not field '"+name+"' of "+obj); - setMember(obj,members,value); - return; - } - } -// throw new LuanException(luan,"invalid member '"+key+"' for: "+obj); - } else { - cls = obj.getClass(); - if( cls.isArray() ) { - Integer i = Luan.asInteger(key); - if( i != null ) { - Array.set(obj,i,value); - return; - } -// throw new LuanException(luan,"invalid member '"+key+"' for java array: "+obj); - } else if( key instanceof String ) { - String name = (String)key; - List<Member> members = getMembers(cls,name); - if( !members.isEmpty() ) { - if( members.size() != 1 ) - throw new RuntimeException("not field '"+name+"' of "+obj); - setMember(obj,members,value); - return; - } - } - } - throw new LuanException( "invalid index for java "+cls ); - } - - private static void setMember(Object obj,List<Member> members,Object value) { - Field field = (Field)members.get(0); - try { - try { - field.set(obj,value); - } catch(IllegalArgumentException e) { - Class cls = field.getType(); - if( value instanceof Number ) { - Number n = (Number)value; - if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) { - int r = n.intValue(); - if( r==n.doubleValue() ) { - field.setInt(obj,r); - return; - } - } - } - throw e; - } - } catch(IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public static boolean privateAccess = false; - private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>(); - - private static synchronized List<Member> getMembers(Class cls,String name) { - Map<String,List<Member>> clsMap = memberMap.get(cls); - if( clsMap == null ) { - clsMap = new HashMap<String,List<Member>>(); - for( Class c : cls.getClasses() ) { - String s = c.getSimpleName(); - List<Member> list = new ArrayList<Member>(); - clsMap.put(s,list); - list.add(new Static(c)); - } - for( Field field : cls.getFields() ) { - String s = field.getName(); - try { - if( !cls.getField(s).equals(field) ) - continue; // not accessible - } catch(NoSuchFieldException e) { - throw new RuntimeException(e); - } - List<Member> list = new ArrayList<Member>(); - clsMap.put(s,list); - list.add(field); - } - for( Method method : cls.getMethods() ) { - String s = method.getName(); - List<Member> list = clsMap.get(s); - if( list == null || !(list.get(0) instanceof Method) ) { - list = new ArrayList<Member>(); - clsMap.put(s,list); - } - list.add(method); - } - if( privateAccess ) { - for( Method method : cls.getDeclaredMethods() ) { - String s = method.getName(); - List<Member> list = clsMap.get(s); - if( list == null ) { - list = new ArrayList<Member>(); - clsMap.put(s,list); - } else if( !(list.get(0) instanceof Method) ) - continue; - if( !list.contains(method) ) { - list.add(method); - } - } - for( Field field : cls.getDeclaredFields() ) { - String s = field.getName(); - List<Member> list = clsMap.get(s); - if( list == null ) { - list = new ArrayList<Member>(); - clsMap.put(s,list); - list.add(field); - } - } - } - for( List<Member> members : clsMap.values() ) { - for( Member m : members ) { - if( m instanceof AccessibleObject ) - ((AccessibleObject)m).setAccessible(true); - } - } - memberMap.put(cls,clsMap); - } - List<Member> rtn = clsMap.get(name); - if( rtn==null ) - rtn = Collections.emptyList(); - return rtn; - } - - private static synchronized List<Member> getStaticMembers(Class cls,String name) { - List<Member> staticMembers = new ArrayList<Member>(); - for( Member m : getMembers(cls,name) ) { - if( Modifier.isStatic(m.getModifiers()) ) - staticMembers.add(m); - } - return staticMembers; - } - - static final class Static implements Member { - final Class cls; - - Static(Class cls) { - this.cls = cls; - } - - @Override public String toString() { - return cls.toString(); - } - - @Override public Class getDeclaringClass() { - return cls.getDeclaringClass(); - } - - @Override public String getName() { - return cls.getName(); - } - - @Override public int getModifiers() { - return cls.getModifiers(); - } - - @Override public boolean isSynthetic() { - return cls.isSynthetic(); - } - - public Object luan_proxy(final LuanState luan,final LuanTable t) throws LuanException { - return Proxy.newProxyInstance( - cls.getClassLoader(), - new Class[]{cls}, - new InvocationHandler() { - public Object invoke(Object proxy,Method method, Object[] args) - throws Throwable - { - if( args==null ) - args = new Object[0]; - String name = method.getName(); - Object fnObj = t.get(luan,name); - if( fnObj == null ) - throw new NullPointerException("luan_proxy couldn't find method '"+name+"'"); - LuanFunction fn = Luan.checkFunction(fnObj); - return Luan.first(fn.call(luan,args)); - } - } - ); - } - } - private static final Method luan_proxyMethod; - static { - try { - luan_proxyMethod = Static.class.getMethod("luan_proxy",LuanState.class,LuanTable.class); - luan_proxyMethod.setAccessible(true); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - public static Static load(LuanState luan,String name) throws LuanException { - checkJava(luan); - Class cls; - try { - cls = Class.forName(name); - } catch(ClassNotFoundException e) { - try { - cls = Thread.currentThread().getContextClassLoader().loadClass(name); - } catch(ClassNotFoundException e2) { - return null; - } - } - return new Static(cls); - } - - private static class AmbiguousJavaFunction extends LuanFunction { - private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>(); - - AmbiguousJavaFunction(List<LuanJavaFunction> fns) { - for( LuanJavaFunction fn : fns ) { - Integer n = fn.getParameterTypes().length; - List<LuanJavaFunction> list = fnMap.get(n); - if( list==null ) { - list = new ArrayList<LuanJavaFunction>(); - fnMap.put(n,list); - } - list.add(fn); - } - } - - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - for( LuanJavaFunction fn : fnMap.get(args.length) ) { - try { - return fn.rawCall(luan,args); - } catch(IllegalArgumentException e) {} - } - throw new LuanException("no method matched args: "+Arrays.asList(args)); - } - } - - private static class InstanceOf { - private final Object obj; - - InstanceOf(Object obj) { - this.obj = obj; - } - - public boolean instanceOf(Static st) { - return st.cls.isInstance(obj); - } - } - private static final Method instanceOf; - static { - try { - instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class); - instanceOf.setAccessible(true); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - -/* - private static class AssertClass { - private final Class cls; - - AssertClass(Class cls) { - this.cls = cls; - } - - public Object assertClass(LuanState luan,Object v) throws LuanException { - if( !cls.isInstance(v) ) { - String got = v.getClass().getSimpleName(); - String expected = cls.getSimpleName(); - throw new LuanException(luan,"bad argument #1 ("+expected+" expected, got "+got+")"); - } - return v; - } - } - private static final Method assertClass; - static { - try { - assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class); - assertClass.setAccessible(true); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - - public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException { - return Proxy.newProxyInstance( - st.cls.getClassLoader(), - new Class[]{st.cls}, - new InvocationHandler() { - public Object invoke(Object proxy,Method method, Object[] args) - throws Throwable - { - if( args==null ) - args = new Object[0]; - String name = method.getName(); - Object fnObj = t.get(name); - if( fnObj==null && base!=null ) - return method.invoke(base,args); - LuanFunction fn = luan.checkFunction(fnObj); - return Luan.first(luan.call(fn,name,args)); - } - } - ); - } -*/ - - - - // security - - public interface Security { - public void check(LuanState luan,String name) throws LuanException; - } - - private static String SECURITY_KEY = "Java.Security"; - - private static void check(LuanState luan,String name) throws LuanException { - Security s = (Security)luan.registry().get(SECURITY_KEY); - if( s!=null ) - s.check(luan,name); - } - - public static void setSecurity(LuanState luan,Security s) { - luan.registry().put(SECURITY_KEY,s); - } - -}
--- a/core/src/luan/modules/Luan.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -java() -local BasicLuan = require "java:luan.modules.BasicLuan" - -local M = {} - -M.assert_binary = BasicLuan.assert_binary -M.assert_boolean = BasicLuan.assert_boolean -M.assert_function = BasicLuan.assert_function -M.assert_integer = BasicLuan.assert_integer -M.assert_long = BasicLuan.assert_long -M.assert_number = BasicLuan.assert_number -M.assert_string = BasicLuan.assert_string -M.assert_table = BasicLuan.assert_table -M.get_metatable = BasicLuan.get_metatable -M.ipairs = BasicLuan.ipairs -M.load = BasicLuan.load -M.load_file = BasicLuan.load_file -M.new_error = BasicLuan.new_error -M.pairs = BasicLuan.pairs -M.pcall = BasicLuan.pcall -M.range = BasicLuan.range -M.raw_equal = BasicLuan.raw_equal -M.raw_get = BasicLuan.raw_get -M.raw_len = BasicLuan.raw_len -M.raw_set = BasicLuan.raw_set -M.set_metatable = BasicLuan.set_metatable -M.to_string = BasicLuan.to_string -M.try = BasicLuan.try_ -M.type = BasicLuan.type -M.values = BasicLuan.values - -function M.do_file(uri) - return M.load_file(uri)() -end - -M.VERSION = M.do_file "classpath:luan/version.luan" - -function M.error(message) - M.new_error(message).throw() -end - -function M.assert(v,message) - return v or M.error(message or "assertion failed!") -end - -function M.eval(s,source_name) - return M.load( "return "..s, source_name or "eval" )() -end - -return M
--- a/core/src/luan/modules/Math.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -java() -local MathLuan = require "java:luan.modules.MathLuan" -local JavaMath = require "java:java.lang.Math" -local Integer = require "java:java.lang.Integer" -local Double = require "java:java.lang.Double" - - -local M = {} - -M.abs = MathLuan.abs -M.acos = MathLuan.acos -M.asin = MathLuan.asin -M.atan = MathLuan.atan -M.atan2 = MathLuan.atan2 -M.ceil = MathLuan.ceil -M.cos = MathLuan.cos -M.cosh = MathLuan.cosh -M.deg = MathLuan.deg -M.exp = MathLuan.exp -M.floor = MathLuan.floor -M.fmod = MathLuan.fmod -M.huge = Double.POSITIVE_INFINITY -M.log = MathLuan.log -M.max = MathLuan.max -M.max_integer = Integer.MAX_VALUE -M.min = MathLuan.min -M.min_integer = Integer.MIN_VALUE -M.modf = MathLuan.modf -M.pi = JavaMath.PI -M.rad = MathLuan.rad -M.random = MathLuan.random -M.sin = MathLuan.sin -M.sqrt = MathLuan.sqrt -M.tan = MathLuan.tan - -return M
--- a/core/src/luan/modules/MathLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -package luan.modules; - -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanException; - - -public final class MathLuan { - - public static double abs(double x) { - return Math.abs(x); - } - - public static double acos(double x) { - return Math.acos(x); - } - - public static double asin(double x) { - return Math.asin(x); - } - - public static double atan(double x) { - return Math.atan(x); - } - - public static double atan2(double y,double x) { - return Math.atan2(y,x); - } - - public static double ceil(double x) { - return Math.ceil(x); - } - - public static double cos(double x) { - return Math.cos(x); - } - - public static double cosh(double x) { - return Math.cosh(x); - } - - public static double deg(double x) { - return Math.toDegrees(x); - } - - public static double exp(double x) { - return Math.exp(x); - } - - public static double floor(double x) { - return Math.floor(x); - } - - public static double fmod(double x,double y) { - return x % y; - } - - public static double log(double x,Double base) { - return base==null ? Math.log(x) : Math.log(x)/Math.log(base); - } - - public static double min(double x,double... a) { - for( double d : a ) { - if( x > d ) - x = d; - } - return x; - } - - public static double max(double x,double... a) { - for( double d : a ) { - if( x < d ) - x = d; - } - return x; - } - - public static double[] modf(double x) { - double i = (int)x; - return new double[]{i,x-i}; - } - - public static double rad(double x) { - return Math.toRadians(x); - } - - public static double random(Integer m,Integer n) { - if( m==null ) - return Math.random(); - if( n==null ) - return Math.floor(m*Math.random()) + 1; - return Math.floor((n-m+1)*Math.random()) + m; - } - - public static double sin(double x) { - return Math.sin(x); - } - - public static double sqrt(double x) { - return Math.sqrt(x); - } - - public static double tan(double x) { - return Math.tan(x); - } - - public static String long_to_string(long i,int radix) { - return Long.toString(i,radix); - } - -}
--- a/core/src/luan/modules/Number.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -java() -local BasicLuan = require "java:luan.modules.BasicLuan" -local MathLuan = require "java:luan.modules.MathLuan" - - -local M = {} - -M.double = BasicLuan.assert_double -M.integer = BasicLuan.assert_integer -M.long = BasicLuan.assert_long -M.long_to_string = MathLuan.long_to_string -M.type = BasicLuan.number_type - -return M
--- a/core/src/luan/modules/Package.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -java() -local PackageLuan = require "java:luan.modules.PackageLuan" - -local M = {} - -M.loaded = PackageLuan.loaded() -M.load = PackageLuan.load - -return M
--- a/core/src/luan/modules/PackageLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -package luan.modules; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanJavaFunction; -import luan.LuanException; - - -public final class PackageLuan { - - public static final LuanFunction requireFn; - static { - try { - requireFn = new LuanJavaFunction(PackageLuan.class.getMethod("require",LuanState.class,String.class),null); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - public static LuanTable loaded(LuanState luan) { - LuanTable tbl = (LuanTable)luan.registry().get("Package.loaded"); - if( tbl == null ) { - tbl = new LuanTable(); - luan.registry().put("Package.loaded",tbl); - } - return tbl; - } - - public static Object require(LuanState luan,String modName) throws LuanException { - Object mod = load(luan,modName); - if( mod==null ) - throw new LuanException( "module '"+modName+"' not found" ); - return mod; - } - - public static Object load(LuanState luan,String modName) throws LuanException { - LuanTable loaded = loaded(luan); - Object mod = loaded.rawGet(modName); - if( mod == null ) { - if( modName.startsWith("java:") ) { - mod = JavaLuan.load(luan,modName.substring(5)); - } else { - String src = read(luan,modName); - if( src == null ) - return null; - LuanFunction loader = Luan.load(src,modName); - mod = Luan.first( - loader.call(luan,new Object[]{modName}) - ); - if( mod == null ) { - mod = loaded.rawGet(modName); - if( mod != null ) - return mod; - throw new LuanException( "module '"+modName+"' returned nil" ); - } - } - loaded.rawPut(modName,mod); - } - return mod; - } - - static String read(LuanState luan,String uri) throws LuanException { - LuanTable t = IoLuan.uri(luan,uri,null); - if( t == null ) - return null; - LuanFunction existsFn = (LuanFunction)t.get(luan,"exists"); - boolean exists = (Boolean)Luan.first(existsFn.call(luan)); - if( !exists ) - return null; - LuanFunction reader = (LuanFunction)t.get(luan,"read_text"); - return (String)Luan.first(reader.call(luan)); - } - -}
--- a/core/src/luan/modules/Parsers.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -java() -local BBCode = require "java:luan.modules.parsers.BBCode" -local Csv = require "java:luan.modules.parsers.Csv" -local Theme = require "java:luan.modules.parsers.Theme" -local Json = require "java:luan.modules.parsers.Json" - -local M = {} - -M.bbcode_to_html = BBCode.toHtml -M.bbcode_to_text = BBCode.toText -M.csv_to_list = Csv.toList -M.json_parse = Json.parse -M.theme_to_luan = Theme.toLuan - -return M
--- a/core/src/luan/modules/Rpc.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -java() -local RpcLuan = require "java:luan.modules.RpcLuan" -local Luan = require "luan:Luan.luan" -local error = Luan.error -local set_metatable = Luan.set_metatable or error() -local try = Luan.try or error() -local Io = require "luan:Io.luan" -local Thread = require "luan:Thread.luan" -local Logging = require "luan:logging/Logging.luan" -- external dependency -local logger = Logging.logger "Rpc" - - -local M = {} - -M.port = 9101 - -M.call = RpcLuan.call -- Rpc.call(socket,fn_name,...) - -M.functions = {} - -function M.respond(socket,fns) - RpcLuan.respond( socket, fns or M.functions ) -end - -function M.remote_socket(socket_uri) - local mt = {} - function mt.__index(_,key) - return function(...) - local socket = Io.uri(socket_uri) - return M.call(socket,key,...) - end - end - local t = {} - set_metatable(t,mt) - return t -end - -function M.remote(domain) - local socket = "socket:" .. domain .. ":" .. M.port - return M.remote_socket(socket) -end - -function M.serve(port,fns) - local server = Io.socket_server(port or M.port) - while true do - try { - function() - local socket = server() - local function respond() - try { - function() - M.respond(socket,fns) - end - catch = function(e) - logger.error(e) - end - } - end - Thread.fork(respond) - end - catch = function(e) - logger.error(e) - end - } - end -end - -return M
--- a/core/src/luan/modules/RpcLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,310 +0,0 @@ -package luan.modules; - -import java.io.InputStream; -import java.io.OutputStream; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.InputStreamReader; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.EOFException; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.util.Set; -import java.util.IdentityHashMap; -import java.util.Collections; -import java.util.Map; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanException; -import luan.LuanMethod; -import luan.modules.parsers.Json; -import luan.modules.parsers.ParseException; - - -public final class RpcLuan { - private static final int NIL = 0; - private static final int STRING = 1; - private static final int BOOLEAN = 2; - private static final int NUMBER = 3; - private static final int BINARY = 4; - private static final int TABLE = 5; - private static final int IO = 6; - private static final int LONG = 7; - - @LuanMethod public static Object[] call(LuanState luan,LuanTable socketTbl,String fnName,Object... args) - throws LuanException, IOException - { - IoLuan.LuanSocket luanSocket = (IoLuan.LuanSocket)socketTbl.rawGet("java"); - Socket socket = luanSocket.socket; - InputStream in = new BufferedInputStream(socket.getInputStream()); - OutputStream out = new BufferedOutputStream(socket.getOutputStream()); - Close close = new Close(); - try { - writeString(out,fnName); - writeObjs(out,luan,args); - out.flush(); - socket.shutdownOutput(); - boolean ok = readBoolean(in); - if( ok ) { - return readObjs(in,luan,close); - } else { - String msg = readString(in); - throw new LuanException(msg); - } - } finally { - if( close.b) { - socket.close(); - } - } - } - - public static void respond(LuanState luan,LuanTable socketTbl,LuanTable fns) - throws IOException, LuanException - { - IoLuan.LuanSocket luanSocket = (IoLuan.LuanSocket)socketTbl.rawGet("java"); - Socket socket = luanSocket.socket; - InputStream in = new BufferedInputStream(socket.getInputStream()); - OutputStream out = new BufferedOutputStream(socket.getOutputStream()); - try { - Object[] rtn; - try { - String fnName = readString(in); - Object[] args = readObjs(in,luan,null); - LuanFunction fn = (LuanFunction)fns.get(luan,fnName); - if( fn == null ) - throw new LuanException( "function not found: " + fnName ); - rtn = Luan.array(fn.call(luan,args)); - } catch(LuanException e) { - writeBoolean(out,false); - writeString(out,e.getFullMessage()); - out.flush(); - return; - } - writeBoolean(out,true); - writeObjs(out,luan,rtn); - out.flush(); - } finally { - socket.close(); - } - } - - private static void writeObjs(OutputStream out,LuanState luan,Object[] a) throws IOException, LuanException { - IoLuan.LuanIn luanIn = null; - writeInt(out,a.length); - for( Object obj : a ) { - if( obj instanceof LuanTable ) { - LuanTable tbl = (LuanTable)obj; - Object java = tbl.rawGet("java"); - if( java instanceof IoLuan.LuanIn ) { - if( luanIn != null ) - throw new LuanException("can't have multiple IO params"); - luanIn = (IoLuan.LuanIn)java; - out.write(IO); - continue; - } - } - writeObj(out,luan,obj); - } - if( luanIn != null ) { - InputStream in = luanIn.inputStream(); - Utils.copyAll(in,out); - } - } - - private static Object[] readObjs(InputStream in,LuanState luan,Close close) throws IOException, LuanException { - int n = readInt(in); - Object[] rtn = new Object[n]; - for( int i=0; i<n; i++ ) { - rtn[i] = readObj(in,luan,close); - } - return rtn; - } - - private static void writeObj(OutputStream out,LuanState luan,Object obj) throws IOException, LuanException { - if( obj == null ) { - out.write(NIL); - } - else if( obj instanceof String ) { - out.write(STRING); - writeString(out,(String)obj); - } - else if( obj instanceof Boolean ) { - out.write(BOOLEAN); - writeBoolean(out,(Boolean)obj); - } - else if( obj instanceof Long ) { - out.write(LONG); - writeString(out,obj.toString()); - } - else if( obj instanceof Number ) { - out.write(NUMBER); - writeString(out,obj.toString()); - } - else if( obj instanceof byte[] ) { - byte[] a = (byte[])obj; - out.write(BINARY); - writeInt(out,a.length); - out.write(a); - } - else if( obj instanceof LuanTable ) { - out.write(TABLE); -// String s = pickle( luan, obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) ); - String s = Json.toString(obj); - writeString(out,s); - } - else - throw new LuanException( "invalid type: " + obj.getClass() ); - } - - private static Object readObj(InputStream in,LuanState luan,Close close) throws IOException, LuanException { - int type = in.read(); - switch(type) { - case NIL: - return null; - case STRING: - return readString(in); - case BOOLEAN: - return readBoolean(in); - case LONG: - return Long.valueOf(readString(in)); - case NUMBER: - return Double.valueOf(readString(in)); - case BINARY: - return readBinary(in,readInt(in)); - case TABLE: - String s = readString(in); -/* - LuanFunction fn = Luan.load("return "+s,"rpc-reader"); - return fn.call(luan); -*/ - try { - return Json.parse(s); - } catch(ParseException e) { - throw new LuanException(e); - } - case IO: - return new LuanInputStream(in,close).table(); - default: - throw new LuanException( "invalid type: " + type ); - } - } - - private static Boolean readBoolean(InputStream in) throws IOException { - return Boolean.valueOf(readString(in)); - } - - private static String readString(InputStream in) throws IOException { - int len = readInt(in); - byte[] a = readBinary(in,len); - return new String(a,StandardCharsets.UTF_8); - } - - private static int readInt(InputStream in) throws IOException { - int ch1 = in.read(); - int ch2 = in.read(); - int ch3 = in.read(); - int ch4 = in.read(); - if ((ch1 | ch2 | ch3 | ch4) < 0) - throw new EOFException(); - return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); - } - - private static byte[] readBinary(InputStream in,int size) throws IOException { - byte[] a = new byte[size]; - int i = 0; - while( i < size ) { - int n = in.read(a,i,size-i); - if( n == -1 ) - throw new EOFException(); - i += n; - } - return a; - } - - private static void writeBoolean(OutputStream out,Boolean b) throws IOException { - writeString(out,b.toString()); - } - - private static void writeString(OutputStream out,String s) throws IOException { - byte[] a = s.getBytes(StandardCharsets.UTF_8); - writeInt(out,a.length); - out.write(a); - } - - private static void writeInt(OutputStream out,int v) throws IOException { - out.write((v >>> 24) & 0xFF); - out.write((v >>> 16) & 0xFF); - out.write((v >>> 8) & 0xFF); - out.write((v >>> 0) & 0xFF); - } - -/* - private static String pickle(LuanState luan,Object obj,Set<LuanTable> set) throws LuanException { - if( obj == null ) - return "nil"; - if( obj instanceof Boolean ) - return obj.toString(); - if( obj instanceof Number ) - return Luan.toString((Number)obj); - if( obj instanceof String ) - return "\"" + Luan.stringEncode((String)obj) + "\""; - if( obj instanceof LuanTable ) { - LuanTable tbl = (LuanTable)obj; - if( !set.add(tbl) ) { - throw new LuanException( "circular reference in table" ); - } - StringBuilder sb = new StringBuilder(); - sb.append( "{" ); - for( Map.Entry<Object,Object> entry : tbl.iterable(luan) ) { - sb.append( "[" ); - sb.append( pickle(luan,entry.getKey(),set) ); - sb.append( "]=" ); - sb.append( pickle(luan,entry.getValue(),set) ); - sb.append( ", " ); - } - sb.append( "}" ); - return sb.toString(); - } - throw new LuanException( "invalid type: " + obj.getClass() ); - } -*/ - - private static class Close { - boolean b = true; - } - - private static class LuanInputStream extends IoLuan.LuanIn { - private final InputStream in; - private final boolean close; - - public LuanInputStream(InputStream in,Close close) { - this.in = in; - this.close = close!=null && close.b; - if(this.close) close.b = false; - } - - @Override public InputStream inputStream() { - return new FilterInputStream(in) { - @Override public void close() throws IOException { - if(close) super.close(); - } - }; - } - - @Override public String to_string() { - return "<input_stream>"; - } - - @Override public String to_uri_string() { - throw new UnsupportedOperationException(); - } - - @Override public boolean exists() { - return true; - } - }; - -}
--- a/core/src/luan/modules/String.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -java() -local StringLuan = require "java:luan.modules.StringLuan" -local Pattern = require "java:java.util.regex.Pattern" - -local M = {} - -M.char = StringLuan.char_ -M.concat = StringLuan.concat -M.encode = StringLuan.encode -M.find = StringLuan.find -M.format = StringLuan.format -M.gmatch = StringLuan.gmatch -M.gsub = StringLuan.gsub -M.literal = Pattern.quote -M.lower = StringLuan.lower -M.match = StringLuan.match -M.matches = StringLuan.matches -M.rep = StringLuan.rep -M.reverse = StringLuan.reverse -M.split = StringLuan.split -M.sub = StringLuan.sub -M.to_binary = StringLuan.to_binary -M.to_number = StringLuan.to_number -M.trim = StringLuan.trim -M.unicode = StringLuan.unicode -M.upper = StringLuan.upper - -return M
--- a/core/src/luan/modules/StringLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,247 +0,0 @@ -package luan.modules; - -import java.util.Arrays; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanException; -import luan.LuanMethod; - - -public final class StringLuan { - - static int start(String s,int i) { - int len = s.length(); - return i==0 ? 0 : i > 0 ? Math.min(i-1,len) : Math.max(len+i,0); - } - - static int start(String s,Integer i,int dflt) { - return i==null ? dflt : start(s,i); - } - - static int end(String s,int i) { - int len = s.length(); - return i==0 ? 0 : i > 0 ? Math.min(i,len) : Math.max(len+i+1,0); - } - - static int end(String s,Integer i,int dflt) { - return i==null ? dflt : end(s,i); - } - - @LuanMethod public static Integer[] unicode(String s,Integer i,Integer j) throws LuanException { - Utils.checkNotNull(s); - int start = start(s,i,1); - int end = end(s,j,start+1); - Integer[] chars = new Integer[end-start]; - for( int k=0; k<chars.length; k++ ) { - chars[k] = (int)s.charAt(start+k); - } - return chars; - } - - public static String char_(int... chars) { - char[] a = new char[chars.length]; - for( int i=0; i<chars.length; i++ ) { - a[i] = (char)chars[i]; - } - return new String(a); - } - - @LuanMethod public static byte[] to_binary(String s) { - return s.getBytes(); - } - - public static String lower(String s) throws LuanException { - Utils.checkNotNull(s); - return s.toLowerCase(); - } - - public static String upper(String s) throws LuanException { - Utils.checkNotNull(s); - return s.toUpperCase(); - } - - public static String trim(String s) throws LuanException { - Utils.checkNotNull(s); - return s.trim(); - } - - public static String reverse(String s) throws LuanException { - Utils.checkNotNull(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) throws LuanException { - Utils.checkNotNull(s); - int start = start(s,i); - int end = end(s,j,s.length()); - return s.substring(start,end); - } - - @LuanMethod public static Object[] 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 Integer[]{i+1,i+pattern.length()}; - } - Matcher m = Pattern.compile(pattern).matcher(s); - if( !m.find(start) ) - return null; - int n = m.groupCount(); - Object[] rtn = new Object[2+n]; - rtn[0] = m.start() + 1; - rtn[1] = m.end(); - for( int i=0; i<n; i++ ) { - rtn[2+i] = m.group(i+1); - } - return rtn; - } - - @LuanMethod 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; - 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+1); - } - return rtn; - } - - public static LuanFunction gmatch(String s,String pattern) throws LuanException { - Utils.checkNotNull(s); - final Matcher m = Pattern.compile(pattern).matcher(s); - return new LuanFunction() { - @Override public Object call(LuanState luan,Object[] args) { - if( !m.find() ) - return null; - final int n = m.groupCount(); - if( n == 0 ) - return m.group(); - String[] rtn = new String[n]; - for( int i=0; i<n; i++ ) { - rtn[i] = m.group(i+1); - } - return rtn; - } - }; - } - - @LuanMethod public static Object[] gsub(LuanState luan,String s,String pattern,Object repl,Integer n) throws LuanException { - Utils.checkNotNull(s); - 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(), i }; - } - if( repl instanceof LuanTable ) { - LuanTable t = (LuanTable)repl; - int i = 0; - StringBuffer sb = new StringBuffer(); - while( i<max && m.find() ) { - String match = m.groupCount()==0 ? m.group() : m.group(1); - Object val = t.get(luan,match); - if( val != null ) { - String replacement = luan.toString(val); - m.appendReplacement(sb,replacement); - } - i++; - } - m.appendTail(sb); - return new Object[]{ sb.toString(), i }; - } - if( repl instanceof LuanFunction ) { - LuanFunction fn = (LuanFunction)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 String[]{m.group()}; - } else { - args = new String[count]; - for( int j=0; j<count; j++ ) { - args[j] = m.group(j+1); - } - } - Object val = Luan.first( fn.call(luan,args) ); - if( val != null ) { - String replacement = luan.toString(val); - m.appendReplacement(sb,replacement); - } - i++; - } - m.appendTail(sb); - return new Object[]{ sb.toString(), i }; - } - throw new LuanException( "bad argument #3 to 'gsub' (string/function/table expected)" ); - } - - // note - String.format() is too stupid to convert between ints and floats. - public static String format(String format,Object... args) { - return String.format(format,args); - } - - public static String concat(LuanState luan,Object... args) throws LuanException { - StringBuilder sb = new StringBuilder(); - for( Object arg : args ) { - sb.append( luan.toString(arg) ); - } - return sb.toString(); - } - - public static String encode(String s) { - return Luan.stringEncode(s); - } - - public static Number to_number(String s,Integer base) throws LuanException { - Utils.checkNotNull(s); - try { - if( base == null ) { - return Double.valueOf(s); - } else { - return Long.valueOf(s,base); - } - } catch(NumberFormatException e) {} - return null; - } - - public static boolean matches(String s,String pattern) throws LuanException { - Utils.checkNotNull(s); - return Pattern.compile(pattern).matcher(s).find(); - } - - public static LuanTable split(String s,String pattern) throws LuanException { - Utils.checkNotNull(s); - return new LuanTable(Arrays.asList(s.split(pattern))); - } - -}
--- a/core/src/luan/modules/Table.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -java() -local TableLuan = require "java:luan.modules.TableLuan" - -local M = {} - -M.clear = TableLuan.clear -M.concat = TableLuan.concat -M.copy = TableLuan.copy -M.insert = TableLuan.insert -M.new_property_table = TableLuan.new_property_table -M.pack = TableLuan.pack -M.remove = TableLuan.remove -M.sort = TableLuan.sort -M.unpack = TableLuan.unpack - - -local Luan = require "luan:Luan.luan" -local pairs = Luan.pairs - -function M.is_empty(t) - return pairs(t)() == nil -end - -return M
--- a/core/src/luan/modules/TableLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -package luan.modules; - -import java.util.Comparator; -import java.util.List; -import java.util.ArrayList; -import java.util.Arrays; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanException; -import luan.LuanRuntimeException; -import luan.LuanMethod; -import luan.LuanPropertyMeta; - - -public final class TableLuan { - - public static String concat(LuanState luan,LuanTable list,String sep,Integer i,Integer j) throws LuanException { - int first = i==null ? 1 : i; - int last = j==null ? list.length(luan) : j; - StringBuilder buf = new StringBuilder(); - for( int k=first; k<=last; k++ ) { - Object val = list.get(luan,k); - if( val==null ) - break; - if( sep!=null && k > first ) - buf.append(sep); - String s = luan.toString(val); - buf.append(s); - } - return buf.toString(); - } - - public static void insert(LuanTable list,int pos,Object value) throws LuanException { - Utils.checkNotNull(list); - if( list.getMetatable() != null ) - throw new LuanException("can't insert into a table with a metatable"); - list.rawInsert(pos,value); - } - - public static Object remove(LuanTable list,int pos) throws LuanException { - if( list.getMetatable() != null ) - throw new LuanException("can't remove from a table with a metatable"); - return list.rawRemove(pos); - } - - private static interface LessThan { - public boolean isLessThan(Object o1,Object o2); - } - - public static void sort(final LuanState luan,LuanTable list,final LuanFunction comp) throws LuanException { - if( list.getMetatable() != null ) - throw new LuanException("can't sort a table with a metatable"); - final LessThan lt; - if( comp==null ) { - lt = new LessThan() { - public boolean isLessThan(Object o1,Object o2) { - try { - return Luan.isLessThan(luan,o1,o2); - } catch(LuanException e) { - throw new LuanRuntimeException(e); - } - } - }; - } else { - lt = new LessThan() { - public boolean isLessThan(Object o1,Object o2) { - try { - return Luan.checkBoolean(Luan.first(comp.call(luan,new Object[]{o1,o2}))); - } catch(LuanException e) { - throw new LuanRuntimeException(e); - } - } - }; - } - try { - list.rawSort( new Comparator<Object>() { - public int compare(Object o1,Object o2) { - return lt.isLessThan(o1,o2) ? -1 : lt.isLessThan(o2,o1) ? 1 : 0; - } - } ); - } catch(LuanRuntimeException e) { - throw (LuanException)e.getCause(); - } - } - - public static LuanTable pack(Object... args) { - LuanTable tbl = new LuanTable(Arrays.asList(args)); - tbl.rawPut( "n", args.length ); - return tbl; - } - - @LuanMethod public static Object[] unpack(LuanState luan,LuanTable tbl,Integer iFrom,Integer iTo) throws LuanException { - int from = iFrom!=null ? iFrom : 1; - int to = iTo!=null ? iTo : tbl.length(luan); - List<Object> list = new ArrayList<Object>(); - for( int i=from; i<=to; i++ ) { - list.add( tbl.get(luan,i) ); - } - return list.toArray(); - } - - public static LuanTable copy(LuanTable list,Integer from,Integer to) { - if( from == null ) - return new LuanTable(list); - if( to == null ) - to = list.rawLength(); - return list.rawSubList(from,to); - } - - public static LuanTable new_property_table() { - return LuanPropertyMeta.INSTANCE.newTable(); - } - - public static void clear(LuanTable tbl) { - tbl.rawClear(); - } - -}
--- a/core/src/luan/modules/Thread.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -java() -local ThreadLuan = require "java:luan.modules.ThreadLuan" - -local M = {} - -M.fork = ThreadLuan.fork -M.schedule = ThreadLuan.schedule -M.synchronized = ThreadLuan.synchronized_ - -return M
--- a/core/src/luan/modules/ThreadLuan.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -package luan.modules; - -import java.io.Closeable; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import luan.Luan; -import luan.LuanState; -import luan.LuanFunction; -import luan.LuanTable; -import luan.LuanException; -import luan.DeepCloner; - - -public final class ThreadLuan { - private static final Executor exec = Executors.newCachedThreadPool(); - private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); - - public static void fork(LuanState luan,LuanFunction fn,Object... args) { - DeepCloner cloner = new DeepCloner(); - final LuanState newLuan = (LuanState)cloner.deepClone(luan); - final LuanFunction newFn = (LuanFunction)cloner.get(fn); - final Object[] newArgs = cloner.deepClone(args); - exec.execute(new Runnable(){public void run() { - try { - newFn.call(newLuan,newArgs); - } catch(LuanException e) { - e.printStackTrace(); - } - }}); - } - - public static LuanFunction synchronized_(final LuanState luan,final LuanFunction fn) throws LuanException { - Utils.checkNotNull(fn); - return new LuanFunction() { - @Override public Object call(LuanState ingored,Object[] args) throws LuanException { - synchronized(luan) { - return fn.call(luan,args); - } - } - }; - } - - public static void schedule(LuanState luan,long delay,boolean repeat,LuanFunction fn,Object... args) { - DeepCloner cloner = new DeepCloner(); - final LuanState newLuan = (LuanState)cloner.deepClone(luan); - final LuanFunction newFn = (LuanFunction)cloner.get(fn); - final Object[] newArgs = cloner.deepClone(args); - Runnable r = new Runnable(){public void run() { - try { - newFn.call(newLuan,newArgs); - } catch(LuanException e) { - e.printStackTrace(); - } - }}; - final ScheduledFuture sf; - if( repeat ) { - sf = scheduler.scheduleWithFixedDelay(r,delay,delay,TimeUnit.MILLISECONDS); - } else { - sf = scheduler.schedule(r,delay,TimeUnit.MILLISECONDS); - } - final Closeable c = new Closeable(){public void close(){ - boolean b = sf.cancel(false); - }}; - luan.registry().put(c,c); // prevent gc - luan.onClose(c); - } - -}
--- a/core/src/luan/modules/Time.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ --- incomplete, will add as needed - -java() -local Luan = require "luan:Luan.luan" -local error = Luan.error -local ipairs = Luan.ipairs or error() -local Table = require "luan:Table.luan" -local System = require "java:java.lang.System" -local Calendar = require "java:java.util.Calendar" -local Date = require "java:java.util.Date" -local TimeZone = require "java:java.util.TimeZone" -local SimpleDateFormat = require "java:java.text.SimpleDateFormat" - -local M = {} - -function M.now() - return System.currentTimeMillis() -end - --- add more as needed -local fields = { - year = Calendar.YEAR; - month = Calendar.MONTH; - day_of_month = Calendar.DAY_OF_MONTH; -} - -function M.get( time, ... ) - local cal = Calendar.getInstance() - cal.setTimeInMillis(time) - local rtn = {} - for i, v in ipairs{...} do - local fld = fields[v.lower()] - fld or error("invalid field: "+v) - local n = cal.get(fld) - if fld == "month" then - n = n + 1 - end - rtn[i] = n - end - return Table.unpack(rtn) -end - -function M.format(time,pattern) - pattern = pattern or "yyyy-MM-dd HH:mm:ss" - return SimpleDateFormat.new(pattern).format(Date.new(time)) -end - -function M.on( year, month, day, hour, minute, second, millis ) - month = month - 1 - local cal = Calendar.getInstance() - cal.setLenient(false) - cal.set( year, month, day, hour or 0, minute or 0, second or 0 ) - cal.set( Calendar.MILLISECOND, millis or 0 ) - return cal.getTimeInMillis() -end - -function M.period( t ) - local cal = Calendar.getInstance() - cal.setTimeZone(TimeZone.getTimeZone("GMT")) - local days = t.days or 0 - days = days + 1 - cal.set( 1970, 0, days, t.hours or 0, t.minutes or 0, t.seconds or 0 ) - cal.set( Calendar.MILLISECOND, t.millis or 0 ) - return cal.getTimeInMillis() -end - -function M.parse( pattern, source ) - return SimpleDateFormat.new(pattern).parse(source).getTime() -end - -return M
--- a/core/src/luan/modules/Utils.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,200 +0,0 @@ -package luan.modules; - -import java.io.Reader; -import java.io.IOException; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.File; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.MalformedURLException; -import luan.LuanException; -import luan.LuanTable; -import luan.LuanFunction; - - -public final class Utils { - private Utils() {} // never - - static final int bufSize = 8192; - - private static void checkNotNull(Object v,String expected,int pos) throws LuanException { - if( v == null ) - throw new LuanException("bad argument #"+pos+" ("+expected+" expected, got nil)"); - } - - public static void checkNotNull(String s,int pos) throws LuanException { - checkNotNull(s,"string",pos); - } - - public static void checkNotNull(String s) throws LuanException { - checkNotNull(s,1); - } - - public static void checkNotNull(byte[] b,int pos) throws LuanException { - checkNotNull(b,"binary",pos); - } - - public static void checkNotNull(byte[] b) throws LuanException { - checkNotNull(b,1); - } - - public static void checkNotNull(LuanTable t,int pos) throws LuanException { - checkNotNull(t,"table",pos); - } - - public static void checkNotNull(LuanTable t) throws LuanException { - checkNotNull(t,1); - } - - public static void checkNotNull(Number n,int pos) throws LuanException { - checkNotNull(n,"number",pos); - } - - public static void checkNotNull(Number n) throws LuanException { - checkNotNull(n,1); - } - - public static void checkNotNull(LuanFunction fn,int pos) throws LuanException { - checkNotNull(fn,"function",pos); - } - - public static void checkNotNull(LuanFunction fn) throws LuanException { - checkNotNull(fn,1); - } - - public static String readAll(Reader in) - throws IOException - { - char[] a = new char[bufSize]; - StringBuilder buf = new StringBuilder(); - int n; - while( (n=in.read(a)) != -1 ) { - buf.append(a,0,n); - } - return buf.toString(); - } - - public static void copyAll(InputStream in,OutputStream out) - throws IOException - { - byte[] a = new byte[bufSize]; - int n; - while( (n=in.read(a)) != -1 ) { - out.write(a,0,n); - } - } - - public static byte[] readAll(InputStream in) - throws IOException - { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - copyAll(in,out); - return out.toByteArray(); - } -/* - public static boolean exists(File file) { - try { - return file.exists() && file.getName().equals(file.getCanonicalFile().getName()); - } catch(IOException e) { - throw new RuntimeException(e); - } - } -*/ -/* - private static File toFile(String path) { - if( path.contains("//") ) - return null; - File file = new File(path); - return file.exists() ? file : null; - } - - private static URL toUrl(String path) { - if( path.indexOf(':') == -1 ) - return null; - if( path.startsWith("classpath:") ) { - path = path.substring(10); - if( path.contains("//") ) - return null; - URL url; - if( !path.contains("#") ) { - url = ClassLoader.getSystemResource(path); - } else { - String[] a = path.split("#"); - url = ClassLoader.getSystemResource(a[0]); - if( url==null ) { - for( int i=1; i<a.length; i++ ) { - url = ClassLoader.getSystemResource(a[0]+"/"+a[i]); - if( url != null ) { - try { - url = new URL(url,"."); - } catch(MalformedURLException e) { - throw new RuntimeException(e); - } - break; - } - } - } - } - return url==null ? null : url; - } - try { - return new URL(path); - } catch(MalformedURLException e) {} - return null; - } - - static boolean exists(String path) { - return toFile(path)!=null || toUrl(path)!=null; - } -*/ - - - -/* replace by uri"os:..." - - // process - - public static class ProcessException extends IOException { - private ProcessException(String msg) { - super(msg); - } - } - - public static void checkProcess(Process proc) - throws IOException - { - try { - proc.waitFor(); - } catch(InterruptedException e) { - throw new RuntimeException(e); - } - int exitVal = proc.exitValue(); - if( exitVal != 0 ) { - Reader err = new InputStreamReader(proc.getErrorStream()); - String error = readAll(err); - err.close(); - throw new ProcessException(error); - } - } - - public static String getOutput(Process proc) - throws IOException - { - Reader in = new InputStreamReader(proc.getInputStream()); - String s = readAll(in); - in.close(); - return s; - } - - public static String execProcess(String... cmd) - throws IOException - { - Process proc = Runtime.getRuntime().exec(cmd); - String s = getOutput(proc); - checkProcess(proc); - return s; - } -*/ -}
--- a/core/src/luan/modules/Which_mod.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local ipairs = Luan.ipairs or error() -local pairs = Luan.pairs or error() -local type = Luan.type or error() -local String = require "luan:String.luan" -local literal = String.literal or error() -local matches = String.matches or error() -local Io = require "luan:Io.luan" -local print = Io.print or error() - - -local M = {} - -M.uris = { - "luan:Luan" - "luan:Binary" - "luan:Html" - "luan:Io" - "luan:Math" - "luan:Package" - "luan:String" - "luan:Table" - "luan:Thread" - "luan:Time" - "luan:host/Hosting" - "luan:http/Http" - "luan:http/Server" - "luan:lucene/Lucene" - "luan:lucene/Versioning" - "luan:mail/Mail" - "luan:logging/Logging" - "luan:stripe/Stripe" -} - -function M.which(name) - local ptn = "[:./]"..literal(name).."$" - for _, uri in ipairs(M.uris) do - local mod = require(uri) - if matches(uri,ptn) then - print(uri) - end - if type(mod) == "table" then - for key in pairs(mod) do - if key == name then - print(uri.." "..key) - end - end - end - end -end - -return M
--- a/core/src/luan/modules/host/Hosting.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ --- Hosting - -local Luan = require "luan:Luan.luan" -local error = Luan.error -local ipairs = Luan.ipairs or error() -local pairs = Luan.pairs or error() -local set_metatable = Luan.set_metatable or error() -local Io = require "luan:Io.luan" -local print = Io.print or error() -local Rpc = require "luan:Rpc.luan" -local String = require "luan:String.luan" -local matches = String.matches or error() - - -local M = {} - - -function M.push(domain,password,dir) - local my_dir = Io.uri("file:"..dir) - my_dir.exists() or error("directory '"..dir.."' not found") - my_dir.is_directory() or error("'"..dir.."' is not a directory") - local host = Rpc.remote(domain) - local tree = host.get(domain,password) - if tree == nil then - print("creating "..domain) - tree = host.create(domain,password) - end - - local function process(there_parent,there,here) - if here.is_file() then - if there == nil or there.last_modified < here.last_modified() then - print("copying "..here.to_string()) - host.copy_file(domain,password,there_parent.path,here.name(),here.read_binary()) - end - elseif here.is_directory() then - if here.name() == "local" then - return - end - if there == nil then - there = host.mkdir(domain,password,there_parent.path,here.name()) - end - for _, here_child in ipairs(here.children()) do - local name = here_child.name() - if not matches(name,[[^\.]]) then - process(there,there.children[name],here_child) - there.children[name] = nil - end - end - for _, there_child in pairs(there.children) do - if host.delete_unused(domain,password,there_child.path)==true then -- remove ==true later - print("deleted "..there_child.name) - end - end - else - error "not file or dir" - end - end - - process( nil, tree, my_dir ) - - host.update_handler(domain,password) -end - -function M.delete(domain,password) - local host = Rpc.remote(domain) - host.delete(domain,password) -end - -function M.exists(domain) - local host = Rpc.remote(domain) - return host.exists(domain) -end - -function M.change_domain(old_domain,new_domain,password) - local host = Rpc.remote(new_domain) - return host.change_domain(old_domain,new_domain,password) -end - -function M.change_password(domain,old_password,new_password) - local host = Rpc.remote(domain) - return host.change_password(domain,old_password,new_password) -end - -function M.caller(domain) - local host = Rpc.remote(domain) - local mt = {} - function mt.__index(_,key) - return function(...) - return host.call(domain,key,...) - end - end - local t = {} - set_metatable(t,mt) - return t -end - -return M
--- a/core/src/luan/modules/host/backup.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local Io = require "luan:Io.luan" -local print = Io.print or error() -local uri = Io.uri or error() -local Hosting = require "luan:host/Hosting.luan" - -if #{...} ~= 2 then - Io.stderr.write "usage: luan luan:host/backup.luan domain password\n" - return -end - -local domain, password = ... - -local zip = Hosting.caller(domain).lucene_backup(password) -uri("file:backup.zip").write(zip) - -print("backed up lucene from "..domain.." to backup.zip")
--- a/core/src/luan/modules/host/delete.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -local Io = require "luan:Io.luan" -local print = Io.print -local Hosting = require "luan:host/Hosting.luan" - -if #{...} ~= 2 then - Io.stderr.write "usage: luan luan:host/delete.luan domain password\n" - return -end - -Hosting.delete(...) - -print("deleted "..(...))
--- a/core/src/luan/modules/host/push.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -local Io = require "luan:Io.luan" -local print = Io.print -local Hosting = require "luan:host/Hosting.luan" - -if #{...} ~= 3 then - Io.stderr.write "usage: luan luan:host/push.luan domain password dir\n" - return -end - -Hosting.push(...) - -print("done with "..(...))
--- a/core/src/luan/modules/host/restore.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local Io = require "luan:Io.luan" -local print = Io.print or error() -local uri = Io.uri or error() -local Hosting = require "luan:host/Hosting.luan" - -if #{...} ~= 2 then - Io.stderr.write "usage: luan luan:host/restore.luan domain password\n" - return -end - -local domain, password = ... - -local zip_file = uri("file:backup.zip") -zip_file.exists() or error "backup.zip not found" -Hosting.caller(domain).lucene_restore(password,zip_file) - -print("restored lucene from backup.zip to "..domain)
--- a/core/src/luan/modules/luan_to_java.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -java() -local LuanCompiler = require "java:luan.impl.LuanCompiler" -local Io = require "luan:Io.luan" - -Io.stdout.write( LuanCompiler.toJava( Io.stdin.read_text(), "stdin" ) )
--- a/core/src/luan/modules/mmake.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -local Luan = require "luan:Luan.luan" -local ipairs = Luan.ipairs -local Table = require "luan:Table.luan" -local Io = require "luan:Io.luan" -local print = Io.print -local String = require "luan:String.luan" -local Time = require "luan:Time.luan" - - -local compiler = Table.concat( { "javac -g -encoding UTF8", ... }, " " ) - - -local function header() - return %> -# Makefile created on <%=Time.format(Time.now())%> by Mmake - -.SUFFIXES: .java .class - -.java.class: - <%=compiler%> '$<' - -all: <% -end - - -local function mmake(dir) - local javas = {} - local dirs = {} - for _, file in ipairs(dir.children()) do - local name = file.name() - if String.matches(name,[[\.java$]]) then - javas[#javas+1] = String.sub(name,1,-6) - end - if file.is_directory() and mmake(file) then - dirs[#dirs+1] = name - end - end - if #javas == 0 and #dirs == 0 then - return false; - end - local out = dir.child("Makefile").text_writer() - out.write( header() ) - for _, s in ipairs(javas) do - s = String.gsub(s,[[\$]],[[\$\$]]) - out.write( "\\\n\t\t", s , ".class" ) - end - for _, s in ipairs(dirs) do - out.write( "\n\tcd ", s, "; make all" ) - end - out.write "\n\nclean:\n\trm -f *.class\n" - for _, s in ipairs(dirs) do - out.write( "\tcd ", s, "; make clean\n" ) - end - out.close() - print(dir.to_string()) - return true -end - -mmake(Io.schemes.file ".")
--- a/core/src/luan/modules/parsers/BBCode.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,311 +0,0 @@ -package luan.modules.parsers; - -import java.util.List; -import java.util.ArrayList; -import luan.Luan; -import luan.LuanState; -import luan.LuanFunction; -import luan.LuanException; -import luan.modules.Utils; - - -public final class BBCode { - - public static String toHtml(LuanState luan,String bbcode,LuanFunction quoter) throws LuanException { - return new BBCode(luan,bbcode,quoter,true).parse(); - } - - public static String toText(LuanState luan,String bbcode,LuanFunction quoter) throws LuanException { - return new BBCode(luan,bbcode,quoter,false).parse(); - } - - private final LuanState luan; - private final Parser parser; - private final LuanFunction quoter; - private final boolean toHtml; - - private BBCode(LuanState luan,String text,LuanFunction quoter,boolean toHtml) throws LuanException { - Utils.checkNotNull(text,1); - Utils.checkNotNull(quoter,2); - this.luan = luan; - this.parser = new Parser(text); - this.quoter = quoter; - this.toHtml = toHtml; - } - - private String parse() throws LuanException { - StringBuilder sb = new StringBuilder(); - while( !parser.endOfInput() ) { - String block = parseBlock(); - if( block != null ) - sb.append(block); - else { - sb.append( parser.currentChar() ); - parser.anyChar(); - } - } - return sb.toString(); - } - - private String parseWellFormed() throws LuanException { - StringBuilder sb = new StringBuilder(); - while( !parser.endOfInput() ) { - String block = parseBlock(); - if( block != null ) { - sb.append(block); - continue; - } - if( couldBeTag() ) - break; - sb.append( parser.currentChar() ); - parser.anyChar(); - } - return sb.toString(); - } - - private boolean couldBeTag() { - if( parser.currentChar() != '[' ) - return false; - return parser.testIgnoreCase("[b]") - || parser.testIgnoreCase("[/b]") - || parser.testIgnoreCase("[i]") - || parser.testIgnoreCase("[/i]") - || parser.testIgnoreCase("[u]") - || parser.testIgnoreCase("[/u]") - || parser.testIgnoreCase("[url]") - || parser.testIgnoreCase("[url=") - || parser.testIgnoreCase("[/url]") - || parser.testIgnoreCase("[code]") - || parser.testIgnoreCase("[/code]") - || parser.testIgnoreCase("[img]") - || parser.testIgnoreCase("[/img]") - || parser.testIgnoreCase("[color=") - || parser.testIgnoreCase("[/color]") - || parser.testIgnoreCase("[size=") - || parser.testIgnoreCase("[/size]") - || parser.testIgnoreCase("[youtube]") - || parser.testIgnoreCase("[/youtube]") - || parser.testIgnoreCase("[quote]") - || parser.testIgnoreCase("[quote=") - || parser.testIgnoreCase("[/quote]") - ; - } - - private String parseBlock() throws LuanException { - if( parser.currentChar() != '[' ) - return null; - String s; - s = parseB(); if(s!=null) return s; - s = parseI(); if(s!=null) return s; - s = parseU(); if(s!=null) return s; - s = parseUrl1(); if(s!=null) return s; - s = parseUrl2(); if(s!=null) return s; - s = parseCode(); if(s!=null) return s; - s = parseImg(); if(s!=null) return s; - s = parseColor(); if(s!=null) return s; - s = parseSize(); if(s!=null) return s; - s = parseYouTube(); if(s!=null) return s; - s = parseQuote1(); if(s!=null) return s; - s = parseQuote2(); if(s!=null) return s; - return null; - } - - private String parseB() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[b]") ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/b]") ) - return parser.failure(null); - String rtn = toHtml ? "<b>"+content+"</b>" : content; - return parser.success(rtn); - } - - private String parseI() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[i]") ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/i]") ) - return parser.failure(null); - String rtn = toHtml ? "<i>"+content+"</i>" : content; - return parser.success(rtn); - } - - private String parseU() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[u]") ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/u]") ) - return parser.failure(null); - String rtn = toHtml ? "<u>"+content+"</u>" : content; - return parser.success(rtn); - } - - private String parseUrl1() { - parser.begin(); - if( !parser.matchIgnoreCase("[url]") ) - return parser.failure(null); - String url = parseRealUrl(); - if( !parser.matchIgnoreCase("[/url]") ) - return parser.failure(null); - String rtn = toHtml ? "<a href='"+url+"'>"+url+"</u>" : url; - return parser.success(rtn); - } - - private String parseUrl2() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[url=") ) - return parser.failure(null); - String url = parseRealUrl(); - if( !parser.match(']') ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/url]") ) - return parser.failure(null); - String rtn = toHtml ? "<a href='"+url+"'>"+content+"</u>" : content; - return parser.success(rtn); - } - - private String parseRealUrl() { - parser.begin(); - while( parser.match(' ') ); - int start = parser.currentIndex(); - if( !parser.matchIgnoreCase("http") ) - return parser.failure(null); - parser.matchIgnoreCase("s"); - if( !parser.matchIgnoreCase("://") ) - return parser.failure(null); - while( parser.noneOf(" []'") ); - String url = parser.textFrom(start); - while( parser.match(' ') ); - return parser.success(url); - } - - private String parseCode() { - parser.begin(); - if( !parser.matchIgnoreCase("[code]") ) - return parser.failure(null); - int start = parser.currentIndex(); - while( !parser.testIgnoreCase("[/code]") ) { - if( !parser.anyChar() ) - return parser.failure(null); - } - String content = parser.textFrom(start); - if( !parser.matchIgnoreCase("[/code]") ) throw new RuntimeException(); - String rtn = toHtml ? "<code>"+content+"</code>" : content; - return parser.success(rtn); - } - - private String parseImg() { - parser.begin(); - if( !parser.matchIgnoreCase("[img]") ) - return parser.failure(null); - String url = parseRealUrl(); - if( !parser.matchIgnoreCase("[/img]") ) - return parser.failure(null); - String rtn = toHtml ? "<img src='"+url+"'>" : ""; - return parser.success(rtn); - } - - private String parseColor() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[color=") ) - return parser.failure(null); - int start = parser.currentIndex(); - parser.match('#'); - while( parser.inCharRange('0','9') - || parser.inCharRange('a','z') - || parser.inCharRange('A','Z') - ); - String color = parser.textFrom(start); - if( !parser.match(']') ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/color]") ) - return parser.failure(null); - String rtn = toHtml ? "<span style='color: "+color+"'>"+content+"</span>" : content; - return parser.success(rtn); - } - - private String parseSize() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[size=") ) - return parser.failure(null); - int start = parser.currentIndex(); - while( parser.match('.') || parser.inCharRange('0','9') ); - String size = parser.textFrom(start); - if( !parser.match(']') ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/size]") ) - return parser.failure(null); - String rtn = toHtml ? "<span style='font-size: "+size+"em'>"+content+"</span>" : content; - return parser.success(rtn); - } - - private String parseYouTube() { - parser.begin(); - if( !parser.matchIgnoreCase("[youtube]") ) - return parser.failure(null); - int start = parser.currentIndex(); - while( parser.inCharRange('0','9') - || parser.inCharRange('a','z') - || parser.inCharRange('A','Z') - || parser.match('-') - || parser.match('_') - ); - String id = parser.textFrom(start); - if( id.length()==0 || !parser.matchIgnoreCase("[/youtube]") ) - return parser.failure(null); - String rtn = toHtml ? "<iframe width='420' height='315' src='https://www.youtube.com/embed/"+id+"' frameborder='0' allowfullscreen></iframe>" : ""; - return parser.success(rtn); - } - - private String quote(Object... args) throws LuanException { - Object obj = quoter.call(luan,args); - if( !(obj instanceof String) ) - throw new LuanException("BBCode quoter function returned "+Luan.type(obj)+" but string required"); - return (String)obj; - } - - private String parseQuote1() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[quote]") ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/quote]") ) - return parser.failure(null); - String rtn = quote(content); - return parser.success(rtn); - } - - private String parseQuote2() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[quote=") ) - return parser.failure(null); - List args = new ArrayList(); - int start = parser.currentIndex(); - while( parser.noneOf("[];") ); - String name = parser.textFrom(start).trim(); - if( name.length() == 0 ) - return parser.failure(null); - args.add(name); - while( parser.match(';') ) { - start = parser.currentIndex(); - while( parser.noneOf("[];'") ); - String src = parser.textFrom(start).trim(); - args.add(src); - } - if( !parser.match(']') ) - return parser.failure(null); - String content = parseWellFormed(); - args.add(0,content); - if( !parser.matchIgnoreCase("[/quote]") ) - return parser.failure(null); - String rtn = quote(args.toArray()); - return parser.success(rtn); - } - -}
--- a/core/src/luan/modules/parsers/Csv.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -package luan.modules.parsers; - -import luan.LuanTable; - - -public final class Csv { - - public static LuanTable toList(String line) throws ParseException { - return new Csv(line).parse(); - } - - private final Parser parser; - - private Csv(String line) { - this.parser = new Parser(line); - } - - private ParseException exception(String msg) { - return new ParseException(parser,msg); - } - - private LuanTable parse() throws ParseException { - LuanTable list = new LuanTable(); - while(true) { - Spaces(); - String field = parseField(); - list.rawPut(list.rawLength()+1,field); - Spaces(); - if( parser.endOfInput() ) - return list; - if( !parser.match(',') ) - throw exception("unexpected char"); - } - } - - private String parseField() throws ParseException { - parser.begin(); - String rtn; - if( parser.match('"') ) { - int start = parser.currentIndex(); - do { - if( parser.endOfInput() ) { - parser.failure(); - throw exception("unclosed quote"); - } - } while( parser.noneOf("\"") ); - rtn = parser.textFrom(start); - parser.match('"'); - } else { - int start = parser.currentIndex(); - while( !parser.endOfInput() && parser.noneOf(",") ); - rtn = parser.textFrom(start).trim(); - } - return parser.success(rtn); - } - - private void Spaces() { - while( parser.anyOf(" \t") ); - } - -}
--- a/core/src/luan/modules/parsers/Html.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -package luan.modules.parsers; - -import java.util.List; -import java.util.ArrayList; -import java.util.Set; -import java.util.HashSet; -import luan.LuanTable; - - -public final class Html { - - public static LuanTable toList(String text,LuanTable containerTagsTbl) throws ParseException { - return new Html(text,containerTagsTbl).parse(); - } - - private final Parser parser; - private final Set<String> containerTags = new HashSet<String>(); - - private Html(String text,LuanTable containerTagsTbl) { - this.parser = new Parser(text); - for( Object v : containerTagsTbl.asList() ) { - containerTags.add((String)v); - } - } - - private LuanTable parse() throws ParseException { - List list = new ArrayList(); - StringBuilder sb = new StringBuilder(); - while( !parser.endOfInput() ) { - if( parser.test('<') ) { - LuanTable tbl = parseTag(); - if( tbl != null ) { - String tagName = (String)tbl.rawGet("name"); - if( containerTags.contains(tagName) ) { - LuanTable container = parseContainer(tbl); - if( container != null ) - tbl = container; - } - if( tbl != null - || (tbl = parseComment()) != null - || (tbl = parseCdata()) != null - ) { - if( sb.length() > 0 ) { - list.add(sb.toString()); - sb.setLength(0); - } - list.add(tbl); - continue; - } - } - } - sb.append( parser.currentChar() ); - parser.anyChar(); - } - if( sb.length() > 0 ) - list.add(sb.toString()); - return new LuanTable(list); - } - - private LuanTable parseComment() { - parser.begin(); - if( !parser.match("<!--") ) - return parser.failure(null); - int start = parser.currentIndex(); - while( !parser.test("-->") ) { - if( !parser.anyChar() ) - return parser.failure(null); - } - String text = parser.textFrom(start); - LuanTable tbl = new LuanTable(); - tbl.rawPut("type","comment"); - tbl.rawPut("text",text); - return parser.success(tbl); - } - - private LuanTable parseCdata() { - parser.begin(); - if( !parser.match("<![CDATA[") ) - return parser.failure(null); - int start = parser.currentIndex(); - while( !parser.test("]]>") ) { - if( !parser.anyChar() ) - return parser.failure(null); - } - String text = parser.textFrom(start); - LuanTable tbl = new LuanTable(); - tbl.rawPut("type","cdata"); - tbl.rawPut("text",text); - return parser.success(tbl); - } - - private LuanTable parseContainer(LuanTable tag) { - String endTagName = '/' + (String)tag.rawGet("name"); - int start = parser.begin(); - int end; - while(true) { - if( parser.test('<') ) { - end = parser.currentIndex(); - LuanTable tag2 = parseTag(); - String s = (String)tag2.rawGet("name"); - if( s.equals(endTagName) ) - break; - } - if( !parser.anyChar() ) - return parser.failure(null); - } - String text = parser.text.substring(start,end); - LuanTable tbl = new LuanTable(); - tbl.rawPut("type","container"); - tbl.rawPut("tag",tag); - tbl.rawPut("text",text); - return parser.success(tbl); - } - - private LuanTable parseTag() { - parser.begin(); - if( !parser.match('<') ) - return parser.failure(null); - int start = parser.currentIndex(); - parser.match('/'); - if( !matchNameChar() ) - return parser.failure(null); - while( matchNameChar() ); - String name = parser.textFrom(start).toLowerCase(); - LuanTable attributes = new LuanTable(); - String attrName; - while( (attrName = parseAttrName()) != null ) { - String attrValue = parseAttrValue(); - attributes.rawPut( attrName, attrValue!=null ? attrValue : true ); - } - while( matchSpace() ); - boolean isEmpty = parser.match('/'); - if( !parser.match('>') ) - return parser.failure(null); - LuanTable tbl = new LuanTable(); - tbl.rawPut("type","tag"); - tbl.rawPut("name",name); - tbl.rawPut("attributes",attributes); - tbl.rawPut("is_empty",isEmpty); - return parser.success(tbl); - } - - private String parseAttrName() { - parser.begin(); - if( !matchSpace() ) - return parser.failure(null); - while( matchSpace() ); - int start = parser.currentIndex(); - if( !matchNameChar() ) - return parser.failure(null); - while( matchNameChar() ); - String name = parser.textFrom(start); - return parser.success(name); - } - - private String parseAttrValue() { - parser.begin(); - while( matchSpace() ); - if( !parser.match('=') ) - return parser.failure(null); - while( matchSpace() ); - if( parser.anyOf("\"'") ) { - char quote = parser.lastChar(); - int start = parser.currentIndex(); - while( !parser.test(quote) ) { - if( !parser.anyChar() ) - return parser.failure(null); - } - String value = parser.textFrom(start); - parser.match(quote); - return parser.success(value); - } - int start = parser.currentIndex(); - if( !matchValueChar() ) - return parser.failure(null); - while( matchValueChar() ); - String value = parser.textFrom(start); - return parser.success(value); - } - - private boolean matchNameChar() { - return parser.inCharRange('a','z') - || parser.inCharRange('A','Z') - || parser.inCharRange('0','9') - || parser.anyOf("_.-:") - ; - } - - private boolean matchValueChar() { - return parser.noneOf(" \t\r\n\"'>/="); - } - - private boolean matchSpace() { - return parser.anyOf(" \t\r\n"); - } - -}
--- a/core/src/luan/modules/parsers/Json.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,313 +0,0 @@ -package luan.modules.parsers; - -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.LinkedHashMap; -import java.util.Iterator; -import luan.LuanTable; -import luan.LuanException; - - -public final class Json { - - public static Object parse(String text) throws ParseException { - return new Json(text).parse(); - } - - private final Parser parser; - - private Json(String text) { - this.parser = new Parser(text); - } - - private ParseException exception(String msg) { - return new ParseException(parser,msg); - } - - private Object parse() throws ParseException { - spaces(); - Object value = value(); - spaces(); - if( !parser.endOfInput() ) - throw exception("unexpected text"); - return value; - } - - private Object value() throws ParseException { - if( parser.match("null") ) - return null; - if( parser.match("true") ) - return Boolean.TRUE; - if( parser.match("false") ) - return Boolean.FALSE; - String s = string(); - if( s != null ) - return s; - Number n = number(); - if( n != null ) - return n; - LuanTable a = array(); - if( a != null ) - return a; - LuanTable o = object(); - if( o != null ) - return o; - throw exception("invalid value"); - } - - private String string() throws ParseException { - parser.begin(); - if( !parser.match('"') ) - return parser.failure(null); - StringBuilder sb = new StringBuilder(); - while( parser.anyChar() ) { - char c = parser.lastChar(); - switch(c) { - case '"': - return parser.success(sb.toString()); - case '\\': - if( parser.anyChar() ) { - c = parser.lastChar(); - switch(c) { - case '"': - case '\\': - case '/': - sb.append(c); - continue; - case 'b': - sb.append('\b'); - continue; - case 'f': - sb.append('\f'); - continue; - case 'n': - sb.append('\n'); - continue; - case 'r': - sb.append('\r'); - continue; - case 't': - sb.append('\t'); - continue; - case 'u': - int n = 0; - for( int i=0; i<4; i++ ) { - int d; - if( parser.inCharRange('0','9') ) { - d = parser.lastChar() - '0'; - } else if( parser.inCharRange('a','f') ) { - d = parser.lastChar() - 'a' + 10; - } else if( parser.inCharRange('A','F') ) { - d = parser.lastChar() - 'A' + 10; - } else { - throw exception("invalid hex digit"); - } - n = 16*n + d; - } - sb.append((char)n); - continue; - } - } - throw exception("invalid escape char"); - default: - sb.append(c); - } - } - parser.failure(); - throw exception("unclosed string"); - } - - private Number number() { - int start = parser.begin(); - boolean isFloat = false; - parser.match('-'); - if( !parser.match('0') ) { - if( !parser.inCharRange('1','9') ) - return parser.failure(null); - while( parser.inCharRange('0','9') ); - } - if( parser.match('.') ) { - if( !parser.inCharRange('0','9') ) - return parser.failure(null); - while( parser.inCharRange('0','9') ); - isFloat = true; - } - if( parser.anyOf("eE") ) { - parser.anyOf("+-"); - if( !parser.inCharRange('0','9') ) - return parser.failure(null); - while( parser.inCharRange('0','9') ); - isFloat = true; - } - String s = parser.textFrom(start); - Number n; - if(isFloat) - n = Double.valueOf(s); - else - n = Long.valueOf(s); - return parser.success(n); - } - - private LuanTable array() throws ParseException { - parser.begin(); - if( !parser.match('[') ) - return parser.failure(null); - spaces(); - if( parser.match(']') ) - return parser.success(new LuanTable()); - List list = new ArrayList(); - list.add( value() ); - spaces(); - while( parser.match(',') ) { - spaces(); - list.add( value() ); - spaces(); - } - if( parser.match(']') ) - return parser.success(new LuanTable(list)); - if( parser.endOfInput() ) { - parser.failure(); - throw exception("unclosed array"); - } - throw exception("unexpected text in array"); - } - - private LuanTable object() throws ParseException { - parser.begin(); - if( !parser.match('{') ) - return parser.failure(null); - spaces(); - if( parser.match('}') ) - return parser.success(new LuanTable()); - Map map = new LinkedHashMap(); - addEntry(map); - while( parser.match(',') ) { - spaces(); - addEntry(map); - } - if( parser.match('}') ) - return parser.success(new LuanTable(map)); - if( parser.endOfInput() ) { - parser.failure(); - throw exception("unclosed object"); - } - throw exception("unexpected text in object"); - } - - private void addEntry(Map map) throws ParseException { - String key = string(); - if( key==null ) - throw exception("invalid object key"); - spaces(); - if( !parser.match(':') ) - throw exception("':' expected"); - spaces(); - Object value = value(); - spaces(); - map.put(key,value); - } - - private void spaces() { - while( parser.anyOf(" \t\r\n") ); - } - - - - - - - - - - public static String toString(Object obj) throws LuanException { - StringBuilder sb = new StringBuilder(); - toString(obj,sb); - return sb.toString(); - } - - private static void toString(Object obj,StringBuilder sb) throws LuanException { - if( obj == null || obj instanceof Boolean || obj instanceof Number ) { - sb.append(obj); - return; - } - if( obj instanceof String ) { - toString((String)obj,sb); - return; - } - if( obj instanceof LuanTable ) { - toString((LuanTable)obj,sb); - return; - } - throw new LuanException("can't handle type "+obj.getClass().getName()); - } - - private static void toString(final String s,StringBuilder sb) { - sb.append('"'); - for( int i=0; i<s.length(); i++ ) { - char c = s.charAt(i); - switch(c) { - case '"': - sb.append("\\\""); - break; - case '\\': - sb.append("\\\\"); - break; - case '\b': - sb.append("\\b"); - break; - case '\f': - sb.append("\\f"); - break; - case '\n': - sb.append("\\n"); - break; - case '\r': - sb.append("\\r"); - break; - case '\t': - sb.append("\\t"); - break; - default: - sb.append(c); - } - } - sb.append('"'); - } - - private static void toString(LuanTable t,StringBuilder sb) throws LuanException { - if( t.isList() ) { - final List list = t.asList(); - if( list.isEmpty() ) { - sb.append("{}"); - return; - } - sb.append('['); - toString(list.get(0),sb); - for( int i=1; i<list.size(); i++ ) { - sb.append(','); - toString(list.get(i),sb); - } - sb.append(']'); - return; - } - sb.append('{'); - Iterator<Map.Entry<Object,Object>> i = t.rawIterator(); - toString(i.next(),sb); - while( i.hasNext() ) { - sb.append(','); - toString(i.next(),sb); - } - sb.append('}'); - } - - private static void toString(Map.Entry<Object,Object> entry,StringBuilder sb) throws LuanException { - Object key = entry.getKey(); - if( !(key instanceof String) ) - throw new LuanException("table keys must be strings"); - toString((String)key,sb); - sb.append(':'); - toString(entry.getValue(),sb); - } - -}
--- a/core/src/luan/modules/parsers/ParseException.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -package luan.modules.parsers; - - -public final class ParseException extends Exception { - public final String text; - public final int errorIndex; - public final int highIndex; - - ParseException(Parser parser,String msg) { - super(msg); - this.text = parser.text; - this.errorIndex = parser.currentIndex(); - this.highIndex = parser.highIndex(); - } - - private class Location { - final int line; - final int pos; - - Location(int index) { - int line = 0; - int i = -1; - while(true) { - int j = text.indexOf('\n',i+1); - if( j == -1 || j >= index ) - break; - i = j; - line++; - } - this.line = line; - this.pos = index - i - 1; - } - } - - private String[] lines() { - return text.split("\n",-1); - } - - @Override public String getMessage() { - Location loc = new Location(errorIndex); - String line = lines()[loc.line]; - String msg = super.getMessage() + " (line " + (loc.line+1) + ", pos " + (loc.pos+1) + ")\n"; - StringBuilder sb = new StringBuilder(msg); - sb.append( line + "\n" ); - for( int i=0; i<loc.pos; i++ ) { - sb.append( line.charAt(i)=='\t' ? '\t' : ' ' ); - } - sb.append("^\n"); - return sb.toString(); - } -}
--- a/core/src/luan/modules/parsers/Parser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -package luan.modules.parsers; - - -public class Parser { - public final String text; - private final int len; - private int[] stack = new int[256]; - private int frame = 0; - private int iHigh; - - Parser(String text) { - this.text = text; - this.len = text.length(); - } - - private int i() { - return stack[frame]; - } - - private void i(int i) { - stack[frame] += i; - if( iHigh < stack[frame] ) - iHigh = stack[frame]; - } - - public int begin() { - frame++; - if( frame == stack.length ) { - int[] a = new int[2*frame]; - System.arraycopy(stack,0,a,0,frame); - stack = a; - } - stack[frame] = stack[frame-1]; - return i(); - } - - public void rollback() { - stack[frame] = stack[frame-1]; - } - - public <T> T success(T t) { - success(); - return t; - } - - public boolean success() { - frame--; - stack[frame] = stack[frame+1]; - return true; - } - - public <T> T failure(T t) { - failure(); - return t; - } - - public boolean failure() { - frame--; - return false; - } - - public int currentIndex() { - return i(); - } -/* - public int errorIndex() { - return frame > 0 ? stack[frame-1] : 0; - } -*/ - public int highIndex() { - return iHigh; - } - - public char lastChar() { - return text.charAt(i()-1); - } - - public char currentChar() { - return text.charAt(i()); - } - - public boolean endOfInput() { - return i() >= len; - } - - public boolean match(char c) { - if( endOfInput() || text.charAt(i()) != c ) - return false; - i(1); - return true; - } - - public boolean match(String s) { - int n = s.length(); - if( !text.regionMatches(i(),s,0,n) ) - return false; - i(n); - return true; - } - - public boolean matchIgnoreCase(String s) { - int n = s.length(); - if( !text.regionMatches(true,i(),s,0,n) ) - return false; - i(n); - return true; - } - - public boolean anyOf(String s) { - if( endOfInput() || s.indexOf(text.charAt(i())) == -1 ) - return false; - i(1); - return true; - } - - public boolean noneOf(String s) { - if( endOfInput() || s.indexOf(text.charAt(i())) != -1 ) - return false; - i(1); - return true; - } - - public boolean inCharRange(char cLow, char cHigh) { - if( endOfInput() ) - return false; - char c = text.charAt(i()); - if( !(cLow <= c && c <= cHigh) ) - return false; - i(1); - return true; - } - - public boolean anyChar() { - if( endOfInput() ) - return false; - i(1); - return true; - } - - public boolean test(char c) { - return !endOfInput() && text.charAt(i()) == c; - } - - public boolean test(String s) { - return text.regionMatches(i(),s,0,s.length()); - } - - public boolean testIgnoreCase(String s) { - return text.regionMatches(true,i(),s,0,s.length()); - } - - public String textFrom(int start) { - return text.substring(start,i()); - } - -}
--- a/core/src/luan/modules/parsers/Theme.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -package luan.modules.parsers; - -import luan.LuanException; - - -public final class Theme { - - public static String toLuan(String source) throws LuanException { - try { - return new Theme(source).parse(); - } catch(ParseException e) { - throw new LuanException(e.getMessage()); - } - } - - private final Parser parser; - - private Theme(String source) { - this.parser = new Parser(source); - } - - private ParseException exception(String msg) { -// parser.failure(); - return new ParseException(parser,msg); - } - - private String parse() throws ParseException { - StringBuilder stmts = new StringBuilder(); - stmts.append( "local M = {}; " ); - while( !parser.endOfInput() ) { - String def = parseDef(); - if( def != null ) { - stmts.append(def); - } else { -// parser.anyChar(); - stmts.append(parsePadding()); - } - } - stmts.append( "\n\nreturn M\n" ); - return stmts.toString(); - } - - private String parsePadding() throws ParseException { - int start = parser.currentIndex(); - if( parser.match("--") ) { - while( parser.noneOf("\r\n") ); - } else if( !parser.anyOf(" \t\r\n") ) { - throw exception("unexpected text"); - } - return parser.textFrom(start); - } - - private String parseDef() throws ParseException { - int start = parser.begin(); - if( !parser.match('{') ) - return parser.failure(null); - spaces(); - if( !parser.match("define:") ) - return parser.failure(null); - String name = parseName(); - if( name==null ) - throw exception("invalid block name"); - spaces(); - if( !parser.match('}') ) - throw exception("unclosed define tag"); - String block = parseBody("define:"+name); - String rtn = "function M." + name + "(env) " + block + " end; "; - return parser.success(rtn); - } - - private String parseBody(String tagName) throws ParseException { - StringBuilder stmts = new StringBuilder(); - int start = parser.currentIndex(); - int end = start; - while( !matchEndTag(tagName) ) { - if( parser.endOfInput() ) { - parser.failure(); - throw exception("unclosed block"); - } - String block = parseBlock(); - if( block != null ) { - addText(start,end,stmts); - start = parser.currentIndex(); - stmts.append(block); - continue; - } - String simpleTag = parseSimpleTag(); - if( simpleTag != null ) { - addText(start,end,stmts); - start = parser.currentIndex(); - stmts.append(simpleTag); - continue; - } - if( parser.match("<%") ) { - addText(start,end,stmts); - start = parser.currentIndex(); - stmts.append("%><%='<%'%><%"); - continue; - } - parser.anyChar(); - end = parser.currentIndex(); - } - addText(start,end,stmts); - return stmts.toString(); - } - - private boolean matchEndTag(String tagName) { - parser.begin(); - if( !parser.match('{') ) - return parser.failure(); - spaces(); - if( !(parser.match('/') && parser.match(tagName)) ) - return parser.failure(); - spaces(); - if( !parser.match('}') ) - return parser.failure(); - return parser.success(); - } - - private void addText(int start,int end,StringBuilder stmts) { - if( start < end ) { - stmts.append( "%>" ).append( parser.text.substring(start,end) ).append( "<%" ); - } - } - - private String parseBlock() throws ParseException { - int start = parser.begin(); - if( !parser.match('{') ) - return parser.failure(null); - spaces(); - if( !parser.match("block:") ) - return parser.failure(null); - String name = parseName(); - if( name==null ) { - parser.failure(); - throw exception("invalid block name"); - } - spaces(); - if( !parser.match('}') ) - return parser.failure(null); - String block = parseBody("block:"+name); - String rtn = " env."+ name + "( env, function(env) " + block + "end); "; -// String rtn = "<% env." + tag.name + "(" + (tag.attrs.isEmpty() ? "nil" : table(tag.attrs)) + ",env,function(env) %>" + block + "<% end) %>"; - return parser.success(rtn); - } - - private String parseSimpleTag() throws ParseException { - int start = parser.begin(); - if( !parser.match('{') ) - return parser.failure(null); - spaces(); - String name = parseName(); - if( name==null ) - return parser.failure(null); - spaces(); - if( !parser.match('}') ) - return parser.failure(null); -// rtn = "<% env." + name + (attrs.isEmpty() ? "()" : table(attrs)) + " %>"; - String rtn = " env." + name + "(env); "; - return parser.success(rtn); - } - - private boolean BlankLine() { - parser.begin(); - while( parser.anyOf(" \t") ); - return EndOfLine() ? parser.success() : parser.failure(); - } - - private boolean EndOfLine() { - return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' ); - } - - private String parseName() throws ParseException { - int start = parser.begin(); - if( parser.match('/') ) { - parser.failure(); - throw exception("bad closing tag"); - } - if( parser.match("define:") ) { - parser.failure(); - throw exception("unexpected definition"); - } - if( !FirstNameChar() ) - return parser.failure(null); - while( NameChar() ); - String match = parser.textFrom(start); - return parser.success(match); - } - - private boolean FirstNameChar() { - return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_'); - } - - private boolean NameChar() { - return FirstNameChar() || parser.inCharRange('0', '9'); - } - - private void spaces() { - while( parser.anyOf(" \t") ); - } - -}
--- a/core/src/luan/modules/theme_to_luan.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -local Io = require "luan:Io.luan" -local Parsers = require "luan:Parsers.luan" - -Io.stdout.write( Parsers.theme_to_luan( Io.stdin.read_text(), "stdin" ) )
--- a/core/src/luan/modules/url/LuanUrl.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,302 +0,0 @@ -package luan.modules.url; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URL; -import java.net.URLConnection; -import java.net.HttpURLConnection; -import java.net.URLEncoder; -import java.util.Map; -import java.util.HashMap; -import java.util.List; -import java.util.Base64; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanJavaFunction; -import luan.LuanException; -import luan.modules.IoLuan; -import luan.modules.Utils; - - -public final class LuanUrl extends IoLuan.LuanIn { - - private static enum Method { GET, POST, DELETE } - - private URL url; - private Method method = Method.GET; - private Map headers; - private String content = null; - private MultipartClient multipart = null; - private int timeout = 0; - - public LuanUrl(LuanState luan,URL url,LuanTable options) throws LuanException { - this.url = url; - if( options != null ) { - Map map = options.asMap(luan); - String methodStr = getString(map,"method"); - if( methodStr != null ) { - methodStr = methodStr.toUpperCase(); - try { - this.method = Method.valueOf(methodStr); - } catch(IllegalArgumentException e) { - throw new LuanException( "invalid method: "+methodStr ); - } - } - Map headerMap = getMap(luan,map,"headers"); - if( headerMap != null ) { - headers = new HashMap(); - for( Object hack : headerMap.entrySet() ) { - Map.Entry entry = (Map.Entry)hack; - String key = (String)entry.getKey(); - Object val = entry.getValue(); - String name = toHttpHeaderName(key); - if( val instanceof String ) { - headers.put(name,val); - } else { - if( !(val instanceof LuanTable) ) - throw new LuanException( "header '"+key+"' must be string or table" ); - LuanTable t = (LuanTable)val; - if( !t.isList() ) - throw new LuanException( "header '"+key+"' table must be list" ); - headers.put(name,t.asList()); - } - } - } - Map auth = getMap(luan,map,"authorization"); - if( auth != null ) { - if( headers!=null && headers.containsKey("Authorization") ) - throw new LuanException( "can't define authorization with header 'Authorization' defined" ); - String user = getString(auth,"user"); - String password = getString(auth,"password"); - if( !auth.isEmpty() ) - throw new LuanException( "unrecognized authorization options: "+auth ); - StringBuilder sb = new StringBuilder(); - if( user != null ) - sb.append(user); - sb.append(':'); - if( password != null ) - sb.append(password); - String val = "Basic " + Base64.getEncoder().encodeToString(sb.toString().getBytes()); - if( headers == null ) - headers = new HashMap(); - headers.put("Authorization",val); - } - Map params = getMap(luan,map,"parameters"); - String enctype = getString(map,"enctype"); - if( enctype != null ) { - if( !enctype.equals("multipart/form-data") ) - throw new LuanException( "unrecognized enctype: "+enctype ); - if( this.method!=Method.POST ) - throw new LuanException( "multipart/form-data can only be used with POST" ); - if( params==null ) - throw new LuanException( "multipart/form-data requires parameters" ); - if( params.isEmpty() ) - throw new LuanException( "multipart/form-data parameters can't be empty" ); - multipart = new MultipartClient(params); - } - else if( params != null ) { - StringBuilder sb = new StringBuilder(); - for( Object hack : params.entrySet() ) { - Map.Entry entry = (Map.Entry)hack; - String key = (String)entry.getKey(); - Object val = entry.getValue(); - String keyEnc = encode(key); - if( val instanceof String ) { - and(sb); - sb.append( keyEnc ).append( '=' ).append( encode((String)val) ); - } else { - if( !(val instanceof LuanTable) ) - throw new LuanException( "parameter '"+key+"' must be string or table" ); - LuanTable t = (LuanTable)val; - if( !t.isList() ) - throw new LuanException( "parameter '"+key+"' table must be list" ); - for( Object obj : t.asList() ) { - if( !(obj instanceof String) ) - throw new LuanException( "parameter '"+key+"' values must be strings" ); - and(sb); - sb.append( keyEnc ).append( '=' ).append( encode((String)obj) ); - } - } - } - if( this.method==Method.DELETE ) - throw new LuanException( "the DELETE method cannot take parameters" ); - if( this.method==Method.POST ) { - content = sb.toString(); - } else { // GET - String urlS = this.url.toString(); - if( urlS.indexOf('?') == -1 ) { - urlS += '?'; - } else { - urlS += '&'; - } - urlS += sb; - try { - this.url = new URL(urlS); - } catch(IOException e) { - throw new RuntimeException(e); - } - } - } - Integer timeout = getInt(map,"time_out"); - if( timeout != null ) - this.timeout = timeout; - if( !map.isEmpty() ) - throw new LuanException( "unrecognized options: "+map ); - } - } - - public static String toHttpHeaderName(String luanName) { - luanName = luanName.toLowerCase(); - StringBuilder buf = new StringBuilder(); - boolean capitalize = true; - char[] a = luanName.toCharArray(); - for( int i=0; i<a.length; i++ ) { - char c = a[i]; - if( c == '_' || c == '-' ) { - a[i] = '-'; - capitalize = true; - } else if( capitalize ) { - a[i] = Character.toUpperCase(c); - capitalize = false; - } - } - return String.valueOf(a); - } - - private static void and(StringBuilder sb) { - if( sb.length() > 0 ) - sb.append('&'); - } - - private static String encode(String s) { - try { - return URLEncoder.encode(s,"UTF-8"); - } catch(UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - private static String getString(Map map,String key) throws LuanException { - Object val = map.remove(key); - if( val!=null && !(val instanceof String) ) - throw new LuanException( "parameter '"+key+"' must be a string" ); - return (String)val; - } - - private static Integer getInt(Map map,String key) throws LuanException { - Object val = map.remove(key); - if( val==null ) - return null; - Integer i = Luan.asInteger(val); - if( i==null ) - throw new LuanException( "parameter '"+key+"' must be an integer" ); - return i; - } - - private static LuanTable getTable(Map map,String key) throws LuanException { - Object val = map.remove(key); - if( val!=null && !(val instanceof LuanTable) ) - throw new LuanException( "parameter '"+key+"' must be a table" ); - return (LuanTable)val; - } - - private static Map getMap(LuanState luan,Map map,String key) throws LuanException { - LuanTable t = getTable(map,key); - return t==null ? null : t.asMap(luan); - } - - @Override public InputStream inputStream() throws IOException, LuanException { - URLConnection con = url.openConnection(); - if( timeout != 0 ) { - con.setConnectTimeout(timeout); - con.setReadTimeout(timeout); - } - if( headers != null ) { - for( Object hack : headers.entrySet() ) { - Map.Entry entry = (Map.Entry)hack; - String key = (String)entry.getKey(); - Object val = entry.getValue(); - if( val instanceof String ) { - con.addRequestProperty(key,(String)val); - } else { - List list = (List)val; - for( Object obj : list ) { - con.addRequestProperty(key,(String)obj); - } - } - } - } - if( method==Method.GET ) { - return con.getInputStream(); - } - - HttpURLConnection httpCon = (HttpURLConnection)con; - - if( method==Method.DELETE ) { - httpCon.setRequestMethod("DELETE"); - return httpCon.getInputStream(); - } - - // POST - -// httpCon.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); - httpCon.setDoOutput(true); - httpCon.setRequestMethod("POST"); - - OutputStream out; - if( multipart != null ) { - out = multipart.write(httpCon); - } else { - byte[] post = content.getBytes(); -// httpCon.setRequestProperty("Content-Length",Integer.toString(post.length)); - out = httpCon.getOutputStream(); - out.write(post); - } - out.flush(); - try { - try { - return httpCon.getInputStream(); - } catch(IOException e) { - InputStream is = httpCon.getErrorStream(); - if( is == null ) - throw e; - Reader in = new InputStreamReader(is); - String msg = Utils.readAll(in); - in.close(); - throw new LuanException(msg,e); - } - } finally { - out.close(); - } - } - - @Override public String to_string() { - return url.toString(); - } - - @Override public String to_uri_string() { - return url.toString(); - } -/* - public String post(String postS) throws IOException { - return new UrlCall(url).post(postS); - } - - @Override public LuanTable table() { - LuanTable tbl = super.table(); - try { - tbl.rawPut( "post", new LuanJavaFunction( - LuanUrl.class.getMethod( "post", String.class ), this - ) ); - } catch(NoSuchMethodException e) { - throw new RuntimeException(e); - } - return tbl; - } -*/ -}
--- a/core/src/luan/modules/url/MultiPartOutputStream.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// -// This horrible broken code from jetty is just here for me to look at. It isn't used. -fschmidt - -package luan.modules.url; - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; - - -/* ================================================================ */ -/** Handle a multipart MIME response. - * - * - * -*/ -public class MultiPartOutputStream extends FilterOutputStream -{ - /* ------------------------------------------------------------ */ - private static final byte[] __CRLF={'\r','\n'}; - private static final byte[] __DASHDASH={'-','-'}; - - public static String MULTIPART_MIXED="multipart/mixed"; - public static String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace"; - public static final String __ISO_8859_1="ISO-8859-1"; - - public static String newBoundary(Object obj) { - return "jetty"+System.identityHashCode(obj)+ - Long.toString(System.currentTimeMillis(),36); - } - - /* ------------------------------------------------------------ */ - private final String boundary; - private final byte[] boundaryBytes; - - /* ------------------------------------------------------------ */ - private boolean inPart=false; - - /* ------------------------------------------------------------ */ - public MultiPartOutputStream(OutputStream out,String boundary) - throws IOException - { - super(out); - - this.boundary = boundary; - boundaryBytes=boundary.getBytes(__ISO_8859_1); - - inPart=false; - } - - - - /* ------------------------------------------------------------ */ - /** End the current part. - * @exception IOException IOException - */ - @Override - public void close() - throws IOException - { - if (inPart) - out.write(__CRLF); - out.write(__DASHDASH); - out.write(boundaryBytes); - out.write(__DASHDASH); - out.write(__CRLF); - inPart=false; - super.close(); - } - - /* ------------------------------------------------------------ */ - public String getBoundary() - { - return boundary; - } - - public OutputStream getOut() {return out;} - - /* ------------------------------------------------------------ */ - /** Start creation of the next Content. - */ - public void startPart(String contentType) - throws IOException - { - if (inPart) - out.write(__CRLF); - inPart=true; - out.write(__DASHDASH); - out.write(boundaryBytes); - out.write(__CRLF); - if (contentType != null) - out.write(("Content-Type: "+contentType).getBytes(__ISO_8859_1)); - out.write(__CRLF); - out.write(__CRLF); - } - - /* ------------------------------------------------------------ */ - /** Start creation of the next Content. - */ - public void startPart(String contentType, String[] headers) - throws IOException - { - if (inPart) - out.write(__CRLF); - inPart=true; - out.write(__DASHDASH); - out.write(boundaryBytes); - out.write(__CRLF); - if (contentType != null) - out.write(("Content-Type: "+contentType).getBytes(__ISO_8859_1)); - out.write(__CRLF); - for (int i=0;headers!=null && i<headers.length;i++) - { - out.write(headers[i].getBytes(__ISO_8859_1)); - out.write(__CRLF); - } - out.write(__CRLF); - } - - /* ------------------------------------------------------------ */ - @Override - public void write(byte[] b, int off, int len) throws IOException - { - out.write(b,off,len); - } -} - - - -
--- a/core/src/luan/modules/url/MultipartClient.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -package luan.modules.url; - -import java.io.OutputStream; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; -import luan.LuanTable; -import luan.LuanException; - - -public final class MultipartClient { - private static final byte[] __CRLF = {'\r','\n'}; - private static final byte[] __DASHDASH = {'-','-'}; - private static final String __ISO_8859_1 = "ISO-8859-1"; - - private final Map params = new HashMap(); - - MultipartClient(Map params) throws LuanException { - for( Object hack : params.entrySet() ) { - Map.Entry entry = (Map.Entry)hack; - String key = (String)entry.getKey(); - Object val = entry.getValue(); - List list = new ArrayList(); - if( val instanceof String ) { - list.add(val); - } else { - if( !(val instanceof LuanTable) ) - throw new LuanException( "parameter '"+key+"' must be string or table" ); - LuanTable t = (LuanTable)val; - if( !t.isList() ) - throw new LuanException( "parameter '"+key+"' table must be list" ); - for( Object obj : t.asList() ) { - if( !(obj instanceof String) ) - throw new LuanException( "parameter '"+key+"' values must be strings" ); - list.add(obj); - } - } - this.params.put(key,list); - } - } - - public OutputStream write(HttpURLConnection httpCon) throws IOException { - String boundary = "luan" + System.identityHashCode(this) + Long.toString(System.currentTimeMillis(),36); - byte[] boundaryBytes = boundary.getBytes(__ISO_8859_1); - - httpCon.setRequestProperty("Content-Type","multipart/form-data; boundary="+boundary); - OutputStream out = httpCon.getOutputStream(); - for( Object hack : params.entrySet() ) { - Map.Entry entry = (Map.Entry)hack; - String name = (String)entry.getKey(); - List list = (List)entry.getValue(); - for( Object obj : list ) { - String val = (String)obj; - out.write(__DASHDASH); - out.write(boundaryBytes); - out.write(__CRLF); - out.write(("Content-Disposition: form-data; name=\""+name+"\"").getBytes(__ISO_8859_1)); - out.write(__CRLF); - out.write(__CRLF); - out.write(val.getBytes()); - out.write(__CRLF); - } - } - out.write(__DASHDASH); - out.write(boundaryBytes); - out.write(__DASHDASH); - out.write(__CRLF); - return out; - } -}
--- a/core/src/luan/modules/url/UrlCall.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -// not used, just for reference - -package luan.modules.url; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.io.IOException; -import java.net.URLConnection; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Map; -import java.util.HashMap; -import luan.modules.Utils; - - -public final class UrlCall { - public final URLConnection connection; - - public UrlCall(String url) throws IOException { - this(new URL(url)); - } - - public UrlCall(URL url) throws IOException { - connection = url.openConnection(); - } - - public void acceptJson() { - connection.setRequestProperty("accept","application/json"); - } - - public String get() throws IOException { - Reader in = new InputStreamReader(connection.getInputStream()); - String rtn = Utils.readAll(in); - in.close(); - return rtn; - } - - public String post(String content,String contentType) throws IOException { - HttpURLConnection connection = (HttpURLConnection)this.connection; - - connection.setRequestProperty("Content-type",contentType); - connection.setDoOutput(true); - connection.setRequestMethod("POST"); - - byte[] post = content.getBytes(); - connection.setRequestProperty("Content-Length",Integer.toString(post.length)); - OutputStream out = connection.getOutputStream(); - out.write(post); - out.flush(); - - Reader in; - try { - in = new InputStreamReader(connection.getInputStream()); - } catch(IOException e) { - InputStream is = connection.getErrorStream(); - if( is == null ) - throw e; - in = new InputStreamReader(is); - String msg = Utils.readAll(in); - in.close(); - throw new UrlCallException(msg,e); - } - String rtn = Utils.readAll(in); - in.close(); - out.close(); - return rtn; - } - - public String post(String content) throws IOException { - return post(content,"application/x-www-form-urlencoded"); - } - - public String postJson(String content) throws IOException { - return post(content,"application/json"); - } - - public static final class UrlCallException extends IOException { - UrlCallException(String msg,IOException e) { - super(msg,e); - } - } -}
--- a/core/src/luan/modules/which.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local values = Luan.values or error() -local Which_mod = require "luan:Which_mod.luan" -local which = Which_mod.which or error() - -for name in values(...) do - which(name) -end
--- a/http/src/luan/modules/http/AuthenticationHandler.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -package luan.modules.http; - -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.util.B64Code; - - -public class AuthenticationHandler extends AbstractHandler { - private final String path; - private String password = "password"; - - public AuthenticationHandler(String path) { - this.path = path; - } - - public void setPassword(String password) { - this.password = password; - } - - public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) - throws IOException - { - if( !target.startsWith(path) ) - return; - String pwd = getPassword(request); - if( password.equals(pwd) ) - return; - response.setHeader("WWW-Authenticate","Basic realm=\""+path+"\""); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - baseRequest.setHandled(true); - } - - private static String getPassword(HttpServletRequest request) { - String auth = request.getHeader("Authorization"); - if( auth==null ) - return null; - String[] a = auth.split(" +"); - if( a.length != 2 ) - throw new RuntimeException("auth = "+auth); - if( !a[0].equals("Basic") ) - throw new RuntimeException("auth = "+auth); - auth = new String(B64Code.decode(a[1])); - a = auth.split(":"); - if( a.length != 2 ) - throw new RuntimeException("auth = "+auth); - return a[1]; - } -}
--- a/http/src/luan/modules/http/Dump_mod.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -local Luan = require "luan:Luan.luan" -local pairs = Luan.pairs -local ipairs = Luan.ipairs -local Io = require "luan:Io.luan" -local Http = require "luan:http/Http.luan" -java() -local HttpServicer = require "java:luan.modules.http.HttpServicer" - -local M = {} - -local to_http_header_name = HttpServicer.toHttpHeaderName -M.to_http_header_name = to_http_header_name - -function M.respond() - Http.response.header.content_type = "text/plain" - Io.stdout = Http.response.text_writer() - - local method = Http.request.method - local path = Http.request.path - local query = Http.request.query_string() - if method ~= "POST" and query ~= nil then - path = path.."?"..query - end -%> -<%=method%> <%=path%> <%=Http.request.protocol%> -<% - M.dump_headers(Http.request.headers) -%> - -<% - if method == "POST" and query ~= nil then -%> -<%=query%> -<% - end -end - - -function M.dump_headers(headers) - for name, values in pairs(headers) do - local header_name = to_http_header_name(name) - for _, value in ipairs(values) do -%> -<%=header_name%>: <%=value%> -<% - end - end -end - -return M
--- a/http/src/luan/modules/http/Http.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +0,0 @@ -java() -local Luan = require "luan:Luan.luan" -local error = Luan.error -local ipairs = Luan.ipairs or error() -local pairs = Luan.pairs or error() -local set_metatable = Luan.set_metatable or error() -local Io = require "luan:Io.luan" -local Html = require "luan:Html.luan" -local url_encode = Html.url_encode or error() -local Table = require "luan:Table.luan" -local clear = Table.clear or error() -local Package = require "luan:Package.luan" -local String = require "luan:String.luan" -local matches = String.matches or error() -local HttpServicer = require "java:luan.modules.http.HttpServicer" -local IoLuan = require "java:luan.modules.IoLuan" - -local M = {} - -local singular_metatable = {} - -function singular_metatable.__index(table,key) - local list = table.__plural[key] - return list and list[1] -end - -function singular_metatable.__new_index(table,key,value) - table.__plural[key] = value and {value} -end - -function singular_metatable.__pairs(table) - local iter = pairs(table.__plural) - return function() - local key, value = iter() - return key, value and value[1] - end -end - -local function sent_error() - error "headers are not accessible after you start writing content" -end - -local sent_error_metatable = { __index=sent_error, __new_index=sent_error } - -function M.sent_headers(headers) - clear(headers) - set_metatable(headers,sent_error_metatable) -end - - -local function new_common(this) - this = this or {} - this.headers = {} - this.header = {__plural=this.headers} - set_metatable(this.header,singular_metatable) - return this -end - - -function M.new_request(this) - this = new_common(this) - this.method = "GET" -- default - -- this.path - -- this.protocol - this.scheme = "http" -- default - this.port = 80 -- default - this.parameters = {} - this.parameter = {__plural=this.parameters} - set_metatable(this.parameter,singular_metatable) - this.cookie = {} - - function this.query_string() - local string_uri = Io.uri "string:" - local out = string_uri.text_writer() - local and_char = "" - for name, values in pairs(this.parameters) do - for _, value in ipairs(values) do - out.write( and_char, url_encode(name), "=", url_encode(value) ) - and_char = "&" - end - end - out.close() - local s = string_uri.read_text() - return s ~= "" and s or nil - end - - function this.url() - local url = this.scheme.."://"..this.header.host..this.path - if this.method ~= "POST" then - local query = this.query_string() - if query ~= nil then - url = url.."?"..query - end - end - return url - end - - return this -end - -local STATUS = { - OK = 200; - -- add more as needed -} -M.STATUS = STATUS - -function M.new_response(this) - this = new_common(this) - this.status = STATUS.OK - if this.java ~= nil then - this.send_redirect = this.java.sendRedirect - this.send_error = this.java.sendError - - function this.set_cookie(name,value,is_persistent,domain) - HttpServicer.setCookie(M.request.java,this.java,name,value,is_persistent,domain) - end - - function this.remove_cookie(name,domain) - HttpServicer.removeCookie(M.request.java,this.java,name,domain) - end - - function this.set() - HttpServicer.setResponse(this,this.java) - M.sent_headers(this.headers) - end - - function this.text_writer() - this.set() - return IoLuan.textWriter(this.java.getWriter()) - end - - function this.binary_writer() - this.set() - return IoLuan.binaryWriter(this.java.getOutputStream()) - end - - function this.reset() - this.java.reset() - set_metatable(this.headers,nil) - end - end - return this -end - --- request = new_request{} -- filled in by HttpServicer --- response = new_response{} -- filled in by HttpServicer - - -M.per_session_pages = {} - -function M.per_session(page) - M.per_session_pages[page] = true -end - -function M.clear_session() - M.request.java.getSession().removeAttribute("luan") -end - - -function M.uncache_site() - for k in pairs(Table.copy(Package.loaded)) do - if matches(k,"^site:") then - Package.loaded[k] = nil - end - end -end - -M.run_later = HttpServicer.run_later - -return M
--- a/http/src/luan/modules/http/HttpServicer.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,302 +0,0 @@ -package luan.modules.http; - -import java.io.InputStream; -import java.io.BufferedInputStream; -import java.io.PrintWriter; -import java.io.IOException; -import java.util.Map; -import java.util.Set; -import java.util.List; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Enumeration; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.Part; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.eclipse.jetty.util.MultiPartInputStream; -import luan.Luan; -import luan.LuanState; -import luan.LuanFunction; -import luan.LuanException; -import luan.LuanTable; -import luan.LuanMeta; -import luan.LuanPropertyMeta; -import luan.DeepCloner; -import luan.modules.PackageLuan; -import luan.modules.IoLuan; -import luan.modules.TableLuan; -import luan.modules.Utils; -import luan.modules.url.LuanUrl; - - -public final class HttpServicer { - private static final Logger logger = LoggerFactory.getLogger(HttpServicer.class); - - public static boolean service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName) - throws LuanException - { - LuanFunction fn; - synchronized(luan) { - LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan"); - LuanTable per_session_pages = (LuanTable)module.rawGet("per_session_pages"); - Object mod = PackageLuan.load(luan,modName); - if( mod==null ) - return false; - if( !(mod instanceof LuanFunction) ) - throw new LuanException( "module '"+modName+"' must return a function" ); - if( Boolean.TRUE.equals(per_session_pages.rawGet(mod)) ) { - HttpSession session = request.getSession(); - LuanState sessionLuan = (LuanState)session.getAttribute("luan"); - if( sessionLuan!=null ) { - luan = sessionLuan; - } else { - DeepCloner cloner = new DeepCloner(); - luan = (LuanState)cloner.deepClone(luan); - session.setAttribute("luan",luan); - } - fn = (LuanFunction)PackageLuan.require(luan,modName); - } else { - DeepCloner cloner = new DeepCloner(); - luan = (LuanState)cloner.deepClone(luan); - fn = (LuanFunction)cloner.get(mod); - } - } - - LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan"); - - // request - LuanFunction newRequestFn = (LuanFunction)module.rawGet("new_request"); - LuanTable requestTbl = (LuanTable)newRequestFn.call(luan); - module.rawPut("request",requestTbl); - requestTbl.rawPut("java",request); - requestTbl.rawPut("method",request.getMethod()); - requestTbl.rawPut("path",request.getRequestURI()); - requestTbl.rawPut("protocol",request.getProtocol()); - requestTbl.rawPut("scheme",request.getScheme()); - requestTbl.rawPut("port",request.getServerPort()); - - LuanTable headersTbl = (LuanTable)requestTbl.rawGet("headers"); - for( Enumeration<String> enKeys = request.getHeaderNames(); enKeys.hasMoreElements(); ) { - String key = enKeys.nextElement(); - LuanTable values = new LuanTable(); - for( Enumeration<String> en = request.getHeaders(key); en.hasMoreElements(); ) { - values.rawPut(values.rawLength()+1,en.nextElement()); - } - key = toLuanHeaderName(key); - headersTbl.rawPut(key,values); - } - - LuanTable parametersTbl = (LuanTable)requestTbl.rawGet("parameters"); - String contentType = request.getContentType(); - if( contentType==null || !contentType.startsWith("multipart/form-data") ) { - for( Map.Entry<String,String[]> entry : request.getParameterMap().entrySet() ) { - parametersTbl.rawPut(entry.getKey(),new LuanTable(Arrays.asList(entry.getValue()))); - } - } else { // multipart - try { - InputStream in = new BufferedInputStream(request.getInputStream()); - final MultiPartInputStream mpis = new MultiPartInputStream(in,contentType,null,null); - mpis.setDeleteOnExit(true); - for( Part p : mpis.getParts() ) { - final MultiPartInputStream.MultiPart part = (MultiPartInputStream.MultiPart)p; - String name = part.getName(); -/* -System.out.println("name = "+name); -System.out.println("getContentType = "+part.getContentType()); -System.out.println("getHeaderNames = "+part.getHeaderNames()); -System.out.println("content-disposition = "+part.getHeader("content-disposition")); -System.out.println(); -*/ - Object value; - String filename = part.getContentDispositionFilename(); - if( filename == null ) { - value = new String(part.getBytes()); - } else { - LuanTable partTbl = LuanPropertyMeta.INSTANCE.newTable(); - partTbl.rawPut("filename",filename); - partTbl.rawPut("content_type",part.getContentType()); - LuanPropertyMeta.INSTANCE.getters(partTbl).rawPut( "content", new LuanFunction() { - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - try { - InputStream in = part.getInputStream(); - byte[] content = Utils.readAll(in); - in.close(); - return content; - } catch(IOException e) { - throw new RuntimeException(e); - } - } - } ); - value = partTbl; - } - LuanTable list = (LuanTable)parametersTbl.rawGet(name); - if( list == null ) { - list = new LuanTable(); - parametersTbl.rawPut(name,list); - } - list.rawPut(parametersTbl.rawLength()+1,value); - } - } catch(IOException e) { - throw new RuntimeException(e); - } catch(ServletException e) { - throw new RuntimeException(e); - } - } - - LuanTable cookieTbl = (LuanTable)requestTbl.rawGet("cookie"); - for( Cookie cookie : request.getCookies() ) { - cookieTbl.rawPut( cookie.getName(), unescape(cookie.getValue()) ); - } - - - // response - LuanTable responseTbl = new LuanTable(); - responseTbl.rawPut("java",response); - LuanFunction newResponseFn = (LuanFunction)module.rawGet("new_response"); - newResponseFn.call( luan, new Object[]{responseTbl} ); - module.rawPut("response",responseTbl); - - fn.call(luan); - handle_run_later(luan); - return true; - } - - public static void setResponse(LuanTable responseTbl,HttpServletResponse response) throws LuanException { - int status = Luan.asInteger(responseTbl.rawGet("status")); - response.setStatus(status); - LuanTable responseHeaders = (LuanTable)responseTbl.rawGet("headers"); - for( Map.Entry<Object,Object> entry : responseHeaders.rawIterable() ) { - String name = (String)entry.getKey(); - name = toHttpHeaderName(name); - LuanTable values = (LuanTable)entry.getValue(); - for( Object value : values.asList() ) { - if( value instanceof String ) { - response.setHeader(name,(String)value); - continue; - } - Integer i = Luan.asInteger(value); - if( i != null ) { - response.setIntHeader(name,i); - continue; - } - throw new IllegalArgumentException("value must be string or integer for headers table"); - } - } - } - - - - // static utils - - public static String toLuanHeaderName(String httpName) { - return httpName.toLowerCase().replace('-','_'); - } - - public static String toHttpHeaderName(String luanName) { -/* - StringBuilder buf = new StringBuilder(); - boolean capitalize = true; - char[] a = luanName.toCharArray(); - for( int i=0; i<a.length; i++ ) { - char c = a[i]; - if( c == '_' ) { - a[i] = '-'; - capitalize = true; - } else if( capitalize ) { - a[i] = Character.toUpperCase(c); - capitalize = false; - } - } - return String.valueOf(a); -*/ - return LuanUrl.toHttpHeaderName(luanName); - } - - private static String escape(String value) { - return value.replaceAll(";", "%3B"); - } - - private static String unescape(String value) { - return value.replaceAll("%3B", ";"); - } - - private static Cookie getCookie(HttpServletRequest request,String name) { - Cookie[] cookies = request.getCookies(); - if( cookies == null ) - return null; - for (Cookie cookie : cookies) { - if (cookie.getName().equals(name)) - return cookie; - } - return null; - } - - public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) { - Cookie cookie = getCookie(request,name); - if( cookie==null || !cookie.getValue().equals(value) ) { - cookie = new Cookie(name, escape(value)); - cookie.setPath("/"); - if (domain != null && domain.length() > 0) - cookie.setDomain(domain); - if( isPersistent ) - cookie.setMaxAge(10000000); - response.addCookie(cookie); - } - } - - public static void removeCookie(HttpServletRequest request, - HttpServletResponse response, - String name, - String domain - ) { - Cookie cookie = getCookie(request, name); - if(cookie != null) { - Cookie delCookie = new Cookie(name, "delete"); - delCookie.setPath("/"); - delCookie.setMaxAge(0); - if (domain != null && domain.length() > 0) - delCookie.setDomain(domain); - response.addCookie(delCookie); - } - } - - - - private static String RUN_LATER_KEY = "Http.run_later"; - private static final Executor exec = Executors.newSingleThreadExecutor(); - - public static void run_later(final LuanState luan,final LuanFunction fn,final Object... args) { - List list = (List)luan.registry().get(RUN_LATER_KEY); - if( list == null ) { - list = new ArrayList(); - luan.registry().put(RUN_LATER_KEY,list); - } - list.add( - new Runnable(){public void run() { - try { - fn.call(luan,args); - } catch(LuanException e) { - e.printStackTrace(); - } - }} - ); - } - - private static void handle_run_later(LuanState luan) { - List list = (List)luan.registry().get(RUN_LATER_KEY); - if( list==null ) - return; - for( Object obj : list ) { - exec.execute((Runnable)obj); - } - } -}
--- a/http/src/luan/modules/http/Http_test.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local set_metatable = Luan.set_metatable or error() -local try = Luan.try or error() -local Package = require "luan:Package.luan" -local Io = require "luan:Io.luan" -local String = require "luan:String.luan" -local matches = String.matches or error() -local Http = require "luan:http/Http.luan" - - -local M = {} - -M.welcome_file = "index.html" -M.cookie = {} - -function M.get_page(path) - Http.request.path = path - if M.welcome_file ~= nil and matches(path,"/$") then - path = path .. M.welcome_file - end - local old_out = Io.stdout - try { - function() - local mod = Package.load("site:"..path..".luan") - if mod ~= nil then - mod() - else - local not_found = Package.load("site:/not_found.luan") - not_found or error(path.." not found") - not_found() - end - M.text_writer.close() - end - finally = function() - Io.stdout = old_out - end - } - return M.result.read_text() -end - -function M.init() - Http.request = Http.new_request{} - Http.request.cookie = M.cookie - - Http.response = Http.new_response{ - - text_writer = function() - Http.sent_headers(Http.response.headers) - M.result = Io.uri "string:" - M.text_writer = M.result.text_writer() - return M.text_writer - end - - set_cookie = function(name,value) - M.cookie[name] = value - end - - remove_cookie = function(name) - M.cookie[name] = nil - end - - send_redirect = function(url) - Http.response.redirect = url - end - - send_error = function(code) - error("sent error "..code) - end - - } - -end - -return M
--- a/http/src/luan/modules/http/LuanHandler.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -package luan.modules.http; - -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.DeepCloner; -import luan.LuanException; -import luan.modules.PackageLuan; - - -public class LuanHandler extends AbstractHandler { - private final LuanState luan; - private final Logger logger; - private String welcomeFile = "index.html"; - - public LuanHandler(LuanState luan,String loggerRoot) { - this.luan = luan; - if( loggerRoot==null ) - loggerRoot = ""; - logger = LoggerFactory.getLogger(loggerRoot+LuanHandler.class.getName()); - } - - @Override public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) - throws IOException - { - if( target.endsWith("/") ) - target += welcomeFile; - try { - if( !HttpServicer.service(luan,request,response,"site:"+target+".luan") ) - return; - } catch(LuanException e) { - String err = e.getFullMessage(); - logger.error(err); - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,err); - } - baseRequest.setHandled(true); - } - - public void setWelcomeFile(String welcomeFile) { - this.welcomeFile = welcomeFile; - } - - @Override protected void doStop() throws Exception { - synchronized(luan) { - luan.close(); - } -//System.out.println("qqqqqqqqqqqqqqqqqqqq doStop "+this); - super.doStop(); - } -/* - @Override public void destroy() { -System.out.println("qqqqqqqqqqqqqqqqqqqq destroy "+this); - super.destroy(); - } -*/ - - public Object call_rpc(String fnName,Object... args) throws LuanException { - return callRpc(luan,fnName,args); - } - - public static Object callRpc(LuanState luan,String fnName,Object... args) throws LuanException { - synchronized(luan) { - DeepCloner cloner = new DeepCloner(); - luan = (LuanState)cloner.deepClone(luan); - } - LuanTable rpc = (LuanTable)PackageLuan.require(luan,"luan:Rpc.luan"); - LuanTable fns = (LuanTable)rpc.get(luan,"functions"); - LuanFunction fn = (LuanFunction)fns.get(luan,fnName); - if( fn == null ) - throw new LuanException( "function not found: " + fnName ); - return fn.call(luan,args); - } - -}
--- a/http/src/luan/modules/http/NotFound.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -package luan.modules.http; - -import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.Request; -import luan.LuanState; - - -public class NotFound extends LuanHandler { - - public NotFound(LuanState luan,String loggerRoot) { - super(luan,loggerRoot); - } - - @Override public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) - throws IOException - { - super.handle("/not_found",baseRequest,request,response); - } - -}
--- a/http/src/luan/modules/http/Server.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local String = require "luan:String.luan" -local gsub = String.gsub or error() -local matches = String.matches or error() -local Io = require "luan:Io.luan" -local Package = require "luan:Package.luan" -local Rpc = require "luan:Rpc.luan" -local Thread = require "luan:Thread.luan" -local Http = require "luan:http/Http.luan" -require "luan:logging/init.luan" -- initialize logging -local Logging = require "luan:logging/Logging.luan" -local logger = Logging.logger "http/Server" - -java() -local Server = require "java:org.eclipse.jetty.server.Server" -local NCSARequestLog = require "java:org.eclipse.jetty.server.NCSARequestLog" -local DefaultHandler = require "java:org.eclipse.jetty.server.handler.DefaultHandler" -local HandlerList = require "java:org.eclipse.jetty.server.handler.HandlerList" -local HandlerCollection = require "java:org.eclipse.jetty.server.handler.HandlerCollection" -local ResourceHandler = require "java:org.eclipse.jetty.server.handler.ResourceHandler" -local RequestLogHandler = require "java:org.eclipse.jetty.server.handler.RequestLogHandler" -local ContextHandler = require "java:org.eclipse.jetty.server.handler.ContextHandler" -local GzipHandler = require "java:org.eclipse.jetty.server.handler.GzipHandler" -local HandlerWrapper = require "java:org.eclipse.jetty.server.handler.HandlerWrapper" -local SessionHandler = require "java:org.eclipse.jetty.server.session.SessionHandler" -local AuthenticationHandler = require "java:luan.modules.http.AuthenticationHandler" -local LuanHandler = require "java:luan.modules.http.LuanHandler" -local NotFound = require "java:luan.modules.http.NotFound" - -local M = {} - -M.port = 8080 - -M.welcome_file = "index.html" - - -M.authentication_handler = AuthenticationHandler.new("/private/") - -M.luan_handler = LuanHandler.new() - -M.resource_handler = ResourceHandler.new() -M.resource_handler.setDirectoriesListed(true) - -M.handlers = HandlerList.new() -M.handlers.setHandlers { M.authentication_handler, M.luan_handler, M.resource_handler } - -function M.add_folder(context,dir) - local rh = ResourceHandler.new() - rh.setResourceBase(dir) - rh.setDirectoriesListed(true) - local ch = ContextHandler.new(context) - ch.setHandler(rh) - M.handlers.addHandler(ch) - return rh -end - -M.handler_wrapper = HandlerWrapper.new() -M.handler_wrapper.setHandler(M.handlers) - -function M.zip() - local h = GzipHandler.new() - h.setHandler(M.handler_wrapper.getHandler()) - M.handler_wrapper.setHandler(h) -end - -M.log = NCSARequestLog.new() -M.log.setExtended(false) -M.log_handler = RequestLogHandler.new() -M.log_handler.setRequestLog(M.log) - -function M.set_log_file(file_name) - M.log.setFilename(file_name) -end - -local hc = HandlerCollection.new() -hc.setHandlers { SessionHandler.new(), M.handler_wrapper, DefaultHandler.new(), M.log_handler } - - -function M.init(dir) - dir = gsub(dir,"/$","") -- remove trailing '/' if any - Http.dir = dir - function Io.schemes.site(path) - return Io.uri( dir..path ) - end - M.authentication_handler.setPassword(Io.password) - local base = dir - if matches(base,"^classpath:") then - base = dir.."#"..M.welcome_file.."#"..M.welcome_file..".luan" - end - M.resource_handler.setResourceBase(Io.uri(base).to_string()) - M.resource_handler.setWelcomeFiles {M.welcome_file} - M.luan_handler.setWelcomeFile(M.welcome_file) - M.handlers.addHandler(NotFound.new()) - M.server = Server.new(M.port) - M.server.setHandler(hc) - Package.load("site:/init.luan") -end - -function M.start() - M.server.start() -end - -function M.start_rpc() - function Rpc.functions.call(domain,fn_name,...) - return M.luan_handler.call_rpc(fn_name,...) - end - - Thread.fork(Rpc.serve) -end - -function M.serve(dir) - M.init(dir) - M.start() - M.start_rpc() -end - -return M
--- a/http/src/luan/modules/http/Shell_mod.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local ipairs = Luan.ipairs or error() -local load = Luan.load or error() -local try = Luan.try or error() -local Io = require "luan:Io.luan" -local print = Io.print or error() -local Http = require "luan:http/Http.luan" - - -local M = {} - -local history = {} -M.env = {} - -function M.respond() - if Http.request.parameter.clear ~= nil then - Http.clear_session() - Http.response.send_redirect(Http.request.path) -- reload page - return - else - local cmd = Http.request.parameter.cmd - if cmd ~= nil then - Io.stdout = {} - function Io.stdout.write(...) - for v in Luan.values(...) do - history[#history+1] = v - end - end - print( "% "..cmd ) - try { - function() - local line - try { - function() - line = load("return "..cmd,"<web_shell>",M.env) - end - catch = function(e) - line = load(cmd,"<web_shell>",M.env) - end - } - print( line() ) - end - catch = function(e) - Io.print_to(Io.stderr,e) - print(e) - end - } - end - end - - Io.stdout = Http.response.text_writer() -%> -<html> - <head> - <title>Luan Shell</title> - <style> - body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - margin: 2em 5% 0 5%; - } - pre { - font: inherit; - } - input[type="text"] { - font: inherit; - padding: .5em .8em; - border-radius: 8px; - border-style: groove; - } - input[type="text"]:focus { - border-color: #66afe9; - outline: none; - } - input[type="submit"] { - color: white; - background: #337ab7; - border-color: #337ab7; - font: inherit; - padding: .5em; - border-radius: 4px; - } - input[type="submit"]:hover { - background: #236aa7 !important; - } - </style> - </head> - <body> - <h2>Luan Shell</h2> - <p>This is a command shell. Enter commands below.</p> - <pre><% - for _,v in ipairs(history) do - Io.stdout.write(v) - end - %></pre> - <form name='form0' method='post'> - % <input type="text" name='cmd' size="80" autofocus> - <input type="submit" value="run"> - <input type="submit" name="clear" value="clear"> - </form> - </body> -</html> -<% -end - -Http.per_session(M.respond) - -return M
--- a/http/src/luan/modules/http/dump.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -return require("luan:http/Dump_mod.luan").respond
--- a/http/src/luan/modules/http/run.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local load = Luan.load or error() -local try = Luan.try or error() -local Io = require "luan:Io.luan" -local print = Io.print or error() -local String = require "luan:String.luan" -local gmatch = String.gmatch or error() -local Http = require "luan:http/Http.luan" - - -local function lines(s) - local matcher = gmatch(s,"([^\n]*)\n|([^\n])+$") - return function() - local m1, m2 = matcher() - return m1 or m2 - end -end - -local function print_with_line_numbers(s) - local i = 1 - for line in lines(s) do - print(i,line) - i = i + 1 - end -end - -local function form() %> -<html> - <head> - <title>Run Luan Code</title> - <style> - body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - text-align: center; - margin-top: 1em; - } - h2 { - margin-bottom: .3em; - font-weight: normal; - } - textarea { - font: inherit; - border-radius: 4px; - padding: .5em .8em; - } - input[type="submit"] { - margin-top: .3em; - color: white; - background: #337ab7; - border-color: #337ab7; - font: inherit; - padding: .5em; - border-radius: 4px; - } - input[type="submit"]:hover { - background: #236aa7 !important; - } - </style> - </head> - <body> - <h2>Run Luan Code</h2> - <form name="form0" method="post"> - <input type="hidden" name="content_type" value="text/plain" /> - <div> - <textarea name="code" rows="20" cols="90" wrap="off" autofocus></textarea> - </div> - <div> - <input type="submit" value="Execute Luan Code"/> - </div> - </form> - </body> -</html> -<% end - -return function() - local content_type = Http.request.parameter.content_type - if content_type ~= nil then - Http.response.header.content_type = content_type - end - Io.stdout = Http.response.text_writer() - local code = Http.request.parameter.code - if code == nil then - form() - return - end - local env = { - request = Http.request; - response = Http.response; - } - try { - function() - local run = load(code,"<web_run>",env) - run() - end; - catch = function(e) - Http.response.reset() - Http.response.header.content_type = "text/plain" - Io.stdout = Http.response.text_writer() - print(e) - print"" - print"" - print_with_line_numbers(code) - end; - } -end
--- a/http/src/luan/modules/http/serve.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -local Io = require "luan:Io.luan" -local Server = require "luan:http/Server.luan" - -if #{...} ~= 1 then - Io.stderr.write "usage: luan luan:http/serve dir-URI\n" - return -end - -Server.serve(...)
--- a/http/src/luan/modules/http/shell.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -return require("luan:http/Shell_mod.luan").respond
--- a/http/src/luan/modules/http/test.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -local Luan = require "luan:Luan.luan" -local Io = require "luan:Io.luan" -local Server = require "luan:http/Server.luan" - -if #{...} ~= 2 then - Io.stderr.write "usage: luan luan:http/serve dir-URI test-URI\n" - return -end - -local dir, test = ... -Server.init(dir) -Luan.do_file(test)
--- a/logging/src/luan/modules/logging/Logging.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -java() -local Logger = require "java:org.apache.log4j.Logger" -local EnhancedPatternLayout = require "java:org.apache.log4j.EnhancedPatternLayout" -local ConsoleAppender = require "java:org.apache.log4j.ConsoleAppender" -local Level = require "java:org.apache.log4j.Level" -local RollingFileAppender = require "java:org.apache.log4j.RollingFileAppender" -local LuanLogger = require "java:luan.modules.logging.LuanLogger" - -local M = {} - -M.layout = "%d %-5p %c - %m%n" - -M.level = "INFO" - -M.console = "System.err" -- or "System.out" or set to nil for no console - -M.file = nil -- set to file name if you want logging to a file - -M.max_file_size = nil -- by default is "10MB" - - -M.log4j_root_logger = Logger.getRootLogger() - -local function to_level(level) - return level and Level.toLevel(level) -end - -function M.log_to_file(file,logger_name) -- logger_name is optional, defaults to root logger - local appender = RollingFileAppender.new(M.ptn_layout, file) - appender.setMaxFileSize(M.max_file_size) - local logger = logger_name and Logger.getLogger(logger_name) or M.log4j_root_logger - logger.addAppender(appender) - return appender -end - -function M.init() - M.log4j_root_logger.removeAllAppenders() - M.log4j_root_logger.setLevel( to_level(M.level) ) - M.ptn_layout = EnhancedPatternLayout.new(M.layout) - - if M.console ~= nil then - M.console_appender = ConsoleAppender.new(M.ptn_layout,M.console) - M.log4j_root_logger.addAppender(M.console_appender) - else - M.console_appender = nil - end - - if M.file ~= nil then - M.file_appender = M.log_to_file(M.file) - else - M.file_appender = nil - end -end - - -local function to_luan_logger(log4j_logger) - local tbl = {} - - local luanLogger = LuanLogger.new(log4j_logger) - tbl.error = luanLogger.error - tbl.warn = luanLogger.warn - tbl.info = luanLogger.info - tbl.debug = luanLogger.debug - - function tbl.get_level() - local level = log4j_logger.getLevel() - return level and level.toString() - end - - function tbl.get_effective_level() - local level = log4j_logger.getEffectiveLevel() - return level and level.toString() - end - - function tbl.set_level(level) - log4j_logger.setLevel( to_level(level) ) - end - - return tbl -end - -function M.logger(name) - return to_luan_logger( Logger.getLogger(name) ) -end - -function M.root_logger() - return to_luan_logger( Logger.getRootLogger() ) -end - -return M
--- a/logging/src/luan/modules/logging/LuanLogger.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -package luan.modules.logging; - -import org.apache.log4j.Logger; -import luan.Luan; -import luan.LuanState; -import luan.LuanException; -import luan.LuanTable; - - -public final class LuanLogger { - private final Logger logger; - - public LuanLogger(Logger logger) { - this.logger = logger; - } - - public void error(LuanState luan,Object obj) throws LuanException { - logger.error( luan.toString(obj) ); - } - - public void warn(LuanState luan,Object obj) throws LuanException { - logger.warn( luan.toString(obj) ); - } - - public void info(LuanState luan,Object obj) throws LuanException { - logger.info( luan.toString(obj) ); - } - - public void debug(LuanState luan,Object obj) throws LuanException { - logger.debug( luan.toString(obj) ); - } - -}
--- a/logging/src/luan/modules/logging/init.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -require "luan:logging/Logging.luan".init() -return true
--- a/lucene/src/luan/modules/lucene/Ab_testing.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,272 +0,0 @@ -local Luan = require "luan:Luan.luan" -local pairs = Luan.pairs -local ipairs = Luan.ipairs -local error = Luan.error -local range = Luan.range -local Math = require "luan:Math.luan" -local Table = require "luan:Table.luan" -local String = require "luan:String.luan" -local gsub = String.gsub -local Io = require "luan:Io.luan" -local Http = require "luan:http/Http.luan" -local Logging = require "luan:logging/Logging.luan" -local Lucene = require "luan:lucene/Lucene.luan" - -local M = {} - -local logger = Logging.logger "Ab_testing" - -function M.of(index) - - local ab_testing = {} - - ab_testing.test_map = {} - - function ab_testing.test(test) - test.name or error "name not defined" - test.values or error "values not defined" - - -- list of event names - test.events or error "events not defined" - - -- map of event name to aggregator factory - test.aggregator_factories or error "aggregator_factories not defined" - - -- test.date_field is optional - - local field = "ab_test_" .. test.name - index.indexed_fields[field] == nil or error("test "+test.name+" already defined") - index.indexed_fields[field] = Lucene.type.string - test.field = field - - -- returns map of event name to (map of value to result) and "start_date" - function test.results() - local results = {} - for name in pairs(test.aggregator_factories) do - results[name] = {} - end - local date_field = test.date_field - local start_date = nil - for _, value in ipairs(test.values) do - local aggregators = {} - for name, factory in pairs(test.aggregator_factories) do - aggregators[name] = factory() - end - local query = field..":"..value - index.advanced_search(query, function(_,doc_fn) - local doc = doc_fn() - for _, aggregator in pairs(aggregators) do - aggregator.aggregate(doc) - end - if date_field ~= nil then - local date = doc[date_field] - if date ~= nil and (start_date==nil or start_date > date) then - start_date = date - end - end - end) - for name, aggregator in pairs(aggregators) do - results[name][value] = aggregator.result - end - end - results.start_date = start_date - return results - end - - function test.fancy_results() - local events = test.events - local results = test.results() - local fancy = {} - fancy.start_date = results.start_date - local event = events[1] - fancy[event] = {} - for value, count in pairs(results[event]) do - fancy[event][value] = {} - fancy[event][value].count = count - fancy[event][value].pct_of_total = 100 - fancy[event][value].pct_of_prev = 100 - end - local all = results[event] - local prev = all - for i in range(2,#events) do - event = events[i] - fancy[event] = {} - for value, count in pairs(results[event]) do - fancy[event][value] = {} - fancy[event][value].count = count - fancy[event][value].pct_of_total = M.percent(count,all[value]) - fancy[event][value].pct_of_prev = M.percent(count,prev[value]) - end - prev = results[event] - end - return fancy - end - - ab_testing.test_map[test.name] = test - - return test - end - - function ab_testing.value(test_name,values) - return values[test_name] or ab_testing.test_map[test_name].values[1] - end - - -- returns map from test name to value - function ab_testing.from_doc(doc) - local values = {} - for _, test in pairs(ab_testing.test_map) do - values[test.name] = doc[test.field] - end - return values - end - - function ab_testing.to_doc(doc,values,tests) - tests = tests or ab_testing.test_map - if values == nil then - values = {} - for _, test in pairs(tests) do - values[test.name] = test.values[Math.random(#test.values)] - end - end - for _, test in pairs(tests) do - doc[test.field] = values[test.name] - end - return values - end - - function ab_testing.web_page(test_names) - return function() - local results = {} - for _, name in ipairs(test_names) do - local test = ab_testing.test_map[name] - test or error("test not found: "..name) - results[name] = test.fancy_results() - end - Io.stdout = Http.response.text_writer() - M.html(test_names,ab_testing.test_map,results) - end - end - - return ab_testing -end - - --- aggregator factories - --- fn(doc) should return boolean whether doc should be counted -function M.count(fn) - return function() - local aggregator = {} - aggregator.result = 0 - function aggregator.aggregate(doc) - if fn(doc) then - aggregator.result = aggregator.result + 1 - end - end - return aggregator - end -end - -M.count_all = M.count( function(doc) return true end ) - --- fn(doc) should return number to add to result, return 0 for nothing -function M.sum(fn) - return function() - local aggregator = {} - aggregator.result = 0 - function aggregator.aggregate(doc) - aggregator.result = aggregator.result + fn(doc) - end - return aggregator - end -end - - - -function M.percent(x,total) - if total==0 then - return 0 - else - return 100 * x / total - end -end - --- I will change this to use SimplyHTML when this is used again. -local function basic_style() %> - body {font-family:'Arial',sans-serif;font-size:16px;padding:1em 2em} - h1 {font-weight:bold;font-size:20px} - h2 {margin:2em 0 0em;font-size:18px;color:#3589B1} - table.results {margin-top:.5em;border-collapse:collapse;font-size:90%} - table.results th {background:#eee} - table.results th,table.results td {border-left:1px solid #bbb;padding:.4em 2em} - table.results tr:nth-child(odd) td {background:#f8f8f8} -<% end - -local function format(v) - v = v .. '' - return gsub( v, [[(\d+\.\d{1})\d+]], '$1' ) -end - -function M.html(test_names,tests,results) %> -<!DOCTYPE html> -<html lang="en"> - <head> - <title>A/B Test Results</title> - <style><% basic_style() %></style> - </head> - <body> - <h1>A/B Test Results</h1> - <% - for _, test_name in ipairs(test_names) do - local test = tests[test_name] - local result = results[test_name] - local n = #test.values - %> - <h2><%=test_name%></h2> - <table class="results"> - <tr> - <th>Event</th> - <th class="top" colspan="<%=n%>">Count</th> - <th class="top" colspan="<%=n%>">% of total</th> - <th class="top" colspan="<%=n%>">% of prev</th> - </tr> - <tr> - <th></th> - <% - for _ in range(1,3) do - for _, value in ipairs(test.values) do - %><th class="top"><%=value%></th><% - end - end - %> - </tr> - <% - for _, event in ipairs(test.events) do - local event_values = result[event] - %> - <tr> - <td><%=event%></td> - <% - for _, value in ipairs(test.values) do - %><td><%=format(event_values[value].count)%></th><% - end - for _, value in ipairs(test.values) do - %><td><%=format(event_values[value].pct_of_total)%></th><% - end - for _, value in ipairs(test.values) do - %><td><%=format(event_values[value].pct_of_prev)%></th><% - end - %> - </tr> - <% - end - %> - </table> - <% - end - %> - </body> -</html> -<% end - -return M
--- a/lucene/src/luan/modules/lucene/Lucene.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -java() -local Luan = require "luan:Luan.luan" -local error = Luan.error -local ipairs = Luan.ipairs or error() -local type = Luan.type or error() -local Html = require "luan:Html.luan" -local Io = require "luan:Io.luan" -local uri = Io.uri or error() -local String = require "luan:String.luan" -local matches = String.matches or error() -local Rpc = require "luan:Rpc.luan" -local LuceneIndex = require "java:luan.modules.lucene.LuceneIndex" -local NumberFieldParser = require "java:luan.modules.lucene.queryparser.NumberFieldParser" -local StringFieldParser = require "java:luan.modules.lucene.queryparser.StringFieldParser" -local SaneQueryParser = require "java:luan.modules.lucene.queryparser.SaneQueryParser" -local Version = require "java:org.apache.lucene.util.Version" -local EnglishAnalyzer = require "java:org.apache.lucene.analysis.en.EnglishAnalyzer" - - -local M = {} - -M.instances = {} - -M.type = { - string = LuceneIndex.STRING_FIELD_PARSER; - integer = NumberFieldParser.INT; - long = NumberFieldParser.LONG; - double = NumberFieldParser.DOUBLE; - - english = StringFieldParser.new(EnglishAnalyzer.new(Version.LUCENE_CURRENT)) -} - -M.literal = SaneQueryParser.literal - -function M.index(index_dir,default_type,default_fields) - local index = {} - index.dir = index_dir - local java_index = LuceneIndex.new(index_dir,default_type,default_fields) - index.indexed_fields = java_index.indexedFieldsMeta.newTable() - - -- index.indexed_only_fields[type][field] = fn(doc) - index.indexed_only_fields = java_index.indexed_only_fields - - index.to_string = java_index.to_string --- index.backup = java_index.backup - index.snapshot = java_index.snapshot - index.advanced_search = java_index.advanced_search - index.search_in_transaction = java_index.search_in_transaction - index.delete_all = java_index.delete_all - index.delete = java_index.delete - index.save = java_index.save - index.update_in_transaction = java_index.update_in_transaction --- index.close = java_index.close - index.ensure_open = java_index.ensure_open - index.next_id = java_index.nextId - index.highlighter = java_index.highlighter - - M.instances[index] = true - - function index.close() - M.instances[index] = nil - java_index.close() - end - - function index.search(query, from, to, sort) - from or error "missing 'from' parameter" - to or error "missing 'to' parameter" - local results = {} - local function fn(i,doc_fn) - if i >= from then - results[#results+1] = doc_fn() - end - end - local total_hits = index.advanced_search(query,fn,to,sort) - return results, total_hits - end - - function index.get_document(query) - local doc - local function fn(_,doc_fn) - doc = doc_fn() - end - local total_hits = index.advanced_search(query,fn,1) - total_hits <= 1 or error( "found " .. total_hits .. " documents" ) - return doc - end - - function index.count(query) - return index.advanced_search(query) - end - - function index.html_highlighter(query,formatter,container_tags) - local highlighter = index.highlighter(query,formatter) - return function(html) - local list = Html.parse(html,container_tags) - local result = {} - for _, obj in ipairs(list) do - if type(obj) == "string" then - obj = highlighter(obj) - end - result[#result+1] = obj - end - return Html.to_string(result) - end - end - - function index.zip(zip_file) - index.snapshot( function(dir_path,file_names) - zip_file.delete() - local zip_path = zip_file.canonical().to_string() - local dir = uri("file:"..dir_path) - local dir_name = dir.name() - local options = {dir=dir.parent()} - for _, file_name in ipairs(file_names) do - local cmd = "zip "..zip_path.." "..dir_name.."/"..file_name - Io.uri("os:"..cmd,options).read_text() - end - end ) - end - - function index.restore(zip_file) - java_index.run_in_lock( function() - local lucene_dir = uri("file:"..index.dir) - local before_restore = lucene_dir.parent().child("before_restore.zip") - index.zip(before_restore) - java_index.close() - lucene_dir.delete() - Io.uri("os:unzip "..zip_file.canonical().to_string(),{dir=lucene_dir.parent()}).read_text() - java_index.reopen() - end ) - end - - local function multi_error() - error "multiple lucene instances" - end - - if Rpc.functions.backup == nil then - - function Rpc.functions.lucene_backup(password) - Io.password == password or error "wrong password" - local zip_file = uri("file:"..index.dir).parent().child("backup.zip") - index.zip(zip_file) - return zip_file - end - - function Rpc.functions.lucene_restore(password,zip_file) - Io.password == password or error "wrong password" - local backup_zip = uri("file:"..index.dir).parent().child("backup.zip") - backup_zip.write(zip_file) - index.restore(backup_zip) - end - - else - Rpc.functions.lucene_backup = multi_error - Rpc.functions.lucene_restore = multi_error - end - - return index -end - -return M
--- a/lucene/src/luan/modules/lucene/LuceneIndex.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,631 +0,0 @@ -package luan.modules.lucene; - -import java.io.Closeable; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Iterator; -import java.util.Map; -import java.util.List; -import java.util.ArrayList; -import java.util.Set; -import java.util.HashSet; -import java.util.Collections; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.zip.ZipOutputStream; -import java.util.zip.ZipEntry; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.core.KeywordAnalyzer; -import org.apache.lucene.document.Document; -import org.apache.lucene.document.Field; -import org.apache.lucene.document.StoredField; -import org.apache.lucene.document.StringField; -import org.apache.lucene.document.TextField; -import org.apache.lucene.document.IntField; -import org.apache.lucene.document.LongField; -import org.apache.lucene.document.DoubleField; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.index.IndexWriter; -import org.apache.lucene.index.IndexWriterConfig; -import org.apache.lucene.index.DirectoryReader; -import org.apache.lucene.index.Term; -import org.apache.lucene.index.SnapshotDeletionPolicy; -import org.apache.lucene.index.IndexCommit; -import org.apache.lucene.index.AtomicReaderContext; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.FSDirectory; -import org.apache.lucene.util.Version; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.NumericUtils; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.SortField; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.TotalHitCountCollector; -import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.Collector; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.highlight.Formatter; -import org.apache.lucene.search.highlight.Highlighter; -import org.apache.lucene.search.highlight.InvalidTokenOffsetsException; -import org.apache.lucene.search.highlight.NullFragmenter; -import org.apache.lucene.search.highlight.QueryScorer; -import org.apache.lucene.search.highlight.TokenGroup; -import luan.modules.lucene.queryparser.SaneQueryParser; -import luan.modules.lucene.queryparser.FieldParser; -import luan.modules.lucene.queryparser.MultiFieldParser; -import luan.modules.lucene.queryparser.StringFieldParser; -import luan.modules.lucene.queryparser.NumberFieldParser; -import luan.modules.lucene.queryparser.ParseException; -import luan.modules.Utils; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanFunction; -import luan.LuanException; -import luan.LuanMeta; -import luan.LuanRuntimeException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -public final class LuceneIndex implements Closeable { - private static final Logger logger = LoggerFactory.getLogger(LuceneIndex.class); - - private static final String FLD_NEXT_ID = "nextId"; - public static final StringFieldParser STRING_FIELD_PARSER = new StringFieldParser(new KeywordAnalyzer()); - - private static final Version version = Version.LUCENE_4_9; - private final ReentrantLock writeLock = new ReentrantLock(); - private final File indexDir; - private SnapshotDeletionPolicy snapshotDeletionPolicy; - private IndexWriter writer; - private DirectoryReader reader; - private IndexSearcher searcher; - private final ThreadLocal<IndexSearcher> threadLocalSearcher = new ThreadLocal<IndexSearcher>(); - private boolean isClosed = true; - private final MultiFieldParser mfp; - public final LuanTable indexed_only_fields = new LuanTable(); - private final Analyzer analyzer; - - private static ConcurrentMap<File,AtomicInteger> globalWriteCounters = new ConcurrentHashMap<File,AtomicInteger>(); - private File fileDir; - private int writeCount; - - public LuceneIndex(LuanState luan,String indexDirStr,FieldParser defaultFieldParser,String[] defaultFields) throws LuanException, IOException { - mfp = defaultFieldParser==null ? new MultiFieldParser() : new MultiFieldParser(defaultFieldParser,defaultFields); - mfp.fields.put( "type", STRING_FIELD_PARSER ); - mfp.fields.put( "id", NumberFieldParser.LONG ); - File indexDir = new File(indexDirStr); - this.indexDir = indexDir; - Analyzer analyzer = STRING_FIELD_PARSER.analyzer; - if( defaultFieldParser instanceof StringFieldParser ) { - StringFieldParser sfp = (StringFieldParser)defaultFieldParser; - analyzer = sfp.analyzer; - } - this.analyzer = analyzer; - luan.onClose(this); - reopen(); - } - - public void reopen() throws LuanException, IOException { - if( !isClosed ) throw new RuntimeException(); - isClosed = false; - IndexWriterConfig conf = new IndexWriterConfig(version,analyzer); - snapshotDeletionPolicy = new SnapshotDeletionPolicy(conf.getIndexDeletionPolicy()); - conf.setIndexDeletionPolicy(snapshotDeletionPolicy); - FSDirectory dir = FSDirectory.open(indexDir); - fileDir = dir.getDirectory(); - globalWriteCounters.putIfAbsent(fileDir,new AtomicInteger()); - writer = new IndexWriter(dir,conf); - writer.commit(); // commit index creation - reader = DirectoryReader.open(dir); - searcher = new IndexSearcher(reader); - initId(); - } - - private int globalWriteCount() { - return globalWriteCounters.get(fileDir).get(); - } - - private void wrote() { - globalWriteCounters.get(fileDir).incrementAndGet(); - } - - public void delete_all() throws IOException { - boolean commit = !writeLock.isHeldByCurrentThread(); - writeLock.lock(); - try { - writer.deleteAll(); - id = idLim = 0; - if(commit) writer.commit(); - } finally { - wrote(); - writeLock.unlock(); - } - } - - private static Term term(String key,long value) { - BytesRef br = new BytesRef(); - NumericUtils.longToPrefixCoded(value,0,br); - return new Term(key,br); - } - - public void delete(LuanState luan,String queryStr) throws LuanException, IOException, ParseException { - Query query = SaneQueryParser.parseQuery(mfp,queryStr); - - boolean commit = !writeLock.isHeldByCurrentThread(); - writeLock.lock(); - try { - writer.deleteDocuments(query); - if(commit) writer.commit(); - } finally { - wrote(); - writeLock.unlock(); - } - } - - public void save(LuanState luan,LuanTable doc) throws LuanException, IOException { - Set indexedOnlySet = new HashSet(); - Object typeObj = doc.get(luan,"type"); - if( typeObj==null ) - throw new LuanException("missing 'type' field"); - if( !(typeObj instanceof String) ) - throw new LuanException("type must be string"); - String type = (String)typeObj; - Object indexedOnlyObj = indexed_only_fields.get(luan,type); - if( indexedOnlyObj != null ) { - if( !(indexedOnlyObj instanceof LuanTable) ) - throw new LuanException("indexed_only_fields elements must be tables"); - LuanTable indexedOnly = (LuanTable)indexedOnlyObj; - for( Map.Entry<Object,Object> entry : indexedOnly.iterable(luan) ) { - Object key = entry.getKey(); - if( !(key instanceof String) ) - throw new LuanException("indexed_only_fields."+type+" entries must be strings"); - String name = (String)key; - Object value = entry.getValue(); - if( !(value instanceof LuanFunction) ) - throw new LuanException("indexed_only_fields."+type+" values must be functions"); - LuanFunction fn = (LuanFunction)value; - value = Luan.first(fn.call(luan,new Object[]{doc})); - doc.put(luan, name, value ); - indexedOnlySet.add(name); - } - } - Object obj = doc.get(luan,"id"); - Long id; - try { - id = (Long)obj; - } catch(ClassCastException e) { - throw new LuanException("id should be Long but is "+obj.getClass().getSimpleName()); - } - - boolean commit = !writeLock.isHeldByCurrentThread(); - writeLock.lock(); - try { - if( id == null ) { - id = nextId(luan); - doc.put(luan,"id",id); - writer.addDocument(toLucene(luan,doc,indexedOnlySet)); - } else { - writer.updateDocument( term("id",id), toLucene(luan,doc,indexedOnlySet) ); - } - if(commit) writer.commit(); - } finally { - wrote(); - writeLock.unlock(); - } - } - - public void update_in_transaction(LuanState luan,LuanFunction fn) throws IOException, LuanException { - boolean commit = !writeLock.isHeldByCurrentThread(); - writeLock.lock(); - try { - fn.call(luan); - if(commit) writer.commit(); - } finally { - wrote(); - writeLock.unlock(); - } - } - - public void run_in_lock(LuanState luan,LuanFunction fn) throws IOException, LuanException { - if( writeLock.isHeldByCurrentThread() ) - throw new RuntimeException(); - writeLock.lock(); - try { - synchronized(this) { - fn.call(luan); - } - } finally { - wrote(); - writeLock.unlock(); - } - } - - - private long id; - private long idLim; - private final int idBatch = 10; - - private void initId() throws LuanException, IOException { - TopDocs td = searcher.search(new TermQuery(new Term("type","next_id")),1); - switch(td.totalHits) { - case 0: - id = 0; - idLim = 0; - break; - case 1: - idLim = (Long)searcher.doc(td.scoreDocs[0].doc).getField(FLD_NEXT_ID).numericValue(); - id = idLim; - break; - default: - throw new RuntimeException(); - } - } - - public synchronized long nextId(LuanState luan) throws LuanException, IOException { - if( ++id > idLim ) { - idLim += idBatch; - LuanTable doc = new LuanTable(); - doc.rawPut( "type", "next_id" ); - doc.rawPut( FLD_NEXT_ID, idLim ); - writer.updateDocument(new Term("type","next_id"),toLucene(luan,doc,Collections.EMPTY_SET)); - wrote(); - } - return id; - } - -/* - public void backup(String zipFile) throws LuanException, IOException { - if( !zipFile.endsWith(".zip") ) - throw new LuanException("file "+zipFile+" doesn't end with '.zip'"); - IndexCommit ic = snapshotDeletionPolicy.snapshot(); - try { - ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile)); - for( String fileName : ic.getFileNames() ) { - out.putNextEntry(new ZipEntry(fileName)); - FileInputStream in = new FileInputStream(new File(indexDir,fileName)); - Utils.copyAll(in,out); - in.close(); - out.closeEntry(); - } - out.close(); - } finally { - snapshotDeletionPolicy.release(ic); - } - } -*/ - public Object snapshot(LuanState luan,LuanFunction fn) throws LuanException, IOException { - IndexCommit ic = snapshotDeletionPolicy.snapshot(); - try { - String dir = fileDir.toString(); - LuanTable fileNames = new LuanTable(new ArrayList(ic.getFileNames())); - return fn.call(luan,new Object[]{dir,fileNames}); - } finally { - snapshotDeletionPolicy.release(ic); - } - } - - - - public String to_string() { - return writer.getDirectory().toString(); - } - - public void close() throws IOException { - if( !isClosed ) { - writer.close(); - reader.close(); - isClosed = true; - } - } - - protected void finalize() throws Throwable { - if( !isClosed ) { - logger.error("not closed"); - close(); - } - super.finalize(); - } - - - - private static class DocFn extends LuanFunction { - final IndexSearcher searcher; - int docID; - - DocFn(IndexSearcher searcher) { - this.searcher = searcher; - } - - @Override public Object call(LuanState luan,Object[] args) throws LuanException { - try { - return toTable(searcher.doc(docID)); - } catch(IOException e) { - throw new LuanException(e); - } - } - } - - private static abstract class MyCollector extends Collector { - int docBase; - int i = 0; - - @Override public void setScorer(Scorer scorer) {} - @Override public void setNextReader(AtomicReaderContext context) { - this.docBase = context.docBase; - } - @Override public boolean acceptsDocsOutOfOrder() { - return true; - } - } - - private synchronized IndexSearcher openSearcher() throws IOException { - int gwc = globalWriteCount(); - if( writeCount != gwc ) { - writeCount = gwc; - DirectoryReader newReader = DirectoryReader.openIfChanged(reader); - if( newReader != null ) { - reader.decRef(); - reader = newReader; - searcher = new IndexSearcher(reader); - } - } - reader.incRef(); - return searcher; - } - - // call in finally block - private static void close(IndexSearcher searcher) throws IOException { - searcher.getIndexReader().decRef(); - } - - public void ensure_open() throws IOException { - close(openSearcher()); - } - - public int advanced_search( final LuanState luan, String queryStr, LuanFunction fn, Integer n, String sortStr ) throws LuanException, IOException, ParseException { - Utils.checkNotNull(queryStr); - Query query = SaneQueryParser.parseQuery(mfp,queryStr); - IndexSearcher searcher = threadLocalSearcher.get(); - boolean inTransaction = searcher != null; - if( !inTransaction ) - searcher = openSearcher(); - try { - if( fn!=null && n==null ) { - if( sortStr != null ) - throw new LuanException("sort must be nil when n is nil"); - final DocFn docFn = new DocFn(searcher); - MyCollector col = new MyCollector() { - @Override public void collect(int doc) { - try { - docFn.docID = docBase + doc; - fn.call(luan,new Object[]{++i,docFn}); - } catch(LuanException e) { - throw new LuanRuntimeException(e); - } - } - }; - try { - searcher.search(query,col); - } catch(LuanRuntimeException e) { - throw (LuanException)e.getCause(); - } - return col.i; - } - if( fn==null || n==0 ) { - TotalHitCountCollector thcc = new TotalHitCountCollector(); - searcher.search(query,thcc); - return thcc.getTotalHits(); - } - Sort sort = sortStr==null ? null : SaneQueryParser.parseSort(mfp,sortStr); - TopDocs td = sort==null ? searcher.search(query,n) : searcher.search(query,n,sort); - final ScoreDoc[] scoreDocs = td.scoreDocs; - DocFn docFn = new DocFn(searcher); - for( int i=0; i<scoreDocs.length; i++ ) { - docFn.docID = scoreDocs[i].doc; - fn.call(luan,new Object[]{i+1,docFn}); - } - return td.totalHits; - } finally { - if( !inTransaction ) - close(searcher); - } - } - - public Object search_in_transaction(LuanState luan,LuanFunction fn) throws LuanException, IOException { - if( threadLocalSearcher.get() != null ) - throw new LuanException("can't nest search_in_transaction calls"); - IndexSearcher searcher = openSearcher(); - threadLocalSearcher.set(searcher); - try { - return fn.call(luan); - } finally { - threadLocalSearcher.set(null); - close(searcher); - } - } - - - - public final LuanMeta indexedFieldsMeta = new LuanMeta() { - - @Override public boolean canNewindex() { - return true; - } - - @Override public Object __index(LuanState luan,LuanTable tbl,Object key) { - return mfp.fields.get(key); - } - - @Override public void __new_index(LuanState luan,LuanTable tbl,Object key,Object value) throws LuanException { - if( !(key instanceof String) ) - throw new LuanException("key must be string"); - String field = (String)key; - if( value==null ) { // delete - mfp.fields.remove(field); - return; - } - if( !(value instanceof FieldParser) ) - throw new LuanException("value must be FieldParser like the values of Lucene.type"); - FieldParser parser = (FieldParser)value; - mfp.fields.put( field, parser ); - } - - @Override public final Iterator keys(LuanTable tbl) { - return mfp.fields.keySet().iterator(); - } - - @Override protected String type(LuanTable tbl) { - return "lucene-indexed-fields"; - } - - }; - - - - private IndexableField newField(String name,Object value,Field.Store store,Set<String> indexed) - throws LuanException - { - if( value instanceof String ) { - String s = (String)value; - FieldParser fp = mfp.fields.get(name); - if( fp != null ) { - if( fp instanceof StringFieldParser && fp != STRING_FIELD_PARSER ) { - return new TextField(name, s, store); - } else { - return new StringField(name, s, store); - } - } else { - return new StoredField(name, s); - } - } else if( value instanceof Integer ) { - int i = (Integer)value; - if( indexed.contains(name) ) { - return new IntField(name, i, store); - } else { - return new StoredField(name, i); - } - } else if( value instanceof Long ) { - long i = (Long)value; - if( indexed.contains(name) ) { - return new LongField(name, i, store); - } else { - return new StoredField(name, i); - } - } else if( value instanceof Double ) { - double i = (Double)value; - if( indexed.contains(name) ) { - return new DoubleField(name, i, store); - } else { - return new StoredField(name, i); - } - } else if( value instanceof byte[] ) { - byte[] b = (byte[])value; - return new StoredField(name, b); - } else - throw new LuanException("invalid value type "+value.getClass()+"' for '"+name+"'"); - } - - private Document toLucene(LuanState luan,LuanTable table,Set indexOnly) throws LuanException { - Set<String> indexed = mfp.fields.keySet(); - Document doc = new Document(); - for( Map.Entry<Object,Object> entry : table.iterable(luan) ) { - Object key = entry.getKey(); - if( !(key instanceof String) ) - throw new LuanException("key must be string"); - String name = (String)key; - Object value = entry.getValue(); - Field.Store store = indexOnly.contains(name) ? Field.Store.NO : Field.Store.YES; - if( !(value instanceof LuanTable) ) { - doc.add(newField(name, value, store, indexed)); - } else { // list - LuanTable list = (LuanTable)value; - for( Object el : list.asList() ) { - doc.add(newField(name, el, store, indexed)); - } - } - } - return doc; - } - - private static Object getValue(IndexableField ifld) throws LuanException { - BytesRef br = ifld.binaryValue(); - if( br != null ) - return br.bytes; - Number n = ifld.numericValue(); - if( n != null ) - return n; - String s = ifld.stringValue(); - if( s != null ) - return s; - throw new LuanException("invalid field type for "+ifld); - } - - private static LuanTable toTable(Document doc) throws LuanException { - if( doc==null ) - return null; - LuanTable table = new LuanTable(); - for( IndexableField ifld : doc ) { - String name = ifld.name(); - Object value = getValue(ifld); - Object old = table.rawGet(name); - if( old == null ) { - table.rawPut(name,value); - } else { - LuanTable list; - if( old instanceof LuanTable ) { - list = (LuanTable)old; - } else { - list = new LuanTable(); - list.rawPut(1,old); - table.rawPut(name,list); - } - list.rawPut(list.rawLength()+1,value); - } - } - return table; - } - - - public LuanFunction highlighter(LuanState luan,String queryStr,LuanFunction formatter) throws ParseException { - Query query = SaneQueryParser.parseQuery(mfp,queryStr); - Formatter fmt = new Formatter() { - public String highlightTerm(String originalText,TokenGroup tokenGroup) { - if( tokenGroup.getTotalScore() <= 0 ) - return originalText; - try { - return (String)Luan.first(formatter.call(luan,new Object[]{originalText})); - } catch(LuanException e) { - throw new LuanRuntimeException(e); - } - } - }; - Highlighter hl = new Highlighter( fmt, new QueryScorer(query) ); - hl.setTextFragmenter( new NullFragmenter() ); - return new LuanFunction() { - @Override public String call(LuanState luan,Object[] args) throws LuanException { - String text = (String)args[0]; - try { - String s = hl.getBestFragment(analyzer,null,text); - return s!=null ? s : text; - } catch(LuanRuntimeException e) { - throw (LuanException)e.getCause(); - } catch(IOException e) { - throw new RuntimeException(e); - } catch(InvalidTokenOffsetsException e) { - throw new RuntimeException(e); - } - } - }; - } -}
--- a/lucene/src/luan/modules/lucene/Versioning.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -local Luan = require "luan:Luan.luan" -local error = Luan.error -local pairs = Luan.pairs or error() -local Number = require "luan:Number.luan" -local integer = Number.integer or error() -local long = Number.long or error() -local String = require "luan:String.luan" -local matches = String.matches or error() -local sub = String.sub or error() -local string_to_number = String.to_number or error() -local Table = require "luan:Table.luan" -local copy = Table.copy or error() -local Lucene = require "luan:lucene/Lucene.luan" -require "luan:logging/init.luan" -local Logging = require "luan:logging/Logging.luan" - -local logger = Logging.logger "lucene versioning" - - -local M = {} - -function M.update(db,steps,version) - local doc = db.get_document"type:version" or { type="version", version=integer(0) } - while doc.version < version do - doc.version = integer(doc.version + 1) - logger.error("step "..doc.version) - db.update_in_transaction( function() - local step = steps[doc.version] - step and step(db) - db.save(doc) - end ) - end -end - - --- hack to deal with latest changes -function M.a_big_step(db) - db.indexed_fields["id index"] = Lucene.type.string - db.advanced_search( Lucene.literal"id index" .. ":*", function(_,doc_fn) - local doc = doc_fn() - for field, value in pairs(copy(doc)) do - if matches(field," index$") then - local new_field = sub(field,1,-7) - db.indexed_fields[new_field] or error("field '"..new_field.."' not indexed") - doc[new_field] = value - doc[field] = nil - end - end - doc.id = long(string_to_number(doc.id)) - db.save(doc) - end ) - db.indexed_fields["type index"] = Lucene.type.string - db.delete( Lucene.literal"type index" .. ":*" ) -end - -return M
--- a/lucene/src/luan/modules/lucene/Web_search.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ - local Luan = require "luan:Luan.luan" -local error = Luan.error -local pairs = Luan.pairs or error() -local ipairs = Luan.ipairs or error() -local range = Luan.range or error() -local to_string = Luan.to_string or error() -local Io = require "luan:Io.luan" -local repr = Io.repr or error() -local Http = require "luan:http/Http.luan" -local String = require "luan:String.luan" -local string_to_number = String.to_number or error() -local Html = require "luan:Html.luan" - - -local M = {} - -local function style() %> - body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - margin: 2em 5%; - } - h2 { - margin-bottom: .5em; - } - label { - text-align: right; - min-width: 6em; - display: inline-block; - margin-right: .5em; - } -<% -end - -local function form() %> -<html> - <head> - <title>Lucene Query</title> - <style> - <% style() %> - input { - margin-top: 1em; - } - input[type="text"] { - font: inherit; - padding: .5em .8em; - border-radius: 8px; - border-style: groove; - } - input[type="text"]:focus { - border-color: #66afe9; - outline: none; - } - span[tip] { - color: #888; - font-size: smaller; - margin-left: .5em; - } - input[type="submit"] { - color: white; - background: #337ab7; - border-color: #337ab7; - font: inherit; - padding: .5em; - border-radius: 4px; - } - input[type="submit"]:hover { - background: #236aa7 !important; - } - </style> - </head> - <body> - <h2>Lucene Query</h2> - <form horizontal name="form0" method="post"> - <div> - <label>Query:</label> - <input type=text name="query" size="80" autofocus /> - </div> - <div> - <label></label> - <span tip>Query examples: <i>type:user</i> or <i>+type:user +name:Joe"</i></span> - </div> - <div> - <label>Max Rows:</label> - <input type=text name="rows" value="100" size="3" maxlength="5" /> - </div> - <div> - <label>Sort:</label> - <input type=text name="sort" size="60" /> - </div> - <div> - <label></label> - <span tip>Sort examples: <i>name, id</i></span> - </div> - <div> - <label></label> - <input type="submit" /> - </div> - </form> - </body> -</html> -<% end - - -local function result(query,sort,headers,table) %> -<html> - <head> - <title>Lucene Query</title> - <style> - <% style() %> - table { - border-collapse: collapse; - width: 100%; - font-size: smaller; - } - th, td { - text-align: left; - padding: .5em; - border: solid 1px #ddd; - } - </style> - </head> - <body> - <h2>Lucene Query Results</h2> - <p><label>Query:</label> <b><%=Html.encode(to_string(query))%></b></p> - <p><label>Sort:</label> <b><%=Html.encode(to_string(sort))%></b></p> - <table> - <tr> - <th></th> - <% for _, header in ipairs(headers) do %> - <th><%=header%></th> - <% end %> - </tr> - <% for i, row in ipairs(table) do %> - <tr> - <td><%=i%></td> - <% - for col in range(1, #headers) do - local val = row[col] - %><td><%= val and repr(val) or "" %></td><% - end - %> - </tr> - <% end %> - </table> - </body> -</html> -<% end - - -local function index_of(tbl,val) - for i, v in ipairs(tbl) do - if v == val then - return i - end - end - local n = #tbl + 1 - tbl[n] = val - return n -end - - -function M.of(index) - index or error "index is nil" - - return function() - Io.stdout = Http.response.text_writer() - local query = Http.request.parameter.query - if query == nil then - form() - return - end - local rows = string_to_number(Http.request.parameter.rows) - local sort = Http.request.parameter.sort - local results = index.search(query,1,rows,sort) - local headers = {} - local table = {} - for _, doc in ipairs(results) do - local row = {} - for field, value in pairs(doc) do - row[index_of(headers,field)] = value - end - table[#table+1] = row - end - result(query,sort,headers,table) - end - -end - -return M
--- a/lucene/src/luan/modules/lucene/queryparser/FieldParser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -package luan.modules.lucene.queryparser; - -import org.apache.lucene.search.Query; -import org.apache.lucene.search.SortField; - - -public interface FieldParser { - public Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException; - public Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException; - public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) throws ParseException; -}
--- a/lucene/src/luan/modules/lucene/queryparser/MultiFieldParser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -package luan.modules.lucene.queryparser; - -import java.util.Map; -import java.util.HashMap; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.SortField; - - -public class MultiFieldParser implements FieldParser { - - /** - * maps field name to FieldParser - */ - public final Map<String,FieldParser> fields = new HashMap<String,FieldParser>(); - public boolean allowUnspecifiedFields = false; - private final FieldParser defaultFieldParser; - private final String[] defaultFields; - - public MultiFieldParser() { - this.defaultFieldParser = null; - this.defaultFields = null; - } - - public MultiFieldParser(FieldParser defaultFieldParser,String... defaultFields) { - this.defaultFieldParser = defaultFieldParser; - this.defaultFields = defaultFields; - for( String field : defaultFields ) { - fields.put(field,defaultFieldParser); - } - } - - @Override public Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException { - if( field == null ) { - if( defaultFieldParser == null ) - throw new ParseException(qp,"no defaults were specified, so a field is required"); - if( defaultFields.length == 1 ) - return defaultFieldParser.getQuery(qp,defaultFields[0],query); - BooleanQuery bq = new BooleanQuery(); - for( String f : defaultFields ) { - bq.add( defaultFieldParser.getQuery(qp,f,query), BooleanClause.Occur.SHOULD ); - } - return bq; - } else { - FieldParser fp = fields.get(field); - if( fp != null ) - return fp.getQuery(qp,field,query); - if( allowUnspecifiedFields ) - return defaultFieldParser.getQuery(qp,field,query); - throw new ParseException(qp,"unrecognized field '"+field+"'"); - } - } - - @Override public Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException { - if( field == null ) { - if( defaultFieldParser == null ) - throw new ParseException(qp,"no defaults were specified, so a field is required"); - if( defaultFields.length == 1 ) - return defaultFieldParser.getRangeQuery(qp,defaultFields[0],minQuery,maxQuery,includeMin,includeMax); - BooleanQuery bq = new BooleanQuery(); - for( String f : defaultFields ) { - bq.add( defaultFieldParser.getRangeQuery(qp,f,minQuery,maxQuery,includeMin,includeMax), BooleanClause.Occur.SHOULD ); - } - return bq; - } else { - FieldParser fp = fields.get(field); - if( fp != null ) - return fp.getRangeQuery(qp,field,minQuery,maxQuery,includeMin,includeMax); - if( allowUnspecifiedFields ) - return defaultFieldParser.getRangeQuery(qp,field,minQuery,maxQuery,includeMin,includeMax); - throw new ParseException(qp,"field '"+field+"' not specified"); - } - } - - @Override public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) throws ParseException { - FieldParser fp = fields.get(field); - if( fp != null ) - return fp.getSortField(qp,field,reverse); - if( allowUnspecifiedFields ) - return defaultFieldParser.getSortField(qp,field,reverse); - throw new ParseException(qp,"field '"+field+"' not specified"); - } - -}
--- a/lucene/src/luan/modules/lucene/queryparser/NumberFieldParser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -package luan.modules.lucene.queryparser; - -import org.apache.lucene.search.Query; -import org.apache.lucene.search.NumericRangeQuery; -import org.apache.lucene.search.SortField; - - -public abstract class NumberFieldParser implements FieldParser { - - @Override public final Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException { - return getRangeQuery(qp,field,query,query,true,true); - } - - @Override public final Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException { - try { - return getRangeQuery(field,minQuery,maxQuery,includeMin,includeMax); - } catch(NumberFormatException e) { - throw new ParseException(qp,e); - } - } - - abstract protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax); - - @Override public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) { - return new SortField( field, sortType(), reverse ); - } - - abstract protected SortField.Type sortType(); - - - public static final FieldParser INT = new NumberFieldParser() { - - @Override protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) { - int min = Integer.parseInt(minQuery); - int max = Integer.parseInt(maxQuery); - return NumericRangeQuery.newIntRange(field,min,max,includeMin,includeMax); - } - - @Override protected SortField.Type sortType() { - return SortField.Type.INT; - } - }; - - public static final FieldParser LONG = new NumberFieldParser() { - - @Override protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) { - long min = Long.parseLong(minQuery); - long max = Long.parseLong(maxQuery); - return NumericRangeQuery.newLongRange(field,min,max,includeMin,includeMax); - } - - @Override protected SortField.Type sortType() { - return SortField.Type.LONG; - } - }; - - public static final FieldParser FLOAT = new NumberFieldParser() { - - @Override protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) { - float min = Float.parseFloat(minQuery); - float max = Float.parseFloat(maxQuery); - return NumericRangeQuery.newFloatRange(field,min,max,includeMin,includeMax); - } - - @Override protected SortField.Type sortType() { - return SortField.Type.FLOAT; - } - }; - - public static final FieldParser DOUBLE = new NumberFieldParser() { - - @Override protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) { - double min = Double.parseDouble(minQuery); - double max = Double.parseDouble(maxQuery); - return NumericRangeQuery.newDoubleRange(field,min,max,includeMin,includeMax); - } - - @Override protected SortField.Type sortType() { - return SortField.Type.DOUBLE; - } - }; - -}
--- a/lucene/src/luan/modules/lucene/queryparser/ParseException.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -package luan.modules.lucene.queryparser; - - -public final class ParseException extends Exception { - public final String text; - public final int errorIndex; - public final int highIndex; - - public ParseException(SaneQueryParser qp) { - this(qp,"Invalid input"); - } - - public ParseException(SaneQueryParser qp,String msg) { - super(msg+" at position "+(qp.parser.errorIndex()+1)); - Parser parser = qp.parser; - this.text = parser.text; - this.errorIndex = parser.errorIndex(); - this.highIndex = parser.highIndex(); - } - - public ParseException(SaneQueryParser qp,Exception cause) { - this(qp,cause.getMessage(),cause); - } - - public ParseException(SaneQueryParser qp,String msg,Exception cause) { - super(msg+" at position "+(qp.parser.errorIndex()+1),cause); - Parser parser = qp.parser; - this.text = parser.text; - this.errorIndex = parser.errorIndex(); - this.highIndex = parser.highIndex(); - } - -}
--- a/lucene/src/luan/modules/lucene/queryparser/Parser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -package luan.modules.lucene.queryparser; - -/** - * A general parser utility class. - * This class is not needed to use SaneQueryParser. - */ -public class Parser { - public final String text; - private final int len; - private int[] stack = new int[256]; - private int frame = 0; - private int iHigh; - - Parser(String text) { - this.text = text; - this.len = text.length(); - } - - private int i() { - return stack[frame]; - } - - private void i(int i) { - stack[frame] += i; - if( iHigh < stack[frame] ) - iHigh = stack[frame]; - } - - public int begin() { - frame++; - if( frame == stack.length ) { - int[] a = new int[2*frame]; - System.arraycopy(stack,0,a,0,frame); - stack = a; - } - stack[frame] = stack[frame-1]; - return i(); - } - - public void rollback() { - stack[frame] = stack[frame-1]; - } - - public <T> T success(T t) { - success(); - return t; - } - - public boolean success() { - frame--; - stack[frame] = stack[frame+1]; - return true; - } - - public <T> T failure(T t) { - failure(); - return t; - } - - public boolean failure() { - frame--; - return false; - } - - public int currentIndex() { - return i(); - } - - public int errorIndex() { - return frame > 0 ? stack[frame-1] : 0; - } - - public int highIndex() { - return iHigh; - } - - public char lastChar() { - return text.charAt(i()-1); - } - - public char currentChar() { - return text.charAt(i()); - } - - public boolean endOfInput() { - return i() >= len; - } - - public boolean match(char c) { - if( endOfInput() || text.charAt(i()) != c ) - return false; - i(1); - return true; - } - - public boolean match(String s) { - int n = s.length(); - if( !text.regionMatches(i(),s,0,n) ) - return false; - i(n); - return true; - } - - public boolean matchIgnoreCase(String s) { - int n = s.length(); - if( !text.regionMatches(true,i(),s,0,n) ) - return false; - i(n); - return true; - } - - public boolean anyOf(String s) { - if( endOfInput() || s.indexOf(text.charAt(i())) == -1 ) - return false; - i(1); - return true; - } - - public boolean noneOf(String s) { - if( endOfInput() || s.indexOf(text.charAt(i())) != -1 ) - return false; - i(1); - return true; - } - - public boolean inCharRange(char cLow, char cHigh) { - if( endOfInput() ) - return false; - char c = text.charAt(i()); - if( !(cLow <= c && c <= cHigh) ) - return false; - i(1); - return true; - } - - public boolean anyChar() { - if( endOfInput() ) - return false; - i(1); - return true; - } - - public boolean test(char c) { - return !endOfInput() && text.charAt(i()) == c; - } - - public boolean test(String s) { - return text.regionMatches(i(),s,0,s.length()); - } - - public String textFrom(int start) { - return text.substring(start,i()); - } - -}
--- a/lucene/src/luan/modules/lucene/queryparser/SaneQueryParser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,247 +0,0 @@ -package luan.modules.lucene.queryparser; - -import java.util.List; -import java.util.ArrayList; -import java.util.regex.Pattern; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.SortField; - - -public class SaneQueryParser { - - public static Query parseQuery(FieldParser fieldParser,String query) throws ParseException { - return new SaneQueryParser(fieldParser,query).parseQuery(); - } - - private static Pattern specialChar = Pattern.compile("[ \\t\\r\\n\":\\[\\]{}^+\\-(),?*\\\\]"); - - public static String literal(String s) { - return specialChar.matcher(s).replaceAll("\\\\$0"); - } - - public static Sort parseSort(FieldParser fieldParser,String sort) throws ParseException { - return new SaneQueryParser(fieldParser,sort).parseSort(); - } - - - private static final String NOT_IN_TERM = " \t\r\n\":[]{}^+-()"; - private static final String NOT_IN_FIELD = NOT_IN_TERM + ","; - private final FieldParser fieldParser; - final Parser parser; - - private SaneQueryParser(FieldParser fieldParser,String query) { - this.fieldParser = fieldParser; - this.parser = new Parser(query); - } - - private Query parseQuery() throws ParseException { - Spaces(); - BooleanQuery bq = new BooleanQuery(); - while( !parser.endOfInput() ) { - bq.add( Term(null) ); - } - BooleanClause[] clauses = bq.getClauses(); - switch( clauses.length ) { - case 0: - return new MatchAllDocsQuery(); - case 1: - { - BooleanClause bc = clauses[0]; - if( bc.getOccur() != BooleanClause.Occur.MUST_NOT ) - return bc.getQuery(); - } - default: - return bq; - } - } - - private BooleanClause Term(String defaultField) throws ParseException { - BooleanClause.Occur occur; - if( parser.match('+') ) { - occur = BooleanClause.Occur.MUST; - Spaces(); - } else if( parser.match('-') ) { - occur = BooleanClause.Occur.MUST_NOT; - Spaces(); - } else { - occur = BooleanClause.Occur.SHOULD; - } - String field = QueryField(); - if( field == null ) - field = defaultField; - Query query = NestedTerm(field); - if( query == null ) - query = RangeTerm(field); - if( query == null ) { - parser.begin(); - String match = SimpleTerm(); - query = fieldParser.getQuery(this,field,match); - parser.success(); - } - if( parser.match('^') ) { - Spaces(); - int start = parser.begin(); - try { - while( parser.anyOf("0123456789.") ); - String match = parser.textFrom(start); - float boost = Float.parseFloat(match); - query.setBoost(boost); - } catch(NumberFormatException e) { - throw new ParseException(this,e); - } - parser.success(); - Spaces(); - } - BooleanClause bc = new BooleanClause(query,occur); - return bc; - } - - private Query NestedTerm(String field) throws ParseException { - parser.begin(); - if( !parser.match('(') ) - return parser.failure(null); - BooleanQuery bq = new BooleanQuery(); - while( !parser.match(')') ) { - if( parser.endOfInput() ) - throw new ParseException(this,"unclosed parentheses"); - bq.add( Term(field) ); - } - Spaces(); - BooleanClause[] clauses = bq.getClauses(); - switch( clauses.length ) { - case 0: - throw new ParseException(this,"empty parentheses"); - case 1: - { - BooleanClause bc = clauses[0]; - if( bc.getOccur() != BooleanClause.Occur.MUST_NOT ) - return parser.success(bc.getQuery()); - } - default: - return parser.success(bq); - } - } - - private Query RangeTerm(String field) throws ParseException { - parser.begin(); - if( !parser.anyOf("[{") ) - return parser.failure(null); - boolean includeMin = parser.lastChar() == '['; - Spaces(); - String minQuery = SimpleTerm(); - TO(); - String maxQuery = SimpleTerm(); - if( !parser.anyOf("]}") ) - throw new ParseException(this,"unclosed range"); - boolean includeMax = parser.lastChar() == ']'; - Spaces(); - Query query = fieldParser.getRangeQuery(this,field,minQuery,maxQuery,includeMin,includeMax); - return parser.success(query); - } - - private void TO() throws ParseException { - parser.begin(); - if( !(parser.match("TO") && Space()) ) - throw new ParseException(this,"'TO' expected"); - Spaces(); - parser.success(); - } - - private String SimpleTerm() throws ParseException { - parser.begin(); - String match; - if( parser.match('"') ) { - int start = parser.currentIndex() - 1; - while( !parser.match('"') ) { - if( parser.endOfInput() ) - throw new ParseException(this,"unclosed quotes"); - parser.anyChar(); - checkEscape(); - } - match = parser.textFrom(start); - Spaces(); - } else { - match = Unquoted(NOT_IN_TERM); - } - if( match.length() == 0 ) - throw new ParseException(this); - return parser.success(match); - } - - private String QueryField() throws ParseException { - parser.begin(); - String match = Field(); - if( match==null || !parser.match(':') ) - return parser.failure((String)null); - Spaces(); - return parser.success(match); - } - - private String Field() throws ParseException { - parser.begin(); - String match = Unquoted(NOT_IN_FIELD); - if( match.length()==0 ) - return parser.failure((String)null); - match = StringFieldParser.escape(this,match); - return parser.success(match); - } - - private String Unquoted(String exclude) throws ParseException { - int start = parser.begin(); - while( parser.noneOf(exclude) ) { - checkEscape(); - } - String match = parser.textFrom(start); - Spaces(); - return parser.success(match); - } - - private void checkEscape() { - if( parser.lastChar() == '\\' ) - parser.anyChar(); - } - - private void Spaces() { - while( Space() ); - } - - private boolean Space() { - return parser.anyOf(" \t\r\n"); - } - - - // sort - - private Sort parseSort() throws ParseException { - Spaces(); - if( parser.endOfInput() ) - return null; - List<SortField> list = new ArrayList<SortField>(); - list.add( SortField() ); - while( !parser.endOfInput() ) { - parser.begin(); - if( !parser.match(',') ) - throw new ParseException(this,"',' expected"); - Spaces(); - parser.success(); - list.add( SortField() ); - } - return new Sort(list.toArray(new SortField[0])); - } - - private SortField SortField() throws ParseException { - parser.begin(); - String field = Field(); - if( field==null ) - throw new ParseException(this); - boolean reverse = !parser.matchIgnoreCase("asc") && parser.matchIgnoreCase("desc"); - Spaces(); - SortField sf = fieldParser.getSortField(this,field,reverse); - return parser.success(sf); - } - -}
--- a/lucene/src/luan/modules/lucene/queryparser/StringFieldParser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +0,0 @@ -package luan.modules.lucene.queryparser; - -import java.io.StringReader; -import java.io.IOException; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.TermRangeQuery; -import org.apache.lucene.search.PhraseQuery; -import org.apache.lucene.search.WildcardQuery; -import org.apache.lucene.search.PrefixQuery; -import org.apache.lucene.search.SortField; -import org.apache.lucene.index.Term; - - -public class StringFieldParser implements FieldParser { - public int slop = 0; - public final Analyzer analyzer; - - public StringFieldParser(Analyzer analyzer) { - this.analyzer = analyzer; - } - - @Override public Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException { - String wildcard = wildcard(qp,query); - if( wildcard != null ) - return new WildcardQuery(new Term(field,wildcard)); - if( query.endsWith("*") && !query.endsWith("\\*") ) - return new PrefixQuery(new Term(field,query.substring(0,query.length()-1))); - query = escape(qp,query); - PhraseQuery pq = new PhraseQuery(); - try { - TokenStream ts = analyzer.tokenStream(field,new StringReader(query)); - CharTermAttribute termAttr = ts.addAttribute(CharTermAttribute.class); - PositionIncrementAttribute posAttr = ts.addAttribute(PositionIncrementAttribute.class); - ts.reset(); - int pos = -1; - while( ts.incrementToken() ) { - pos += posAttr.getPositionIncrement(); - pq.add( new Term(field,termAttr.toString()), pos ); - } - ts.end(); - ts.close(); - } catch(IOException e) { - throw new RuntimeException(e); - } - Term[] terms = pq.getTerms(); - if( terms.length==1 && pq.getPositions()[0]==0 ) - return new TermQuery(terms[0]); - return pq; - } - - @Override public Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException { - minQuery = escape(qp,minQuery); - maxQuery = escape(qp,maxQuery); - return TermRangeQuery.newStringRange(field,minQuery,maxQuery,includeMin,includeMax); - } - - static String escape(SaneQueryParser qp,String s) throws ParseException { - final char[] a = s.toCharArray(); - int i, n; - if( a[0] == '"' ) { - if( a[a.length-1] != '"' ) throw new RuntimeException(); - i = 1; - n = a.length - 1; - } else { - i = 0; - n = a.length; - } - StringBuilder sb = new StringBuilder(); - for( ; i<n; i++ ) { - char c = a[i]; - if( c == '\\' ) { - if( ++i == a.length ) - throw new ParseException(qp,"ends with '\\'"); - c = a[i]; - } - sb.append(c); - } - return sb.toString(); - } - - private static String wildcard(SaneQueryParser qp,String s) throws ParseException { - final char[] a = s.toCharArray(); - if( a[0] == '"' ) - return null; - boolean hasWildcard = false; - StringBuilder sb = new StringBuilder(); - for( int i=0; i<a.length; i++ ) { - char c = a[i]; - if( c=='?' || c=='*' && i<a.length-1 ) - hasWildcard = true; - if( c == '\\' ) { - if( ++i == a.length ) - throw new ParseException(qp,"ends with '\\'"); - c = a[i]; - if( c=='?' || c=='*' ) - sb.append('\\'); - } - sb.append(c); - } - return hasWildcard ? sb.toString() : null; - } - - @Override public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) { - return new SortField( field, SortField.Type.STRING, reverse ); - } - -}
--- a/lucene/src/luan/modules/lucene/queryparser/SynonymParser.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -package luan.modules.lucene.queryparser; - -import java.util.Map; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.SortField; - - -public class SynonymParser implements FieldParser { - private final FieldParser fp; - private final Map<String,String[]> synonymMap; - - public SynonymParser(FieldParser fp,Map<String,String[]> synonymMap) { - this.fp = fp; - this.synonymMap = synonymMap; - } - - protected String[] getSynonyms(String query) { - return synonymMap.get(query); - } - - public Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException { - String[] synonyms = getSynonyms(query); - if( synonyms == null ) - return fp.getQuery(qp,field,query); - BooleanQuery bq = new BooleanQuery(); - bq.add( fp.getQuery(qp,field,query), BooleanClause.Occur.SHOULD ); - for( String s : synonyms ) { - bq.add( fp.getQuery(qp,field,s), BooleanClause.Occur.SHOULD ); - } - return bq; - } - - public Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException { - return fp.getRangeQuery(qp,field,minQuery,maxQuery,includeMin,includeMax); - } - - public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) throws ParseException { - return fp.getSortField(qp,field,reverse); - } -}
--- a/mail/src/luan/modules/mail/Mail.luan Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -java() -local Luan = require "luan:Luan.luan" -local assert_table = Luan.assert_table -local System = require "java:java.lang.System" -local SmtpCon = require "java:luan.modules.mail.SmtpCon" - -local M = {} - -System.setProperty( "mail.mime.charset", "UTF-8" ) - -function M.Sender(params) - assert_table(params) - local smtpCon = SmtpCon.new(params) - return { send = smtpCon.send } -end - -return M
--- a/mail/src/luan/modules/mail/SmtpCon.java Fri Aug 26 03:15:41 2016 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,187 +0,0 @@ -package luan.modules.mail; - -import java.io.IOException; -import java.util.Map; -import java.util.HashMap; -import java.util.Properties; -import javax.mail.Authenticator; -import javax.mail.PasswordAuthentication; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Part; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import javax.mail.internet.MimeBodyPart; -import luan.Luan; -import luan.LuanState; -import luan.LuanTable; -import luan.LuanException; - - -public final class SmtpCon { - private final Session session; - - public SmtpCon(LuanState luan,LuanTable paramsTbl) throws LuanException { - Map<Object,Object> params = new HashMap<Object,Object>(paramsTbl.asMap(luan)); - Properties props = new Properties(System.getProperties()); - - String host = getString(params,"host"); - if( host==null ) - throw new LuanException( "parameter 'host' is required" ); - props.setProperty("mail.smtp.host",host); - - Object port = params.remove("port"); - if( port != null ) { - String s; - if( port instanceof String ) { - s = (String)port; - } else if( port instanceof Number ) { - Integer i = Luan.asInteger(port); - if( i == null ) - throw new LuanException( "parameter 'port' must be an integer" ); - s = i.toString(); - } else { - throw new LuanException( "parameter 'port' must be an integer" ); - } - props.setProperty("mail.smtp.socketFactory.port", s); - props.setProperty("mail.smtp.port", s); - } - - String username = getString(params,"username"); - if( username == null ) { - session = Session.getInstance(props); - } else { - String password = getString(params,"password"); - if( password==null ) - throw new LuanException( "parameter 'password' is required with 'username'" ); - props.setProperty("mail.smtp.auth","true"); - final PasswordAuthentication pa = new PasswordAuthentication(username,password); - Authenticator auth = new Authenticator() { - protected PasswordAuthentication getPasswordAuthentication() { - return pa; - } - }; - session = Session.getInstance(props,auth); - } - - if( !params.isEmpty() ) - throw new LuanException( "unrecognized parameters: "+params ); - } - - private String getString(Map<Object,Object> params,String key) throws LuanException { - Object val = params.remove(key); - if( val!=null && !(val instanceof String) ) - throw new LuanException( "parameter '"+key+"' must be a string" ); - return (String)val; - } - - - public void send(LuanState luan,LuanTable mailTbl) throws LuanException { - try { - Map<Object,Object> mailParams = new HashMap<Object,Object>(mailTbl.asMap(luan)); - MimeMessage msg = new MimeMessage(session); - - String from = getString(mailParams,"from"); - if( from != null ) - msg.setFrom(from); - - String to = getString(mailParams,"to"); - if( to != null ) - msg.setRecipients(Message.RecipientType.TO,to); - - String cc = getString(mailParams,"cc"); - if( cc != null ) - msg.setRecipients(Message.RecipientType.CC,cc); - - String subject = getString(mailParams,"subject"); - if( subject != null ) - msg.setSubject(subject); - - Object body = mailParams.remove("body"); - Object attachments = mailParams.remove("attachments"); - Part bodyPart = attachments==null ? msg : new MimeBodyPart(); - - if( body != null ) { - if( body instanceof String ) { - bodyPart.setText((String)body); - } else if( body instanceof LuanTable ) { - LuanTable bodyTbl = (LuanTable)body; - Map<Object,Object> map = new HashMap<Object,Object>(bodyTbl.asMap(luan)); - MimeMultipart mp = new MimeMultipart("alternative"); - String text = (String)map.remove("text"); - if( text != null ) { - MimeBodyPart part = new MimeBodyPart(); - part.setText(text); - mp.addBodyPart(part); - } - String html = (String)map.remove("html"); - if( html != null ) { - MimeBodyPart part = new MimeBodyPart(); - part.setContent(html,"text/html"); - mp.addBodyPart(part); - } - if( !map.isEmpty() ) - throw new LuanException( "invalid body types: " + map ); - bodyPart.setContent(mp); - } else - throw new LuanException( "parameter 'body' must be a string or table" ); - } - - if( attachments != null ) { - if( !(attachments instanceof LuanTable) ) - throw new LuanException( "parameter 'attachments' must be a table" ); - LuanTable attachmentsTbl = (LuanTable)attachments; - if( !attachmentsTbl.isList() ) - throw new LuanException( "parameter 'attachments' must be a list" ); - MimeMultipart mp = new MimeMultipart("mixed"); - if( body != null ) - mp.addBodyPart((MimeBodyPart)bodyPart); - for( Object attachment : attachmentsTbl.asList() ) { - if( !(attachment instanceof LuanTable) ) - throw new LuanException( "each attachment must be a table" ); - Map<Object,Object> attachmentMap = new HashMap<Object,Object>(((LuanTable)attachment).asMap(luan)); - Object obj; - - obj = attachmentMap.remove("filename"); - if( obj==null ) - throw new LuanException( "an attachment is missing 'filename'" ); - if( !(obj instanceof String) ) - throw new LuanException( "an attachment filename must be a string" ); - String filename = (String)obj; - - obj = attachmentMap.remove("content_type"); - if( obj==null ) - throw new LuanException( "an attachment is missing 'content_type'" ); - if( !(obj instanceof String) ) - throw new LuanException( "an attachment content_type must be a string" ); - String content_type = (String)obj; - - Object content = attachmentMap.remove("content"); - if( content==null ) - throw new LuanException( "an attachment is missing 'content'" ); - if( content_type.startsWith("text/") && content instanceof byte[] ) - content = new String((byte[])content); - - if( !attachmentMap.isEmpty() ) - throw new LuanException( "unrecognized attachment parameters: "+attachmentMap ); - - MimeBodyPart part = new MimeBodyPart(); - part.setContent(content,content_type); - part.setFileName(filename); - mp.addBodyPart(part); - } - msg.setContent(mp); - } - - if( !mailParams.isEmpty() ) - throw new LuanException( "unrecognized parameters: "+mailParams ); - - Transport.send(msg); - } catch(MessagingException e) { - throw new LuanException(e); - } - } - -}
--- a/scripts/build-luan.sh Fri Aug 26 03:15:41 2016 -0600 +++ b/scripts/build-luan.sh Fri Aug 26 14:36:40 2016 -0600 @@ -13,51 +13,15 @@ mkdir $LUAN_BUILD/luan/jars cd $LUAN_HOME -echo "return '$VERSION'" >core/src/luan/version.luan +echo "return '$VERSION'" >src/luan/version.luan find . -name *.class -delete -cd $LUAN_HOME -SRC=core/src -CLASSPATH=$LUAN_HOME/$SRC -javac -classpath $CLASSPATH `find $SRC -name *.java` -cd $SRC +CLASSPATH=$LUAN_HOME/src +for i in $LUAN_HOME/lib/* ; do CLASSPATH=$CLASSPATH:$i ; done +cd src +javac -classpath $CLASSPATH `find . -name *.java` jar cvf $LUAN_BUILD/luan/jars/luan-$VERSION.jar `find . -name *.class -o -name *.luan` - -cd $LUAN_HOME -SRC=http/src -CLASSPATH=$LUAN_HOME/core/src:$LUAN_HOME/$SRC:$SLF4J -for i in $LUAN_HOME/http/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done -javac -classpath $CLASSPATH `find $SRC -name *.java` -cd $SRC -jar uvf $LUAN_BUILD/luan/jars/luan-$VERSION.jar `find . -name *.class -o -name *.luan` -cp $LUAN_HOME/http/ext/* $LUAN_BUILD/luan/jars - -cd $LUAN_HOME -SRC=logging/src -CLASSPATH=$LUAN_HOME/core/src:$LUAN_HOME/$SRC -for i in $LUAN_HOME/logging/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done -javac -classpath $CLASSPATH `find $SRC -name *.java` -cd $SRC -jar uvf $LUAN_BUILD/luan/jars/luan-$VERSION.jar `find . -name *.class -o -name *.luan` -cp $LUAN_HOME/logging/ext/* $LUAN_BUILD/luan/jars - -cd $LUAN_HOME -SRC=mail/src -CLASSPATH=$LUAN_HOME/core/src:$LUAN_HOME/$SRC -for i in $LUAN_HOME/mail/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done -javac -classpath $CLASSPATH `find $SRC -name *.java` -cd $SRC -jar uvf $LUAN_BUILD/luan/jars/luan-$VERSION.jar `find . -name *.class -o -name *.luan` -cp $LUAN_HOME/mail/ext/* $LUAN_BUILD/luan/jars - -cd $LUAN_HOME -SRC=lucene/src -CLASSPATH=$LUAN_HOME/core/src:$LUAN_HOME/$SRC:$SLF4J -for i in $LUAN_HOME/lucene/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done -javac -classpath $CLASSPATH `find $SRC -name *.java` -cd $SRC -jar uvf $LUAN_BUILD/luan/jars/luan-$VERSION.jar `find . -name *.class -o -name *.luan` -cp $LUAN_HOME/lucene/ext/* $LUAN_BUILD/luan/jars +cp $LUAN_HOME/lib/* $LUAN_BUILD/luan/jars cp $LUAN_HOME/scripts/install.sh $LUAN_BUILD/luan chmod +x $LUAN_BUILD/luan/install.sh
--- a/scripts/cp-luan Fri Aug 26 03:15:41 2016 -0600 +++ b/scripts/cp-luan Fri Aug 26 14:36:40 2016 -0600 @@ -1,20 +1,7 @@ LUAN_HOME=~/hg/luan -CLASSPATH=$LUAN_HOME/core/src - -CLASSPATH=$CLASSPATH:$LUAN_HOME/logging/src -for i in $LUAN_HOME/logging/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done - -CLASSPATH=$CLASSPATH:$LUAN_HOME/http/src -for i in $LUAN_HOME/http/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done +CLASSPATH=$LUAN_HOME/src -CLASSPATH=$CLASSPATH:$LUAN_HOME/mail/src -for i in $LUAN_HOME/mail/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done - -CLASSPATH=$CLASSPATH:$LUAN_HOME/lucene/src -for i in $LUAN_HOME/lucene/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done - -CLASSPATH=$CLASSPATH:$LUAN_HOME/stripe/src -for i in $LUAN_HOME/stripe/ext/* ; do CLASSPATH=$CLASSPATH:$i ; done +for i in $LUAN_HOME/lib/* ; do CLASSPATH=$CLASSPATH:$i ; done export CLASSPATH
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/DeepCloneable.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,7 @@ +package luan; + + +public interface DeepCloneable { + public DeepCloneable shallowClone(); + public void deepenClone(DeepCloneable clone,DeepCloner cloner); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/DeepCloner.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,60 @@ +package luan; + +import java.util.Map; +import java.util.HashMap; +import java.util.IdentityHashMap; + + +public final class DeepCloner { + private final Map cloned = new IdentityHashMap(); + + public DeepCloneable deepClone(DeepCloneable obj) { + if( obj==null ) + return null; + DeepCloneable rtn = (DeepCloneable)cloned.get(obj); + if( rtn == null ) { + rtn = obj.shallowClone(); + cloned.put(obj,rtn); + obj.deepenClone(rtn,this); + } + return rtn; + } + + public Object[] deepClone(Object[] obj) { + if( obj.length == 0 ) + return obj; + Object[] rtn = (Object[])cloned.get(obj); + if( rtn == null ) { + rtn = obj.clone(); + cloned.put(obj,rtn); + for( int i=0; i<rtn.length; i++ ) { + rtn[i] = get(rtn[i]); + } + } + return rtn; + } + + public Map deepClone(Map obj) { + if( !obj.getClass().equals(HashMap.class) ) + throw new RuntimeException("can only clone HashMap"); + Map rtn = (Map)cloned.get(obj); + if( rtn == null ) { + rtn = new HashMap(); + for( Object stupid : obj.entrySet() ) { + Map.Entry entry = (Map.Entry)stupid; + rtn.put( get(entry.getKey()), get(entry.getValue()) ); + } + } + return rtn; + } + + public Object get(Object obj) { + if( obj instanceof DeepCloneable ) + return deepClone((DeepCloneable)obj); + if( obj instanceof Object[] ) + return deepClone((Object[])obj); + if( obj instanceof Map ) + return deepClone((Map)obj); + return obj; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/Luan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,157 @@ +package luan; + +import java.util.List; +import luan.modules.BasicLuan; +import luan.impl.LuanCompiler; + + +public final class Luan { + + public static void main(String[] args) throws LuanException { + LuanState luan = new LuanState(); + LuanFunction standalone = (LuanFunction)BasicLuan.load_file(luan,"classpath:luan/cmd_line.luan"); + standalone.call(luan,args); + } + + public static Object first(Object obj) { + if( !(obj instanceof Object[]) ) + return obj; + Object[] a = (Object[])obj; + return a.length==0 ? null : a[0]; + } + + public static Object[] array(Object obj) { + return obj instanceof Object[] ? (Object[])obj : new Object[]{obj}; + } + + public static String type(Object obj) { + if( obj == null ) + return "nil"; + if( obj instanceof String ) + return "string"; + if( obj instanceof Boolean ) + return "boolean"; + if( obj instanceof Number ) + return "number"; + if( obj instanceof LuanTable ) + return "table"; + if( obj instanceof LuanFunction ) + return "function"; + if( obj instanceof byte[] ) + return "binary"; + return "java"; + } + + public static String toString(Number n) { + if( n instanceof Integer ) + return n.toString(); + int i = n.intValue(); + if( i == n.doubleValue() ) + return Integer.toString(i); + String s = n.toString(); + int iE = s.indexOf('E'); + String ending = null; + if( iE != -1 ) { + ending = s.substring(iE); + s = s.substring(0,iE); + } + if( s.endsWith(".0") ) + s = s.substring(0,s.length()-2); + if( ending != null ) + s += ending; + return s; + } + + public static Integer asInteger(Object obj) { + if( obj instanceof Integer ) + return (Integer)obj; + if( !(obj instanceof Number) ) + return null; + Number n = (Number)obj; + int i = n.intValue(); + return i==n.doubleValue() ? Integer.valueOf(i) : null; + } + + public static String stringEncode(String s) { + s = s.replace("\\","\\\\"); + s = s.replace("\u0007","\\a"); + s = s.replace("\b","\\b"); + s = s.replace("\f","\\f"); + s = s.replace("\n","\\n"); + s = s.replace("\r","\\r"); + s = s.replace("\t","\\t"); + s = s.replace("\u000b","\\v"); + s = s.replace("\"","\\\""); + s = s.replace("\'","\\'"); + return s; + } + + + // from LuanState + + public static Boolean checkBoolean(Object obj) throws LuanException { + if( obj instanceof Boolean ) + return (Boolean)obj; + throw new LuanException("attempt to use a " + Luan.type(obj) + " value as a boolean" ); + } + + public static String checkString(Object obj) throws LuanException { + if( obj instanceof String ) + return (String)obj; + throw new LuanException("attempt to use a " + Luan.type(obj) + " value as a string" ); + } + + public static LuanFunction checkFunction(Object obj) throws LuanException { + if( obj instanceof LuanFunction ) + return (LuanFunction)obj; + throw new LuanException("attempt to call a " + Luan.type(obj) + " value" ); + } + + public static boolean isLessThan(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 instanceof Number && o2 instanceof Number ) { + Number n1 = (Number)o1; + Number n2 = (Number)o2; + return n1.doubleValue() < n2.doubleValue(); + } + if( o1 instanceof String && o2 instanceof String ) { + String s1 = (String)o1; + String s2 = (String)o2; + return s1.compareTo(s2) < 0; + } + LuanFunction fn = getBinHandler("__lt",o1,o2); + if( fn != null ) + return checkBoolean( Luan.first(fn.call(luan,new Object[]{o1,o2})) ); + throw new LuanException( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) ); + } + + public static LuanFunction getBinHandler(String op,Object o1,Object o2) throws LuanException { + if( o1 instanceof LuanTable ) { + LuanFunction f1 = getHandlerFunction(op,(LuanTable)o1); + if( f1 != null ) + return f1; + } + return o2 instanceof LuanTable ? getHandlerFunction(op,(LuanTable)o2) : null; + } + + public static LuanFunction getHandlerFunction(String op,LuanTable t) throws LuanException { + Object f = t.getHandler(op); + if( f == null ) + return null; + return checkFunction(f); + } + + public static LuanFunction load(String text,String sourceName,LuanTable env) + throws LuanException + { + return LuanCompiler.compile(text,sourceName,env); + } + + public static LuanFunction load(String text,String sourceName) + throws LuanException + { + return load(text,sourceName,null); + } + + + private Luan() {} // never +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanException.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,151 @@ +package luan; + +import java.io.StringWriter; +import java.io.PrintWriter; +import java.util.List; +import java.util.ArrayList; + + +public final class LuanException extends Exception implements DeepCloneable { + private LuanTable table; + + public LuanException(String msg,Throwable cause) { + super(msg,cause); + initTable(); + } + + public LuanException(String msg) { + super(msg); + initTable(); + } + + public LuanException(Throwable cause) { + super(cause); + initTable(); + } + + private LuanException(String msg,Throwable cause,int nonsense) { + super(msg,cause); + } + + @Override public LuanException shallowClone() { + return new LuanException(getMessage(),getCause(),99); + } + + @Override public void deepenClone(DeepCloneable dc,DeepCloner cloner) { + LuanException clone = (LuanException)dc; + clone.table = (LuanTable)cloner.get(table); + } + + public LuanTable table() { + return table; + } + + private void initTable() { + table = new LuanTable(); + table.rawPut( "java", this ); + LuanTable metatable = new LuanTable(); + table.setMetatable(metatable); + try { + table.rawPut( "get_message", new LuanJavaFunction( + LuanException.class.getMethod( "getMessage" ), this + ) ); + table.rawPut( "throw", new LuanJavaFunction( + LuanException.class.getMethod( "throwThis" ), this + ) ); + table.rawPut( "get_java_stack_trace_string", new LuanJavaFunction( + LuanException.class.getMethod( "getJavaStackTraceString" ), this + ) ); + metatable.rawPut( "__to_string", new LuanJavaFunction( + LuanException.class.getMethod( "getFullMessage" ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public void throwThis() throws LuanException { + throw this; + } + + public String getFullMessage() { + return getLuanStackTraceString(); +// return getLuanStackTraceString()+"\n"+getJavaStackTraceString(); +/* + StringBuilder buf = new StringBuilder(); + + Object msg = table.rawGet("message"); + String msgStr = (String)table.rawGet("message_string"); + buf.append( msgStr ); + + for( int i = table.rawLength(); i>=1; i-- ) { + LuanTable tbl = (LuanTable)table.rawGet(i); + buf.append( "\n\t" ).append( tbl.rawGet("source") ).append( " line " ).append( tbl.rawGet("line") ); + Object callTo = tbl.rawGet("call_to"); + if( callTo != null ) + buf.append( " in call to '" ).append( callTo ).append( "'" ); + } + + if( msg instanceof Throwable ) { + buf.append( "\nCaused by: " ); + Throwable cause = (Throwable)msg; + StringWriter sw = new StringWriter(); + cause.printStackTrace(new PrintWriter(sw)); + buf.append( sw ); + } + + return buf.toString(); +*/ + } + + public String getJavaStackTraceString() { + return getJavaStackTraceString(this); + } + + private static String getJavaStackTraceString(Throwable th) { + StringWriter sw = new StringWriter(); + th.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + + public static List<StackTraceElement> justLuan(StackTraceElement[] orig) { + List<StackTraceElement> list = new ArrayList<StackTraceElement>(); + for( int i=0; i<orig.length; i++ ) { + StackTraceElement ste = orig[i]; + if( !ste.getClassName().startsWith("luan.impl.EXP") ) + continue; + list.add(ste); + if( !ste.getMethodName().equals("doCall") ) + i++; + } + return list; + } + + public static String toString(StackTraceElement ste) { + StringBuilder sb = new StringBuilder(); + sb.append( ste.getFileName() ).append( " line " ).append( ste.getLineNumber() ); + String method = ste.getMethodName(); + if( !method.equals("doCall") ) + sb.append( " in function '" ).append( method.substring(1) ).append( "'" ); + return sb.toString(); + } + + public String getLuanStackTraceString() { + StringBuilder sb = new StringBuilder(); + sb.append( getMessage() ); + for( StackTraceElement ste : justLuan(getStackTrace()) ) { + sb.append( "\n\t" ).append( toString(ste) ); + } + Throwable cause = getCause(); + if( cause != null ) + sb.append( "\nCaused by: " ).append( getJavaStackTraceString(cause) ); + return sb.toString(); + } + + public static String currentSource() { + LuanException ex = new LuanException("currentSource"); + List<StackTraceElement> st = ex.justLuan(ex.getStackTrace()); + return st.isEmpty() ? null : st.get(0).getFileName(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanFunction.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,18 @@ +package luan; + + +public abstract class LuanFunction { + + public abstract Object call(LuanState luan,Object[] args) throws LuanException; + + public static final Object[] NOTHING = new Object[0]; + + public final Object call(LuanState luan) throws LuanException { + return call(luan,NOTHING); + } + + @Override public String toString() { + return "function: " + Integer.toHexString(hashCode()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanJava.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,17 @@ +package luan; + +import luan.DeepCloneable; +import luan.DeepCloner; + + +public final class LuanJava implements DeepCloneable { + public boolean ok = false; + + @Override public LuanJava shallowClone() { + LuanJava java = new LuanJava(); + java.ok = ok; + return java; + } + + @Override public void deepenClone(DeepCloneable clone,DeepCloner cloner) {} +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanJavaFunction.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,572 @@ +package luan; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.Arrays; +import java.util.Collection; + + +public final class LuanJavaFunction extends LuanFunction { + private final JavaMethod method; + private Object obj; + private final RtnConverter rtnConverter; + private final boolean takesLuaState; + private final ArgConverter[] argConverters; + private final Class varArgCls; + + public LuanJavaFunction(Method method,Object obj) { + this( JavaMethod.of(method), obj ); + } + + public LuanJavaFunction(Constructor constr,Object obj) { + this( JavaMethod.of(constr), obj ); + } + + private LuanJavaFunction(JavaMethod method,Object obj) { + this.method = method; + this.obj = obj; + this.rtnConverter = getRtnConverter(method); + this.takesLuaState = takesLuaState(method); + this.argConverters = getArgConverters(takesLuaState,method); + if( method.isVarArgs() ) { + Class[] paramTypes = method.getParameterTypes(); + this.varArgCls = paramTypes[paramTypes.length-1].getComponentType(); + } else { + this.varArgCls = null; + } + } +/* + private LuanJavaFunction(LuanJavaFunction f) { + this.method = f.method; + this.rtnConverter = f.rtnConverter; + this.takesLuaState = f.takesLuaState; + this.argConverters = f.argConverters; + this.varArgCls = f.varArgCls; + } + + @Override public LuanJavaFunction shallowClone() { + return obj==null ? this : new LuanJavaFunction(this); + } + + @Override public void deepenClone(LuanJavaFunction clone,DeepCloner cloner) { + clone.obj = cloner.get(obj); + } +*/ + @Override public String toString() { + return "java-function: " + method; + } + + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + args = fixArgs(luan,args); + return doCall(args); + } catch(IllegalArgumentException e) { + checkArgs(args); + throw e; + } + } + + public Object rawCall(LuanState luan,Object[] args) throws LuanException { + args = fixArgs(luan,args); + return doCall(args); + } + + private Object doCall(Object[] args) throws LuanException { + Object rtn; + try { + rtn = method.invoke(obj,args); + } catch(IllegalAccessException e) { + throw new RuntimeException("method = "+method,e); + } catch(InvocationTargetException e) { + Throwable cause = e.getCause(); + if( cause instanceof Error ) + throw (Error)cause; + if( cause instanceof LuanException ) + throw (LuanException)cause; + throw new LuanException(cause); + } catch(InstantiationException e) { + throw new RuntimeException(e); + } + return rtnConverter.convert(rtn); + } + + private static final Map primitiveMap = new HashMap(); + static { + primitiveMap.put(Boolean.TYPE,Boolean.class); + primitiveMap.put(Character.TYPE,Character.class); + primitiveMap.put(Byte.TYPE,Byte.class); + primitiveMap.put(Short.TYPE,Short.class); + primitiveMap.put(Integer.TYPE,Integer.class); + primitiveMap.put(Long.TYPE,Long.class); + primitiveMap.put(Float.TYPE,Float.class); + primitiveMap.put(Double.TYPE,Double.class); + primitiveMap.put(Void.TYPE,Void.class); + } + + private void checkArgs(Object[] args) throws LuanException { + Class[] a = getParameterTypes(); + int start = takesLuaState ? 1 : 0; + for( int i=start; i<a.length; i++ ) { + Class paramType = a[i]; + Class type = paramType; + if( type.isPrimitive() ) + type = (Class)primitiveMap.get(type); + Object arg = args[i]; + if( !type.isInstance(arg) ) { + String expected; + if( i==a.length-1 && method.isVarArgs() ) + expected = fixType(paramType.getComponentType().getSimpleName())+"..."; + else + expected = fixType(paramType.getSimpleName()); + if( arg==null ) { + if( paramType.isPrimitive() ) + throw new LuanException("bad argument #"+(i+1-start)+" ("+expected+" expected, got nil)"); + } else { + String got = fixType(arg.getClass().getSimpleName()); + throw new LuanException("bad argument #"+(i+1-start)+" ("+expected+" expected, got "+got+")"); + } + } + } + } + + private static String fixType(String type) { + if( type.equals("byte[]") ) + return "binary"; + if( type.equals("Double") ) + return "number"; + if( type.equals("LuanTable") ) + return "table"; + if( type.equals("Boolean") ) + return "boolean"; + if( type.equals("String") ) + return "string"; + if( type.equals("Closure") ) + return "function"; + if( type.equals("LuanJavaFunction") ) + return "function"; + return type; + } + + private Object[] fixArgs(LuanState luan,Object[] args) throws LuanException { + int n = argConverters.length; + Object[] rtn; + int start = 0; + if( !takesLuaState && varArgCls==null && args.length == n ) { + rtn = args; + } else { + if( takesLuaState ) + n++; + rtn = new Object[n]; + if( takesLuaState ) { + rtn[start++] = luan; + } + n = argConverters.length; + if( varArgCls != null ) { + n--; + if( args.length < argConverters.length ) { + rtn[rtn.length-1] = Array.newInstance(varArgCls,0); + } else { + int len = args.length - n; + Object varArgs = Array.newInstance(varArgCls,len); + ArgConverter ac = argConverters[n]; + for( int i=0; i<len; i++ ) { + Array.set( varArgs, i, ac.convert(luan,args[n+i]) ); + } + rtn[rtn.length-1] = varArgs; + } + } + System.arraycopy(args,0,rtn,start,Math.min(args.length,n)); + } + for( int i=0; i<n; i++ ) { + rtn[start+i] = argConverters[i].convert(luan,rtn[start+i]); + } + return rtn; + } + + + private interface RtnConverter { + public Object convert(Object obj); + } + + private static final RtnConverter RTN_NOTHING = new RtnConverter() { + @Override public Object[] convert(Object obj) { + return NOTHING; + } + }; + + private static final RtnConverter RTN_SAME = new RtnConverter() { + @Override public Object convert(Object obj) { + return obj; + } + }; + + private static final RtnConverter RTN_ARRAY = new RtnConverter() { + @Override public Object convert(Object obj) { + if( obj == null ) + return null; + Object[] a = new Object[Array.getLength(obj)]; + for( int i=0; i<a.length; i++ ) { + a[i] = Array.get(obj,i); + } + return new LuanTable(new ArrayList<Object>(Arrays.asList(a))); + } + }; + + private static RtnConverter getRtnConverter(JavaMethod m) { + Class rtnType = m.getReturnType(); + if( rtnType == Void.TYPE ) + return RTN_NOTHING; + if( !m.isLuan() && rtnType.isArray() && !rtnType.getComponentType().isPrimitive() ) { +//System.out.println("qqqqqq "+m); + return RTN_ARRAY; + } + return RTN_SAME; + } + + private static boolean isNumber(Class rtnType) { + return rtnType == Short.TYPE + || rtnType == Integer.TYPE + || rtnType == Long.TYPE + || rtnType == Float.TYPE + || rtnType == Double.TYPE + ; + } + + private interface ArgConverter { + public Object convert(LuanState luan,Object obj) throws LuanException; + } + + private static final ArgConverter ARG_SAME = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + return obj; + } + @Override public String toString() { + return "ARG_SAME"; + } + }; + + private static final ArgConverter ARG_DOUBLE = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Double ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + return n.doubleValue(); + } + return obj; + } + @Override public String toString() { + return "ARG_DOUBLE"; + } + }; + + private static final ArgConverter ARG_FLOAT = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Float ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + return n.floatValue(); + } + return obj; + } + @Override public String toString() { + return "ARG_FLOAT"; + } + }; + + private static final ArgConverter ARG_LONG = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Long ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + long r = n.longValue(); + if( r==n.doubleValue() ) + return r; + } + return obj; + } + @Override public String toString() { + return "ARG_LONG"; + } + }; + + private static final ArgConverter ARG_INTEGER = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Integer ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + int r = n.intValue(); + if( r==n.doubleValue() ) + return r; + } + return obj; + } + @Override public String toString() { + return "ARG_INTEGER"; + } + }; + + private static final ArgConverter ARG_SHORT = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Short ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + short r = n.shortValue(); + if( r==n.doubleValue() ) + return r; + } + return obj; + } + @Override public String toString() { + return "ARG_SHORT"; + } + }; + + private static final ArgConverter ARG_BYTE = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Byte ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + byte r = n.byteValue(); + if( r==n.doubleValue() ) + return r; + } + return obj; + } + @Override public String toString() { + return "ARG_BYTE"; + } + }; + + private static final ArgConverter ARG_TABLE = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj == null ) + return null; + if( obj instanceof List ) { + return new LuanTable((List)obj); + } + if( obj instanceof Map ) { + return new LuanTable((Map)obj); + } + if( obj instanceof Set ) { + return new LuanTable((Set)obj); + } + Class cls = obj.getClass(); + if( cls.isArray() && !cls.getComponentType().isPrimitive() ) { + Object[] a = (Object[])obj; + return new LuanTable(Arrays.asList(a)); + } + return obj; + } + @Override public String toString() { + return "ARG_TABLE"; + } + }; + + private static final ArgConverter ARG_MAP = new ArgConverter() { + public Object convert(LuanState luan,Object obj) throws LuanException { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + return t.asMap(luan); + } + return obj; + } + @Override public String toString() { + return "ARG_MAP"; + } + }; + + private static final ArgConverter ARG_LIST = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + if( t.isList() ) + return t.asList(); + } + return obj; + } + @Override public String toString() { + return "ARG_LIST"; + } + }; + + private static final ArgConverter ARG_SET = new ArgConverter() { + public Object convert(LuanState luan,Object obj) throws LuanException { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + if( t.isSet(luan) ) + return t.asSet(luan); + } + return obj; + } + @Override public String toString() { + return "ARG_SET"; + } + }; + + private static final ArgConverter ARG_COLLECTION = new ArgConverter() { + public Object convert(LuanState luan,Object obj) throws LuanException { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + if( t.isList() ) + return t.asList(); + if( t.isSet(luan) ) + return t.asSet(luan); + } + return obj; + } + @Override public String toString() { + return "ARG_COLLECTION"; + } + }; + + private static class ArgArray implements ArgConverter { + private final Object[] a; + + ArgArray(Class cls) { + a = (Object[])Array.newInstance(cls.getComponentType(),0); + } + + public Object convert(LuanState luan,Object obj) { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + if( t.isList() ) { + try { + return t.asList().toArray(a); + } catch(ArrayStoreException e) {} + } + } + return obj; + } + } + + private static boolean takesLuaState(JavaMethod m) { + Class[] paramTypes = m.getParameterTypes(); + return paramTypes.length > 0 && paramTypes[0].equals(LuanState.class); + } + + private static ArgConverter[] getArgConverters(boolean takesLuaState,JavaMethod m) { + final boolean isVarArgs = m.isVarArgs(); + Class[] paramTypes = m.getParameterTypes(); + if( takesLuaState ) { + Class[] t = new Class[paramTypes.length-1]; + System.arraycopy(paramTypes,1,t,0,t.length); + paramTypes = t; + } + ArgConverter[] a = new ArgConverter[paramTypes.length]; + for( int i=0; i<a.length; i++ ) { + Class paramType = paramTypes[i]; + if( isVarArgs && i == a.length-1 ) + paramType = paramType.getComponentType(); + a[i] = getArgConverter(paramType); + } + return a; + } + + private static ArgConverter getArgConverter(Class cls) { + if( cls == Double.TYPE || cls.equals(Double.class) ) + return ARG_DOUBLE; + if( cls == Float.TYPE || cls.equals(Float.class) ) + return ARG_FLOAT; + if( cls == Long.TYPE || cls.equals(Long.class) ) + return ARG_LONG; + if( cls == Integer.TYPE || cls.equals(Integer.class) ) + 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; + if( cls.equals(LuanTable.class) ) + return ARG_TABLE; + if( cls.equals(Map.class) ) + return ARG_MAP; + if( cls.equals(List.class) ) + return ARG_LIST; + if( cls.equals(Set.class) ) + return ARG_SET; + if( cls.equals(Collection.class) ) + return ARG_COLLECTION; + if( cls.isArray() && !cls.getComponentType().isPrimitive() ) + return new ArgArray(cls); + return ARG_SAME; + } + + + + private static abstract class JavaMethod { + abstract boolean isVarArgs(); + abstract Class[] getParameterTypes(); + abstract Object invoke(Object obj,Object... args) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException; + abstract Class getReturnType(); + abstract boolean isLuan(); + + static JavaMethod of(final Method m) { + return new JavaMethod() { + @Override boolean isVarArgs() { + return m.isVarArgs(); + } + @Override Class[] getParameterTypes() { + return m.getParameterTypes(); + } + @Override Object invoke(Object obj,Object... args) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException + { + return m.invoke(obj,args); + } + @Override Class getReturnType() { + return m.getReturnType(); + } + @Override boolean isLuan() { + return m.getAnnotation(LuanMethod.class) != null; + } + @Override public String toString() { + return m.toString(); + } + }; + } + + static JavaMethod of(final Constructor c) { + return new JavaMethod() { + @Override boolean isVarArgs() { + return c.isVarArgs(); + } + @Override Class[] getParameterTypes() { + return c.getParameterTypes(); + } + @Override Object invoke(Object obj,Object... args) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException + { + return c.newInstance(args); + } + @Override Class getReturnType() { + return c.getDeclaringClass(); + } + @Override boolean isLuan() { + return false; + } + @Override public String toString() { + return c.toString(); + } + }; + } + + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanMeta.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,69 @@ +package luan; + +import java.util.Map; +import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; + + +public abstract class LuanMeta { + + public abstract Object __index(LuanState luan,LuanTable tbl,Object key) throws LuanException; + + protected abstract Iterator keys(LuanTable tbl); + + public LuanFunction __pairs(final LuanState luan,final LuanTable tbl) { + return new LuanFunction() { + final Iterator<Map.Entry<Object,Object>> iter1 = tbl.rawIterator(); + final Iterator<Object> iter2 = keys(tbl); + final Set<Object> set = new HashSet<Object>(); + + @Override public Object[] call(LuanState luan,Object[] args) throws LuanException { + if( iter1.hasNext() ) { + Map.Entry<Object,Object> entry = iter1.next(); + Object key = entry.getKey(); + set.add(key); + return new Object[]{key,entry.getValue()}; + } + while( iter2.hasNext() ) { + Object key = iter2.next(); + if( set.add(key) ) { + Object value = __index(luan,tbl,key); + return new Object[]{key,value}; + } + } + return LuanFunction.NOTHING; + } + }; + } + + public boolean canNewindex() { + return false; + } + + public void __new_index(LuanState luan,LuanTable tbl,Object key,Object value) throws LuanException { + throw new UnsupportedOperationException(); + } + + protected abstract String type(LuanTable tbl); + + public String __to_string(LuanState luan,LuanTable tbl) throws LuanException { + return type(tbl) + "-" + tbl.rawToString(); + } + + public LuanTable newMetatable() { + LuanTable mt = new LuanTable(); + mt.rawPut( "__index", this ); + mt.rawPut( "__pairs", this ); + mt.rawPut( "__to_string", this ); + if( canNewindex() ) + mt.rawPut( "__new_index", this ); + return mt; + } + + public LuanTable newTable() { + LuanTable tbl = new LuanTable(); + tbl.setMetatable( newMetatable() ); + return tbl; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanMethod.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,8 @@ +package luan; + +import java.lang.annotation.*; + + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface LuanMethod {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanPropertyMeta.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,75 @@ +package luan; + +import java.util.Map; +import java.util.Iterator; + + +public final class LuanPropertyMeta extends LuanMeta { + public static final LuanPropertyMeta INSTANCE = new LuanPropertyMeta(); + + private LuanPropertyMeta() {} + + public LuanTable getters(LuanTable tbl) { + return (LuanTable)tbl.getMetatable().rawGet("get"); + } + + public LuanTable setters(LuanTable tbl) { + return (LuanTable)tbl.getMetatable().rawGet("set"); + } + + protected String type(LuanTable tbl) { + return (String)tbl.getMetatable().rawGet("type"); + } + + @Override public Object __index(LuanState luan,LuanTable tbl,Object key) throws LuanException { + Object obj = getters(tbl).rawGet(key); + if( obj == null ) + return null; + if( !(obj instanceof LuanFunction) ) + throw new LuanException("get for '"+key+"' isn't a function"); + LuanFunction fn = (LuanFunction)obj; + return fn.call(luan); + } + + @Override protected Iterator keys(final LuanTable tbl) { + return new Iterator() { + final Iterator<Map.Entry<Object,Object>> iter = getters(tbl).rawIterator(); + + @Override public boolean hasNext() { + return iter.hasNext(); + } + @Override public Object next() { + return iter.next().getKey(); + } + @Override public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + + @Override public boolean canNewindex() { + return true; + } + + @Override public void __new_index(LuanState luan,LuanTable tbl,Object key,Object value) throws LuanException { + Object obj = setters(tbl).rawGet(key); + if( obj == null ) { + tbl.rawPut(key,value); + return; + } + if( !(obj instanceof LuanFunction) ) + throw new LuanException("set for '"+key+"' isn't a function"); + LuanFunction fn = (LuanFunction)obj; + fn.call(luan,new Object[]{value}); + } + + @Override public LuanTable newMetatable() { + LuanTable mt = super.newMetatable(); + mt.rawPut( "get", new LuanTable() ); + mt.rawPut( "set", new LuanTable() ); + mt.rawPut( "type", "property" ); + return mt; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanRuntimeException.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,8 @@ +package luan; + + +public final class LuanRuntimeException extends RuntimeException { + public LuanRuntimeException(LuanException e) { + super(e); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanState.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,97 @@ +package luan; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import luan.impl.LuanCompiler; +import luan.modules.BasicLuan; +import luan.modules.JavaLuan; + + +public final class LuanState implements DeepCloneable { + + public LuanJava java; + private Map registry; + private final List<Reference<Closeable>> onClose = new ArrayList<Reference<Closeable>>(); + + public LuanState() { + java = new LuanJava(); + registry = new HashMap(); + } + + private LuanState(LuanState luan) {} + + @Override public LuanState shallowClone() { + return new LuanState(this); + } + + @Override public void deepenClone(DeepCloneable dc,DeepCloner cloner) { + LuanState clone = (LuanState)dc; + clone.registry = cloner.deepClone(registry); + clone.java = (LuanJava)cloner.deepClone(java); + } + + public final Map registry() { + return registry; + } + + public void onClose(Closeable c) { + onClose.add(new WeakReference<Closeable>(c)); + } + + public void close() throws IOException { + for( Reference<Closeable> ref : onClose ) { + Closeable c = ref.get(); + if( c != null ) + c.close(); + } + onClose.clear(); + } +/* + public final Object eval(String cmd) throws LuanException { + return eval(cmd,new LuanTable()); + } + + public final Object eval(String cmd,LuanTable env) throws LuanException { + LuanFunction fn = BasicLuan.load(this,cmd,"eval",env,true); + return fn.call(this); + } +*/ + + public String toString(Object obj) throws LuanException { + if( obj instanceof LuanTable ) { + LuanTable tbl = (LuanTable)obj; + return tbl.toString(this); + } + if( obj == null ) + return "nil"; + if( obj instanceof Number ) + return Luan.toString((Number)obj); + if( obj instanceof byte[] ) + return "binary: " + Integer.toHexString(obj.hashCode()); + return obj.toString(); + } + + public Object index(Object obj,Object key) throws LuanException { + if( obj instanceof LuanTable ) { + LuanTable tbl = (LuanTable)obj; + return tbl.get(this,key); + } + if( obj != null && java.ok ) + return JavaLuan.__index(this,obj,key,false); + throw new LuanException("attempt to index a " + Luan.type(obj) + " value" ); + } + +/* + public Number checkNumber(Object obj) throws LuanException { + if( obj instanceof Number ) + return (Number)obj; + throw new LuanException( "attempt to perform arithmetic on '"+context()+"' (a " + Luan.type(obj) + " value)" ); + } +*/ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanTable.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,451 @@ +package luan; + +import java.util.Iterator; +import java.util.ListIterator; +import java.util.Map; +import java.util.AbstractMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Set; +import java.util.HashSet; + + +public final class LuanTable implements DeepCloneable { + private Map map = null; + private List list = null; + private LuanTable metatable = null; + public LuanJava java; + + public LuanTable() {} + + public LuanTable(List list) { + int n = list.size(); + for( int i=0; i<n; i++ ) { + Object val = list.get(i); + if( val != null ) + rawPut(i+1,val); + } + } + + public LuanTable(Map map) { + for( Object stupid : map.entrySet() ) { + Map.Entry entry = (Map.Entry)stupid; + Object key = entry.getKey(); + Object value = entry.getValue(); + if( key != null && value != null ) + rawPut(key,value); + } + } + + public LuanTable(Set set) { + for( Object el : set ) { + if( el != null ) + rawPut(el,Boolean.TRUE); + } + } + + public LuanTable(LuanTable tbl) { + if( tbl.map != null && !tbl.map.isEmpty() ) + this.map = new LinkedHashMap<Object,Object>(tbl.map); + if( tbl.rawLength() > 0 ) + this.list = new ArrayList<Object>(tbl.list); + this.metatable = tbl.metatable; + } + + @Override public LuanTable shallowClone() { + return new LuanTable(); + } + + @Override public void deepenClone(DeepCloneable dc,DeepCloner cloner) { + LuanTable clone = (LuanTable)dc; + if( map != null ) { + clone.map = newMap(); + for( Object stupid : map.entrySet() ) { + Map.Entry entry = (Map.Entry)stupid; + clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) ); + } + } + if( list != null ) { + clone.list = new ArrayList<Object>(); + for( Object obj : list ) { + clone.list.add( cloner.get(obj) ); + } + } + if( metatable != null ) + clone.metatable = (LuanTable)cloner.get(metatable); + clone.java = (LuanJava)cloner.deepClone(java); + } + + public boolean isList() { + return map==null || map.isEmpty(); + } + + public List<Object> asList() { + return list!=null ? list : Collections.emptyList(); + } + + public String toString(LuanState luan) throws LuanException { + Object h = getHandler("__to_string"); + if( h == null ) + return rawToString(); + if( h instanceof LuanMeta ) { + LuanMeta meta = (LuanMeta)h; + return meta.__to_string(luan,this); + } + LuanFunction fn = Luan.checkFunction(h); + return Luan.checkString( Luan.first( fn.call(luan,new Object[]{this}) ) ); + } + + public String rawToString() { + return "table: " + Integer.toHexString(hashCode()); + } + + public Object get(LuanState luan,Object key) throws LuanException { + Object value = rawGet(key); + if( value != null ) + return value; + Object h = getHandler("__index"); + if( h==null ) + return null; + if( h instanceof LuanFunction ) { + LuanFunction fn = (LuanFunction)h; + return Luan.first(fn.call(luan,new Object[]{this,key})); + } + if( h instanceof LuanMeta ) { + LuanMeta meta = (LuanMeta)h; + return meta.__index(luan,this,key); + } + return luan.index(h,key); + } + + public Object rawGet(Object key) { + if( list != null ) { + Integer iT = Luan.asInteger(key); + if( iT != null ) { + int i = iT - 1; + if( i>=0 && i<list.size() ) + return list.get(i); + } + } + if( map==null ) + return null; + if( key instanceof Number && !(key instanceof Double) ) { + Number n = (Number)key; + key = Double.valueOf(n.doubleValue()); + } + return map.get(key); + } + + public void put(LuanState luan,Object key,Object value) throws LuanException { + Object h = getHandler("__new_index"); + if( h==null || rawGet(key)!=null ) { + rawPut(key,value); + return; + } + if( h instanceof LuanFunction ) { + LuanFunction fn = (LuanFunction)h; + fn.call(luan,new Object[]{this,key,value}); + return; + } + if( h instanceof LuanMeta ) { + LuanMeta meta = (LuanMeta)h; + meta.__new_index(luan,this,key,value); + return; + } + if( h instanceof LuanTable ) { + LuanTable tbl = (LuanTable)h; + tbl.put(luan,key,value); + return; + } + throw new LuanException("invalid type "+Luan.type(h)+" for metamethod __new_index"); + } + + public void rawPut(Object key,Object val) { + Integer iT = Luan.asInteger(key); + if( iT != null ) { + int i = iT - 1; + if( list != null || i == 0 ) { + if( i == list().size() ) { + if( val != null ) { + list.add(val); + mapToList(); + } + return; + } else if( i>=0 && i<list.size() ) { + list.set(i,val); + if( val == null ) { + listToMap(i); + } + return; + } + } + } + if( map==null ) + map = newMap(); + if( key instanceof Number && !(key instanceof Double) ) { + Number n = (Number)key; + key = Double.valueOf(n.doubleValue()); + } + if( val == null ) { + map.remove(key); + } else { + map.put(key,val); + } + } + + private void mapToList() { + if( map != null ) { + while(true) { + Object v = map.remove(Double.valueOf(list.size()+1)); + if( v == null ) + break; + list.add(v); + } + } + } + + private void listToMap(int from) { + if( list != null ) { + while( list.size() > from ) { + int i = list.size() - 1; + Object v = list.remove(i); + if( v != null ) { + if( map==null ) + map = newMap(); + map.put(i+1,v); + } + } + } + } + + private List<Object> list() { + if( list == null ) { + list = new ArrayList<Object>(); + mapToList(); + } + return list; + } + + public void rawInsert(int pos,Object value) { + if( value==null ) + throw new IllegalArgumentException("can't insert a nil value"); + list().add(pos-1,value); + mapToList(); + } + + public Object rawRemove(int pos) { + return list().remove(pos-1); + } + + public void rawSort(Comparator<Object> cmp) { + Collections.sort(list(),cmp); + } + + public int length(LuanState luan) throws LuanException { + Object h = getHandler("__len"); + if( h != null ) { + LuanFunction fn = Luan.checkFunction(h); + return (Integer)Luan.first(fn.call(luan,new Object[]{this})); + } + return rawLength(); + } + + public int rawLength() { + return list==null ? 0 : list.size(); + } + + public Iterable<Map.Entry<Object,Object>> iterable(LuanState luan) throws LuanException { + final Iterator<Map.Entry<Object,Object>> iter = iterator(luan); + return new Iterable<Map.Entry<Object,Object>>() { + public Iterator<Map.Entry<Object,Object>> iterator() { + return iter; + } + }; + } + + public Iterable<Map.Entry<Object,Object>> rawIterable() throws LuanException { + final Iterator<Map.Entry<Object,Object>> iter = rawIterator(); + return new Iterable<Map.Entry<Object,Object>>() { + public Iterator<Map.Entry<Object,Object>> iterator() { + return iter; + } + }; + } + + public Iterator<Map.Entry<Object,Object>> iterator(final LuanState luan) throws LuanException { + if( getHandler("__pairs") == null ) + return rawIterator(); + final LuanFunction fn = pairs(luan); + return new Iterator<Map.Entry<Object,Object>>() { + private Map.Entry<Object,Object> next = getNext(); + + private Map.Entry<Object,Object> getNext() { + try { + Object obj = fn.call(luan); + if( obj==null ) + return null; + Object[] a = (Object[])obj; + if( a.length == 0 || a[0]==null ) + return null; + return new AbstractMap.SimpleEntry<Object,Object>(a[0],a[1]); + } catch(LuanException e) { + throw new LuanRuntimeException(e); + } + } + + public boolean hasNext() { + return next != null; + } + + public Map.Entry<Object,Object> next() { + Map.Entry<Object,Object> rtn = next; + next = getNext(); + return rtn; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public LuanFunction pairs(LuanState luan) throws LuanException { + Object h = getHandler("__pairs"); + if( h != null ) { + if( h instanceof LuanFunction ) { + LuanFunction fn = (LuanFunction)h; + Object obj = Luan.first(fn.call(luan,new Object[]{this})); + if( !(obj instanceof LuanFunction) ) + throw new LuanException( "metamethod __pairs should return function but returned " + Luan.type(obj) ); + return (LuanFunction)obj; + } + if( h instanceof LuanMeta ) { + LuanMeta meta = (LuanMeta)h; + return meta.__pairs(luan,this); + } + throw new LuanException( "invalid type of metamethod __pairs: " + Luan.type(h) ); + } + return rawPairs(); + } + + private LuanFunction rawPairs() { + return new LuanFunction() { + final Iterator<Map.Entry<Object,Object>> iter = rawIterator(); + + @Override public Object[] call(LuanState luan,Object[] args) { + if( !iter.hasNext() ) + return LuanFunction.NOTHING; + Map.Entry<Object,Object> entry = iter.next(); + return new Object[]{entry.getKey(),entry.getValue()}; + } + }; + } + + public Iterator<Map.Entry<Object,Object>> rawIterator() { + if( list == null ) { + if( map == null ) + return Collections.<Map.Entry<Object,Object>>emptyList().iterator(); + return map.entrySet().iterator(); + } + if( map == null ) + return listIterator(); + return new Iterator<Map.Entry<Object,Object>>() { + Iterator<Map.Entry<Object,Object>> iter = listIterator(); + boolean isList = true; + + public boolean hasNext() { + boolean b = iter.hasNext(); + if( !b && isList ) { + iter = map.entrySet().iterator(); + isList = false; + b = iter.hasNext(); + } + return b; + } + + public Map.Entry<Object,Object> next() { + return iter.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + private Iterator<Map.Entry<Object,Object>> listIterator() { + if( list == null ) + return Collections.<Map.Entry<Object,Object>>emptyList().iterator(); + final ListIterator iter = list.listIterator(); + return new Iterator<Map.Entry<Object,Object>>() { + + public boolean hasNext() { + return iter.hasNext(); + } + + public Map.Entry<Object,Object> next() { + Integer key = iter.nextIndex()+1; + return new AbstractMap.SimpleEntry<Object,Object>(key,iter.next()); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public LuanTable rawSubList(int from,int to) { + LuanTable tbl = shallowClone(); + tbl.list = new ArrayList<Object>(list().subList(from-1,to-1)); + return tbl; + } + + public LuanTable getMetatable() { + return metatable; + } + + public void setMetatable(LuanTable metatable) { + this.metatable = metatable; + } + + public Object getHandler(String op) { + return metatable==null ? null : metatable.rawGet(op); + } + + private Map<Object,Object> newMap() { + return new LinkedHashMap<Object,Object>(); + } + + public boolean isSet(LuanState luan) throws LuanException { + for( Map.Entry<Object,Object> entry : iterable(luan) ) { + if( !entry.getValue().equals(Boolean.TRUE) ) + return false; + } + return true; + } + + public Set<Object> asSet(LuanState luan) throws LuanException { + Set<Object> set = new HashSet<Object>(); + for( Map.Entry<Object,Object> entry : iterable(luan) ) { + set.add(entry.getKey()); + } + return set; + } + + public Map<Object,Object> asMap(LuanState luan) throws LuanException { + Map<Object,Object> map = newMap(); + for( Map.Entry<Object,Object> entry : iterable(luan) ) { + map.put(entry.getKey(),entry.getValue()); + } + return map; + } + + public void rawClear() { + map = null; + list = null; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/cmd_line.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,31 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local ipairs = Luan.ipairs or error() +local load_file = Luan.load_file or error() +local try = Luan.try or error() +local Table = require "luan:Table.luan" +local Io = require "luan:Io.luan" +local print = Io.print or error() + + +local args = {...} +if #args == 0 then + print("Luan "..Luan.VERSION) + Io.debug("> ") +else + local file = args[1] + Luan.arg = {} + for j,v in ipairs(args) do + Luan.arg[j-1] = v + end + try { + function() + local main_file = load_file(file) + print( main_file( Table.unpack(Luan.arg) ) ) + end; + catch = function(e) +-- java(); e.java.printStackTrace(); + Io.print_to(Io.stderr, e ) + end; + } +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/Closure.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,48 @@ +package luan.impl; + +import luan.Luan; +import luan.LuanFunction; +import luan.LuanState; +import luan.LuanException; +import luan.DeepCloner; +import luan.DeepCloneable; +import luan.LuanJava; + + +public abstract class Closure extends LuanFunction implements DeepCloneable, Cloneable { + public Pointer[] upValues; + public LuanJava java; + + public Closure(int nUpValues,LuanJava java) throws LuanException { + this.upValues = new Pointer[nUpValues]; + this.java = java; + } + + @Override public Closure shallowClone() { + try { + return (Closure)clone(); + } catch(CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + @Override public void deepenClone(DeepCloneable dc,DeepCloner cloner) { + Closure clone = (Closure)dc; + clone.upValues = (Pointer[])cloner.deepClone(upValues); + clone.java = (LuanJava)cloner.deepClone(java); + } + + @Override public final Object call(LuanState luan,Object[] args) throws LuanException { + LuanJava old = luan.java; + luan.java = java; + try { + return doCall(luan,args); + } catch(StackOverflowError e) { + throw new LuanException( "stack overflow" ); + } finally { + luan.java = old; + } + } + + public abstract Object doCall(LuanState luan,Object[] args) throws LuanException; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/LuanCompiler.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,87 @@ +package luan.impl; + +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.HashMap; +import luan.LuanFunction; +import luan.LuanState; +import luan.LuanException; +import luan.LuanTable; +import luan.LuanJava; +import luan.modules.JavaLuan; +import luan.modules.PackageLuan; + + +public final class LuanCompiler { + private static final Map<String,WeakReference<Class>> map = new HashMap<String,WeakReference<Class>>(); + + public static LuanFunction compile(String sourceText,String sourceName,LuanTable env) throws LuanException { + Class fnClass = env==null ? getClass(sourceText,sourceName) : getClass(sourceText,sourceName,env); + LuanJava java; + if( env == null ) { + java = new LuanJava(); + } else { + java = env.java; + if( java == null ) { + java = new LuanJava(); + env.java = java; + } + } + Closure closure; + try { + closure = (Closure)fnClass.getConstructor(LuanJava.class).newInstance(java); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } catch(InstantiationException e) { + throw new RuntimeException(e); + } catch(IllegalAccessException e) { + throw new RuntimeException(e); + } catch(InvocationTargetException e) { + throw new RuntimeException(e); + } + closure.upValues[0].o = JavaLuan.javaFn; + closure.upValues[1].o = PackageLuan.requireFn; + if( env != null ) closure.upValues[2].o = env; + return closure; + } + + private static synchronized Class getClass(String sourceText,String sourceName) throws LuanException { + String key = sourceName + "~~~" + sourceText; + WeakReference<Class> ref = map.get(key); + if( ref != null ) { + Class cls = ref.get(); + if( cls != null ) + return cls; + } + Class cls = getClass(sourceText,sourceName,null); + map.put(key,new WeakReference<Class>(cls)); + return cls; + } + + private static Class getClass(String sourceText,String sourceName,LuanTable env) throws LuanException { + LuanParser parser = new LuanParser(sourceText,sourceName); + parser.addVar( "java" ); + parser.addVar( "require" ); + if( env != null ) parser.addVar( "_ENV" ); + try { + return parser.RequiredModule(); + } catch(ParseException e) { +//e.printStackTrace(); + throw new LuanException( e.getFancyMessage() ); + } + } + + public static String toJava(String sourceText,String sourceName) throws LuanException { + LuanParser parser = new LuanParser(sourceText,sourceName); + parser.addVar( "java" ); + parser.addVar( "require" ); + try { + return parser.RequiredModuleSource(); + } catch(ParseException e) { + throw new LuanException( e.getFancyMessage() ); + } + } + + private LuanCompiler() {} // never +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/LuanImpl.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,250 @@ +package luan.impl; + +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.modules.JavaLuan; + + +public final class LuanImpl { + private LuanImpl() {} // never + + public static int len(LuanState luan,Object o) throws LuanException { + if( o instanceof String ) { + String s = (String)o; + return s.length(); + } + if( o instanceof byte[] ) { + byte[] a = (byte[])o; + return a.length; + } + if( o instanceof LuanTable ) { + LuanTable t = (LuanTable)o; + return t.length(luan); + } + throw new LuanException( "attempt to get length of a " + Luan.type(o) + " value" ); + } + + public static Object unm(LuanState luan,Object o) throws LuanException { + if( o instanceof Number ) + return -((Number)o).doubleValue(); + if( o instanceof LuanTable ) { + LuanFunction fn = Luan.getHandlerFunction("__unm",(LuanTable)o); + if( fn != null ) { + return Luan.first(fn.call(luan,new Object[]{o})); + } + } + throw new LuanException("attempt to perform arithmetic on a "+Luan.type(o)+" value"); + } + + private static Object arithmetic(LuanState luan,String op,Object o1,Object o2) throws LuanException { + LuanFunction fn = Luan.getBinHandler(op,o1,o2); + if( fn != null ) + return Luan.first(fn.call(luan,new Object[]{o1,o2})); + String type = !(o1 instanceof Number) ? Luan.type(o1) : Luan.type(o2); + throw new LuanException("attempt to perform arithmetic on a "+type+" value"); + } + + public static Object pow(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 instanceof Number && o2 instanceof Number ) + return Math.pow( ((Number)o1).doubleValue(), ((Number)o2).doubleValue() ); + return arithmetic(luan,"__pow",o1,o2); + } + + public static Object mul(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 instanceof Number && o2 instanceof Number ) + return ((Number)o1).doubleValue() * ((Number)o2).doubleValue(); + return arithmetic(luan,"__mul",o1,o2); + } + + public static Object div(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 instanceof Number && o2 instanceof Number ) + return ((Number)o1).doubleValue() / ((Number)o2).doubleValue(); + return arithmetic(luan,"__div",o1,o2); + } + + public static Object mod(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 instanceof Number && o2 instanceof Number ) { + double d1 = ((Number)o1).doubleValue(); + double d2 = ((Number)o2).doubleValue(); + return d1 - Math.floor(d1/d2)*d2; + } + return arithmetic(luan,"__mod",o1,o2); + } + + public static Object add(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 instanceof Number && o2 instanceof Number ) + return ((Number)o1).doubleValue() + ((Number)o2).doubleValue(); + return arithmetic(luan,"__add",o1,o2); + } + + public static Object sub(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 instanceof Number && o2 instanceof Number ) + return ((Number)o1).doubleValue() - ((Number)o2).doubleValue(); + return arithmetic(luan,"__sub",o1,o2); + } + + public static Object concat(LuanState luan,Object o1,Object o2) throws LuanException { + LuanFunction fn = Luan.getBinHandler("__concat",o1,o2); + if( fn != null ) + return Luan.first(fn.call(luan,new Object[]{o1,o2})); + String s1 = luan.toString(o1); + String s2 = luan.toString(o2); + return s1 + s2; + } + + public static boolean eq(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 == o2 || o1 != null && o1.equals(o2) ) + return true; + if( o1 instanceof Number && o2 instanceof Number ) { + Number n1 = (Number)o1; + Number n2 = (Number)o2; + return n1.doubleValue() == n2.doubleValue(); + } + if( o1 instanceof byte[] && o2 instanceof byte[] ) { + byte[] b1 = (byte[])o1; + byte[] b2 = (byte[])o2; + return Arrays.equals(b1,b2); + } + if( !(o1 instanceof LuanTable && o2 instanceof LuanTable) ) + return false; + LuanTable t1 = (LuanTable)o1; + LuanTable t2 = (LuanTable)o2; + LuanTable mt1 = t1.getMetatable(); + LuanTable mt2 = t2.getMetatable(); + if( mt1==null || mt2==null ) + return false; + Object f = mt1.rawGet("__eq"); + if( f == null || !f.equals(mt2.rawGet("__eq")) ) + return false; + LuanFunction fn = Luan.checkFunction(f); + return Luan.checkBoolean( Luan.first(fn.call(luan,new Object[]{o1,o2})) ); + } + + public static boolean le(LuanState luan,Object o1,Object o2) throws LuanException { + if( o1 instanceof Number && o2 instanceof Number ) { + Number n1 = (Number)o1; + Number n2 = (Number)o2; + return n1.doubleValue() <= n2.doubleValue(); + } + if( o1 instanceof String && o2 instanceof String ) { + String s1 = (String)o1; + String s2 = (String)o2; + return s1.compareTo(s2) <= 0; + } + LuanFunction fn = Luan.getBinHandler("__le",o1,o2); + if( fn != null ) + return Luan.checkBoolean( Luan.first(fn.call(luan,new Object[]{o1,o2})) ); + fn = Luan.getBinHandler("__lt",o1,o2); + if( fn != null ) + return !Luan.checkBoolean( Luan.first(fn.call(luan,new Object[]{o2,o1})) ); + throw new LuanException( "attempt to compare " + Luan.type(o1) + " with " + Luan.type(o2) ); + } + + public static boolean lt(LuanState luan,Object o1,Object o2) throws LuanException { + return Luan.isLessThan(luan,o1,o2); + } + + public static boolean cnd(Object o) throws LuanException { + return !(o == null || Boolean.FALSE.equals(o)); + } + + public static void nop(Object o) {} + + public static void put(LuanState luan,Object t,Object key,Object value) throws LuanException { + if( t instanceof LuanTable ) { + LuanTable tbl = (LuanTable)t; + tbl.put(luan,key,value); + return; + } + if( t != null && luan.java.ok ) + JavaLuan.__new_index(luan,t,key,value); + else + throw new LuanException( "attempt to index a " + Luan.type(t) + " value" ); + } + + public static Object pick(Object o,int i) { + if( i < 1 ) + throw new RuntimeException(); + if( !(o instanceof Object[]) ) + return null; + Object[] a = (Object[])o; + return i<a.length ? a[i] : null; + } + + public static Object[] varArgs(Object[] a,int i) { + if( i >= a.length ) + return LuanFunction.NOTHING; + Object[] rtn = new Object[a.length - i]; + System.arraycopy(a,i,rtn,0,rtn.length); + return rtn; + } + + public static Object[] concatArgs(Object o1,Object o2) { + if( o1 instanceof Object[] ) { + Object[] a1 = (Object[])o1; + if( o2 instanceof Object[] ) { + Object[] a2 = (Object[])o2; + Object[] rtn = new Object[a1.length+a2.length]; + System.arraycopy(a1,0,rtn,0,a1.length); + System.arraycopy(a2,0,rtn,a1.length,a2.length); + return rtn; + } else { + Object[] rtn = new Object[a1.length+1]; + System.arraycopy(a1,0,rtn,0,a1.length); + rtn[a1.length] = o2; + return rtn; + } + } else { + if( o2 instanceof Object[] ) { + Object[] a2 = (Object[])o2; + Object[] rtn = new Object[1+a2.length]; + rtn[0] = o1; + System.arraycopy(a2,0,rtn,1,a2.length); + return rtn; + } else { + Object[] rtn = new Object[2]; + rtn[0] = o1; + rtn[2] = o2; + return rtn; + } + } + } + + public static LuanTable table(Object[] a) { + LuanTable table = new LuanTable(); + int i = 0; + for( Object fld : a ) { + if( fld instanceof TableField ) { + TableField tblFld = (TableField)fld; + Object key = tblFld.key; + Object value = tblFld.value; + if( key != null && value != null ) + table.rawPut(key,value); + } else { + i++; + if( fld != null ) + table.rawPut(i,fld); + } + } + return table; + } + + public static Object first(Object[] a) { + return a.length==0 ? null : a[0]; + } + + public static String strconcat(String... a) { + StringBuilder sb = new StringBuilder(); + for( String s : a ) { + sb.append(s); + } + return sb.toString(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/LuanJavaCompiler.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,100 @@ +package luan.impl; + +import java.io.OutputStream; +import java.io.ByteArrayOutputStream; +import java.io.StringWriter; +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import javax.tools.FileObject; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; +import javax.tools.JavaFileManager; +import javax.tools.StandardJavaFileManager; +import javax.tools.ForwardingJavaFileManager; + + +public final class LuanJavaCompiler { + private LuanJavaCompiler() {} // never + + private static class MyJavaFileObject extends SimpleJavaFileObject { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + MyJavaFileObject() { + super(URI.create("whatever"),JavaFileObject.Kind.CLASS); + } + + @Override public OutputStream openOutputStream() { + return baos; + } + + byte[] byteCode(String sourceName) { + byte[] byteCode = baos.toByteArray(); + final int len = sourceName.length(); + int max = byteCode.length-len-3; + outer: + for( int i=0; true; i++ ) { + if( i > max ) + throw new RuntimeException("len="+len); + if( byteCode[i]==1 && (byteCode[i+1] << 8 | 0xFF & byteCode[i+2]) == len ) { + for( int j=i+3; j<i+3+len; j++ ) { + if( byteCode[j] != '$' ) + continue outer; + } + System.arraycopy(sourceName.getBytes(),0,byteCode,i+3,len); + break; + } + } + return byteCode; + } + } + + public static Class compile(final String className,final String sourceName,final String code) throws ClassNotFoundException { + final int len = sourceName.length(); + StringBuilder sb = new StringBuilder(sourceName); + for( int i=0; i<len; i++ ) + sb.setCharAt(i,'$'); + JavaFileObject sourceFile = new SimpleJavaFileObject(URI.create(sb.toString()),JavaFileObject.Kind.SOURCE) { + @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return code; + } + @Override public String getName() { + return sourceName; + } + @Override public boolean isNameCompatible(String simpleName,JavaFileObject.Kind kind) { + return true; + } + }; + final Map<String,MyJavaFileObject> map = new HashMap<String,MyJavaFileObject>(); + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager sjfm = compiler.getStandardFileManager(null,null,null); + ForwardingJavaFileManager fjfm = new ForwardingJavaFileManager(sjfm) { + @Override public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { + if( map.containsKey(className) ) + throw new RuntimeException(className); + MyJavaFileObject classFile = new MyJavaFileObject(); + map.put(className,classFile); + return classFile; + } + }; + StringWriter out = new StringWriter(); + boolean b = compiler.getTask(out, fjfm, null, null, null, Collections.singletonList(sourceFile)).call(); + if( !b ) + throw new RuntimeException("\n"+out+"\ncode:\n"+code+"\n"); + ClassLoader cl = new ClassLoader() { + @Override protected Class<?> findClass(String name) throws ClassNotFoundException { + MyJavaFileObject jfo = map.get(name); + if( jfo != null ) { + byte[] byteCode = jfo.byteCode(sourceName); + return defineClass(name, byteCode, 0, byteCode.length); + } + return super.findClass(name); + } + }; + return cl.loadClass(className); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/LuanParser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,2004 @@ +package luan.impl; + +//import java.io.StringWriter; +//import java.io.PrintWriter; +import java.util.Set; +import java.util.HashSet; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicInteger; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.modules.PackageLuan; + + +final class LuanParser { + + private interface Sym { + public Expr exp(); + } + + private int symCounter = 0; + + private class LocalSym implements Sym { + final String name; + final String javaName; + boolean isPointer = false; + + LocalSym(String name) { + this.name = name; + this.javaName = name + "_" + (++symCounter); + } + + Stmts declaration(Expr value) { + Stmts stmt = new Stmts(); + if( value==null ) { + stmt.add( new Object() { + @Override public String toString() { + if( !isPointer ) + return "Object " + javaName + "; "; + else + return "final Pointer " + javaName + " = new Pointer(); "; + } + } ); + } else { + if( value.valType != Val.SINGLE ) throw new RuntimeException(); + stmt.add( new Object() { + @Override public String toString() { + if( !isPointer ) + return "Object " + javaName + " = "; + else + return "final Pointer " + javaName + " = new Pointer("; + } + } ); + stmt.addAll(value); + stmt.add( new Object() { + @Override public String toString() { + if( !isPointer ) + return "; "; + else + return "); "; + } + } ); + } + return stmt; + } + + @Override public Expr exp() { + Expr exp = new Expr(Val.SINGLE,false); + exp.add( new Object() { + @Override public String toString() { + if( !isPointer ) + return javaName; + else + return javaName + ".o"; + } + } ); + return exp; + } + } + + private class UpSym implements Sym { + final String name; + final int i; + final String value; + + UpSym(String name,int i,String value) { + this.name = name; + this.i = i; + this.value = value; + } + + String init() { + return "upValues[" + i + "] = " + value + "; "; + } + + @Override public Expr exp() { + Expr exp = new Expr(Val.SINGLE,false); + exp.add( new Object() { + @Override public String toString() { + return "upValues[" + i + "].o"; + } + } ); + return exp; + } + } + + private final class Frame { + final Frame parent; + final List<LocalSym> symbols = new ArrayList<LocalSym>(); + int loops = 0; + boolean isVarArg = false; + final List<UpSym> upValueSymbols = new ArrayList<UpSym>(); + + Frame() { + this.parent = null; + } + + Frame(Frame parent) { + this.parent = parent; + } + + LocalSym addLocalSym(String name) { + LocalSym sym = new LocalSym(name); + symbols.add(sym); + return sym; + } + + UpSym addUpSym(String name,String value) { + UpSym sym = new UpSym( name, upValueSymbols.size(), value ); + upValueSymbols.add(sym); + return sym; + } + + LocalSym getLocalSym(String name) { + int i = symbols.size(); + while( --i >= 0 ) { + LocalSym sym = symbols.get(i); + if( sym.name.equals(name) ) + return sym; + } + return null; + } + + UpSym getUpSym(String name) { + for( UpSym upSym : upValueSymbols ) { + if( upSym.name.equals(name) ) + return upSym; + } + if( parent != null ) { + LocalSym sym = parent.getLocalSym(name); + if( sym != null ) { + sym.isPointer = true; + return addUpSym(name,sym.javaName); + } + UpSym upSym = parent.getUpSym(name); + if( upSym != null ) { + return addUpSym(name,"parentUpValues["+upSym.i+"]"); + } + } + return null; + } + + Sym getSym(String name) { + Sym sym = getLocalSym(name); + return sym != null ? sym : getUpSym(name); + } + + } + + private static class In { + static final In NOTHING = new In(false); + + final boolean template; + + private In(boolean template) { + this.template = template; + } + + In template() { + return template ? this : new In(true); + } + } + + private Frame frame; + private final Parser parser; + private final Stmts top; + + LuanParser(String sourceText,String sourceName) { + this.frame = new Frame(); + this.parser = new Parser(sourceText,sourceName); + this.top = new Stmts(); + } + + void addVar(String name) { + UpSym upSym = frame.addUpSym( "-ADDED-" ,"new Pointer()"); + LocalSym sym = frame.addLocalSym( name ); + sym.isPointer = true; + top.add( "final Pointer " + sym.javaName + " = upValues[" + upSym.i + "]; " ); + } + + private int symbolsSize() { + return frame.symbols.size(); + } + + private Stmts addSymbol(String name,Expr value) { + final LocalSym sym = frame.addLocalSym(name); + return sym.declaration(value); + } + + private Sym getSym(String name) { + return frame.getSym(name); + } + + private void popSymbols(int n) { + List<LocalSym> symbols = frame.symbols; + while( n-- > 0 ) { + symbols.remove(symbols.size()-1); + } + } + + private void incLoops() { + frame.loops++; + } + + private void decLoops() { + frame.loops--; + } + + private <T> T required(T t) throws ParseException { + if( t==null ) + throw parser.exception(); + return t; + } + + private <T> T required(T t,String msg) throws ParseException { + if( t==null ) + throw parser.exception(msg); + return t; + } + + private Class newFnClass(Stmts stmt) { + return toFnClass( stmt, frame.upValueSymbols ); + } + + private Expr newFnExp(Stmts stmt,String name) { + return toFnExp( stmt, frame.upValueSymbols, name ); + } +/* + Class Expression() throws ParseException { + Spaces(); + parser.begin(); + Expr expr = ExprZ(In.NOTHING); + if( expr != null && parser.endOfInput() ) { + top.add( "return " ); + top.addAll( expr ); + top.add( "; " ); + top.hasReturn = true; + return parser.success(newFnClass(top)); + } + return parser.failure(null); + } +*/ + Class RequiredModule() throws ParseException { + GetRequiredModule(); + return newFnClass(top); + } + + String RequiredModuleSource() throws ParseException { + GetRequiredModule(); + return toFnString( top, frame.upValueSymbols ); + } + + void GetRequiredModule() throws ParseException { + Spaces(); + parser.begin(); + frame.isVarArg = true; + top.add( "final Object[] varArgs = LuanImpl.varArgs(args,0); " ); + Stmts block = RequiredBlock(); + top.addAll( block ); + top.hasReturn = block.hasReturn; + if( !parser.endOfInput() ) + throw parser.exception(); + parser.success(); + } + + private Stmts RequiredBlock() throws ParseException { + Stmts stmts = new Stmts(); + int stackStart = symbolsSize(); + do { + Spaces(); + stmts.addNewLines(); + Stmts stmt = Stmt(); + if( stmt != null ) { + stmts.addAll(stmt); + stmts.hasReturn = stmt.hasReturn; + } + } while( !stmts.hasReturn && (StmtSep() || TemplateSep(stmts)) ); + Spaces(); + while( StmtSep() ) + Spaces(); + stmts.addNewLines(); + int stackEnd = symbolsSize(); + popSymbols( stackEnd - stackStart ); + return stmts; + } + + private boolean StmtSep() throws ParseException { + return parser.match( ';' ) || EndOfLine(); + } + + private boolean TemplateSep(Stmts stmts) throws ParseException { + Stmts stmt = TemplateStmt(); + if( stmt != null ) { + stmts.addAll(stmt); + return true; + } + return false; + } + + private boolean EndOfLine() { + if( MatchEndOfLine() ) { + parser.sb().append('\n'); + return true; + } else { + return false; + } + } + + private boolean MatchEndOfLine() { + return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' ); + } + + private Stmts Stmt() throws ParseException { + Stmts stmt; + if( (stmt=ReturnStmt()) != null + || (stmt=FunctionStmt()) != null + || (stmt=LocalStmt()) != null + || (stmt=LocalFunctionStmt()) != null + || (stmt=BreakStmt()) != null + || (stmt=ForStmt()) != null + || (stmt=DoStmt()) != null + || (stmt=WhileStmt()) != null + || (stmt=RepeatStmt()) != null + || (stmt=IfStmt()) != null + || (stmt=SetStmt()) != null + || (stmt=ExpressionsStmt()) != null + ) { + return stmt; + } + return null; + } + + private Expr indexExpStr(Expr exp1,Expr exp2) { + Expr exp = new Expr(Val.SINGLE,false); + exp.add( "luan.index(" ); + exp.addAll( exp1.single() ); + exp.add( "," ); + exp.addAll( exp2.single() ); + exp.add( ")" ); + return exp; + } + + private Expr callExpStr(Expr fn,Expr args) { + Expr exp = new Expr(null,true); + exp.add( "Luan.checkFunction(" ); + exp.addAll( fn.single() ); + exp.add( ").call(luan," ); + exp.addAll( args.array() ); + exp.add( ")" ); + return exp; + } + + private Stmts TemplateStmt() throws ParseException { + Expr exprs = TemplateExpressions(In.NOTHING); + if( exprs == null ) + return null; + Stmts stmt = new Stmts(); + stmt.add( "Luan.checkFunction(luan.index(PackageLuan.require(luan,\"luan:Io.luan\"),\"template_write\")).call(luan," ); + stmt.addAll( exprs.array() ); + stmt.add( "); " ); + return stmt; + } + + private Expr TemplateExpressions(In in) throws ParseException { + if( in.template ) + return null; + int start = parser.begin(); + if( !parser.match( "%>" ) ) + return parser.failure(null); + EndOfLine(); + In inTemplate = in.template(); + List<Expr> builder = new ArrayList<Expr>(); + while(true) { + if( parser.match( "<%=" ) ) { + Spaces(); + Expr exp = new Expr(Val.SINGLE,false); + exp.addAll( RequiredExpr(inTemplate).single() ); + builder.add(exp); + RequiredMatch( "%>" ); + } else if( parser.match( "<%" ) ) { + Spaces(); + return parser.success(expString(builder)); + } else { + Expr exp = new Expr(Val.SINGLE,false); + int i = parser.currentIndex(); + do { + if( parser.match( "%>" ) ) + throw parser.exception("'%>' unexpected"); + if( !(EndOfLine() || parser.anyChar()) ) + throw parser.exception("Unclosed template expression"); + } while( !parser.test( "<%" ) ); + String match = parser.textFrom(i); + String rtns = parser.sb().toString(); + parser.sb().setLength(0); + exp.addAll( constExpStr(match) ); + if( rtns.length() > 0 ) + exp.add(rtns); + builder.add(exp); + } + } + } + + private Stmts ReturnStmt() throws ParseException { + parser.begin(); + if( !Keyword("return") ) + return parser.failure(null); + Expr exprs = ExpStringList(In.NOTHING); + Stmts stmt = new Stmts(); + stmt.add( "return " ); + if( exprs != null ) + stmt.addAll( exprs ); + else + stmt.add( "LuanFunction.NOTHING" ); + stmt.add( "; " ); + stmt.hasReturn = true; + return parser.success( stmt ); + } + + private Stmts FunctionStmt() throws ParseException { + parser.begin(); + if( !Keyword("function") ) + return parser.failure(null); + + parser.currentIndex(); + String name = RequiredName(); + Var var = nameVar(name); + while( parser.match( '.' ) ) { + Spaces(); +// Expr exp = NameExpr(); + name = Name(); + if( name==null ) + return parser.failure(null); + var = indexVar( var.exp(), constExpStr(name) ); + } + + Expr fnDef = RequiredFunction(name); + return parser.success( var.set(fnDef) ); + } + + private Stmts LocalFunctionStmt() throws ParseException { + parser.begin(); + if( !(Keyword("local") && Keyword("function")) ) + return parser.failure(null); + Stmts stmt = new Stmts(); + String name = RequiredName(); + stmt.addAll( addSymbol(name,null) ); + Expr fnDef = RequiredFunction(name); + stmt.addAll( nameVar(name).set(fnDef) ); + return parser.success( stmt ); + } + + private Stmts BreakStmt() throws ParseException { + parser.begin(); + if( !Keyword("break") ) + return parser.failure(null); + if( frame.loops <= 0 ) + throw parser.exception("'break' outside of loop"); + Stmts stmt = new Stmts(); + stmt.add( "break; " ); + return parser.success( stmt ); + } + + int forCounter = 0; + + private Stmts ForStmt() throws ParseException { + parser.begin(); + int stackStart = symbolsSize(); + if( !Keyword("for") ) + return parser.failure(null); + List<String> names = RequiredNameList(); + if( !Keyword("in") ) + return parser.failure(null); + Expr expr = RequiredExpr(In.NOTHING).single(); + RequiredKeyword("do"); + + String fnVar = "fn"+ ++forCounter; + Expr fnExp = new Expr(null,false); + fnExp.add( fnVar + ".call(luan)" ); + Stmts stmt = new Stmts(); + stmt.add( "" + +"LuanFunction "+fnVar+" = Luan.checkFunction(" + ); + stmt.addAll( expr ); + stmt.add( "); " ); + stmt.add( "while(true) { " ); + stmt.addAll( makeLocalSetStmt(names,fnExp) ); + stmt.add( "if( " ); + stmt.addAll( nameVar(names.get(0)).exp() ); + stmt.add( "==null ) break; " ); + Stmts loop = RequiredLoopBlock(); + RequiredKeyword("end"); + stmt.addAll( loop ); + stmt.add( "} " ); + popSymbols( symbolsSize() - stackStart ); + return parser.success(stmt); + } + + private Stmts DoStmt() throws ParseException { + parser.begin(); + if( !Keyword("do") ) + return parser.failure(null); + Stmts stmt = RequiredBlock(); + RequiredKeyword("end"); + return parser.success(stmt); + } + + private Stmts LocalStmt() throws ParseException { + parser.begin(); + if( !Keyword("local") ) + return parser.failure(null); + List<String> names = NameList(); + if( names==null ) { + if( Keyword("function") ) + return parser.failure(null); // handled later + throw parser.exception("Invalid local statement"); + } + Stmts stmt = new Stmts(); + if( parser.match( '=' ) ) { + Spaces(); + Expr values = ExpStringList(In.NOTHING); + if( values==null ) + throw parser.exception("Expressions expected"); + stmt.addAll( makeLocalSetStmt(names,values) ); + } else { + Expr value = new Expr(Val.SINGLE,false); + value.add( "null" ); + for( String name : names ) { + stmt.addAll( addSymbol(name,value) ); + } + } + return parser.success(stmt); + } + + private List<String> RequiredNameList() throws ParseException { + parser.begin(); + List<String> names = NameList(); + if( names==null ) + throw parser.exception("Name expected"); + return parser.success(names); + } + + private List<String> NameList() throws ParseException { + String name = Name(); + if( name==null ) + return null; + List<String> names = new ArrayList<String>(); + names.add(name); + while( (name=anotherName()) != null ) { + names.add(name); + } + return names; + } + + private String anotherName() throws ParseException { + parser.begin(); + if( !parser.match( ',' ) ) + return parser.failure(null); + Spaces(); + String name = Name(); + if( name==null ) + return parser.failure(null); + return parser.success(name); + } + + private Stmts WhileStmt() throws ParseException { + parser.begin(); + if( !Keyword("while") ) + return parser.failure(null); + Expr cnd = RequiredExpr(In.NOTHING).single(); + RequiredKeyword("do"); + Stmts loop = RequiredLoopBlock(); + RequiredKeyword("end"); + Stmts stmt = new Stmts(); + stmt.add( "while( Luan.checkBoolean(" ); + stmt.addAll( cnd ); + stmt.add( ") ) { " ); + stmt.addAll( loop ); + stmt.add( "} " ); + return parser.success( stmt ); + } + + private Stmts RepeatStmt() throws ParseException { + parser.begin(); + if( !Keyword("repeat") ) + return parser.failure(null); + Stmts loop =RequiredLoopBlock(); + RequiredKeyword("until"); + Expr cnd = RequiredExpr(In.NOTHING).single(); + Stmts stmt = new Stmts(); + stmt.add( "do { " ); + stmt.addAll( loop ); + stmt.add( "} while( !Luan.checkBoolean(" ); + stmt.addAll( cnd ); + stmt.add( ") ); " ); + return parser.success( stmt ); + } + + private Stmts RequiredLoopBlock() throws ParseException { + incLoops(); + Stmts stmt = RequiredBlock(); + decLoops(); + return stmt; + } + + private Stmts IfStmt() throws ParseException { + parser.begin(); + if( !Keyword("if") ) + return parser.failure(null); + Stmts stmt = new Stmts(); + Expr cnd; + Stmts block; + boolean hasReturn = true; + cnd = RequiredExpr(In.NOTHING).single(); + RequiredKeyword("then"); + block = RequiredBlock(); + stmt.add( "if( Luan.checkBoolean(" ); + stmt.addAll( cnd ); + stmt.add( ") ) { " ); + stmt.addAll( block ); + if( !block.hasReturn ) + hasReturn = false; + while( Keyword("elseif") ) { + cnd = RequiredExpr(In.NOTHING).single(); + RequiredKeyword("then"); + block = RequiredBlock(); + stmt.add( "} else if( Luan.checkBoolean(" ); + stmt.addAll( cnd ); + stmt.add( ") ) { " ); + stmt.addAll( block ); + if( !block.hasReturn ) + hasReturn = false; + } + if( Keyword("else") ) { + block = RequiredBlock(); + stmt.add( "} else { " ); + stmt.addAll( block ); + if( !block.hasReturn ) + hasReturn = false; + } else { + hasReturn = false; + } + RequiredKeyword("end"); + stmt.add( "} " ); + stmt.hasReturn = hasReturn; + return parser.success( stmt ); + } + + private Stmts SetStmt() throws ParseException { + parser.begin(); + List<Var> vars = new ArrayList<Var>(); + Var v = SettableVar(); + if( v == null ) + return parser.failure(null); + vars.add(v); + while( parser.match( ',' ) ) { + Spaces(); + v = SettableVar(); + if( v == null ) + return parser.failure(null); + vars.add(v); + } + if( !parser.match( '=' ) ) + return parser.failure(null); + Spaces(); + Expr values = ExpStringList(In.NOTHING); + if( values==null ) +// throw parser.exception("Expressions expected"); + return parser.failure(null); + return parser.success( makeSetStmt(vars,values) ); + } + + private Stmts makeSetStmt(List<Var> vars,Expr values) throws ParseException { + int n = vars.size(); + if( n == 1 ) + return vars.get(0).set(values); + Stmts stmt = new Stmts(); + String varName = values.valType==Val.ARRAY ? "a" : "t"; + stmt.add( varName + " = " ); + stmt.addAll( values ); + stmt.add( "; " ); + Expr t = new Expr(values.valType,false); + t.add( varName ); + t = t.single(); + stmt.addAll( vars.get(0).set(t) ); + for( int i=1; i<n; i++ ) { + t.clear(); + t.add( "LuanImpl.pick(" + varName + ","+i+")" ); + stmt.addAll( vars.get(i).set(t) ); + } + return stmt; + } + + private Stmts makeLocalSetStmt(List<String> names,Expr values) throws ParseException { + int n = names.size(); + if( n == 1 ) + return addSymbol(names.get(0),values.single()); + Stmts stmt = new Stmts(); + String varName = values.valType==Val.ARRAY ? "a" : "t"; + stmt.add( varName + " = " ); + stmt.addAll( values ); + stmt.add( "; " ); + Expr t = new Expr(values.valType,false); + t.add( varName ); + t = t.single(); + stmt.addAll( addSymbol(names.get(0),t) ); + for( int i=1; i<n; i++ ) { + t.clear(); + t.add( "LuanImpl.pick(" + varName + ","+i+")" ); + stmt.addAll( addSymbol(names.get(i),t) ); + } + return stmt; + } + + private Stmts ExpressionsStmt() throws ParseException { + parser.begin(); + Expr exp = ExprZ(In.NOTHING); + if( exp != null && exp.isStmt ) { + Stmts stmt = new Stmts(); + if( exp.valType==Val.SINGLE ) { + stmt.add( "LuanImpl.nop(" ); + stmt.addAll( exp ); + stmt.add( ")" ); + } else { + stmt.addAll( exp ); + } + stmt.add( "; " ); + return parser.success( stmt ); + } + return parser.failure(null); + } + + private Var SettableVar() throws ParseException { + int start = parser.begin(); + Var var = VarZ(In.NOTHING); + if( var==null || !var.isSettable() ) + return parser.failure(null); + return parser.success( var ); + } + + private Expr RequiredExpr(In in) throws ParseException { + parser.begin(); + return parser.success(required(ExprZ(in),"Bad expression")); + } + + private Expr ExprZ(In in) throws ParseException { + return OrExpr(in); + } + + private Expr OrExpr(In in) throws ParseException { + parser.begin(); + Expr exp = AndExpr(in); + if( exp==null ) + return parser.failure(null); + while( Keyword("or") ) { + exp = exp.single(); + Expr exp2 = required(AndExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,true); + newExp.add( "(LuanImpl.cnd(t = " ); + newExp.addAll( exp ); + newExp.add( ") ? t : (" ); + newExp.addAll( exp2 ); + newExp.add( "))" ); + exp = newExp; + } + return parser.success(exp); + } + + private Expr AndExpr(In in) throws ParseException { + parser.begin(); + Expr exp = RelExpr(in); + if( exp==null ) + return parser.failure(null); + while( Keyword("and") ) { + exp = exp.single(); + Expr exp2 = required(RelExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,true); + newExp.add( "(LuanImpl.cnd(t = " ); + newExp.addAll( exp ); + newExp.add( ") ? (" ); + newExp.addAll( exp2 ); + newExp.add( ") : t)" ); + exp = newExp; + } + return parser.success(exp); + } + + private Expr RelExpr(In in) throws ParseException { + parser.begin(); + Expr exp = ConcatExpr(in); + if( exp==null ) + return parser.failure(null); + while(true) { + if( parser.match("==") ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(ConcatExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.eq(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else if( parser.match("~=") ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(ConcatExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "!LuanImpl.eq(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else if( parser.match("<=") ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(ConcatExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.le(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else if( parser.match(">=") ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(ConcatExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.le(luan," ); + newExp.addAll( exp2 ); + newExp.add( "," ); + newExp.addAll( exp ); + newExp.add( ")" ); + exp = newExp; + } else if( parser.match("<") ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(ConcatExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.lt(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else if( parser.match(">") ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(ConcatExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.lt(luan," ); + newExp.addAll( exp2 ); + newExp.add( "," ); + newExp.addAll( exp ); + newExp.add( ")" ); + exp = newExp; + } else + break; + } + return parser.success(exp); + } + + private Expr ConcatExpr(In in) throws ParseException { + parser.begin(); + Expr exp = SumExpr(in); + if( exp==null ) + return parser.failure(null); + if( parser.match("..") ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(ConcatExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.concat(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } + return parser.success(exp); + } + + private Expr SumExpr(In in) throws ParseException { + parser.begin(); + Expr exp = TermExpr(in); + if( exp==null ) + return parser.failure(null); + while(true) { + if( parser.match('+') ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(TermExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.add(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else if( Minus() ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(TermExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.sub(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else + break; + } + return parser.success(exp); + } + + private boolean Minus() { + parser.begin(); + return parser.match('-') && !parser.match('-') ? parser.success() : parser.failure(); + } + + private Expr TermExpr(In in) throws ParseException { + parser.begin(); + Expr exp = UnaryExpr(in); + if( exp==null ) + return parser.failure(null); + while(true) { + if( parser.match('*') ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(UnaryExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.mul(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else if( parser.match('/') ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(UnaryExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.div(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else if( Mod() ) { + Spaces(); + exp = exp.single(); + Expr exp2 = required(UnaryExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.mod(luan," ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( exp2 ); + newExp.add( ")" ); + exp = newExp; + } else + break; + } + return parser.success(exp); + } + + private boolean Mod() { + parser.begin(); + return parser.match('%') && !parser.match('>') ? parser.success() : parser.failure(); + } + + private Expr UnaryExpr(In in) throws ParseException { + parser.begin(); + if( parser.match('#') ) { + Spaces(); + Expr exp = required(UnaryExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.len(luan," ); + newExp.addAll( exp ); + newExp.add( ")" ); + return parser.success(newExp); + } + if( Minus() ) { + Spaces(); + Expr exp = required(UnaryExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.unm(luan," ); + newExp.addAll( exp ); + newExp.add( ")" ); + return parser.success(newExp); + } + if( Keyword("not") ) { + Spaces(); + Expr exp = required(UnaryExpr(in)).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "!Luan.checkBoolean(" ); + newExp.addAll( exp ); + newExp.add( ")" ); + return parser.success(newExp); + } + Expr exp = PowExpr(in); + if( exp==null ) + return parser.failure(null); + return parser.success(exp); + } + + private Expr PowExpr(In in) throws ParseException { + parser.begin(); + Expr exp1 = SingleExpr(in); + if( exp1==null ) + return parser.failure(null); + if( parser.match('^') ) { + Spaces(); + Expr exp2 = required(PowExpr(in)); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "LuanImpl.pow(luan," ); + newExp.addAll( exp1.single() ); + newExp.add( "," ); + newExp.addAll( exp2.single() ); + newExp.add( ")" ); + exp1 = newExp; + } + return parser.success(exp1); + } + + private Expr SingleExpr(In in) throws ParseException { + parser.begin(); + Expr exp = FunctionExpr(); + if( exp != null ) + return parser.success(exp); + exp = VarExp(in); + if( exp != null ) + return parser.success(exp); + exp = VarArgs(); + if( exp != null ) + return parser.success(exp); + return parser.failure(null); + } + + private Expr FunctionExpr() throws ParseException { + if( !Keyword("function") ) + return null; + return RequiredFunction(null); + } + + private Expr RequiredFunction(String name) throws ParseException { + parser.begin(); + RequiredMatch('('); + Spaces(); + frame = new Frame(frame); + Stmts stmt = new Stmts(); + List<String> names = NameList(); + if( names != null ) { + Expr args = new Expr(Val.ARRAY,false); + args.add( "args" ); + stmt.addAll( makeLocalSetStmt(names,args) ); + if( parser.match(',') ) { + Spaces(); + if( !parser.match("...") ) + throw parser.exception(); + Spaces(); + frame.isVarArg = true; + stmt.add( "final Object[] varArgs = LuanImpl.varArgs(args," + names.size() + "); " ); + } + } else if( parser.match("...") ) { + Spaces(); + frame.isVarArg = true; + stmt.add( "final Object[] varArgs = LuanImpl.varArgs(args,0); " ); + } + RequiredMatch(')'); + Spaces(); + Stmts block = RequiredBlock(); + stmt.addAll( block ); + stmt.hasReturn = block.hasReturn; + Expr fnDef = newFnExp(stmt,name); + RequiredKeyword("end"); + frame = frame.parent; + return parser.success(fnDef); + } + + private Expr VarArgs() throws ParseException { + parser.begin(); + if( !frame.isVarArg || !parser.match("...") ) + return parser.failure(null); + Spaces(); + Expr exp = new Expr(Val.ARRAY,false); + exp.add("varArgs"); + return parser.success(exp); + } + + private Expr TableExpr() throws ParseException { + parser.begin(); + if( !parser.match('{') ) + return parser.failure(null); + Expr tblExp = new Expr(Val.SINGLE,false); + tblExp.add( "LuanImpl.table(" ); + Expr lastExp = tblExp; + List<Expr> builder = new ArrayList<Expr>(); +/* + Spaces(); + Field(builder); + while( FieldSep() ) { + Spaces(); + Field(builder); + } +*/ + do { + Spaces(); lastExp.addNewLines(); + Expr exp = Field(); + if( exp != null ) { + builder.add(exp); + lastExp = exp; + Spaces(); lastExp.addNewLines(); + } + } while( FieldSep() ); + Expr exp = TemplateExpressions(In.NOTHING); + if( exp != null ) + builder.add(exp); + if( !parser.match('}') ) + throw parser.exception("Expected table element or '}'"); + tblExp.addAll( expString(builder).array() ); + tblExp.add( ")" ); + Spaces(); + tblExp.addNewLines(); + return parser.success( tblExp ); + } + + private boolean FieldSep() throws ParseException { + return parser.anyOf(",;") || EndOfLine(); + } + + private Expr Field() throws ParseException { + parser.begin(); + Expr exp = SubExpr(In.NOTHING); + if( exp==null ) + exp = NameExpr(); + if( exp!=null && parser.match('=') ) { + Spaces(); + Expr val = RequiredExpr(In.NOTHING).single(); + Expr newExp = new Expr(Val.SINGLE,false); + newExp.add( "new TableField(" ); + newExp.addAll( exp ); + newExp.add( "," ); + newExp.addAll( val ); + newExp.add( ")" ); + return parser.success(newExp); + } + parser.rollback(); + Expr exprs = ExprZ(In.NOTHING); + if( exprs != null ) { + return parser.success(exprs); + } + return parser.failure(null); + } + + private Expr VarExp(In in) throws ParseException { + Var var = VarZ(in); + return var==null ? null : var.exp(); + } + + private Var VarZ(In in) throws ParseException { + parser.begin(); + Var var = VarStart(in); + if( var==null ) + return parser.failure(null); + Var var2; + while( (var2=Var2(in,var.exp())) != null ) { + var = var2; + } + return parser.success(var); + } + + private Var VarStart(In in) throws ParseException { + if( parser.match('(') ) { + Spaces(); + Expr exp = RequiredExpr(in).single(); + RequiredMatch(')'); + Spaces(); + return exprVar(exp); + } + String name = Name(); + if( name != null ) + return nameVar(name); + Expr exp; + exp = TableExpr(); + if( exp != null ) + return exprVar(exp); + exp = Literal(); + if( exp != null ) + return exprVar(exp); + return null; + } + + private Var Var2(In in,Expr exp1) throws ParseException { + parser.begin(); + Expr exp2 = SubExpr(in); + if( exp2 != null ) + return parser.success(indexVar(exp1,exp2)); + if( parser.match('.') ) { + Spaces(); + exp2 = NameExpr(); + if( exp2!=null ) + return parser.success(indexVar(exp1,exp2)); + return parser.failure(null); + } + Expr fnCall = Args( in, exp1, new ArrayList<Expr>() ); + if( fnCall != null ) + return parser.success(exprVar(fnCall)); + return parser.failure(null); + } + + private interface Var { + public Expr exp() throws ParseException; +// public Settable settable() throws ParseException; + public boolean isSettable(); + public Stmts set(Expr val) throws ParseException; + } + + private Expr env() { + Sym sym = getSym("_ENV"); + if( sym != null ) + return sym.exp(); + return null; + } + + private Var nameVar(final String name) { + return new Var() { + + public Expr exp() throws ParseException { + Sym sym = getSym(name); + if( sym != null ) + return sym.exp(); + Expr envExpr = env(); + if( envExpr != null ) + return indexExpStr( envExpr, constExpStr(name) ); + parser.failure(null); + throw parser.exception("name '"+name+"' not defined"); + } + + public boolean isSettable() { + return true; + } + + public Stmts set(Expr val) throws ParseException { + Sym sym = getSym(name); + if( sym != null ) { + Stmts stmt = new Stmts(); + stmt.addAll( sym.exp() ); + stmt.add( " = " ); + stmt.addAll( val.single() ); + stmt.add( "; " ); + return stmt; + } + Expr envExpr = env(); + if( envExpr != null ) + return indexVar( envExpr, constExpStr(name) ).set(val); + parser.failure(null); + throw parser.exception("name '"+name+"' not defined"); + } + }; + } + + private Var exprVar(final Expr expr) { + return new Var() { + + public Expr exp() { + return expr; + } + + public boolean isSettable() { + return false; + } + + public Stmts set(Expr val) { + throw new RuntimeException(); + } + }; + } + + private Var indexVar(final Expr table,final Expr key) { + return new Var() { + + public Expr exp() { + return indexExpStr( table, key ); + } + + public boolean isSettable() { + return true; + } + + public Stmts set(Expr val) { + Stmts stmt = new Stmts(); + stmt.add( "LuanImpl.put(luan," ); + stmt.addAll( table.single() ); + stmt.add( "," ); + stmt.addAll( key.single() ); + stmt.add( "," ); + stmt.addAll( val.single() ); + stmt.add( "); " ); + return stmt; + } + }; + } + + private Expr Args(In in,Expr fn,List<Expr> builder) throws ParseException { + parser.begin(); + return args(in,builder) + ? parser.success( callExpStr( fn, expString(builder) ) ) + : parser.failure((Expr)null); + } + + private boolean args(In in,List<Expr> builder) throws ParseException { + parser.begin(); + if( parser.match('(') ) { + Spaces(); + ExpList(in,builder); // optional + if( !parser.match(')') ) + throw parser.exception("Expression or ')' expected"); + Spaces(); + return parser.success(); + } + Expr exp = TableExpr(); + if( exp != null ) { + builder.add(exp); + return parser.success(); + } + exp = StringLiteral(); + if( exp != null ) { + builder.add(exp); + return parser.success(); + } + return parser.failure(); + } + + private Expr ExpStringList(In in) throws ParseException { + List<Expr> builder = new ArrayList<Expr>(); + return ExpList(in,builder) ? expString(builder) : null; + } + + private boolean ExpList(In in,List<Expr> builder) throws ParseException { + parser.begin(); + Expr exp = TemplateExpressions(in); + if( exp != null ) { + builder.add(exp); + return parser.success(); + } + exp = ExprZ(in); + if( exp==null ) + return parser.failure(); + exp.addNewLines(); + builder.add(exp); + while( parser.match(',') ) { + Spaces(); + exp = TemplateExpressions(in); + if( exp != null ) { + builder.add(exp); + return parser.success(); + } + exp = RequiredExpr(in); + exp.addNewLines(); + builder.add(exp); + } + return parser.success(); + } + + private Expr SubExpr(In in) throws ParseException { + parser.begin(); + if( !parser.match('[') || parser.test("[") || parser.test("=") ) + return parser.failure(null); + Spaces(); + Expr exp = RequiredExpr(In.NOTHING).single(); + RequiredMatch(']'); + Spaces(); + return parser.success(exp); + } + + private Expr NameExpr() throws ParseException { + parser.begin(); + String name = Name(); + if( name==null ) + return parser.failure(null); + return parser.success(constExpStr(name)); + } + + private String RequiredName() throws ParseException { + parser.begin(); + String name = Name(); + if( name==null ) + throw parser.exception("Name expected"); + return parser.success(name); + } + + private String Name() throws ParseException { + int start = parser.begin(); + if( !NameFirstChar() ) + return parser.failure(null); + while( NameChar() ); + String match = parser.textFrom(start); + if( keywords.contains(match) ) + return parser.failure(null); + Spaces(); + return parser.success(match); + } + + private boolean NameChar() { + return NameFirstChar() || Digit(); + } + + private boolean NameFirstChar() { + return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_'); + } + + private void RequiredMatch(char c) throws ParseException { + if( !parser.match(c) ) + throw parser.exception("'"+c+"' expected"); + } + + private void RequiredMatch(String s) throws ParseException { + if( !parser.match(s) ) + throw parser.exception("'"+s+"' expected"); + } + + private void RequiredKeyword(String keyword) throws ParseException { + if( !Keyword(keyword) ) + throw parser.exception("'"+keyword+"' expected"); + } + + private boolean Keyword(String keyword) throws ParseException { + parser.begin(); + if( !parser.match(keyword) || NameChar() ) + return parser.failure(); + Spaces(); + return parser.success(); + } + + private static final Set<String> keywords = new HashSet<String>(Arrays.asList( + "and", + "break", + "do", + "else", + "elseif", + "end", + "false", + "for", + "function", + "goto", + "if", + "in", + "local", + "nil", + "not", + "or", + "repeat", + "return", + "then", + "true", + "until", + "while" + )); + + private Expr Literal() throws ParseException { + parser.begin(); + if( NilLiteral() ) { + Expr exp = new Expr(Val.SINGLE,false); + exp.add( "null" ); + return parser.success(exp); + } + Boolean b = BooleanLiteral(); + if( b != null ) { + Expr exp = new Expr(Val.SINGLE,false); + exp.add( b.toString() ); + return parser.success(exp); + } + Number n = NumberLiteral(); + if( n != null ) { + String s = n.toString(); + if( n instanceof Long ) + s += "L"; + Expr exp = new Expr(Val.SINGLE,false); + exp.add( s ); + return parser.success(exp); + } + Expr s = StringLiteral(); + if( s != null ) + return parser.success(s); + return parser.failure(null); + } + + private static int STR_LIM = 65000; + + private Expr constExpStr(String s) { + s = s + .replace("\\","\\\\") + .replace("\"","\\\"") + .replace("\n","\\n") + .replace("\r","\\r") + .replace("\t","\\t") + .replace("\b","\\b") + ; + if( s.length() > STR_LIM ) { + int len = s.length(); + StringBuilder sb = new StringBuilder(); + sb.append( "LuanImpl.strconcat(" ); + int start = 0; + while(true) { + int end = start + STR_LIM; + if( end >= len ) + break; + sb.append( "\"" ).append( s.substring(start,end) ).append( "\"," ); + start = end; + } + sb.append( "\"" ).append( s.substring(start) ).append( "\")" ); + s = sb.toString(); + } else + s = "\"" + s + "\""; + Expr exp = new Expr(Val.SINGLE,false); + exp.add( s ); + return exp; + } + + private boolean NilLiteral() throws ParseException { + return Keyword("nil"); + } + + private Boolean BooleanLiteral() throws ParseException { + if( Keyword("true") ) + return true; + if( Keyword("false") ) + return false; + return null; + } + + private Number NumberLiteral() throws ParseException { + parser.begin(); + Number n; + if( parser.matchIgnoreCase("0x") ) { + n = HexNumber(); + } else { + n = DecNumber(); + } + if( n==null || NameChar() ) + return parser.failure(null); + Spaces(); + return parser.success(n); + } + + private Number DecNumber() { + int start = parser.begin(); + boolean isInt = true; + if( Int() ) { + if( parser.match('.') ) { + isInt = false; + Int(); // optional + } + } else if( parser.match('.') && Int() ) { + // ok + isInt = false; + } else + return parser.failure(null); + if( Exponent() ) // optional + isInt = false; + String s = parser.textFrom(start); + if( isInt ) { + try { + return parser.success(Integer.valueOf(s)); + } catch(NumberFormatException e) {} + try { + return parser.success(Long.valueOf(s)); + } catch(NumberFormatException e) {} + } + return parser.success(Double.valueOf(s)); + } + + private boolean Exponent() { + parser.begin(); + if( !parser.matchIgnoreCase("e") ) + return parser.failure(); + parser.anyOf("+-"); // optional + if( !Int() ) + return parser.failure(); + return parser.success(); + } + + private boolean Int() { + if( !Digit() ) + return false; + while( Digit() ); + return true; + } + + private boolean Digit() { + return parser.inCharRange('0', '9'); + } + + private Number HexNumber() { + int start = parser.begin(); + long nLong = 0; + double n; + if( HexInt() ) { + nLong = Long.parseLong(parser.textFrom(start),16); + n = (double)nLong; + if( parser.match('.') ) { + start = parser.currentIndex(); + if( HexInt() ) { + String dec = parser.textFrom(start); + n += (double)Long.parseLong(dec,16) / Math.pow(16,dec.length()); + } + } + } else if( parser.match('.') && HexInt() ) { + String dec = parser.textFrom(start+1); + n = (double)Long.parseLong(dec,16) / Math.pow(16,dec.length()); + } else { + return parser.failure(null); + } + if( parser.matchIgnoreCase("p") ) { + parser.anyOf("+-"); // optional + start = parser.currentIndex(); + if( !HexInt() ) + return parser.failure(null); + n *= Math.pow(2,(double)Long.parseLong(parser.textFrom(start))); + } + if( nLong == n ) { + int nInt = (int)nLong; + if( nInt == nLong ) + return parser.success(Integer.valueOf(nInt)); + return parser.success(Long.valueOf(nLong)); + } + return parser.success(Double.valueOf(n)); + } + + private boolean HexInt() { + if( !HexDigit() ) + return false; + while( HexDigit() ); + return true; + } + + + private boolean HexDigit() { + return Digit() || parser.anyOf("abcdefABCDEF"); + } + + private Expr StringLiteral() throws ParseException { + Expr s; + if( (s=QuotedString('"'))==null + && (s=QuotedString('\''))==null + && (s=LongString())==null + ) + return null; + Spaces(); + return s; + } + + private Expr LongString() throws ParseException { + parser.begin(); + if( !parser.match('[') ) + return parser.failure(null); + int start = parser.currentIndex(); + while( parser.match('=') ); + int nEquals = parser.currentIndex() - start; + if( !parser.match('[') ) + return parser.failure(null); + EndOfLine(); + start = parser.currentIndex(); + while( !LongBracketsEnd(nEquals) ) { + if( !(EndOfLine() || parser.anyChar()) ) + throw parser.exception("Unclosed long string"); + } + String s = parser.text.substring( start, parser.currentIndex() - nEquals - 2 ); + String rtns = parser.sb().toString(); + parser.sb().setLength(0); + Expr exp = constExpStr(s); + if( rtns.length() > 0 ) + exp.add(rtns); + return parser.success(exp); + } + + private Expr QuotedString(char quote) throws ParseException { + parser.begin(); + if( !parser.match(quote) ) + return parser.failure(null); + StringBuilder buf = new StringBuilder(); + while( !parser.match(quote) ) { + Character c = EscSeq(); + if( c != null ) { + buf.append(c); + } else { + if( parser.test('\r') || parser.test('\n') || !parser.anyChar() ) + throw parser.exception("Unclosed string"); + buf.append(parser.lastChar()); + } + } + return parser.success(constExpStr(buf.toString())); + } + + private Character EscSeq() { + parser.begin(); + if( !parser.match('\\') ) + return parser.failure(null); + if( parser.match('a') ) return parser.success('\u0007'); + if( parser.match('b') ) return parser.success('\b'); + if( parser.match('f') ) return parser.success('\f'); + if( parser.match('n') ) return parser.success('\n'); + if( parser.match('r') ) return parser.success('\r'); + if( parser.match('t') ) return parser.success('\t'); + if( parser.match('v') ) return parser.success('\u000b'); + if( parser.match('\\') ) return parser.success('\\'); + if( parser.match('"') ) return parser.success('"'); + if( parser.match('\'') ) return parser.success('\''); + int start = parser.currentIndex(); + if( parser.match('x') && HexDigit() && HexDigit() ) + return parser.success((char)Integer.parseInt(parser.textFrom(start+1),16)); + if( parser.match('u') && HexDigit() && HexDigit() && HexDigit() && HexDigit() ) + return parser.success((char)Integer.parseInt(parser.textFrom(start+1),16)); + if( Digit() ) { + if( Digit() ) Digit(); // optional + return parser.success((char)Integer.parseInt(parser.textFrom(start))); + } + if( MatchEndOfLine() ) { + return parser.success('\n'); + } + return parser.failure(null); + } + + private void Spaces() throws ParseException { + while( parser.anyOf(" \t") || Comment() || ContinueOnNextLine() ); + } + + private boolean ContinueOnNextLine() { + parser.begin(); + if( parser.match('\\') && EndOfLine() ) { + parser.upSb(); + return parser.success(); + } else + return parser.failure(); + } + + private boolean Comment() throws ParseException { + if( LongComment() ) + return true; + if( parser.match("--") ) { + while( parser.noneOf("\r\n") ); + return true; + } + return false; + } + + private boolean LongComment() throws ParseException { + parser.begin(); + if( !parser.match("--[") ) + return parser.failure(); + int start = parser.currentIndex(); + while( parser.match('=') ); + int nEquals = parser.currentIndex() - start; + if( !parser.match('[') ) + return parser.failure(); + while( !LongBracketsEnd(nEquals) ) { + if( !(EndOfLine() || parser.anyChar()) ) + throw parser.exception("Unclosed comment"); + } + parser.upSb(); + return parser.success(); + } + + private boolean LongBracketsEnd(int nEquals) { + parser.begin(); + if( !parser.match(']') ) + return parser.failure(); + while( nEquals-- > 0 ) { + if( !parser.match('=') ) + return parser.failure(); + } + if( !parser.match(']') ) + return parser.failure(); + return parser.success(); + } + + + + private class ParseList extends ArrayList { + + void addNewLines() { + if( parser.sb().length() > 0 ) { + add( parser.sb().toString() ); + parser.sb().setLength(0); +/* +if( parser.sourceName.equals("stdin") ) { + StringWriter sw = new StringWriter(); + new Throwable().printStackTrace(new PrintWriter(sw,true)); +// add(sw.toString()); +} +*/ + } + } + + ParseList() { + addNewLines(); + } + + @Override public boolean add(Object obj) { + if( obj instanceof List ) throw new RuntimeException(); + return super.add(obj); + } + + @Override public void add(int index,Object obj) { + if( obj instanceof List ) throw new RuntimeException(); + super.add(index,obj); + } + + @Override public String toString() { + StringBuilder sb = new StringBuilder(); + for( Object o : this ) { + sb.append( o.toString() ); + } + return sb.toString(); + } + } + + + private static AtomicInteger classCounter = new AtomicInteger(); + + private enum Val { SINGLE, ARRAY } + + private class Expr extends ParseList { + final Val valType; + final boolean isStmt; + + Expr(Val valType,boolean isStmt) { + this.valType = valType; + this.isStmt = isStmt; + } + + Expr single() { + if( valType==Val.SINGLE ) + return this; + Expr exp = new Expr(Val.SINGLE,isStmt); + exp.add( valType==Val.ARRAY ? "LuanImpl.first(" : "Luan.first(" ); + exp.addAll( this ); + exp.add( ")" ); + return exp; + } + + Expr array() { + if( valType==Val.ARRAY ) + return this; + Expr exp = new Expr(Val.ARRAY,isStmt); + if( valType==Val.SINGLE ) { + exp.add( "new Object[]{" ); + exp.addAll( this ); + exp.add( "}" ); + } else { + exp.add( "Luan.array(" ); + exp.addAll( this ); + exp.add( ")" ); + } + return exp; + } + + } + + private Expr expString(List<Expr> list) { + Expr exp = new Expr(Val.ARRAY,false); + switch(list.size()) { + case 0: + exp.add("LuanFunction.NOTHING"); + return exp; + case 1: + return list.get(0); + default: + int lastI = list.size() - 1; + exp.add( "new Object[]{" ); + for( int i=0; i<lastI; i++ ) { + exp.addAll( list.get(i).single() ); + exp.add( "," ); + } + Expr last = list.get(lastI); + if( last.valType==Val.SINGLE ) { + exp.addAll( last ); + exp.add( "}" ); + } else { + exp.add( "}" ); + exp.add( 0, "LuanImpl.concatArgs(" ); + exp.add( "," ); + exp.addAll( last ); + exp.add( ")" ); + } + return exp; + } + } + + private class Stmts extends ParseList { + boolean hasReturn = false; + } + + private Class toFnClass(Stmts stmts,List<UpSym> upValueSymbols) { + String className = "EXP" + classCounter.incrementAndGet(); + String classCode = toFnString(stmts,upValueSymbols,className); + try { +//System.out.println(parser.sourceName); +//System.out.println(classCode); + return LuanJavaCompiler.compile("luan.impl."+className,parser.sourceName,classCode); + } catch(ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + private String toFnString(Stmts stmts,List<UpSym> upValueSymbols) { + String className = "EXP" + classCounter.incrementAndGet(); + return toFnString(stmts,upValueSymbols,className); + } + + private String toFnString(Stmts stmts,List<UpSym> upValueSymbols,String className) { + if( !stmts.hasReturn ) + stmts.add( "\nreturn LuanFunction.NOTHING;" ); + return "" + +"package luan.impl; " + +"import luan.Luan; " + +"import luan.LuanFunction; " + +"import luan.LuanState; " + +"import luan.LuanJava; " + +"import luan.LuanException; " + +"import luan.modules.PackageLuan; " + + +"public class " + className +" extends Closure { " + +"public "+className+"(LuanJava java) throws LuanException { " + +"super("+upValueSymbols.size()+",java); " + + init(upValueSymbols) + +"} " + + +"@Override public Object doCall(LuanState luan,Object[] args) throws LuanException { " + +"final Pointer[] parentUpValues = upValues; " + +"Object t; " + +"Object[] a; " + + stmts + +"\n} " + +"}\n" + ; + } + + private Expr toFnExp(Stmts stmt,List<UpSym> upValueSymbols,String name) { + stmt.addNewLines(); + if( !stmt.hasReturn ) + stmt.add( "return LuanFunction.NOTHING; " ); + Expr exp = new Expr(Val.SINGLE,false); + exp.add( "" + +"new Closure("+upValueSymbols.size()+",java) { " + +"{ " + + init(upValueSymbols) + +"} " + +"@Override public Object doCall(LuanState luan,Object[] args) throws LuanException { " + ); + if( name != null ) { + exp.add( "" + +"return _" + name + "(luan,args); " + +"} " + +"private Object _" + name + "(LuanState luan,Object[] args) throws LuanException { " + ); + } + exp.add( "" + +"final Pointer[] parentUpValues = upValues; " + +"Object t; " + +"Object[] a; " + ); + exp.addAll( stmt ); + exp.add( "" + +"} " + +"} " + ); + return exp; + } + + private static String init(List<UpSym> upValueSymbols) { + StringBuilder sb = new StringBuilder(); + for( UpSym upSym : upValueSymbols ) { + sb.append( upSym.init() ); + } + return sb.toString(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/ParseException.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,57 @@ +package luan.impl; + + +public final class ParseException extends Exception { +// public final LuanSource src; + public final String sourceName; + public final String text; + public final int iCurrent; + public final int iHigh; + + ParseException(String msg,String text,String sourceName,int iCurrent,int iHigh) { + super(msg); +// this.src = src; + this.text = text; + this.sourceName = sourceName; + this.iCurrent = iCurrent; + this.iHigh = iHigh; +//System.out.println("iCurrent = "+iCurrent); +//System.out.println("iHigh = "+iHigh); + } + + private class Location { + final int line; + final int pos; + + Location(int index) { + int line = 0; + int i = -1; + while(true) { + int j = text.indexOf('\n',i+1); + if( j == -1 || j >= index ) + break; + i = j; + line++; + } + this.line = line; + this.pos = index - i - 1; + } + } + + private String[] lines() { + return text.split("\n",-1); + } + + public String getFancyMessage() { + Location loc = new Location(iCurrent); + String line = lines()[loc.line]; + String msg = getMessage() + " (line " + (loc.line+1) + ", pos " + (loc.pos+1) + ") in " + sourceName + "\n"; + StringBuilder sb = new StringBuilder(msg); + sb.append( line + "\n" ); + for( int i=0; i<loc.pos; i++ ) { + sb.append( line.charAt(i)=='\t' ? '\t' : ' ' ); + } + sb.append("^\n"); + return sb.toString(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/Parser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,190 @@ +package luan.impl; + + +final class Parser { + + private static class Frame { + int i; + StringBuilder sb; + } + +// private final LuanSource src; + public final String text; + public final String sourceName; + private final int len; + private Frame[] stack = new Frame[256]; + private int frame = 0; + private int iHigh; + + public Parser(String text,String sourceName) { +// this.src = src; + this.text = text; + this.sourceName = sourceName; + this.len = text.length(); + stack[0] = new Frame(); + } + + private int i() { + return stack[frame].i; + } + + private void i(int i) { + Frame f = stack[frame]; + f.i += i; + if( iHigh < f.i ) + iHigh = f.i; + } + + public int begin() { + frame++; + if( frame == stack.length ) { + Frame[] a = new Frame[2*frame]; + System.arraycopy(stack,0,a,0,frame); + stack = a; + } + Frame f = new Frame(); + f.i = stack[frame-1].i; + stack[frame] = f; + return i(); + } + + public void rollback() { + Frame f = stack[frame]; + f.i = stack[frame-1].i; + f.sb = null; + } + + public <T> T success(T t) { + success(); + return t; + } + + public boolean success() { + Frame f = stack[frame]; + if( f.sb != null && f.sb.length() > 0 ) throw new RuntimeException("sb not emtpy"); + frame--; + stack[frame].i = f.i; + return true; + } + + public <T> T failure(T t) { + failure(); + return t; + } + + public boolean failure() { + frame--; + return false; + } + + public ParseException exception(String msg) { + return new ParseException(msg,text,sourceName,i(),iHigh); + } + + public ParseException exception() { + return exception("Invalid input"); + } + + public StringBuilder sb() { + Frame f = stack[frame]; + if( f.sb == null ) + f.sb = new StringBuilder(); + return f.sb; + } + + public void upSb() { + Frame f = stack[frame]; + StringBuilder sb = f.sb; + if( sb != null && sb.length() > 0 ) { + Frame fUp = stack[frame-1]; + if( fUp.sb == null ) + fUp.sb = sb; + else + fUp.sb.append(sb.toString()); + f.sb = null; + } + } + + public int currentIndex() { + return i(); + } + + public char lastChar() { + return text.charAt(i()-1); + } + + public char currentChar() { + return text.charAt(i()); + } + + public boolean endOfInput() { + return i() >= len; + } + + public boolean match(char c) { + if( endOfInput() || text.charAt(i()) != c ) + return false; + i(1); + return true; + } + + public boolean match(String s) { + int n = s.length(); + if( !text.regionMatches(i(),s,0,n) ) + return false; + i(n); + return true; + } + + public boolean matchIgnoreCase(String s) { + int n = s.length(); + if( !text.regionMatches(true,i(),s,0,n) ) + return false; + i(n); + return true; + } + + public boolean anyOf(String s) { + if( endOfInput() || s.indexOf(text.charAt(i())) == -1 ) + return false; + i(1); + return true; + } + + public boolean noneOf(String s) { + if( endOfInput() || s.indexOf(text.charAt(i())) != -1 ) + return false; + i(1); + return true; + } + + public boolean inCharRange(char cLow, char cHigh) { + if( endOfInput() ) + return false; + char c = text.charAt(i()); + if( !(cLow <= c && c <= cHigh) ) + return false; + i(1); + return true; + } + + public boolean anyChar() { + if( endOfInput() ) + return false; + i(1); + return true; + } + + public boolean test(char c) { + return !endOfInput() && text.charAt(i()) == c; + } + + public boolean test(String s) { + return text.regionMatches(i(),s,0,s.length()); + } + + public String textFrom(int start) { + return text.substring(start,i()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/Pointer.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,23 @@ +package luan.impl; + +import luan.DeepCloneable; +import luan.DeepCloner; + + +public final class Pointer implements DeepCloneable { + public Object o; + + public Pointer() {} + + public Pointer(Object o) { + this.o = o; + } + + @Override public Pointer shallowClone() { + return new Pointer(); + } + + @Override public void deepenClone(DeepCloneable clone,DeepCloner cloner) { + ((Pointer)clone).o = cloner.get(o); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/impl/TableField.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,12 @@ +package luan.impl; + + +public final class TableField { + final Object key; + final Object value; + + public TableField(Object key,Object value) { + this.key = key; + this.value = value; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/BasicLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,244 @@ +package luan.modules; + +import java.io.InputStreamReader; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanMethod; +import luan.LuanMeta; + + +public final class BasicLuan { + + public static String type(Object obj) { + return Luan.type(obj); + } + + public static LuanFunction load(String text,String sourceName,LuanTable env) + throws LuanException + { + Utils.checkNotNull(text); + Utils.checkNotNull(sourceName,1); + return Luan.load(text,sourceName,env); + } + + public static LuanFunction load_file(LuanState luan,String fileName) throws LuanException { + if( fileName == null ) { + fileName = "stdin:"; + } else if( fileName.indexOf(':') == -1 ) { + fileName = "file:" + fileName; + } + String src = PackageLuan.read(luan,fileName); + if( src == null ) + throw new LuanException("file '"+fileName+"' not found" ); + return load(src,fileName,null); + } + + public static LuanFunction pairs(final LuanState luan,final LuanTable t) throws LuanException { + Utils.checkNotNull(t); + return t.pairs(luan); + } + + public static LuanFunction ipairs(final LuanTable t) throws LuanException { + Utils.checkNotNull(t); + return new LuanFunction() { + List<Object> list = t.asList(); + int i = 0; + final int size = list.size(); + + @Override public Object[] call(LuanState luan,Object[] args) { + if( i >= size ) + return LuanFunction.NOTHING; + Object val = list.get(i++); + return new Object[]{i,val}; + } + }; + } + + public static Object get_metatable(LuanTable table) throws LuanException { + Utils.checkNotNull(table); + LuanTable metatable = table.getMetatable(); + if( metatable == null ) + return null; + Object obj = metatable.rawGet("__metatable"); + return obj!=null ? obj : metatable; + } + + public static void set_metatable(LuanTable table,LuanTable metatable) throws LuanException { + Utils.checkNotNull(table); + if( table.getHandler("__metatable") != null ) + throw new LuanException("cannot change a protected metatable"); + table.setMetatable(metatable); + } + + public static boolean raw_equal(Object v1,Object v2) { + return v1 == v2 || v1 != null && v1.equals(v2); + } + + public static Object raw_get(LuanTable table,Object index) { + return table.rawGet(index); + } + + public static void raw_set(LuanTable table,Object index,Object value) { + table.rawPut(index,value); + } + + public static int raw_len(Object v) throws LuanException { + if( v instanceof String ) { + String s = (String)v; + return s.length(); + } + if( v instanceof LuanTable ) { + LuanTable t = (LuanTable)v; + return t.rawLength(); + } + throw new LuanException( "bad argument #1 to 'raw_len' (table or string expected)" ); + } + + public static String to_string(LuanState luan,Object v) throws LuanException { + return luan.toString(v); + } + + public static LuanTable new_error(LuanState luan,Object msg) throws LuanException { + String s = luan.toString(msg); + LuanTable tbl = new LuanException(s).table(); + tbl.rawPut( "message", msg ); + return tbl; + } + + public static String assert_string(String v) throws LuanException { + Utils.checkNotNull(v); + return v; + } + + public static Number assert_number(Number v) throws LuanException { + Utils.checkNotNull(v); + return v; + } + + public static LuanTable assert_table(LuanTable v) throws LuanException { + Utils.checkNotNull(v); + return v; + } + + public static boolean assert_boolean(boolean v) throws LuanException { + return v; + } + + public static int assert_integer(int v) throws LuanException { + return v; + } + + public static long assert_long(long v) throws LuanException { + return v; + } + + public static double assert_double(double v) throws LuanException { + return v; + } + + @LuanMethod public static byte[] assert_binary(byte[] v) throws LuanException { + Utils.checkNotNull(v); + return v; + } + + public static LuanFunction assert_function(LuanFunction v) throws LuanException { + Utils.checkNotNull(v); + return v; + } + + public static LuanFunction range(final double from,final double to,Double stepV) throws LuanException { + final double step = stepV==null ? 1.0 : stepV; + if( step == 0.0 ) + throw new LuanException("bad argument #3 (step may not be zero)"); + return new LuanFunction() { + double v = from; + + @Override public Object call(LuanState luan,Object[] args) { + if( step > 0.0 && v > to || step < 0.0 && v < to ) + return LuanFunction.NOTHING; + double rtn = v; + v += step; + return rtn; + } + }; + } + + public static LuanFunction values(final Object... args) throws LuanException { + return new LuanFunction() { + int i = 0; + + @Override public Object call(LuanState luan,Object[] unused) { + if( i >= args.length ) + return LuanFunction.NOTHING; + return args[i++]; + } + }; + } + + private LuanFunction fn(Object obj) { + return obj instanceof LuanFunction ? (LuanFunction)obj : null; + } + + public static Object try_(LuanState luan,LuanTable blocks,Object... args) throws LuanException { + Utils.checkNotNull(blocks); + Object obj = blocks.get(luan,1); + if( obj == null ) + throw new LuanException("missing 'try' value"); + if( !(obj instanceof LuanFunction) ) + throw new LuanException("bad 'try' value (function expected, got "+Luan.type(obj)+")"); + LuanFunction tryFn = (LuanFunction)obj; + LuanFunction catchFn = null; + obj = blocks.get(luan,"catch"); + if( obj != null ) { + if( !(obj instanceof LuanFunction) ) + throw new LuanException("bad 'catch' value (function expected, got "+Luan.type(obj)+")"); + catchFn = (LuanFunction)obj; + } + LuanFunction finallyFn = null; + obj = blocks.get(luan,"finally"); + if( obj != null ) { + if( !(obj instanceof LuanFunction) ) + throw new LuanException("bad 'finally' value (function expected, got "+Luan.type(obj)+")"); + finallyFn = (LuanFunction)obj; + } + try { + return tryFn.call(luan,args); + } catch(LuanException e) { + if( catchFn == null ) + throw e; + return catchFn.call(luan,new Object[]{e.table()}); + } finally { + if( finallyFn != null ) + finallyFn.call(luan); + } + } + + @LuanMethod public static Object[] pcall(LuanState luan,LuanFunction f,Object... args) { + try { + Object[] r = Luan.array(f.call(luan,args)); + Object[] rtn = new Object[r.length+1]; + rtn[0] = true; + for( int i=0; i<r.length; i++ ) { + rtn[i+1] = r[i]; + } + return rtn; + } catch(LuanException e) { + return new Object[]{false,e.table()}; + } + } + + public static String number_type(Number v) throws LuanException { + Utils.checkNotNull(v); + return v.getClass().getSimpleName().toLowerCase(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Binary.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,11 @@ +java() +local BinaryLuan = require "java:luan.modules.BinaryLuan" + + +local M = {} + +M.binary = BinaryLuan.binary +M.byte = BinaryLuan.byte_ +M.to_string = BinaryLuan.to_string + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/BinaryLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,50 @@ +package luan.modules; + +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanMethod; + + +public final class BinaryLuan { + + static int start(byte[] binary,int i) { + int len = binary.length; + return i==0 ? 0 : i > 0 ? Math.min(i-1,len) : Math.max(len+i,0); + } + + static int start(byte[] binary,Integer i,int dflt) { + return i==null ? dflt : start(binary,i); + } + + static int end(byte[] binary,int i) { + int len = binary.length; + return i==0 ? 0 : i > 0 ? Math.min(i,len) : Math.max(len+i+1,0); + } + + static int end(byte[] binary,Integer i,int dflt) { + return i==null ? dflt : end(binary,i); + } + + @LuanMethod public static Byte[] byte_(byte[] binary,Integer i,Integer j) throws LuanException { + Utils.checkNotNull(binary); + int start = start(binary,i,1); + int end = end(binary,j,start+1); + Byte[] bytes = new Byte[end-start]; + for( int k=0; k<bytes.length; k++ ) { + bytes[k] = binary[start+k]; + } + return bytes; + } + + @LuanMethod public static byte[] binary(byte... bytes) { + return bytes; + } + + public static String to_string(byte[] binary) { + return new String(binary); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Html.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,76 @@ +java() +local HtmlLuan = require "java:luan.modules.HtmlLuan" +local HtmlParser = require "java:luan.modules.parsers.Html" +local URLEncoder = require "java:java.net.URLEncoder" +local Luan = require "luan:Luan.luan" +local error = Luan.error +local ipairs = Luan.ipairs or error() +local pairs = Luan.pairs or error() +local type = Luan.type or error() +local Io = require "luan:Io.luan" +local output_of = Io.output_of or error() + + +local M = {} + +M.encode = HtmlLuan.encode + +local quote = HtmlLuan.quote +M.quote = quote + +function M.parse(text,container_tags) + text or error "text required" + container_tags = container_tags or {"script","style","textarea"} + return HtmlParser.toList(text,container_tags) +end + +function M.url_encode(s) + return URLEncoder.encode(s,"UTF-8") +end + +local function output_tag(tag) + %><<%= tag.name %><% + local attributes = tag.attributes + for name, value in pairs(attributes) do + %> <%= name %><% + if value ~= true then + %>=<%= quote(value) %><% + end + end + if tag.is_empty then + %>/<% + end + %>><% +end + +function M.to_string(list) + return output_of( function() + for _, obj in ipairs(list) do + local tp = type(obj) + if tp == "string" then + %><%= obj %><% + elseif tp == "table" then + tp = obj.type + if tp == nil then + error "no type in element of table for 'Html.to_string'" + elseif tp == "comment" then + %><!--<%= obj.text %>--><% + elseif tp == "cdata" then + %><![CDATA[<%= obj.text %>]]><% + elseif tp == "tag" then + output_tag(obj) + elseif tp == "container" then + local tag = obj.tag + output_tag(tag) + %><%= obj.text %></<%= tag.name %>><% + else + error "invalid element type for 'Html.to_string'" + end + else + error("invalid value ("..tp..") in list for 'Html.to_string'") + end + end + end ) +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/HtmlLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,348 @@ +package luan.modules; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanException; + + +public final class HtmlLuan { + + public static String encode(String s) throws LuanException { + Utils.checkNotNull(s); + char[] a = s.toCharArray(); + StringBuilder buf = new StringBuilder(); + for( int i=0; i<a.length; i++ ) { + char c = a[i]; + switch(c) { + case '&': + buf.append("&"); + break; + case '<': + buf.append("<"); + break; + case '>': + buf.append(">"); + break; + case '"': + buf.append("""); + break; + default: + buf.append(c); + } + } + return buf.toString(); + } + +/* +// public static final String TEXTAREA = "textarea"; + public static final String SCRIPT = "script"; + public static final String STYLE = "style"; + + public static Set<String> containerTags = new HashSet<String>(Arrays.asList(SCRIPT,STYLE)); +*/ +/* + public static LuanTable parse(LuanState luan,String text,LuanTable containerTagsTbl) + throws LuanException + { + Utils.checkNotNull(luan,text); + Utils.checkNotNull(luan,containerTagsTbl); + Set<String> containerTags = new HashSet<String>(); + for( Object v : containerTagsTbl.asList() ) { + containerTags.add((String)v); + } + List<Object> html = new ArrayList<Object>(); + int len = text.length(); + int i = 0; +outer: + while( i < len ) { + int i2 = text.indexOf('<',i); + while( i2 != -1 && i2+1 < len ) { + char c = text.charAt(i2+1); + if( Character.isLetter(c) || c=='/' || c=='!' ) + break; + i2 = text.indexOf('<',i2+1); + } + if( i2 == -1 ) { + html.add( text.substring(i) ); + break; + } + if( i < i2 ) + html.add( text.substring(i,i2) ); + if( text.startsWith("<!--",i2) ) { + i = text.indexOf("-->",i2+4); + if( i == -1 ) { + html.add( text.substring(i2) ); + break; + } + html.add( comment( text.substring(i2+4,i) ) ); + i += 3; + } else if( text.startsWith("<![CDATA[",i2) ) { + i = text.indexOf("]]>",i2+9); + if( i == -1 ) { + html.add( text.substring(i2) ); + break; + } + html.add( cdata( text.substring(i2+9,i) ) ); + i += 3; + } else { + i = text.indexOf('>',i2); + if( i == -1 ) { + html.add( text.substring(i2) ); + break; + } + String tagText = text.substring(i2+1,i); + try { + LuanTable tag = parseTag(tagText); + String tagName = (String)tag.rawGet("name"); + if( containerTags.contains(tagName) ) { + i2 = i; + String endTagName = '/' + tagName; + while(true) { + i2 = text.indexOf('<',i2+1); + if( i2 == -1 ) + break; + int i3 = text.indexOf('>',i2); + if( i3 == -1 ) + break; + int j = i2+1; + while( j<i3 && !Character.isWhitespace(text.charAt(j)) ) j++; + String s = text.substring(i2+1,j); + if( s.equalsIgnoreCase(endTagName) ) { + String text2 = text.substring(i+1,i2); + LuanTable textContainer = textContainer(tag,text2); + html.add( textContainer ); + i = i3 + 1; + continue outer; + } + } +// logger.warn("unclosed "+tagName); + } + i += 1; + html.add( tag ); + } catch(BadTag e) { +// logger.debug("bad tag",e); + i += 1; +// if( !removeBadTags ) { + html.add( "<" ); + html.add( encode(luan,tagText) ); + html.add( ">" ); +// } + } + } + } + return new LuanTable(html); + } + + static LuanTable comment(String text) { + LuanTable tbl = new LuanTable(); + tbl.rawPut("type","comment"); + tbl.rawPut("text",text); + return tbl; + } + + static LuanTable cdata(String text) { + LuanTable tbl = new LuanTable(); + tbl.rawPut("type","cdata"); + tbl.rawPut("text",text); + return tbl; + } + + static LuanTable textContainer(LuanTable tag,String text) { + LuanTable tbl = new LuanTable(); + tbl.rawPut("type","container"); + tbl.rawPut("tag",tag); + tbl.rawPut("text",text); + return tbl; + } + + + + static final class BadTag extends RuntimeException { + private BadTag(String msg) { + super(msg); + } + } + + static LuanTable parseTag(String text) { + LuanTable tbl = new LuanTable(); + tbl.rawPut("type","tag"); + if( text.endsWith("/") ) { + text = text.substring(0,text.length()-1); + tbl.rawPut("is_empty",true); + } else { + tbl.rawPut("is_empty",false); + } + int len = text.length(); + int i = 0; + int i2 = i; + if( i2<len && text.charAt(i2)=='/' ) + i2++; + while( i2<len ) { + char c = text.charAt(i2); + if( Character.isWhitespace(c) ) + break; + if( !( Character.isLetterOrDigit(c) || c=='_' || c=='.' || c=='-' || c==':' ) ) + throw new BadTag("invalid tag name for <"+text+">"); + i2++; + } + String name = text.substring(i,i2).toLowerCase(); + tbl.rawPut("name",name); + LuanTable attributes = new LuanTable(); + tbl.rawPut("attributes",attributes); + i = i2; + while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; + while( i<len ) { + i2 = toEndName(text,i,len); + String attrName = unquote(text.substring(i,i2).toLowerCase()); + if( attributes.rawGet(attrName) != null ) + throw new BadTag("duplicate attribute: "+attrName); + i = i2; + while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; + if( i<len && text.charAt(i) == '=' ) { + i++; + i2 = i; + while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; + i2 = toEndValue(text,i,len); + String attrValue = text.substring(i,i2); + if( attrValue.indexOf('<') != -1 || attrValue.indexOf('>') != -1 ) + throw new BadTag("invalid attribute value: "+attrValue); + attrValue = unquote(attrValue); + attributes.rawPut(attrName,attrValue); + i = i2; + while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; + } else { + attributes.rawPut(attrName,true); + } + } + return tbl; + } + + private static int toEndName(String text,int i,int len) { + if( i==len ) + return i; + char c = text.charAt(i); + switch(c) { + case '"': + case '\'': + i = text.indexOf(c,i+1); + return i==-1 ? len : i+1; + default: + if( Character.isWhitespace(c) ) { + throw new RuntimeException("text="+text+" i="+i); + } + do { + i++; + } while( i<len && (c=text.charAt(i))!='=' && !Character.isWhitespace(c) ); + return i; + } + } + + private static int toEndValue(String text,int i,int len) { + if( i==len ) + return i; + char c = text.charAt(i); + switch(c) { + case '"': + case '\'': + i = text.indexOf(c,i+1); + return i==-1 ? len : i+1; + default: + if( Character.isWhitespace(c) ) { + throw new RuntimeException("text="+text+" i="+i); + } + do { + i++; + } while( i<len && !Character.isWhitespace(text.charAt(i)) ); + return i; + } + } + + public static String unquote(String s) { + if( s==null || s.length()<=1 ) + return s; + char c = s.charAt(0); + return (c=='"' || c=='\'') && s.charAt(s.length()-1)==c + ? s.substring(1,s.length()-1) : s; + } +*/ + + +/* + public static String to_string(LuanState luan,LuanTable tbl) throws LuanException { + List<Object> html = tbl.asList(); + StringBuilder buf = new StringBuilder(); + for( Object o : html ) { + if( o instanceof String ) { + buf.append( o ); + } else if( o instanceof LuanTable ) { + LuanTable t = (LuanTable)o; + String type = (String)t.get(luan,"type"); + if( type==null ) + throw new LuanException(luan, "no type in element of table for 'Html.to_string'" ); + if( type.equals("comment") ) { + buf.append( "<!--" ).append( t.get(luan,"text") ).append( "-->" ); + } else if( type.equals("cdata") ) { + buf.append( "<![CDATA[" ).append( t.get(luan,"text") ).append( "]]" ); + } else if( type.equals("tag") ) { + buf.append( tagToString(luan,t) ); + } else if( type.equals("container") ) { + LuanTable tag = (LuanTable)t.get(luan,"tag"); + buf.append( tagToString(luan,tag) ); + buf.append( t.get(luan,"text") ); + buf.append( "</" ).append( tag.get(luan,"name") ).append( ">" ); + } else { + throw new LuanException(luan, "invalid element type for 'Html.to_string'" ); + } + } else + throw new LuanException(luan, "invalid value ("+Luan.type(o)+") in table for 'Html.to_string'" ); + } + return buf.toString(); + } + + private static String tagToString(LuanState luan,LuanTable tbl) throws LuanException { + StringBuilder buf = new StringBuilder(); + buf.append('<'); + buf.append(tbl.get(luan,"name")); + LuanTable attributes = (LuanTable)tbl.get(luan,"attributes"); + for( Map.Entry<Object,Object> attr : attributes.iterable(luan) ) { + buf.append( ' ' ); + buf.append( attr.getKey() ); + Object val = attr.getValue(); + if( !val.equals(Boolean.TRUE) ) { + buf.append( '=' ); + buf.append( quote((String)val) ); + } + } + if( tbl.get(luan,"is_empty").equals(Boolean.TRUE) ) + buf.append('/'); + buf.append('>'); + return buf.toString(); + } +*/ + public static String quote(String s) { + StringBuilder buf = new StringBuilder(); + buf.append('"'); + int i = 0; + while(true) { + int i2 = s.indexOf('"',i); + if( i2 == -1 ) { + buf.append(s.substring(i)); + break; + } else { + buf.append(s.substring(i,i2)); + buf.append("""); + i = i2 + 1; + } + } + buf.append('"'); + return buf.toString(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Io.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,185 @@ +java() +local IoLuan = require "java:luan.modules.IoLuan" +local System = require "java:java.lang.System" + +local M = {} + +M.ip = IoLuan.ip +M.my_ips = IoLuan.my_ips +M.read_console_line = IoLuan.read_console_line +M.schemes = IoLuan.newSchemes() +M.uri = IoLuan.uri +M.stdin = IoLuan.defaultStdin.table() +M.socket_server = IoLuan.socket_server +M.stdout = IoLuan.textWriter(System.out) +M.stderr = IoLuan.textWriter(System.err) + +-- used by http and rpc +M.password = "password" + +local Luan = require "luan:Luan.luan" +local error = Luan.error +local to_string = Luan.to_string or error() +local type = Luan.type or error() +local try = Luan.try or error() +local ipairs = Luan.ipairs or error() +local pairs = Luan.pairs or error() +local values = Luan.values or error() +local load = Luan.load or error() +local Table = require "luan:Table.luan" +local unpack = Table.unpack or error() +local String = require "luan:String.luan" +local encode = String.encode or error() +local matches = String.matches or error() + + +-- do not change +function M.template_write(...) + return M.stdout.write(...) +end + + +function M.print_to(out,...) + local list = {} + for v in values(...) do + list[#list+1] = to_string(v) + list[#list+1] = '\t' + end + if #list > 0 then + list[#list] = '\n' + out.write( unpack(list) ) + end +end + +function M.print(...) + M.print_to(M.stdout,...) +end + + +function M.output_to(out,fn,...) + local old_out = M.stdout + return try( { + function(...) + M.stdout = out + fn(...) + end; + finally = function() + M.stdout = old_out + end; + }, ... ) +end + +local uri = M.uri -- make local + +function M.output_of(fn,...) + local string_uri = uri "string:" + local out = string_uri.text_writer() + M.output_to(out,fn,...) + out.close() + return string_uri.read_text() +end + + +-- repr + +local function do_repr(out,obj,strict,done) + local tp = type(obj) + if tp == "table" then + if done[obj] == true then + strict and error "circular reference" + out.write "<circular reference>" + return + end + done[obj] = true + out.write( "{" ) + local is_first = true + local in_list = {} + for key, value in ipairs(obj) do + if is_first then is_first = false else out.write ", " end + do_repr(out,value,strict,done) + in_list[key] = true + end + for key, value in pairs(obj) do + if in_list[key] ~= true then + if is_first then is_first = false else out.write ", " end + if type(key) == "string" and matches(key,"^[a-zA-Z_][a-zA-Z_0-9]*$") ~= nil then + out.write( key ) + elseif type(key) == "table" then + out.write( "[<", key, ">]" ) + else + out.write "[" + do_repr(out,key,strict,done) + out.write "]" + end + out.write "=" + do_repr(out,value,strict,done) + end + end + out.write "}" + elseif tp == "string" then + out.write( '"', encode(obj), '"' ) + elseif tp == "nil" or tp == "boolean" or tp == "number" then + out.write( obj ) + else + strict and error("can't repr type '"..tp.."' of "..obj) + out.write( "<", obj, ">" ) + end +end + +function M.repr(obj,strict) + local string_uri = uri "string:" + local out = string_uri.text_writer() + do_repr(out,obj,strict or false,{}) + out.close() + return string_uri.read_text() +end + + +-- useful for SimplyHTML responsiveness + +local NO = {} +M.NO = NO + +function M.dont_write_when_no(write_fn) + return function(...) + for v in values(...) do + if v == NO then + return + end + end + write_fn(...) + end +end + + +-- debug + +function M.debug(prompt) + prompt = prompt or "luan_debug> " + local function console() + return M.read_console_line(prompt) + end + local env = {} + for line in console do + try { + function() + local fn + try { + function() + fn = load("return "..line,"stdin",env) + end + catch = function(e) + fn = load(line,"stdin",env) + end + } + M.print( fn() ) + end + catch = function(e) + M.print(e) + end + } + end +end + + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/IoLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,904 @@ +package luan.modules; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Reader; +import java.io.Writer; +import java.io.StringReader; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.StringWriter; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.net.URL; +import java.net.Socket; +import java.net.ServerSocket; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.NetworkInterface; +import java.net.MalformedURLException; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanJavaFunction; +import luan.LuanException; +import luan.modules.url.LuanUrl; + + +public final class IoLuan { + + private static void add(LuanTable t,String method,Class... parameterTypes) throws NoSuchMethodException { + t.rawPut( method, new LuanJavaFunction(IoLuan.class.getMethod(method,parameterTypes),null) ); + } + + public static String read_console_line(String prompt) throws IOException { + if( prompt==null ) + prompt = "> "; + return System.console().readLine(prompt); + } + + + public interface LuanWriter { + public void write(LuanState luan,Object... args) throws LuanException, IOException; + public void close() throws IOException; + } + + public static LuanTable textWriter(final PrintStream out) { + LuanWriter luanWriter = new LuanWriter() { + + public void write(LuanState luan,Object... args) throws LuanException { + for( Object obj : args ) { + out.print( luan.toString(obj) ); + } + } + + public void close() { + out.close(); + } + }; + return writer(luanWriter); + } + + public static LuanTable textWriter(final Writer out) { + LuanWriter luanWriter = new LuanWriter() { + + public void write(LuanState luan,Object... args) throws LuanException, IOException { + for( Object obj : args ) { + out.write( luan.toString(obj) ); + } + } + + public void close() throws IOException { + out.close(); + } + }; + return writer(luanWriter); + } + + private static LuanTable writer(LuanWriter luanWriter) { + LuanTable writer = new LuanTable(); + try { + writer.rawPut( "write", new LuanJavaFunction( + LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter + ) ); + writer.rawPut( "close", new LuanJavaFunction( + LuanWriter.class.getMethod( "close" ), luanWriter + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return writer; + } + + + public static LuanTable binaryWriter(final OutputStream out) { + LuanTable writer = new LuanTable(); + try { + writer.rawPut( "write", new LuanJavaFunction( + OutputStream.class.getMethod( "write", new byte[0].getClass() ), out + ) ); + writer.rawPut( "close", new LuanJavaFunction( + OutputStream.class.getMethod( "close" ), out + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return writer; + } + + static LuanFunction lines(final BufferedReader in) { + return new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + if( args.length > 0 ) { + if( args.length > 1 || !"close".equals(args[0]) ) + throw new LuanException( "the only argument allowed is 'close'" ); + in.close(); + return null; + } + String rtn = in.readLine(); + if( rtn==null ) + in.close(); + return rtn; + } catch(IOException e) { + throw new LuanException(e); + } + } + }; + } + + static LuanFunction blocks(final InputStream in,final int blockSize) { + return new LuanFunction() { + final byte[] a = new byte[blockSize]; + + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + if( args.length > 0 ) { + if( args.length > 1 || !"close".equals(args[0]) ) + throw new LuanException( "the only argument allowed is 'close'" ); + in.close(); + return null; + } + if( in.read(a) == -1 ) { + in.close(); + return null; + } + return a; + } catch(IOException e) { + throw new LuanException(e); + } + } + }; + } + + + private static File objToFile(Object obj) { + if( obj instanceof String ) { + return new File((String)obj); + } + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + Object java = t.rawGet("java"); + if( java instanceof LuanFile ) { + LuanFile luanFile = (LuanFile)java; + return luanFile.file; + } + } + return null; + } + + + public static abstract class LuanIn { + public abstract InputStream inputStream() throws IOException, LuanException; + public abstract String to_string(); + public abstract String to_uri_string(); + + public Reader reader() throws IOException, LuanException { + return new InputStreamReader(inputStream()); + } + + public String read_text() throws IOException, LuanException { + Reader in = reader(); + String s = Utils.readAll(in); + in.close(); + return s; + } + + public byte[] read_binary() throws IOException, LuanException { + InputStream in = inputStream(); + byte[] a = Utils.readAll(in); + in.close(); + return a; + } + + public LuanFunction read_lines() throws IOException, LuanException { + return lines(new BufferedReader(reader())); + } + + public LuanFunction read_blocks(Integer blockSize) throws IOException, LuanException { + int n = blockSize!=null ? blockSize : Utils.bufSize; + return blocks(inputStream(),n); + } + + public boolean exists() throws IOException, LuanException { + try { + inputStream().close(); + return true; + } catch(FileNotFoundException e) { + return false; + } + } + + public LuanTable table() { + LuanTable tbl = new LuanTable(); + try { + tbl.rawPut( "java", this ); + tbl.rawPut( "to_string", new LuanJavaFunction( + LuanIn.class.getMethod( "to_string" ), this + ) ); + tbl.rawPut( "to_uri_string", new LuanJavaFunction( + LuanIn.class.getMethod( "to_uri_string" ), this + ) ); + tbl.rawPut( "read_text", new LuanJavaFunction( + LuanIn.class.getMethod( "read_text" ), this + ) ); + tbl.rawPut( "read_binary", new LuanJavaFunction( + LuanIn.class.getMethod( "read_binary" ), this + ) ); + tbl.rawPut( "read_lines", new LuanJavaFunction( + LuanIn.class.getMethod( "read_lines" ), this + ) ); + tbl.rawPut( "read_blocks", new LuanJavaFunction( + LuanIn.class.getMethod( "read_blocks", Integer.class ), this + ) ); + tbl.rawPut( "exists", new LuanJavaFunction( + LuanIn.class.getMethod( "exists" ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } + } + + public static final LuanIn defaultStdin = new LuanIn() { + + @Override public InputStream inputStream() { + return System.in; + } + + @Override public String to_string() { + return "<stdin>"; + } + + @Override public String to_uri_string() { + return "stdin:"; + } + + @Override public String read_text() throws IOException { + return Utils.readAll(new InputStreamReader(System.in)); + } + + @Override public byte[] read_binary() throws IOException { + return Utils.readAll(System.in); + } + + @Override public boolean exists() { + return true; + } + }; + + public static abstract class LuanIO extends LuanIn { + abstract OutputStream outputStream() throws IOException; + + public void write(Object obj) throws LuanException, IOException { + if( obj instanceof String ) { + String s = (String)obj; + Writer out = new OutputStreamWriter(outputStream()); + out.write(s); + out.close(); + return; + } + if( obj instanceof byte[] ) { + byte[] a = (byte[])obj; + OutputStream out = outputStream(); + Utils.copyAll(new ByteArrayInputStream(a),out); + out.close(); + return; + } + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + Object java = t.rawGet("java"); + if( java instanceof LuanIn ) { + LuanIn luanIn = (LuanIn)java; + InputStream in = luanIn.inputStream(); + OutputStream out = outputStream(); + Utils.copyAll(in,out); + out.close(); + in.close(); + return; + } + } + throw new LuanException( "bad argument #1 to 'write' (string or binary or Io.uri expected)" ); + } + + public LuanTable text_writer() throws IOException { + return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream()))); + } + + public LuanTable binary_writer() throws IOException { + return binaryWriter(new BufferedOutputStream(outputStream())); + } + + @Override public LuanTable table() { + LuanTable tbl = super.table(); + try { + tbl.rawPut( "write", new LuanJavaFunction( + LuanIO.class.getMethod( "write", Object.class ), this + ) ); + tbl.rawPut( "text_writer", new LuanJavaFunction( + LuanIO.class.getMethod( "text_writer" ), this + ) ); + tbl.rawPut( "binary_writer", new LuanJavaFunction( + LuanIO.class.getMethod( "binary_writer" ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } + } + + private static final LuanIO nullIO = new LuanIO() { + private final InputStream in = new InputStream() { + @Override public int read() { + return -1; + } + }; + private final OutputStream out = new OutputStream() { + @Override public void write(int b) {} + }; + + @Override public InputStream inputStream() { + return in; + } + + @Override OutputStream outputStream() { + return out; + } + + @Override public String to_string() { + return "<null>"; + } + + @Override public String to_uri_string() { + return "null:"; + } + + }; + + public static final class LuanString extends LuanIO { + private String s; + + private LuanString(String s) { + this.s = s; + } + + @Override public InputStream inputStream() { + throw new UnsupportedOperationException(); + } + + @Override OutputStream outputStream() { + throw new UnsupportedOperationException(); + } + + @Override public String to_string() { + return "<string>"; + } + + @Override public String to_uri_string() { + return "string:" + s; + } + + @Override public Reader reader() { + return new StringReader(s); + } + + @Override public String read_text() { + return s; + } + + @Override public boolean exists() { + return true; + } + + @Override public LuanTable text_writer() throws IOException { + LuanWriter luanWriter = new LuanWriter() { + private final Writer out = new StringWriter(); + + public void write(LuanState luan,Object... args) throws LuanException, IOException { + for( Object obj : args ) { + out.write( luan.toString(obj) ); + } + } + + public void close() throws IOException { + s = out.toString(); + } + }; + return writer(luanWriter); + } + } + + public static final class LuanFile extends LuanIO { + public final File file; + + private LuanFile(LuanState luan,File file) throws LuanException { + this(file); + check(luan,"file:"+file.toString()); + } + + private LuanFile(File file) { + this.file = file; + } + + @Override public InputStream inputStream() throws IOException { + return new FileInputStream(file); + } + + @Override OutputStream outputStream() throws IOException { + return new FileOutputStream(file); + } + + @Override public String to_string() { + return file.toString(); + } + + @Override public String to_uri_string() { + return "file:" + file.toString(); + } + + public LuanTable child(LuanState luan,String name) throws LuanException { + return new LuanFile(luan,new File(file,name)).table(); + } + + public LuanTable children(LuanState luan) throws LuanException { + File[] files = file.listFiles(); + if( files==null ) + return null; + LuanTable list = new LuanTable(); + for( File f : files ) { + list.rawPut(list.rawLength()+1,new LuanFile(luan,f).table()); + } + return list; + } + + public LuanTable parent(LuanState luan) throws LuanException, IOException { + File parent = file.getParentFile(); + if( parent==null ) + parent = file.getCanonicalFile().getParentFile(); + return new LuanFile(luan,parent).table(); + } + + @Override public boolean exists() { + return file.exists(); + } + + public void rename_to(Object destObj) throws LuanException { + File dest = objToFile(destObj); + if( dest==null ) + throw new LuanException( "bad argument #1 to 'objToFile' (string or file table expected)" ); + if( !file.renameTo(dest) ) + throw new LuanException("couldn't rename file "+file+" to "+dest); + } + + public LuanTable canonical(LuanState luan) throws LuanException, IOException { + return new LuanFile(luan,file.getCanonicalFile()).table(); + } + + public LuanTable create_temp_file(LuanState luan,String prefix,String suffix) throws LuanException, IOException { + File tmp = File.createTempFile(prefix,suffix,file); + return new LuanFile(luan,tmp).table(); + } + + public void delete() throws LuanException { + if( file.exists() ) + delete(file); + } + + private static void delete(File file) throws LuanException { + File[] children = file.listFiles(); + if( children != null ) { + for( File child : children ) { + delete(child); + } + } + if( !file.delete() ) + throw new LuanException("couldn't delete file "+file); + } + + public void mkdir() throws LuanException { + if( !file.isDirectory() ) { + if( !file.mkdirs() ) + throw new LuanException("couldn't make directory "+file); + } + } + + public void set_last_modified(long time) throws LuanException { + if( !file.setLastModified(time) ) + throw new LuanException("couldn't set_last_modified on "+file); + } + + @Override public LuanTable table() { + LuanTable tbl = super.table(); + try { + tbl.rawPut( "name", new LuanJavaFunction( + File.class.getMethod( "getName" ), file + ) ); + tbl.rawPut( "is_directory", new LuanJavaFunction( + File.class.getMethod( "isDirectory" ), file + ) ); + tbl.rawPut( "is_file", new LuanJavaFunction( + File.class.getMethod( "isFile" ), file + ) ); + tbl.rawPut( "delete", new LuanJavaFunction( + LuanFile.class.getMethod( "delete" ), this + ) ); + tbl.rawPut( "delete_on_exit", new LuanJavaFunction( + File.class.getMethod( "deleteOnExit" ), file + ) ); + tbl.rawPut( "mkdir", new LuanJavaFunction( + LuanFile.class.getMethod( "mkdir" ), this + ) ); + tbl.rawPut( "last_modified", new LuanJavaFunction( + File.class.getMethod( "lastModified" ), file + ) ); + tbl.rawPut( "set_last_modified", new LuanJavaFunction( + LuanFile.class.getMethod( "set_last_modified", Long.TYPE ), this + ) ); + tbl.rawPut( "length", new LuanJavaFunction( + File.class.getMethod( "length" ), file + ) ); + tbl.rawPut( "child", new LuanJavaFunction( + LuanFile.class.getMethod( "child", LuanState.class, String.class ), this + ) ); + tbl.rawPut( "children", new LuanJavaFunction( + LuanFile.class.getMethod( "children", LuanState.class ), this + ) ); + tbl.rawPut( "parent", new LuanJavaFunction( + LuanFile.class.getMethod( "parent", LuanState.class ), this + ) ); + tbl.rawPut( "rename_to", new LuanJavaFunction( + LuanFile.class.getMethod( "rename_to", Object.class ), this + ) ); + tbl.rawPut( "canonical", new LuanJavaFunction( + LuanFile.class.getMethod( "canonical", LuanState.class ), this + ) ); + tbl.rawPut( "create_temp_file", new LuanJavaFunction( + LuanFile.class.getMethod( "create_temp_file", LuanState.class, String.class, String.class ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } + } + + public static LuanTable null_() { + return nullIO.table(); + } + + public static LuanTable string(String s) throws LuanException { + Utils.checkNotNull(s); + return new LuanString(s).table(); + } + + public static LuanTable file(LuanState luan,String name) throws LuanException { + File file = new File(name); + return new LuanFile(file).table(); + } + + public static LuanTable classpath(LuanState luan,String name) throws LuanException { + if( name.contains("//") ) + return null; + String path = name; + check(luan,"classpath:"+path); + URL url; + if( !path.contains("#") ) { + url = ClassLoader.getSystemResource(path); + } else { + String[] a = path.split("#"); + url = ClassLoader.getSystemResource(a[0]); + if( url==null ) { + for( int i=1; i<a.length; i++ ) { + url = ClassLoader.getSystemResource(a[0]+"/"+a[i]); + if( url != null ) { + try { + url = new URL(url,"."); + } catch(MalformedURLException e) { + throw new RuntimeException(e); + } + break; + } + } + } + } + if( url != null ) + return new LuanUrl(luan,url,null).table(); + + return null; + } + + private static LuanTable url(LuanState luan,String url,LuanTable options) throws IOException, LuanException { + return new LuanUrl(luan,new URL(url),options).table(); + } + + public static LuanTable http(LuanState luan,String path,LuanTable options) throws IOException, LuanException { + return url(luan,"http:"+path,options); + } + + public static LuanTable https(LuanState luan,String path,LuanTable options) throws IOException, LuanException { + return url(luan,"https:"+path,options); + } + + public static LuanTable luan(LuanState luan,String path) throws LuanException { + return classpath( luan, "luan/modules/" + path ); + } + + public static LuanTable stdin(LuanState luan) throws LuanException { + LuanTable io = (LuanTable)PackageLuan.require(luan,"luan:Io.luan"); + return (LuanTable)io.get(luan,"stdin"); + } + + public static LuanTable newSchemes() { + LuanTable schemes = new LuanTable(); + try { + schemes.rawPut( "null", new LuanJavaFunction(IoLuan.class.getMethod("null_"),null) ); + add( schemes, "string", String.class ); + add( schemes, "file", LuanState.class, String.class ); + add( schemes, "classpath", LuanState.class, String.class ); + add( schemes, "socket", String.class ); + add( schemes, "http", LuanState.class, String.class, LuanTable.class ); + add( schemes, "https", LuanState.class, String.class, LuanTable.class ); + add( schemes, "luan", LuanState.class, String.class ); + add( schemes, "stdin", LuanState.class ); + add( schemes, "os", LuanState.class, String.class, LuanTable.class ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return schemes; + } + + private static LuanTable schemes(LuanState luan) throws LuanException { + LuanTable t = (LuanTable)PackageLuan.loaded(luan).rawGet("luan:Io.luan"); + if( t == null ) + return newSchemes(); + t = (LuanTable)t.get(luan,"schemes"); + if( t == null ) + return newSchemes(); + return t; + } + + public static LuanTable uri(LuanState luan,String name,LuanTable options) throws LuanException { + int i = name.indexOf(':'); + if( i == -1 ) + throw new LuanException( "invalid Io.uri name '"+name+"', missing scheme" ); + String scheme = name.substring(0,i); + String location = name.substring(i+1); + LuanTable schemes = schemes(luan); + LuanFunction opener = (LuanFunction)schemes.get(luan,scheme); + if( opener == null ) + throw new LuanException( "invalid scheme '"+scheme+"' in '"+name+"'" ); + return (LuanTable)Luan.first(opener.call(luan,new Object[]{location,options})); + } + + public static final class LuanSocket extends LuanIO { + public final Socket socket; + + private LuanSocket(String host,int port) throws LuanException { + try { + this.socket = new Socket(host,port); + } catch(IOException e) { + throw new LuanException(e.toString()); + } + } + + private LuanSocket(Socket socket) { + this.socket = socket; + } + + @Override public InputStream inputStream() throws IOException { + return socket.getInputStream(); + } + + @Override OutputStream outputStream() throws IOException { + return socket.getOutputStream(); + } + + @Override public String to_string() { + return socket.toString(); + } + + @Override public String to_uri_string() { + throw new UnsupportedOperationException(); + } + } + + public static LuanTable socket(String name) throws LuanException, IOException { + int i = name.indexOf(':'); + if( i == -1 ) + throw new LuanException( "invalid socket '"+name+"', format is: <host>:<port>" ); + String host = name.substring(0,i); + String portStr = name.substring(i+1); + int port = Integer.parseInt(portStr); + return new LuanSocket(host,port).table(); + } + + public static LuanFunction socket_server(int port) throws IOException { + final ServerSocket ss = new ServerSocket(port); + return new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + if( args.length > 0 ) { + if( args.length > 1 || !"close".equals(args[0]) ) + throw new LuanException( "the only argument allowed is 'close'" ); + ss.close(); + return null; + } + return new LuanSocket(ss.accept()).table(); + } catch(IOException e) { + throw new LuanException(e); + } + } + }; + } + + + public static final class LuanOs extends LuanIO { + private final String cmd; + private final Process proc; + + private LuanOs(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { + this.cmd = cmd; + File dir = null; + if( options != null ) { + Map map = options.asMap(luan); + Object obj = map.remove("dir"); + dir = objToFile(obj); + if( dir==null ) + throw new LuanException( "bad option 'dir' (string or file table expected)" ); + if( !map.isEmpty() ) + throw new LuanException( "unrecognized options: "+map ); + } + this.proc = Runtime.getRuntime().exec(cmd,null,dir); + } + + @Override public InputStream inputStream() throws IOException { + return proc.getInputStream(); + } + + @Override OutputStream outputStream() throws IOException { + return proc.getOutputStream(); + } + + @Override public String to_string() { + return proc.toString(); + } + + @Override public String to_uri_string() { + throw new UnsupportedOperationException(); + } + + @Override public boolean exists() { + return true; + } + + public void wait_for() + throws IOException, LuanException + { + try { + proc.waitFor(); + } catch(InterruptedException e) { + throw new RuntimeException(e); + } + int exitVal = proc.exitValue(); + if( exitVal != 0 ) { + Reader err = new InputStreamReader(proc.getErrorStream()); + String error = "error in: "+cmd+"\n"+Utils.readAll(err); + err.close(); + throw new LuanException(error); + } + } + + @Override public String read_text() throws IOException, LuanException { + String s = super.read_text(); + wait_for(); + return s; + } + + @Override public LuanTable table() { + LuanTable tbl = super.table(); + try { + tbl.rawPut( "wait_for", new LuanJavaFunction( + LuanOs.class.getMethod( "wait_for" ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } + } + + public static LuanTable os(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { + return new LuanOs(luan,cmd,options).table(); + } + + + public static String ip(String domain) { + try { + return InetAddress.getByName(domain).getHostAddress(); + } catch(UnknownHostException e) { + return null; + } + } + + public static LuanTable my_ips() throws IOException { + LuanTable tbl = new LuanTable(); + for( Enumeration<NetworkInterface> e1 = NetworkInterface.getNetworkInterfaces(); e1.hasMoreElements(); ) { + NetworkInterface ni = e1.nextElement(); + for( Enumeration<InetAddress> e2 = ni.getInetAddresses(); e2.hasMoreElements(); ) { + InetAddress ia = e2.nextElement(); + if( ia instanceof Inet4Address ) + tbl.rawPut(ia.getHostAddress(),true); + } + } + return tbl; + } + +/* + // files maps zip name to uri + public static void zip(LuanState luan,String zipUri,LuanTable files) throws LuanException, IOException { + Object obj = uri(luan,zipUri,null).rawGet("java"); + if( !(obj instanceof LuanIO) ) + throw new LuanException("invalid uri for zip"); + LuanIO zipIo = (LuanIO)obj; + ZipOutputStream out = new ZipOutputStream(zipIo.outputStream()); + for( Map.Entry<Object,Object> entry : files.iterable(luan) ) { + obj = entry.getKey(); + if( !(obj instanceof String) ) + throw new LuanException("zip file table keys must be strings"); + String fileName = (String)obj; + obj = entry.getValue(); + if( !(obj instanceof String) ) + throw new LuanException("zip file table values must be strings"); + String uriStr = (String)obj; + out.putNextEntry(new ZipEntry(fileName)); + obj = uri(luan,uriStr,null).rawGet("java"); + if( !(obj instanceof LuanIn) ) + throw new LuanException("invalid uri for zip"); + LuanIn zipIn = (LuanIn)obj; + InputStream in = zipIn.inputStream(); + Utils.copyAll(in,out); + in.close(); + out.closeEntry(); + } + out.close(); + } +*/ + + // security + + public interface Security { + public void check(LuanState luan,String name) throws LuanException; + } + + private static String SECURITY_KEY = "Io.Security"; + + private static void check(LuanState luan,String name) throws LuanException { + Security s = (Security)luan.registry().get(SECURITY_KEY); + if( s!=null ) + s.check(luan,name); + } + + public static void setSecurity(LuanState luan,Security s) { + luan.registry().put(SECURITY_KEY,s); + } + + private void IoLuan() {} // never +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/JavaLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,475 @@ +package luan.modules; + +import java.lang.reflect.Array; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Collections; +import java.util.Arrays; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanException; +import luan.LuanFunction; +import luan.LuanJavaFunction; + + +public final class JavaLuan { + + public static void java(LuanState luan) throws LuanException { + check(luan,LuanException.currentSource()); + luan.java.ok = true; + } + + public static final LuanFunction javaFn; + static { + try { + javaFn = new LuanJavaFunction(JavaLuan.class.getMethod("java",LuanState.class),null); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private static void checkJava(LuanState luan) throws LuanException { + if( !luan.java.ok ) + throw new LuanException("Java isn't allowed"); + } + + static final Object FAIL = new Object(); + + public static Object __index(LuanState luan,Object obj,Object key,boolean canReturnFail) throws LuanException { + checkJava(luan); + Class cls; + if( obj instanceof Static ) { + Static st = (Static)obj; + cls = st.cls; + if( key instanceof String ) { + String name = (String)key; + if( "class".equals(name) ) { + return cls; + } else if( "new".equals(name) ) { + Constructor[] constructors = cls.getConstructors(); + if( constructors.length > 0 ) { + if( constructors.length==1 ) { + return new LuanJavaFunction(constructors[0],null); + } else { + List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); + for( Constructor constructor : constructors ) { + fns.add(new LuanJavaFunction(constructor,null)); + } + return new AmbiguousJavaFunction(fns); + } + } +/* + } else if( "assert".equals(name) ) { + return new LuanJavaFunction(assertClass,new AssertClass(cls)); +*/ + } else if( "luan_proxy".equals(name) ) { + return new LuanJavaFunction(luan_proxyMethod,st); + } else { + List<Member> members = getStaticMembers(cls,name); + if( !members.isEmpty() ) { + return member(null,members); + } + } + } + } else { + cls = obj.getClass(); + if( cls.isArray() ) { + if( "length".equals(key) ) { + return Array.getLength(obj); + } + Integer i = Luan.asInteger(key); + if( i != null ) { + return Array.get(obj,i); + } +// throw new LuanException(luan,"invalid member '"+key+"' for java array: "+obj); + } else if( key instanceof String ) { + String name = (String)key; + if( "instanceof".equals(name) ) { + return new LuanJavaFunction(instanceOf,new InstanceOf(obj)); + } else { + List<Member> members = getMembers(cls,name); + if( !members.isEmpty() ) { + return member(obj,members); + } + } + } + } +//System.out.println("invalid member '"+key+"' for java object: "+obj); + if( canReturnFail ) + return FAIL; + throw new LuanException( "invalid index '"+key+"' for java "+cls ); + } + + private static Object member(Object obj,List<Member> members) throws LuanException { + try { + if( members.size()==1 ) { + Member member = members.get(0); + if( member instanceof Static ) { + return member; + } else if( member instanceof Field ) { + Field field = (Field)member; + Object rtn = field.get(obj); + return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn; + } else { + Method method = (Method)member; + return new LuanJavaFunction(method,obj); + } + } else { + List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>(); + for( Member member : members ) { + Method method = (Method)member; + fns.add(new LuanJavaFunction(method,obj)); + } + return new AmbiguousJavaFunction(fns); + } + } catch(IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static void __new_index(LuanState luan,Object obj,Object key,Object value) throws LuanException { + checkJava(luan); + Class cls; + if( obj instanceof Static ) { + Static st = (Static)obj; + cls = st.cls; + if( key instanceof String ) { + String name = (String)key; + List<Member> members = getStaticMembers(cls,name); + if( !members.isEmpty() ) { + if( members.size() != 1 ) + throw new RuntimeException("not field '"+name+"' of "+obj); + setMember(obj,members,value); + return; + } + } +// throw new LuanException(luan,"invalid member '"+key+"' for: "+obj); + } else { + cls = obj.getClass(); + if( cls.isArray() ) { + Integer i = Luan.asInteger(key); + if( i != null ) { + Array.set(obj,i,value); + return; + } +// throw new LuanException(luan,"invalid member '"+key+"' for java array: "+obj); + } else if( key instanceof String ) { + String name = (String)key; + List<Member> members = getMembers(cls,name); + if( !members.isEmpty() ) { + if( members.size() != 1 ) + throw new RuntimeException("not field '"+name+"' of "+obj); + setMember(obj,members,value); + return; + } + } + } + throw new LuanException( "invalid index for java "+cls ); + } + + private static void setMember(Object obj,List<Member> members,Object value) { + Field field = (Field)members.get(0); + try { + try { + field.set(obj,value); + } catch(IllegalArgumentException e) { + Class cls = field.getType(); + if( value instanceof Number ) { + Number n = (Number)value; + if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) { + int r = n.intValue(); + if( r==n.doubleValue() ) { + field.setInt(obj,r); + return; + } + } + } + throw e; + } + } catch(IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static boolean privateAccess = false; + private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>(); + + private static synchronized List<Member> getMembers(Class cls,String name) { + Map<String,List<Member>> clsMap = memberMap.get(cls); + if( clsMap == null ) { + clsMap = new HashMap<String,List<Member>>(); + for( Class c : cls.getClasses() ) { + String s = c.getSimpleName(); + List<Member> list = new ArrayList<Member>(); + clsMap.put(s,list); + list.add(new Static(c)); + } + for( Field field : cls.getFields() ) { + String s = field.getName(); + try { + if( !cls.getField(s).equals(field) ) + continue; // not accessible + } catch(NoSuchFieldException e) { + throw new RuntimeException(e); + } + List<Member> list = new ArrayList<Member>(); + clsMap.put(s,list); + list.add(field); + } + for( Method method : cls.getMethods() ) { + String s = method.getName(); + List<Member> list = clsMap.get(s); + if( list == null || !(list.get(0) instanceof Method) ) { + list = new ArrayList<Member>(); + clsMap.put(s,list); + } + list.add(method); + } + if( privateAccess ) { + for( Method method : cls.getDeclaredMethods() ) { + String s = method.getName(); + List<Member> list = clsMap.get(s); + if( list == null ) { + list = new ArrayList<Member>(); + clsMap.put(s,list); + } else if( !(list.get(0) instanceof Method) ) + continue; + if( !list.contains(method) ) { + list.add(method); + } + } + for( Field field : cls.getDeclaredFields() ) { + String s = field.getName(); + List<Member> list = clsMap.get(s); + if( list == null ) { + list = new ArrayList<Member>(); + clsMap.put(s,list); + list.add(field); + } + } + } + for( List<Member> members : clsMap.values() ) { + for( Member m : members ) { + if( m instanceof AccessibleObject ) + ((AccessibleObject)m).setAccessible(true); + } + } + memberMap.put(cls,clsMap); + } + List<Member> rtn = clsMap.get(name); + if( rtn==null ) + rtn = Collections.emptyList(); + return rtn; + } + + private static synchronized List<Member> getStaticMembers(Class cls,String name) { + List<Member> staticMembers = new ArrayList<Member>(); + for( Member m : getMembers(cls,name) ) { + if( Modifier.isStatic(m.getModifiers()) ) + staticMembers.add(m); + } + return staticMembers; + } + + static final class Static implements Member { + final Class cls; + + Static(Class cls) { + this.cls = cls; + } + + @Override public String toString() { + return cls.toString(); + } + + @Override public Class getDeclaringClass() { + return cls.getDeclaringClass(); + } + + @Override public String getName() { + return cls.getName(); + } + + @Override public int getModifiers() { + return cls.getModifiers(); + } + + @Override public boolean isSynthetic() { + return cls.isSynthetic(); + } + + public Object luan_proxy(final LuanState luan,final LuanTable t) throws LuanException { + return Proxy.newProxyInstance( + cls.getClassLoader(), + new Class[]{cls}, + new InvocationHandler() { + public Object invoke(Object proxy,Method method, Object[] args) + throws Throwable + { + if( args==null ) + args = new Object[0]; + String name = method.getName(); + Object fnObj = t.get(luan,name); + if( fnObj == null ) + throw new NullPointerException("luan_proxy couldn't find method '"+name+"'"); + LuanFunction fn = Luan.checkFunction(fnObj); + return Luan.first(fn.call(luan,args)); + } + } + ); + } + } + private static final Method luan_proxyMethod; + static { + try { + luan_proxyMethod = Static.class.getMethod("luan_proxy",LuanState.class,LuanTable.class); + luan_proxyMethod.setAccessible(true); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public static Static load(LuanState luan,String name) throws LuanException { + checkJava(luan); + Class cls; + try { + cls = Class.forName(name); + } catch(ClassNotFoundException e) { + try { + cls = Thread.currentThread().getContextClassLoader().loadClass(name); + } catch(ClassNotFoundException e2) { + return null; + } + } + return new Static(cls); + } + + private static class AmbiguousJavaFunction extends LuanFunction { + private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>(); + + AmbiguousJavaFunction(List<LuanJavaFunction> fns) { + for( LuanJavaFunction fn : fns ) { + Integer n = fn.getParameterTypes().length; + List<LuanJavaFunction> list = fnMap.get(n); + if( list==null ) { + list = new ArrayList<LuanJavaFunction>(); + fnMap.put(n,list); + } + list.add(fn); + } + } + + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + for( LuanJavaFunction fn : fnMap.get(args.length) ) { + try { + return fn.rawCall(luan,args); + } catch(IllegalArgumentException e) {} + } + throw new LuanException("no method matched args: "+Arrays.asList(args)); + } + } + + private static class InstanceOf { + private final Object obj; + + InstanceOf(Object obj) { + this.obj = obj; + } + + public boolean instanceOf(Static st) { + return st.cls.isInstance(obj); + } + } + private static final Method instanceOf; + static { + try { + instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class); + instanceOf.setAccessible(true); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + +/* + private static class AssertClass { + private final Class cls; + + AssertClass(Class cls) { + this.cls = cls; + } + + public Object assertClass(LuanState luan,Object v) throws LuanException { + if( !cls.isInstance(v) ) { + String got = v.getClass().getSimpleName(); + String expected = cls.getSimpleName(); + throw new LuanException(luan,"bad argument #1 ("+expected+" expected, got "+got+")"); + } + return v; + } + } + private static final Method assertClass; + static { + try { + assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class); + assertClass.setAccessible(true); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + + public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException { + return Proxy.newProxyInstance( + st.cls.getClassLoader(), + new Class[]{st.cls}, + new InvocationHandler() { + public Object invoke(Object proxy,Method method, Object[] args) + throws Throwable + { + if( args==null ) + args = new Object[0]; + String name = method.getName(); + Object fnObj = t.get(name); + if( fnObj==null && base!=null ) + return method.invoke(base,args); + LuanFunction fn = luan.checkFunction(fnObj); + return Luan.first(luan.call(fn,name,args)); + } + } + ); + } +*/ + + + + // security + + public interface Security { + public void check(LuanState luan,String name) throws LuanException; + } + + private static String SECURITY_KEY = "Java.Security"; + + private static void check(LuanState luan,String name) throws LuanException { + Security s = (Security)luan.registry().get(SECURITY_KEY); + if( s!=null ) + s.check(luan,name); + } + + public static void setSecurity(LuanState luan,Security s) { + luan.registry().put(SECURITY_KEY,s); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Luan.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,50 @@ +java() +local BasicLuan = require "java:luan.modules.BasicLuan" + +local M = {} + +M.assert_binary = BasicLuan.assert_binary +M.assert_boolean = BasicLuan.assert_boolean +M.assert_function = BasicLuan.assert_function +M.assert_integer = BasicLuan.assert_integer +M.assert_long = BasicLuan.assert_long +M.assert_number = BasicLuan.assert_number +M.assert_string = BasicLuan.assert_string +M.assert_table = BasicLuan.assert_table +M.get_metatable = BasicLuan.get_metatable +M.ipairs = BasicLuan.ipairs +M.load = BasicLuan.load +M.load_file = BasicLuan.load_file +M.new_error = BasicLuan.new_error +M.pairs = BasicLuan.pairs +M.pcall = BasicLuan.pcall +M.range = BasicLuan.range +M.raw_equal = BasicLuan.raw_equal +M.raw_get = BasicLuan.raw_get +M.raw_len = BasicLuan.raw_len +M.raw_set = BasicLuan.raw_set +M.set_metatable = BasicLuan.set_metatable +M.to_string = BasicLuan.to_string +M.try = BasicLuan.try_ +M.type = BasicLuan.type +M.values = BasicLuan.values + +function M.do_file(uri) + return M.load_file(uri)() +end + +M.VERSION = M.do_file "classpath:luan/version.luan" + +function M.error(message) + M.new_error(message).throw() +end + +function M.assert(v,message) + return v or M.error(message or "assertion failed!") +end + +function M.eval(s,source_name) + return M.load( "return "..s, source_name or "eval" )() +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Math.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,36 @@ +java() +local MathLuan = require "java:luan.modules.MathLuan" +local JavaMath = require "java:java.lang.Math" +local Integer = require "java:java.lang.Integer" +local Double = require "java:java.lang.Double" + + +local M = {} + +M.abs = MathLuan.abs +M.acos = MathLuan.acos +M.asin = MathLuan.asin +M.atan = MathLuan.atan +M.atan2 = MathLuan.atan2 +M.ceil = MathLuan.ceil +M.cos = MathLuan.cos +M.cosh = MathLuan.cosh +M.deg = MathLuan.deg +M.exp = MathLuan.exp +M.floor = MathLuan.floor +M.fmod = MathLuan.fmod +M.huge = Double.POSITIVE_INFINITY +M.log = MathLuan.log +M.max = MathLuan.max +M.max_integer = Integer.MAX_VALUE +M.min = MathLuan.min +M.min_integer = Integer.MIN_VALUE +M.modf = MathLuan.modf +M.pi = JavaMath.PI +M.rad = MathLuan.rad +M.random = MathLuan.random +M.sin = MathLuan.sin +M.sqrt = MathLuan.sqrt +M.tan = MathLuan.tan + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/MathLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,113 @@ +package luan.modules; + +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; + + +public final class MathLuan { + + public static double abs(double x) { + return Math.abs(x); + } + + public static double acos(double x) { + return Math.acos(x); + } + + public static double asin(double x) { + return Math.asin(x); + } + + public static double atan(double x) { + return Math.atan(x); + } + + public static double atan2(double y,double x) { + return Math.atan2(y,x); + } + + public static double ceil(double x) { + return Math.ceil(x); + } + + public static double cos(double x) { + return Math.cos(x); + } + + public static double cosh(double x) { + return Math.cosh(x); + } + + public static double deg(double x) { + return Math.toDegrees(x); + } + + public static double exp(double x) { + return Math.exp(x); + } + + public static double floor(double x) { + return Math.floor(x); + } + + public static double fmod(double x,double y) { + return x % y; + } + + public static double log(double x,Double base) { + return base==null ? Math.log(x) : Math.log(x)/Math.log(base); + } + + public static double min(double x,double... a) { + for( double d : a ) { + if( x > d ) + x = d; + } + return x; + } + + public static double max(double x,double... a) { + for( double d : a ) { + if( x < d ) + x = d; + } + return x; + } + + public static double[] modf(double x) { + double i = (int)x; + return new double[]{i,x-i}; + } + + public static double rad(double x) { + return Math.toRadians(x); + } + + public static double random(Integer m,Integer n) { + if( m==null ) + return Math.random(); + if( n==null ) + return Math.floor(m*Math.random()) + 1; + return Math.floor((n-m+1)*Math.random()) + m; + } + + public static double sin(double x) { + return Math.sin(x); + } + + public static double sqrt(double x) { + return Math.sqrt(x); + } + + public static double tan(double x) { + return Math.tan(x); + } + + public static String long_to_string(long i,int radix) { + return Long.toString(i,radix); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Number.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,14 @@ +java() +local BasicLuan = require "java:luan.modules.BasicLuan" +local MathLuan = require "java:luan.modules.MathLuan" + + +local M = {} + +M.double = BasicLuan.assert_double +M.integer = BasicLuan.assert_integer +M.long = BasicLuan.assert_long +M.long_to_string = MathLuan.long_to_string +M.type = BasicLuan.number_type + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Package.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,9 @@ +java() +local PackageLuan = require "java:luan.modules.PackageLuan" + +local M = {} + +M.loaded = PackageLuan.loaded() +M.load = PackageLuan.load + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/PackageLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,79 @@ +package luan.modules; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanJavaFunction; +import luan.LuanException; + + +public final class PackageLuan { + + public static final LuanFunction requireFn; + static { + try { + requireFn = new LuanJavaFunction(PackageLuan.class.getMethod("require",LuanState.class,String.class),null); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public static LuanTable loaded(LuanState luan) { + LuanTable tbl = (LuanTable)luan.registry().get("Package.loaded"); + if( tbl == null ) { + tbl = new LuanTable(); + luan.registry().put("Package.loaded",tbl); + } + return tbl; + } + + public static Object require(LuanState luan,String modName) throws LuanException { + Object mod = load(luan,modName); + if( mod==null ) + throw new LuanException( "module '"+modName+"' not found" ); + return mod; + } + + public static Object load(LuanState luan,String modName) throws LuanException { + LuanTable loaded = loaded(luan); + Object mod = loaded.rawGet(modName); + if( mod == null ) { + if( modName.startsWith("java:") ) { + mod = JavaLuan.load(luan,modName.substring(5)); + } else { + String src = read(luan,modName); + if( src == null ) + return null; + LuanFunction loader = Luan.load(src,modName); + mod = Luan.first( + loader.call(luan,new Object[]{modName}) + ); + if( mod == null ) { + mod = loaded.rawGet(modName); + if( mod != null ) + return mod; + throw new LuanException( "module '"+modName+"' returned nil" ); + } + } + loaded.rawPut(modName,mod); + } + return mod; + } + + static String read(LuanState luan,String uri) throws LuanException { + LuanTable t = IoLuan.uri(luan,uri,null); + if( t == null ) + return null; + LuanFunction existsFn = (LuanFunction)t.get(luan,"exists"); + boolean exists = (Boolean)Luan.first(existsFn.call(luan)); + if( !exists ) + return null; + LuanFunction reader = (LuanFunction)t.get(luan,"read_text"); + return (String)Luan.first(reader.call(luan)); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Parsers.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,15 @@ +java() +local BBCode = require "java:luan.modules.parsers.BBCode" +local Csv = require "java:luan.modules.parsers.Csv" +local Theme = require "java:luan.modules.parsers.Theme" +local Json = require "java:luan.modules.parsers.Json" + +local M = {} + +M.bbcode_to_html = BBCode.toHtml +M.bbcode_to_text = BBCode.toText +M.csv_to_list = Csv.toList +M.json_parse = Json.parse +M.theme_to_luan = Theme.toLuan + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Rpc.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,68 @@ +java() +local RpcLuan = require "java:luan.modules.RpcLuan" +local Luan = require "luan:Luan.luan" +local error = Luan.error +local set_metatable = Luan.set_metatable or error() +local try = Luan.try or error() +local Io = require "luan:Io.luan" +local Thread = require "luan:Thread.luan" +local Logging = require "luan:logging/Logging.luan" -- external dependency +local logger = Logging.logger "Rpc" + + +local M = {} + +M.port = 9101 + +M.call = RpcLuan.call -- Rpc.call(socket,fn_name,...) + +M.functions = {} + +function M.respond(socket,fns) + RpcLuan.respond( socket, fns or M.functions ) +end + +function M.remote_socket(socket_uri) + local mt = {} + function mt.__index(_,key) + return function(...) + local socket = Io.uri(socket_uri) + return M.call(socket,key,...) + end + end + local t = {} + set_metatable(t,mt) + return t +end + +function M.remote(domain) + local socket = "socket:" .. domain .. ":" .. M.port + return M.remote_socket(socket) +end + +function M.serve(port,fns) + local server = Io.socket_server(port or M.port) + while true do + try { + function() + local socket = server() + local function respond() + try { + function() + M.respond(socket,fns) + end + catch = function(e) + logger.error(e) + end + } + end + Thread.fork(respond) + end + catch = function(e) + logger.error(e) + end + } + end +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/RpcLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,310 @@ +package luan.modules; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.InputStreamReader; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.EOFException; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.IdentityHashMap; +import java.util.Collections; +import java.util.Map; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanMethod; +import luan.modules.parsers.Json; +import luan.modules.parsers.ParseException; + + +public final class RpcLuan { + private static final int NIL = 0; + private static final int STRING = 1; + private static final int BOOLEAN = 2; + private static final int NUMBER = 3; + private static final int BINARY = 4; + private static final int TABLE = 5; + private static final int IO = 6; + private static final int LONG = 7; + + @LuanMethod public static Object[] call(LuanState luan,LuanTable socketTbl,String fnName,Object... args) + throws LuanException, IOException + { + IoLuan.LuanSocket luanSocket = (IoLuan.LuanSocket)socketTbl.rawGet("java"); + Socket socket = luanSocket.socket; + InputStream in = new BufferedInputStream(socket.getInputStream()); + OutputStream out = new BufferedOutputStream(socket.getOutputStream()); + Close close = new Close(); + try { + writeString(out,fnName); + writeObjs(out,luan,args); + out.flush(); + socket.shutdownOutput(); + boolean ok = readBoolean(in); + if( ok ) { + return readObjs(in,luan,close); + } else { + String msg = readString(in); + throw new LuanException(msg); + } + } finally { + if( close.b) { + socket.close(); + } + } + } + + public static void respond(LuanState luan,LuanTable socketTbl,LuanTable fns) + throws IOException, LuanException + { + IoLuan.LuanSocket luanSocket = (IoLuan.LuanSocket)socketTbl.rawGet("java"); + Socket socket = luanSocket.socket; + InputStream in = new BufferedInputStream(socket.getInputStream()); + OutputStream out = new BufferedOutputStream(socket.getOutputStream()); + try { + Object[] rtn; + try { + String fnName = readString(in); + Object[] args = readObjs(in,luan,null); + LuanFunction fn = (LuanFunction)fns.get(luan,fnName); + if( fn == null ) + throw new LuanException( "function not found: " + fnName ); + rtn = Luan.array(fn.call(luan,args)); + } catch(LuanException e) { + writeBoolean(out,false); + writeString(out,e.getFullMessage()); + out.flush(); + return; + } + writeBoolean(out,true); + writeObjs(out,luan,rtn); + out.flush(); + } finally { + socket.close(); + } + } + + private static void writeObjs(OutputStream out,LuanState luan,Object[] a) throws IOException, LuanException { + IoLuan.LuanIn luanIn = null; + writeInt(out,a.length); + for( Object obj : a ) { + if( obj instanceof LuanTable ) { + LuanTable tbl = (LuanTable)obj; + Object java = tbl.rawGet("java"); + if( java instanceof IoLuan.LuanIn ) { + if( luanIn != null ) + throw new LuanException("can't have multiple IO params"); + luanIn = (IoLuan.LuanIn)java; + out.write(IO); + continue; + } + } + writeObj(out,luan,obj); + } + if( luanIn != null ) { + InputStream in = luanIn.inputStream(); + Utils.copyAll(in,out); + } + } + + private static Object[] readObjs(InputStream in,LuanState luan,Close close) throws IOException, LuanException { + int n = readInt(in); + Object[] rtn = new Object[n]; + for( int i=0; i<n; i++ ) { + rtn[i] = readObj(in,luan,close); + } + return rtn; + } + + private static void writeObj(OutputStream out,LuanState luan,Object obj) throws IOException, LuanException { + if( obj == null ) { + out.write(NIL); + } + else if( obj instanceof String ) { + out.write(STRING); + writeString(out,(String)obj); + } + else if( obj instanceof Boolean ) { + out.write(BOOLEAN); + writeBoolean(out,(Boolean)obj); + } + else if( obj instanceof Long ) { + out.write(LONG); + writeString(out,obj.toString()); + } + else if( obj instanceof Number ) { + out.write(NUMBER); + writeString(out,obj.toString()); + } + else if( obj instanceof byte[] ) { + byte[] a = (byte[])obj; + out.write(BINARY); + writeInt(out,a.length); + out.write(a); + } + else if( obj instanceof LuanTable ) { + out.write(TABLE); +// String s = pickle( luan, obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) ); + String s = Json.toString(obj); + writeString(out,s); + } + else + throw new LuanException( "invalid type: " + obj.getClass() ); + } + + private static Object readObj(InputStream in,LuanState luan,Close close) throws IOException, LuanException { + int type = in.read(); + switch(type) { + case NIL: + return null; + case STRING: + return readString(in); + case BOOLEAN: + return readBoolean(in); + case LONG: + return Long.valueOf(readString(in)); + case NUMBER: + return Double.valueOf(readString(in)); + case BINARY: + return readBinary(in,readInt(in)); + case TABLE: + String s = readString(in); +/* + LuanFunction fn = Luan.load("return "+s,"rpc-reader"); + return fn.call(luan); +*/ + try { + return Json.parse(s); + } catch(ParseException e) { + throw new LuanException(e); + } + case IO: + return new LuanInputStream(in,close).table(); + default: + throw new LuanException( "invalid type: " + type ); + } + } + + private static Boolean readBoolean(InputStream in) throws IOException { + return Boolean.valueOf(readString(in)); + } + + private static String readString(InputStream in) throws IOException { + int len = readInt(in); + byte[] a = readBinary(in,len); + return new String(a,StandardCharsets.UTF_8); + } + + private static int readInt(InputStream in) throws IOException { + int ch1 = in.read(); + int ch2 = in.read(); + int ch3 = in.read(); + int ch4 = in.read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) + throw new EOFException(); + return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); + } + + private static byte[] readBinary(InputStream in,int size) throws IOException { + byte[] a = new byte[size]; + int i = 0; + while( i < size ) { + int n = in.read(a,i,size-i); + if( n == -1 ) + throw new EOFException(); + i += n; + } + return a; + } + + private static void writeBoolean(OutputStream out,Boolean b) throws IOException { + writeString(out,b.toString()); + } + + private static void writeString(OutputStream out,String s) throws IOException { + byte[] a = s.getBytes(StandardCharsets.UTF_8); + writeInt(out,a.length); + out.write(a); + } + + private static void writeInt(OutputStream out,int v) throws IOException { + out.write((v >>> 24) & 0xFF); + out.write((v >>> 16) & 0xFF); + out.write((v >>> 8) & 0xFF); + out.write((v >>> 0) & 0xFF); + } + +/* + private static String pickle(LuanState luan,Object obj,Set<LuanTable> set) throws LuanException { + if( obj == null ) + return "nil"; + if( obj instanceof Boolean ) + return obj.toString(); + if( obj instanceof Number ) + return Luan.toString((Number)obj); + if( obj instanceof String ) + return "\"" + Luan.stringEncode((String)obj) + "\""; + if( obj instanceof LuanTable ) { + LuanTable tbl = (LuanTable)obj; + if( !set.add(tbl) ) { + throw new LuanException( "circular reference in table" ); + } + StringBuilder sb = new StringBuilder(); + sb.append( "{" ); + for( Map.Entry<Object,Object> entry : tbl.iterable(luan) ) { + sb.append( "[" ); + sb.append( pickle(luan,entry.getKey(),set) ); + sb.append( "]=" ); + sb.append( pickle(luan,entry.getValue(),set) ); + sb.append( ", " ); + } + sb.append( "}" ); + return sb.toString(); + } + throw new LuanException( "invalid type: " + obj.getClass() ); + } +*/ + + private static class Close { + boolean b = true; + } + + private static class LuanInputStream extends IoLuan.LuanIn { + private final InputStream in; + private final boolean close; + + public LuanInputStream(InputStream in,Close close) { + this.in = in; + this.close = close!=null && close.b; + if(this.close) close.b = false; + } + + @Override public InputStream inputStream() { + return new FilterInputStream(in) { + @Override public void close() throws IOException { + if(close) super.close(); + } + }; + } + + @Override public String to_string() { + return "<input_stream>"; + } + + @Override public String to_uri_string() { + throw new UnsupportedOperationException(); + } + + @Override public boolean exists() { + return true; + } + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/String.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,28 @@ +java() +local StringLuan = require "java:luan.modules.StringLuan" +local Pattern = require "java:java.util.regex.Pattern" + +local M = {} + +M.char = StringLuan.char_ +M.concat = StringLuan.concat +M.encode = StringLuan.encode +M.find = StringLuan.find +M.format = StringLuan.format +M.gmatch = StringLuan.gmatch +M.gsub = StringLuan.gsub +M.literal = Pattern.quote +M.lower = StringLuan.lower +M.match = StringLuan.match +M.matches = StringLuan.matches +M.rep = StringLuan.rep +M.reverse = StringLuan.reverse +M.split = StringLuan.split +M.sub = StringLuan.sub +M.to_binary = StringLuan.to_binary +M.to_number = StringLuan.to_number +M.trim = StringLuan.trim +M.unicode = StringLuan.unicode +M.upper = StringLuan.upper + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/StringLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,247 @@ +package luan.modules; + +import java.util.Arrays; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanMethod; + + +public final class StringLuan { + + static int start(String s,int i) { + int len = s.length(); + return i==0 ? 0 : i > 0 ? Math.min(i-1,len) : Math.max(len+i,0); + } + + static int start(String s,Integer i,int dflt) { + return i==null ? dflt : start(s,i); + } + + static int end(String s,int i) { + int len = s.length(); + return i==0 ? 0 : i > 0 ? Math.min(i,len) : Math.max(len+i+1,0); + } + + static int end(String s,Integer i,int dflt) { + return i==null ? dflt : end(s,i); + } + + @LuanMethod public static Integer[] unicode(String s,Integer i,Integer j) throws LuanException { + Utils.checkNotNull(s); + int start = start(s,i,1); + int end = end(s,j,start+1); + Integer[] chars = new Integer[end-start]; + for( int k=0; k<chars.length; k++ ) { + chars[k] = (int)s.charAt(start+k); + } + return chars; + } + + public static String char_(int... chars) { + char[] a = new char[chars.length]; + for( int i=0; i<chars.length; i++ ) { + a[i] = (char)chars[i]; + } + return new String(a); + } + + @LuanMethod public static byte[] to_binary(String s) { + return s.getBytes(); + } + + public static String lower(String s) throws LuanException { + Utils.checkNotNull(s); + return s.toLowerCase(); + } + + public static String upper(String s) throws LuanException { + Utils.checkNotNull(s); + return s.toUpperCase(); + } + + public static String trim(String s) throws LuanException { + Utils.checkNotNull(s); + return s.trim(); + } + + public static String reverse(String s) throws LuanException { + Utils.checkNotNull(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) throws LuanException { + Utils.checkNotNull(s); + int start = start(s,i); + int end = end(s,j,s.length()); + return s.substring(start,end); + } + + @LuanMethod public static Object[] 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 Integer[]{i+1,i+pattern.length()}; + } + Matcher m = Pattern.compile(pattern).matcher(s); + if( !m.find(start) ) + return null; + int n = m.groupCount(); + Object[] rtn = new Object[2+n]; + rtn[0] = m.start() + 1; + rtn[1] = m.end(); + for( int i=0; i<n; i++ ) { + rtn[2+i] = m.group(i+1); + } + return rtn; + } + + @LuanMethod 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; + 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+1); + } + return rtn; + } + + public static LuanFunction gmatch(String s,String pattern) throws LuanException { + Utils.checkNotNull(s); + final Matcher m = Pattern.compile(pattern).matcher(s); + return new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) { + if( !m.find() ) + return null; + final int n = m.groupCount(); + if( n == 0 ) + return m.group(); + String[] rtn = new String[n]; + for( int i=0; i<n; i++ ) { + rtn[i] = m.group(i+1); + } + return rtn; + } + }; + } + + @LuanMethod public static Object[] gsub(LuanState luan,String s,String pattern,Object repl,Integer n) throws LuanException { + Utils.checkNotNull(s); + 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(), i }; + } + if( repl instanceof LuanTable ) { + LuanTable t = (LuanTable)repl; + int i = 0; + StringBuffer sb = new StringBuffer(); + while( i<max && m.find() ) { + String match = m.groupCount()==0 ? m.group() : m.group(1); + Object val = t.get(luan,match); + if( val != null ) { + String replacement = luan.toString(val); + m.appendReplacement(sb,replacement); + } + i++; + } + m.appendTail(sb); + return new Object[]{ sb.toString(), i }; + } + if( repl instanceof LuanFunction ) { + LuanFunction fn = (LuanFunction)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 String[]{m.group()}; + } else { + args = new String[count]; + for( int j=0; j<count; j++ ) { + args[j] = m.group(j+1); + } + } + Object val = Luan.first( fn.call(luan,args) ); + if( val != null ) { + String replacement = luan.toString(val); + m.appendReplacement(sb,replacement); + } + i++; + } + m.appendTail(sb); + return new Object[]{ sb.toString(), i }; + } + throw new LuanException( "bad argument #3 to 'gsub' (string/function/table expected)" ); + } + + // note - String.format() is too stupid to convert between ints and floats. + public static String format(String format,Object... args) { + return String.format(format,args); + } + + public static String concat(LuanState luan,Object... args) throws LuanException { + StringBuilder sb = new StringBuilder(); + for( Object arg : args ) { + sb.append( luan.toString(arg) ); + } + return sb.toString(); + } + + public static String encode(String s) { + return Luan.stringEncode(s); + } + + public static Number to_number(String s,Integer base) throws LuanException { + Utils.checkNotNull(s); + try { + if( base == null ) { + return Double.valueOf(s); + } else { + return Long.valueOf(s,base); + } + } catch(NumberFormatException e) {} + return null; + } + + public static boolean matches(String s,String pattern) throws LuanException { + Utils.checkNotNull(s); + return Pattern.compile(pattern).matcher(s).find(); + } + + public static LuanTable split(String s,String pattern) throws LuanException { + Utils.checkNotNull(s); + return new LuanTable(Arrays.asList(s.split(pattern))); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Table.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,24 @@ +java() +local TableLuan = require "java:luan.modules.TableLuan" + +local M = {} + +M.clear = TableLuan.clear +M.concat = TableLuan.concat +M.copy = TableLuan.copy +M.insert = TableLuan.insert +M.new_property_table = TableLuan.new_property_table +M.pack = TableLuan.pack +M.remove = TableLuan.remove +M.sort = TableLuan.sort +M.unpack = TableLuan.unpack + + +local Luan = require "luan:Luan.luan" +local pairs = Luan.pairs + +function M.is_empty(t) + return pairs(t)() == nil +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/TableLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,120 @@ +package luan.modules; + +import java.util.Comparator; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanRuntimeException; +import luan.LuanMethod; +import luan.LuanPropertyMeta; + + +public final class TableLuan { + + public static String concat(LuanState luan,LuanTable list,String sep,Integer i,Integer j) throws LuanException { + int first = i==null ? 1 : i; + int last = j==null ? list.length(luan) : j; + StringBuilder buf = new StringBuilder(); + for( int k=first; k<=last; k++ ) { + Object val = list.get(luan,k); + if( val==null ) + break; + if( sep!=null && k > first ) + buf.append(sep); + String s = luan.toString(val); + buf.append(s); + } + return buf.toString(); + } + + public static void insert(LuanTable list,int pos,Object value) throws LuanException { + Utils.checkNotNull(list); + if( list.getMetatable() != null ) + throw new LuanException("can't insert into a table with a metatable"); + list.rawInsert(pos,value); + } + + public static Object remove(LuanTable list,int pos) throws LuanException { + if( list.getMetatable() != null ) + throw new LuanException("can't remove from a table with a metatable"); + return list.rawRemove(pos); + } + + private static interface LessThan { + public boolean isLessThan(Object o1,Object o2); + } + + public static void sort(final LuanState luan,LuanTable list,final LuanFunction comp) throws LuanException { + if( list.getMetatable() != null ) + throw new LuanException("can't sort a table with a metatable"); + final LessThan lt; + if( comp==null ) { + lt = new LessThan() { + public boolean isLessThan(Object o1,Object o2) { + try { + return Luan.isLessThan(luan,o1,o2); + } catch(LuanException e) { + throw new LuanRuntimeException(e); + } + } + }; + } else { + lt = new LessThan() { + public boolean isLessThan(Object o1,Object o2) { + try { + return Luan.checkBoolean(Luan.first(comp.call(luan,new Object[]{o1,o2}))); + } catch(LuanException e) { + throw new LuanRuntimeException(e); + } + } + }; + } + try { + list.rawSort( new Comparator<Object>() { + public int compare(Object o1,Object o2) { + return lt.isLessThan(o1,o2) ? -1 : lt.isLessThan(o2,o1) ? 1 : 0; + } + } ); + } catch(LuanRuntimeException e) { + throw (LuanException)e.getCause(); + } + } + + public static LuanTable pack(Object... args) { + LuanTable tbl = new LuanTable(Arrays.asList(args)); + tbl.rawPut( "n", args.length ); + return tbl; + } + + @LuanMethod public static Object[] unpack(LuanState luan,LuanTable tbl,Integer iFrom,Integer iTo) throws LuanException { + int from = iFrom!=null ? iFrom : 1; + int to = iTo!=null ? iTo : tbl.length(luan); + List<Object> list = new ArrayList<Object>(); + for( int i=from; i<=to; i++ ) { + list.add( tbl.get(luan,i) ); + } + return list.toArray(); + } + + public static LuanTable copy(LuanTable list,Integer from,Integer to) { + if( from == null ) + return new LuanTable(list); + if( to == null ) + to = list.rawLength(); + return list.rawSubList(from,to); + } + + public static LuanTable new_property_table() { + return LuanPropertyMeta.INSTANCE.newTable(); + } + + public static void clear(LuanTable tbl) { + tbl.rawClear(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Thread.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,10 @@ +java() +local ThreadLuan = require "java:luan.modules.ThreadLuan" + +local M = {} + +M.fork = ThreadLuan.fork +M.schedule = ThreadLuan.schedule +M.synchronized = ThreadLuan.synchronized_ + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/ThreadLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,71 @@ +package luan.modules; + +import java.io.Closeable; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import luan.Luan; +import luan.LuanState; +import luan.LuanFunction; +import luan.LuanTable; +import luan.LuanException; +import luan.DeepCloner; + + +public final class ThreadLuan { + private static final Executor exec = Executors.newCachedThreadPool(); + private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + public static void fork(LuanState luan,LuanFunction fn,Object... args) { + DeepCloner cloner = new DeepCloner(); + final LuanState newLuan = (LuanState)cloner.deepClone(luan); + final LuanFunction newFn = (LuanFunction)cloner.get(fn); + final Object[] newArgs = cloner.deepClone(args); + exec.execute(new Runnable(){public void run() { + try { + newFn.call(newLuan,newArgs); + } catch(LuanException e) { + e.printStackTrace(); + } + }}); + } + + public static LuanFunction synchronized_(final LuanState luan,final LuanFunction fn) throws LuanException { + Utils.checkNotNull(fn); + return new LuanFunction() { + @Override public Object call(LuanState ingored,Object[] args) throws LuanException { + synchronized(luan) { + return fn.call(luan,args); + } + } + }; + } + + public static void schedule(LuanState luan,long delay,boolean repeat,LuanFunction fn,Object... args) { + DeepCloner cloner = new DeepCloner(); + final LuanState newLuan = (LuanState)cloner.deepClone(luan); + final LuanFunction newFn = (LuanFunction)cloner.get(fn); + final Object[] newArgs = cloner.deepClone(args); + Runnable r = new Runnable(){public void run() { + try { + newFn.call(newLuan,newArgs); + } catch(LuanException e) { + e.printStackTrace(); + } + }}; + final ScheduledFuture sf; + if( repeat ) { + sf = scheduler.scheduleWithFixedDelay(r,delay,delay,TimeUnit.MILLISECONDS); + } else { + sf = scheduler.schedule(r,delay,TimeUnit.MILLISECONDS); + } + final Closeable c = new Closeable(){public void close(){ + boolean b = sf.cancel(false); + }}; + luan.registry().put(c,c); // prevent gc + luan.onClose(c); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Time.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,71 @@ +-- incomplete, will add as needed + +java() +local Luan = require "luan:Luan.luan" +local error = Luan.error +local ipairs = Luan.ipairs or error() +local Table = require "luan:Table.luan" +local System = require "java:java.lang.System" +local Calendar = require "java:java.util.Calendar" +local Date = require "java:java.util.Date" +local TimeZone = require "java:java.util.TimeZone" +local SimpleDateFormat = require "java:java.text.SimpleDateFormat" + +local M = {} + +function M.now() + return System.currentTimeMillis() +end + +-- add more as needed +local fields = { + year = Calendar.YEAR; + month = Calendar.MONTH; + day_of_month = Calendar.DAY_OF_MONTH; +} + +function M.get( time, ... ) + local cal = Calendar.getInstance() + cal.setTimeInMillis(time) + local rtn = {} + for i, v in ipairs{...} do + local fld = fields[v.lower()] + fld or error("invalid field: "+v) + local n = cal.get(fld) + if fld == "month" then + n = n + 1 + end + rtn[i] = n + end + return Table.unpack(rtn) +end + +function M.format(time,pattern) + pattern = pattern or "yyyy-MM-dd HH:mm:ss" + return SimpleDateFormat.new(pattern).format(Date.new(time)) +end + +function M.on( year, month, day, hour, minute, second, millis ) + month = month - 1 + local cal = Calendar.getInstance() + cal.setLenient(false) + cal.set( year, month, day, hour or 0, minute or 0, second or 0 ) + cal.set( Calendar.MILLISECOND, millis or 0 ) + return cal.getTimeInMillis() +end + +function M.period( t ) + local cal = Calendar.getInstance() + cal.setTimeZone(TimeZone.getTimeZone("GMT")) + local days = t.days or 0 + days = days + 1 + cal.set( 1970, 0, days, t.hours or 0, t.minutes or 0, t.seconds or 0 ) + cal.set( Calendar.MILLISECOND, t.millis or 0 ) + return cal.getTimeInMillis() +end + +function M.parse( pattern, source ) + return SimpleDateFormat.new(pattern).parse(source).getTime() +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Utils.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,200 @@ +package luan.modules; + +import java.io.Reader; +import java.io.IOException; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.File; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.MalformedURLException; +import luan.LuanException; +import luan.LuanTable; +import luan.LuanFunction; + + +public final class Utils { + private Utils() {} // never + + static final int bufSize = 8192; + + private static void checkNotNull(Object v,String expected,int pos) throws LuanException { + if( v == null ) + throw new LuanException("bad argument #"+pos+" ("+expected+" expected, got nil)"); + } + + public static void checkNotNull(String s,int pos) throws LuanException { + checkNotNull(s,"string",pos); + } + + public static void checkNotNull(String s) throws LuanException { + checkNotNull(s,1); + } + + public static void checkNotNull(byte[] b,int pos) throws LuanException { + checkNotNull(b,"binary",pos); + } + + public static void checkNotNull(byte[] b) throws LuanException { + checkNotNull(b,1); + } + + public static void checkNotNull(LuanTable t,int pos) throws LuanException { + checkNotNull(t,"table",pos); + } + + public static void checkNotNull(LuanTable t) throws LuanException { + checkNotNull(t,1); + } + + public static void checkNotNull(Number n,int pos) throws LuanException { + checkNotNull(n,"number",pos); + } + + public static void checkNotNull(Number n) throws LuanException { + checkNotNull(n,1); + } + + public static void checkNotNull(LuanFunction fn,int pos) throws LuanException { + checkNotNull(fn,"function",pos); + } + + public static void checkNotNull(LuanFunction fn) throws LuanException { + checkNotNull(fn,1); + } + + public static String readAll(Reader in) + throws IOException + { + char[] a = new char[bufSize]; + StringBuilder buf = new StringBuilder(); + int n; + while( (n=in.read(a)) != -1 ) { + buf.append(a,0,n); + } + return buf.toString(); + } + + public static void copyAll(InputStream in,OutputStream out) + throws IOException + { + byte[] a = new byte[bufSize]; + int n; + while( (n=in.read(a)) != -1 ) { + out.write(a,0,n); + } + } + + public static byte[] readAll(InputStream in) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copyAll(in,out); + return out.toByteArray(); + } +/* + public static boolean exists(File file) { + try { + return file.exists() && file.getName().equals(file.getCanonicalFile().getName()); + } catch(IOException e) { + throw new RuntimeException(e); + } + } +*/ +/* + private static File toFile(String path) { + if( path.contains("//") ) + return null; + File file = new File(path); + return file.exists() ? file : null; + } + + private static URL toUrl(String path) { + if( path.indexOf(':') == -1 ) + return null; + if( path.startsWith("classpath:") ) { + path = path.substring(10); + if( path.contains("//") ) + return null; + URL url; + if( !path.contains("#") ) { + url = ClassLoader.getSystemResource(path); + } else { + String[] a = path.split("#"); + url = ClassLoader.getSystemResource(a[0]); + if( url==null ) { + for( int i=1; i<a.length; i++ ) { + url = ClassLoader.getSystemResource(a[0]+"/"+a[i]); + if( url != null ) { + try { + url = new URL(url,"."); + } catch(MalformedURLException e) { + throw new RuntimeException(e); + } + break; + } + } + } + } + return url==null ? null : url; + } + try { + return new URL(path); + } catch(MalformedURLException e) {} + return null; + } + + static boolean exists(String path) { + return toFile(path)!=null || toUrl(path)!=null; + } +*/ + + + +/* replace by uri"os:..." + + // process + + public static class ProcessException extends IOException { + private ProcessException(String msg) { + super(msg); + } + } + + public static void checkProcess(Process proc) + throws IOException + { + try { + proc.waitFor(); + } catch(InterruptedException e) { + throw new RuntimeException(e); + } + int exitVal = proc.exitValue(); + if( exitVal != 0 ) { + Reader err = new InputStreamReader(proc.getErrorStream()); + String error = readAll(err); + err.close(); + throw new ProcessException(error); + } + } + + public static String getOutput(Process proc) + throws IOException + { + Reader in = new InputStreamReader(proc.getInputStream()); + String s = readAll(in); + in.close(); + return s; + } + + public static String execProcess(String... cmd) + throws IOException + { + Process proc = Runtime.getRuntime().exec(cmd); + String s = getOutput(proc); + checkProcess(proc); + return s; + } +*/ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/Which_mod.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,53 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local ipairs = Luan.ipairs or error() +local pairs = Luan.pairs or error() +local type = Luan.type or error() +local String = require "luan:String.luan" +local literal = String.literal or error() +local matches = String.matches or error() +local Io = require "luan:Io.luan" +local print = Io.print or error() + + +local M = {} + +M.uris = { + "luan:Luan" + "luan:Binary" + "luan:Html" + "luan:Io" + "luan:Math" + "luan:Package" + "luan:String" + "luan:Table" + "luan:Thread" + "luan:Time" + "luan:host/Hosting" + "luan:http/Http" + "luan:http/Server" + "luan:lucene/Lucene" + "luan:lucene/Versioning" + "luan:mail/Mail" + "luan:logging/Logging" + "luan:stripe/Stripe" +} + +function M.which(name) + local ptn = "[:./]"..literal(name).."$" + for _, uri in ipairs(M.uris) do + local mod = require(uri) + if matches(uri,ptn) then + print(uri) + end + if type(mod) == "table" then + for key in pairs(mod) do + if key == name then + print(uri.." "..key) + end + end + end + end +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/host/Hosting.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,97 @@ +-- Hosting + +local Luan = require "luan:Luan.luan" +local error = Luan.error +local ipairs = Luan.ipairs or error() +local pairs = Luan.pairs or error() +local set_metatable = Luan.set_metatable or error() +local Io = require "luan:Io.luan" +local print = Io.print or error() +local Rpc = require "luan:Rpc.luan" +local String = require "luan:String.luan" +local matches = String.matches or error() + + +local M = {} + + +function M.push(domain,password,dir) + local my_dir = Io.uri("file:"..dir) + my_dir.exists() or error("directory '"..dir.."' not found") + my_dir.is_directory() or error("'"..dir.."' is not a directory") + local host = Rpc.remote(domain) + local tree = host.get(domain,password) + if tree == nil then + print("creating "..domain) + tree = host.create(domain,password) + end + + local function process(there_parent,there,here) + if here.is_file() then + if there == nil or there.last_modified < here.last_modified() then + print("copying "..here.to_string()) + host.copy_file(domain,password,there_parent.path,here.name(),here.read_binary()) + end + elseif here.is_directory() then + if here.name() == "local" then + return + end + if there == nil then + there = host.mkdir(domain,password,there_parent.path,here.name()) + end + for _, here_child in ipairs(here.children()) do + local name = here_child.name() + if not matches(name,[[^\.]]) then + process(there,there.children[name],here_child) + there.children[name] = nil + end + end + for _, there_child in pairs(there.children) do + if host.delete_unused(domain,password,there_child.path)==true then -- remove ==true later + print("deleted "..there_child.name) + end + end + else + error "not file or dir" + end + end + + process( nil, tree, my_dir ) + + host.update_handler(domain,password) +end + +function M.delete(domain,password) + local host = Rpc.remote(domain) + host.delete(domain,password) +end + +function M.exists(domain) + local host = Rpc.remote(domain) + return host.exists(domain) +end + +function M.change_domain(old_domain,new_domain,password) + local host = Rpc.remote(new_domain) + return host.change_domain(old_domain,new_domain,password) +end + +function M.change_password(domain,old_password,new_password) + local host = Rpc.remote(domain) + return host.change_password(domain,old_password,new_password) +end + +function M.caller(domain) + local host = Rpc.remote(domain) + local mt = {} + function mt.__index(_,key) + return function(...) + return host.call(domain,key,...) + end + end + local t = {} + set_metatable(t,mt) + return t +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/host/backup.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,18 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local Io = require "luan:Io.luan" +local print = Io.print or error() +local uri = Io.uri or error() +local Hosting = require "luan:host/Hosting.luan" + +if #{...} ~= 2 then + Io.stderr.write "usage: luan luan:host/backup.luan domain password\n" + return +end + +local domain, password = ... + +local zip = Hosting.caller(domain).lucene_backup(password) +uri("file:backup.zip").write(zip) + +print("backed up lucene from "..domain.." to backup.zip")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/host/delete.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,12 @@ +local Io = require "luan:Io.luan" +local print = Io.print +local Hosting = require "luan:host/Hosting.luan" + +if #{...} ~= 2 then + Io.stderr.write "usage: luan luan:host/delete.luan domain password\n" + return +end + +Hosting.delete(...) + +print("deleted "..(...))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/host/push.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,12 @@ +local Io = require "luan:Io.luan" +local print = Io.print +local Hosting = require "luan:host/Hosting.luan" + +if #{...} ~= 3 then + Io.stderr.write "usage: luan luan:host/push.luan domain password dir\n" + return +end + +Hosting.push(...) + +print("done with "..(...))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/host/restore.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,19 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local Io = require "luan:Io.luan" +local print = Io.print or error() +local uri = Io.uri or error() +local Hosting = require "luan:host/Hosting.luan" + +if #{...} ~= 2 then + Io.stderr.write "usage: luan luan:host/restore.luan domain password\n" + return +end + +local domain, password = ... + +local zip_file = uri("file:backup.zip") +zip_file.exists() or error "backup.zip not found" +Hosting.caller(domain).lucene_restore(password,zip_file) + +print("restored lucene from backup.zip to "..domain)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/AuthenticationHandler.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,53 @@ +package luan.modules.http; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.B64Code; + + +public class AuthenticationHandler extends AbstractHandler { + private final String path; + private String password = "password"; + + public AuthenticationHandler(String path) { + this.path = path; + } + + public void setPassword(String password) { + this.password = password; + } + + public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) + throws IOException + { + if( !target.startsWith(path) ) + return; + String pwd = getPassword(request); + if( password.equals(pwd) ) + return; + response.setHeader("WWW-Authenticate","Basic realm=\""+path+"\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + baseRequest.setHandled(true); + } + + private static String getPassword(HttpServletRequest request) { + String auth = request.getHeader("Authorization"); + if( auth==null ) + return null; + String[] a = auth.split(" +"); + if( a.length != 2 ) + throw new RuntimeException("auth = "+auth); + if( !a[0].equals("Basic") ) + throw new RuntimeException("auth = "+auth); + auth = new String(B64Code.decode(a[1])); + a = auth.split(":"); + if( a.length != 2 ) + throw new RuntimeException("auth = "+auth); + return a[1]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/Dump_mod.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,50 @@ +local Luan = require "luan:Luan.luan" +local pairs = Luan.pairs +local ipairs = Luan.ipairs +local Io = require "luan:Io.luan" +local Http = require "luan:http/Http.luan" +java() +local HttpServicer = require "java:luan.modules.http.HttpServicer" + +local M = {} + +local to_http_header_name = HttpServicer.toHttpHeaderName +M.to_http_header_name = to_http_header_name + +function M.respond() + Http.response.header.content_type = "text/plain" + Io.stdout = Http.response.text_writer() + + local method = Http.request.method + local path = Http.request.path + local query = Http.request.query_string() + if method ~= "POST" and query ~= nil then + path = path.."?"..query + end +%> +<%=method%> <%=path%> <%=Http.request.protocol%> +<% + M.dump_headers(Http.request.headers) +%> + +<% + if method == "POST" and query ~= nil then +%> +<%=query%> +<% + end +end + + +function M.dump_headers(headers) + for name, values in pairs(headers) do + local header_name = to_http_header_name(name) + for _, value in ipairs(values) do +%> +<%=header_name%>: <%=value%> +<% + end + end +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/Http.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,170 @@ +java() +local Luan = require "luan:Luan.luan" +local error = Luan.error +local ipairs = Luan.ipairs or error() +local pairs = Luan.pairs or error() +local set_metatable = Luan.set_metatable or error() +local Io = require "luan:Io.luan" +local Html = require "luan:Html.luan" +local url_encode = Html.url_encode or error() +local Table = require "luan:Table.luan" +local clear = Table.clear or error() +local Package = require "luan:Package.luan" +local String = require "luan:String.luan" +local matches = String.matches or error() +local HttpServicer = require "java:luan.modules.http.HttpServicer" +local IoLuan = require "java:luan.modules.IoLuan" + +local M = {} + +local singular_metatable = {} + +function singular_metatable.__index(table,key) + local list = table.__plural[key] + return list and list[1] +end + +function singular_metatable.__new_index(table,key,value) + table.__plural[key] = value and {value} +end + +function singular_metatable.__pairs(table) + local iter = pairs(table.__plural) + return function() + local key, value = iter() + return key, value and value[1] + end +end + +local function sent_error() + error "headers are not accessible after you start writing content" +end + +local sent_error_metatable = { __index=sent_error, __new_index=sent_error } + +function M.sent_headers(headers) + clear(headers) + set_metatable(headers,sent_error_metatable) +end + + +local function new_common(this) + this = this or {} + this.headers = {} + this.header = {__plural=this.headers} + set_metatable(this.header,singular_metatable) + return this +end + + +function M.new_request(this) + this = new_common(this) + this.method = "GET" -- default + -- this.path + -- this.protocol + this.scheme = "http" -- default + this.port = 80 -- default + this.parameters = {} + this.parameter = {__plural=this.parameters} + set_metatable(this.parameter,singular_metatable) + this.cookie = {} + + function this.query_string() + local string_uri = Io.uri "string:" + local out = string_uri.text_writer() + local and_char = "" + for name, values in pairs(this.parameters) do + for _, value in ipairs(values) do + out.write( and_char, url_encode(name), "=", url_encode(value) ) + and_char = "&" + end + end + out.close() + local s = string_uri.read_text() + return s ~= "" and s or nil + end + + function this.url() + local url = this.scheme.."://"..this.header.host..this.path + if this.method ~= "POST" then + local query = this.query_string() + if query ~= nil then + url = url.."?"..query + end + end + return url + end + + return this +end + +local STATUS = { + OK = 200; + -- add more as needed +} +M.STATUS = STATUS + +function M.new_response(this) + this = new_common(this) + this.status = STATUS.OK + if this.java ~= nil then + this.send_redirect = this.java.sendRedirect + this.send_error = this.java.sendError + + function this.set_cookie(name,value,is_persistent,domain) + HttpServicer.setCookie(M.request.java,this.java,name,value,is_persistent,domain) + end + + function this.remove_cookie(name,domain) + HttpServicer.removeCookie(M.request.java,this.java,name,domain) + end + + function this.set() + HttpServicer.setResponse(this,this.java) + M.sent_headers(this.headers) + end + + function this.text_writer() + this.set() + return IoLuan.textWriter(this.java.getWriter()) + end + + function this.binary_writer() + this.set() + return IoLuan.binaryWriter(this.java.getOutputStream()) + end + + function this.reset() + this.java.reset() + set_metatable(this.headers,nil) + end + end + return this +end + +-- request = new_request{} -- filled in by HttpServicer +-- response = new_response{} -- filled in by HttpServicer + + +M.per_session_pages = {} + +function M.per_session(page) + M.per_session_pages[page] = true +end + +function M.clear_session() + M.request.java.getSession().removeAttribute("luan") +end + + +function M.uncache_site() + for k in pairs(Table.copy(Package.loaded)) do + if matches(k,"^site:") then + Package.loaded[k] = nil + end + end +end + +M.run_later = HttpServicer.run_later + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/HttpServicer.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,302 @@ +package luan.modules.http; + +import java.io.InputStream; +import java.io.BufferedInputStream; +import java.io.PrintWriter; +import java.io.IOException; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Enumeration; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.Part; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.eclipse.jetty.util.MultiPartInputStream; +import luan.Luan; +import luan.LuanState; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanTable; +import luan.LuanMeta; +import luan.LuanPropertyMeta; +import luan.DeepCloner; +import luan.modules.PackageLuan; +import luan.modules.IoLuan; +import luan.modules.TableLuan; +import luan.modules.Utils; +import luan.modules.url.LuanUrl; + + +public final class HttpServicer { + private static final Logger logger = LoggerFactory.getLogger(HttpServicer.class); + + public static boolean service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName) + throws LuanException + { + LuanFunction fn; + synchronized(luan) { + LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan"); + LuanTable per_session_pages = (LuanTable)module.rawGet("per_session_pages"); + Object mod = PackageLuan.load(luan,modName); + if( mod==null ) + return false; + if( !(mod instanceof LuanFunction) ) + throw new LuanException( "module '"+modName+"' must return a function" ); + if( Boolean.TRUE.equals(per_session_pages.rawGet(mod)) ) { + HttpSession session = request.getSession(); + LuanState sessionLuan = (LuanState)session.getAttribute("luan"); + if( sessionLuan!=null ) { + luan = sessionLuan; + } else { + DeepCloner cloner = new DeepCloner(); + luan = (LuanState)cloner.deepClone(luan); + session.setAttribute("luan",luan); + } + fn = (LuanFunction)PackageLuan.require(luan,modName); + } else { + DeepCloner cloner = new DeepCloner(); + luan = (LuanState)cloner.deepClone(luan); + fn = (LuanFunction)cloner.get(mod); + } + } + + LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan"); + + // request + LuanFunction newRequestFn = (LuanFunction)module.rawGet("new_request"); + LuanTable requestTbl = (LuanTable)newRequestFn.call(luan); + module.rawPut("request",requestTbl); + requestTbl.rawPut("java",request); + requestTbl.rawPut("method",request.getMethod()); + requestTbl.rawPut("path",request.getRequestURI()); + requestTbl.rawPut("protocol",request.getProtocol()); + requestTbl.rawPut("scheme",request.getScheme()); + requestTbl.rawPut("port",request.getServerPort()); + + LuanTable headersTbl = (LuanTable)requestTbl.rawGet("headers"); + for( Enumeration<String> enKeys = request.getHeaderNames(); enKeys.hasMoreElements(); ) { + String key = enKeys.nextElement(); + LuanTable values = new LuanTable(); + for( Enumeration<String> en = request.getHeaders(key); en.hasMoreElements(); ) { + values.rawPut(values.rawLength()+1,en.nextElement()); + } + key = toLuanHeaderName(key); + headersTbl.rawPut(key,values); + } + + LuanTable parametersTbl = (LuanTable)requestTbl.rawGet("parameters"); + String contentType = request.getContentType(); + if( contentType==null || !contentType.startsWith("multipart/form-data") ) { + for( Map.Entry<String,String[]> entry : request.getParameterMap().entrySet() ) { + parametersTbl.rawPut(entry.getKey(),new LuanTable(Arrays.asList(entry.getValue()))); + } + } else { // multipart + try { + InputStream in = new BufferedInputStream(request.getInputStream()); + final MultiPartInputStream mpis = new MultiPartInputStream(in,contentType,null,null); + mpis.setDeleteOnExit(true); + for( Part p : mpis.getParts() ) { + final MultiPartInputStream.MultiPart part = (MultiPartInputStream.MultiPart)p; + String name = part.getName(); +/* +System.out.println("name = "+name); +System.out.println("getContentType = "+part.getContentType()); +System.out.println("getHeaderNames = "+part.getHeaderNames()); +System.out.println("content-disposition = "+part.getHeader("content-disposition")); +System.out.println(); +*/ + Object value; + String filename = part.getContentDispositionFilename(); + if( filename == null ) { + value = new String(part.getBytes()); + } else { + LuanTable partTbl = LuanPropertyMeta.INSTANCE.newTable(); + partTbl.rawPut("filename",filename); + partTbl.rawPut("content_type",part.getContentType()); + LuanPropertyMeta.INSTANCE.getters(partTbl).rawPut( "content", new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + InputStream in = part.getInputStream(); + byte[] content = Utils.readAll(in); + in.close(); + return content; + } catch(IOException e) { + throw new RuntimeException(e); + } + } + } ); + value = partTbl; + } + LuanTable list = (LuanTable)parametersTbl.rawGet(name); + if( list == null ) { + list = new LuanTable(); + parametersTbl.rawPut(name,list); + } + list.rawPut(parametersTbl.rawLength()+1,value); + } + } catch(IOException e) { + throw new RuntimeException(e); + } catch(ServletException e) { + throw new RuntimeException(e); + } + } + + LuanTable cookieTbl = (LuanTable)requestTbl.rawGet("cookie"); + for( Cookie cookie : request.getCookies() ) { + cookieTbl.rawPut( cookie.getName(), unescape(cookie.getValue()) ); + } + + + // response + LuanTable responseTbl = new LuanTable(); + responseTbl.rawPut("java",response); + LuanFunction newResponseFn = (LuanFunction)module.rawGet("new_response"); + newResponseFn.call( luan, new Object[]{responseTbl} ); + module.rawPut("response",responseTbl); + + fn.call(luan); + handle_run_later(luan); + return true; + } + + public static void setResponse(LuanTable responseTbl,HttpServletResponse response) throws LuanException { + int status = Luan.asInteger(responseTbl.rawGet("status")); + response.setStatus(status); + LuanTable responseHeaders = (LuanTable)responseTbl.rawGet("headers"); + for( Map.Entry<Object,Object> entry : responseHeaders.rawIterable() ) { + String name = (String)entry.getKey(); + name = toHttpHeaderName(name); + LuanTable values = (LuanTable)entry.getValue(); + for( Object value : values.asList() ) { + if( value instanceof String ) { + response.setHeader(name,(String)value); + continue; + } + Integer i = Luan.asInteger(value); + if( i != null ) { + response.setIntHeader(name,i); + continue; + } + throw new IllegalArgumentException("value must be string or integer for headers table"); + } + } + } + + + + // static utils + + public static String toLuanHeaderName(String httpName) { + return httpName.toLowerCase().replace('-','_'); + } + + public static String toHttpHeaderName(String luanName) { +/* + StringBuilder buf = new StringBuilder(); + boolean capitalize = true; + char[] a = luanName.toCharArray(); + for( int i=0; i<a.length; i++ ) { + char c = a[i]; + if( c == '_' ) { + a[i] = '-'; + capitalize = true; + } else if( capitalize ) { + a[i] = Character.toUpperCase(c); + capitalize = false; + } + } + return String.valueOf(a); +*/ + return LuanUrl.toHttpHeaderName(luanName); + } + + private static String escape(String value) { + return value.replaceAll(";", "%3B"); + } + + private static String unescape(String value) { + return value.replaceAll("%3B", ";"); + } + + private static Cookie getCookie(HttpServletRequest request,String name) { + Cookie[] cookies = request.getCookies(); + if( cookies == null ) + return null; + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) + return cookie; + } + return null; + } + + public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) { + Cookie cookie = getCookie(request,name); + if( cookie==null || !cookie.getValue().equals(value) ) { + cookie = new Cookie(name, escape(value)); + cookie.setPath("/"); + if (domain != null && domain.length() > 0) + cookie.setDomain(domain); + if( isPersistent ) + cookie.setMaxAge(10000000); + response.addCookie(cookie); + } + } + + public static void removeCookie(HttpServletRequest request, + HttpServletResponse response, + String name, + String domain + ) { + Cookie cookie = getCookie(request, name); + if(cookie != null) { + Cookie delCookie = new Cookie(name, "delete"); + delCookie.setPath("/"); + delCookie.setMaxAge(0); + if (domain != null && domain.length() > 0) + delCookie.setDomain(domain); + response.addCookie(delCookie); + } + } + + + + private static String RUN_LATER_KEY = "Http.run_later"; + private static final Executor exec = Executors.newSingleThreadExecutor(); + + public static void run_later(final LuanState luan,final LuanFunction fn,final Object... args) { + List list = (List)luan.registry().get(RUN_LATER_KEY); + if( list == null ) { + list = new ArrayList(); + luan.registry().put(RUN_LATER_KEY,list); + } + list.add( + new Runnable(){public void run() { + try { + fn.call(luan,args); + } catch(LuanException e) { + e.printStackTrace(); + } + }} + ); + } + + private static void handle_run_later(LuanState luan) { + List list = (List)luan.registry().get(RUN_LATER_KEY); + if( list==null ) + return; + for( Object obj : list ) { + exec.execute((Runnable)obj); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/Http_test.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,75 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local set_metatable = Luan.set_metatable or error() +local try = Luan.try or error() +local Package = require "luan:Package.luan" +local Io = require "luan:Io.luan" +local String = require "luan:String.luan" +local matches = String.matches or error() +local Http = require "luan:http/Http.luan" + + +local M = {} + +M.welcome_file = "index.html" +M.cookie = {} + +function M.get_page(path) + Http.request.path = path + if M.welcome_file ~= nil and matches(path,"/$") then + path = path .. M.welcome_file + end + local old_out = Io.stdout + try { + function() + local mod = Package.load("site:"..path..".luan") + if mod ~= nil then + mod() + else + local not_found = Package.load("site:/not_found.luan") + not_found or error(path.." not found") + not_found() + end + M.text_writer.close() + end + finally = function() + Io.stdout = old_out + end + } + return M.result.read_text() +end + +function M.init() + Http.request = Http.new_request{} + Http.request.cookie = M.cookie + + Http.response = Http.new_response{ + + text_writer = function() + Http.sent_headers(Http.response.headers) + M.result = Io.uri "string:" + M.text_writer = M.result.text_writer() + return M.text_writer + end + + set_cookie = function(name,value) + M.cookie[name] = value + end + + remove_cookie = function(name) + M.cookie[name] = nil + end + + send_redirect = function(url) + Http.response.redirect = url + end + + send_error = function(code) + error("sent error "..code) + end + + } + +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/LuanHandler.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,83 @@ +package luan.modules.http; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.DeepCloner; +import luan.LuanException; +import luan.modules.PackageLuan; + + +public class LuanHandler extends AbstractHandler { + private final LuanState luan; + private final Logger logger; + private String welcomeFile = "index.html"; + + public LuanHandler(LuanState luan,String loggerRoot) { + this.luan = luan; + if( loggerRoot==null ) + loggerRoot = ""; + logger = LoggerFactory.getLogger(loggerRoot+LuanHandler.class.getName()); + } + + @Override public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) + throws IOException + { + if( target.endsWith("/") ) + target += welcomeFile; + try { + if( !HttpServicer.service(luan,request,response,"site:"+target+".luan") ) + return; + } catch(LuanException e) { + String err = e.getFullMessage(); + logger.error(err); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,err); + } + baseRequest.setHandled(true); + } + + public void setWelcomeFile(String welcomeFile) { + this.welcomeFile = welcomeFile; + } + + @Override protected void doStop() throws Exception { + synchronized(luan) { + luan.close(); + } +//System.out.println("qqqqqqqqqqqqqqqqqqqq doStop "+this); + super.doStop(); + } +/* + @Override public void destroy() { +System.out.println("qqqqqqqqqqqqqqqqqqqq destroy "+this); + super.destroy(); + } +*/ + + public Object call_rpc(String fnName,Object... args) throws LuanException { + return callRpc(luan,fnName,args); + } + + public static Object callRpc(LuanState luan,String fnName,Object... args) throws LuanException { + synchronized(luan) { + DeepCloner cloner = new DeepCloner(); + luan = (LuanState)cloner.deepClone(luan); + } + LuanTable rpc = (LuanTable)PackageLuan.require(luan,"luan:Rpc.luan"); + LuanTable fns = (LuanTable)rpc.get(luan,"functions"); + LuanFunction fn = (LuanFunction)fns.get(luan,fnName); + if( fn == null ) + throw new LuanException( "function not found: " + fnName ); + return fn.call(luan,args); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/NotFound.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,22 @@ +package luan.modules.http; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Request; +import luan.LuanState; + + +public class NotFound extends LuanHandler { + + public NotFound(LuanState luan,String loggerRoot) { + super(luan,loggerRoot); + } + + @Override public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) + throws IOException + { + super.handle("/not_found",baseRequest,request,response); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/Server.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,118 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local String = require "luan:String.luan" +local gsub = String.gsub or error() +local matches = String.matches or error() +local Io = require "luan:Io.luan" +local Package = require "luan:Package.luan" +local Rpc = require "luan:Rpc.luan" +local Thread = require "luan:Thread.luan" +local Http = require "luan:http/Http.luan" +require "luan:logging/init.luan" -- initialize logging +local Logging = require "luan:logging/Logging.luan" +local logger = Logging.logger "http/Server" + +java() +local Server = require "java:org.eclipse.jetty.server.Server" +local NCSARequestLog = require "java:org.eclipse.jetty.server.NCSARequestLog" +local DefaultHandler = require "java:org.eclipse.jetty.server.handler.DefaultHandler" +local HandlerList = require "java:org.eclipse.jetty.server.handler.HandlerList" +local HandlerCollection = require "java:org.eclipse.jetty.server.handler.HandlerCollection" +local ResourceHandler = require "java:org.eclipse.jetty.server.handler.ResourceHandler" +local RequestLogHandler = require "java:org.eclipse.jetty.server.handler.RequestLogHandler" +local ContextHandler = require "java:org.eclipse.jetty.server.handler.ContextHandler" +local GzipHandler = require "java:org.eclipse.jetty.server.handler.GzipHandler" +local HandlerWrapper = require "java:org.eclipse.jetty.server.handler.HandlerWrapper" +local SessionHandler = require "java:org.eclipse.jetty.server.session.SessionHandler" +local AuthenticationHandler = require "java:luan.modules.http.AuthenticationHandler" +local LuanHandler = require "java:luan.modules.http.LuanHandler" +local NotFound = require "java:luan.modules.http.NotFound" + +local M = {} + +M.port = 8080 + +M.welcome_file = "index.html" + + +M.authentication_handler = AuthenticationHandler.new("/private/") + +M.luan_handler = LuanHandler.new() + +M.resource_handler = ResourceHandler.new() +M.resource_handler.setDirectoriesListed(true) + +M.handlers = HandlerList.new() +M.handlers.setHandlers { M.authentication_handler, M.luan_handler, M.resource_handler } + +function M.add_folder(context,dir) + local rh = ResourceHandler.new() + rh.setResourceBase(dir) + rh.setDirectoriesListed(true) + local ch = ContextHandler.new(context) + ch.setHandler(rh) + M.handlers.addHandler(ch) + return rh +end + +M.handler_wrapper = HandlerWrapper.new() +M.handler_wrapper.setHandler(M.handlers) + +function M.zip() + local h = GzipHandler.new() + h.setHandler(M.handler_wrapper.getHandler()) + M.handler_wrapper.setHandler(h) +end + +M.log = NCSARequestLog.new() +M.log.setExtended(false) +M.log_handler = RequestLogHandler.new() +M.log_handler.setRequestLog(M.log) + +function M.set_log_file(file_name) + M.log.setFilename(file_name) +end + +local hc = HandlerCollection.new() +hc.setHandlers { SessionHandler.new(), M.handler_wrapper, DefaultHandler.new(), M.log_handler } + + +function M.init(dir) + dir = gsub(dir,"/$","") -- remove trailing '/' if any + Http.dir = dir + function Io.schemes.site(path) + return Io.uri( dir..path ) + end + M.authentication_handler.setPassword(Io.password) + local base = dir + if matches(base,"^classpath:") then + base = dir.."#"..M.welcome_file.."#"..M.welcome_file..".luan" + end + M.resource_handler.setResourceBase(Io.uri(base).to_string()) + M.resource_handler.setWelcomeFiles {M.welcome_file} + M.luan_handler.setWelcomeFile(M.welcome_file) + M.handlers.addHandler(NotFound.new()) + M.server = Server.new(M.port) + M.server.setHandler(hc) + Package.load("site:/init.luan") +end + +function M.start() + M.server.start() +end + +function M.start_rpc() + function Rpc.functions.call(domain,fn_name,...) + return M.luan_handler.call_rpc(fn_name,...) + end + + Thread.fork(Rpc.serve) +end + +function M.serve(dir) + M.init(dir) + M.start() + M.start_rpc() +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/Shell_mod.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,108 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local ipairs = Luan.ipairs or error() +local load = Luan.load or error() +local try = Luan.try or error() +local Io = require "luan:Io.luan" +local print = Io.print or error() +local Http = require "luan:http/Http.luan" + + +local M = {} + +local history = {} +M.env = {} + +function M.respond() + if Http.request.parameter.clear ~= nil then + Http.clear_session() + Http.response.send_redirect(Http.request.path) -- reload page + return + else + local cmd = Http.request.parameter.cmd + if cmd ~= nil then + Io.stdout = {} + function Io.stdout.write(...) + for v in Luan.values(...) do + history[#history+1] = v + end + end + print( "% "..cmd ) + try { + function() + local line + try { + function() + line = load("return "..cmd,"<web_shell>",M.env) + end + catch = function(e) + line = load(cmd,"<web_shell>",M.env) + end + } + print( line() ) + end + catch = function(e) + Io.print_to(Io.stderr,e) + print(e) + end + } + end + end + + Io.stdout = Http.response.text_writer() +%> +<html> + <head> + <title>Luan Shell</title> + <style> + body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + margin: 2em 5% 0 5%; + } + pre { + font: inherit; + } + input[type="text"] { + font: inherit; + padding: .5em .8em; + border-radius: 8px; + border-style: groove; + } + input[type="text"]:focus { + border-color: #66afe9; + outline: none; + } + input[type="submit"] { + color: white; + background: #337ab7; + border-color: #337ab7; + font: inherit; + padding: .5em; + border-radius: 4px; + } + input[type="submit"]:hover { + background: #236aa7 !important; + } + </style> + </head> + <body> + <h2>Luan Shell</h2> + <p>This is a command shell. Enter commands below.</p> + <pre><% + for _,v in ipairs(history) do + Io.stdout.write(v) + end + %></pre> + <form name='form0' method='post'> + % <input type="text" name='cmd' size="80" autofocus> + <input type="submit" value="run"> + <input type="submit" name="clear" value="clear"> + </form> + </body> +</html> +<% +end + +Http.per_session(M.respond) + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/dump.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,1 @@ +return require("luan:http/Dump_mod.luan").respond
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/run.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,106 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local load = Luan.load or error() +local try = Luan.try or error() +local Io = require "luan:Io.luan" +local print = Io.print or error() +local String = require "luan:String.luan" +local gmatch = String.gmatch or error() +local Http = require "luan:http/Http.luan" + + +local function lines(s) + local matcher = gmatch(s,"([^\n]*)\n|([^\n])+$") + return function() + local m1, m2 = matcher() + return m1 or m2 + end +end + +local function print_with_line_numbers(s) + local i = 1 + for line in lines(s) do + print(i,line) + i = i + 1 + end +end + +local function form() %> +<html> + <head> + <title>Run Luan Code</title> + <style> + body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + text-align: center; + margin-top: 1em; + } + h2 { + margin-bottom: .3em; + font-weight: normal; + } + textarea { + font: inherit; + border-radius: 4px; + padding: .5em .8em; + } + input[type="submit"] { + margin-top: .3em; + color: white; + background: #337ab7; + border-color: #337ab7; + font: inherit; + padding: .5em; + border-radius: 4px; + } + input[type="submit"]:hover { + background: #236aa7 !important; + } + </style> + </head> + <body> + <h2>Run Luan Code</h2> + <form name="form0" method="post"> + <input type="hidden" name="content_type" value="text/plain" /> + <div> + <textarea name="code" rows="20" cols="90" wrap="off" autofocus></textarea> + </div> + <div> + <input type="submit" value="Execute Luan Code"/> + </div> + </form> + </body> +</html> +<% end + +return function() + local content_type = Http.request.parameter.content_type + if content_type ~= nil then + Http.response.header.content_type = content_type + end + Io.stdout = Http.response.text_writer() + local code = Http.request.parameter.code + if code == nil then + form() + return + end + local env = { + request = Http.request; + response = Http.response; + } + try { + function() + local run = load(code,"<web_run>",env) + run() + end; + catch = function(e) + Http.response.reset() + Http.response.header.content_type = "text/plain" + Io.stdout = Http.response.text_writer() + print(e) + print"" + print"" + print_with_line_numbers(code) + end; + } +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/serve.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,9 @@ +local Io = require "luan:Io.luan" +local Server = require "luan:http/Server.luan" + +if #{...} ~= 1 then + Io.stderr.write "usage: luan luan:http/serve dir-URI\n" + return +end + +Server.serve(...)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/shell.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,1 @@ +return require("luan:http/Shell_mod.luan").respond
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/http/test.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,12 @@ +local Luan = require "luan:Luan.luan" +local Io = require "luan:Io.luan" +local Server = require "luan:http/Server.luan" + +if #{...} ~= 2 then + Io.stderr.write "usage: luan luan:http/serve dir-URI test-URI\n" + return +end + +local dir, test = ... +Server.init(dir) +Luan.do_file(test)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/logging/Logging.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,90 @@ +java() +local Logger = require "java:org.apache.log4j.Logger" +local EnhancedPatternLayout = require "java:org.apache.log4j.EnhancedPatternLayout" +local ConsoleAppender = require "java:org.apache.log4j.ConsoleAppender" +local Level = require "java:org.apache.log4j.Level" +local RollingFileAppender = require "java:org.apache.log4j.RollingFileAppender" +local LuanLogger = require "java:luan.modules.logging.LuanLogger" + +local M = {} + +M.layout = "%d %-5p %c - %m%n" + +M.level = "INFO" + +M.console = "System.err" -- or "System.out" or set to nil for no console + +M.file = nil -- set to file name if you want logging to a file + +M.max_file_size = nil -- by default is "10MB" + + +M.log4j_root_logger = Logger.getRootLogger() + +local function to_level(level) + return level and Level.toLevel(level) +end + +function M.log_to_file(file,logger_name) -- logger_name is optional, defaults to root logger + local appender = RollingFileAppender.new(M.ptn_layout, file) + appender.setMaxFileSize(M.max_file_size) + local logger = logger_name and Logger.getLogger(logger_name) or M.log4j_root_logger + logger.addAppender(appender) + return appender +end + +function M.init() + M.log4j_root_logger.removeAllAppenders() + M.log4j_root_logger.setLevel( to_level(M.level) ) + M.ptn_layout = EnhancedPatternLayout.new(M.layout) + + if M.console ~= nil then + M.console_appender = ConsoleAppender.new(M.ptn_layout,M.console) + M.log4j_root_logger.addAppender(M.console_appender) + else + M.console_appender = nil + end + + if M.file ~= nil then + M.file_appender = M.log_to_file(M.file) + else + M.file_appender = nil + end +end + + +local function to_luan_logger(log4j_logger) + local tbl = {} + + local luanLogger = LuanLogger.new(log4j_logger) + tbl.error = luanLogger.error + tbl.warn = luanLogger.warn + tbl.info = luanLogger.info + tbl.debug = luanLogger.debug + + function tbl.get_level() + local level = log4j_logger.getLevel() + return level and level.toString() + end + + function tbl.get_effective_level() + local level = log4j_logger.getEffectiveLevel() + return level and level.toString() + end + + function tbl.set_level(level) + log4j_logger.setLevel( to_level(level) ) + end + + return tbl +end + +function M.logger(name) + return to_luan_logger( Logger.getLogger(name) ) +end + +function M.root_logger() + return to_luan_logger( Logger.getRootLogger() ) +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/logging/LuanLogger.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,33 @@ +package luan.modules.logging; + +import org.apache.log4j.Logger; +import luan.Luan; +import luan.LuanState; +import luan.LuanException; +import luan.LuanTable; + + +public final class LuanLogger { + private final Logger logger; + + public LuanLogger(Logger logger) { + this.logger = logger; + } + + public void error(LuanState luan,Object obj) throws LuanException { + logger.error( luan.toString(obj) ); + } + + public void warn(LuanState luan,Object obj) throws LuanException { + logger.warn( luan.toString(obj) ); + } + + public void info(LuanState luan,Object obj) throws LuanException { + logger.info( luan.toString(obj) ); + } + + public void debug(LuanState luan,Object obj) throws LuanException { + logger.debug( luan.toString(obj) ); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/logging/init.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,2 @@ +require "luan:logging/Logging.luan".init() +return true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/luan_to_java.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,5 @@ +java() +local LuanCompiler = require "java:luan.impl.LuanCompiler" +local Io = require "luan:Io.luan" + +Io.stdout.write( LuanCompiler.toJava( Io.stdin.read_text(), "stdin" ) )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/Ab_testing.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,272 @@ +local Luan = require "luan:Luan.luan" +local pairs = Luan.pairs +local ipairs = Luan.ipairs +local error = Luan.error +local range = Luan.range +local Math = require "luan:Math.luan" +local Table = require "luan:Table.luan" +local String = require "luan:String.luan" +local gsub = String.gsub +local Io = require "luan:Io.luan" +local Http = require "luan:http/Http.luan" +local Logging = require "luan:logging/Logging.luan" +local Lucene = require "luan:lucene/Lucene.luan" + +local M = {} + +local logger = Logging.logger "Ab_testing" + +function M.of(index) + + local ab_testing = {} + + ab_testing.test_map = {} + + function ab_testing.test(test) + test.name or error "name not defined" + test.values or error "values not defined" + + -- list of event names + test.events or error "events not defined" + + -- map of event name to aggregator factory + test.aggregator_factories or error "aggregator_factories not defined" + + -- test.date_field is optional + + local field = "ab_test_" .. test.name + index.indexed_fields[field] == nil or error("test "+test.name+" already defined") + index.indexed_fields[field] = Lucene.type.string + test.field = field + + -- returns map of event name to (map of value to result) and "start_date" + function test.results() + local results = {} + for name in pairs(test.aggregator_factories) do + results[name] = {} + end + local date_field = test.date_field + local start_date = nil + for _, value in ipairs(test.values) do + local aggregators = {} + for name, factory in pairs(test.aggregator_factories) do + aggregators[name] = factory() + end + local query = field..":"..value + index.advanced_search(query, function(_,doc_fn) + local doc = doc_fn() + for _, aggregator in pairs(aggregators) do + aggregator.aggregate(doc) + end + if date_field ~= nil then + local date = doc[date_field] + if date ~= nil and (start_date==nil or start_date > date) then + start_date = date + end + end + end) + for name, aggregator in pairs(aggregators) do + results[name][value] = aggregator.result + end + end + results.start_date = start_date + return results + end + + function test.fancy_results() + local events = test.events + local results = test.results() + local fancy = {} + fancy.start_date = results.start_date + local event = events[1] + fancy[event] = {} + for value, count in pairs(results[event]) do + fancy[event][value] = {} + fancy[event][value].count = count + fancy[event][value].pct_of_total = 100 + fancy[event][value].pct_of_prev = 100 + end + local all = results[event] + local prev = all + for i in range(2,#events) do + event = events[i] + fancy[event] = {} + for value, count in pairs(results[event]) do + fancy[event][value] = {} + fancy[event][value].count = count + fancy[event][value].pct_of_total = M.percent(count,all[value]) + fancy[event][value].pct_of_prev = M.percent(count,prev[value]) + end + prev = results[event] + end + return fancy + end + + ab_testing.test_map[test.name] = test + + return test + end + + function ab_testing.value(test_name,values) + return values[test_name] or ab_testing.test_map[test_name].values[1] + end + + -- returns map from test name to value + function ab_testing.from_doc(doc) + local values = {} + for _, test in pairs(ab_testing.test_map) do + values[test.name] = doc[test.field] + end + return values + end + + function ab_testing.to_doc(doc,values,tests) + tests = tests or ab_testing.test_map + if values == nil then + values = {} + for _, test in pairs(tests) do + values[test.name] = test.values[Math.random(#test.values)] + end + end + for _, test in pairs(tests) do + doc[test.field] = values[test.name] + end + return values + end + + function ab_testing.web_page(test_names) + return function() + local results = {} + for _, name in ipairs(test_names) do + local test = ab_testing.test_map[name] + test or error("test not found: "..name) + results[name] = test.fancy_results() + end + Io.stdout = Http.response.text_writer() + M.html(test_names,ab_testing.test_map,results) + end + end + + return ab_testing +end + + +-- aggregator factories + +-- fn(doc) should return boolean whether doc should be counted +function M.count(fn) + return function() + local aggregator = {} + aggregator.result = 0 + function aggregator.aggregate(doc) + if fn(doc) then + aggregator.result = aggregator.result + 1 + end + end + return aggregator + end +end + +M.count_all = M.count( function(doc) return true end ) + +-- fn(doc) should return number to add to result, return 0 for nothing +function M.sum(fn) + return function() + local aggregator = {} + aggregator.result = 0 + function aggregator.aggregate(doc) + aggregator.result = aggregator.result + fn(doc) + end + return aggregator + end +end + + + +function M.percent(x,total) + if total==0 then + return 0 + else + return 100 * x / total + end +end + +-- I will change this to use SimplyHTML when this is used again. +local function basic_style() %> + body {font-family:'Arial',sans-serif;font-size:16px;padding:1em 2em} + h1 {font-weight:bold;font-size:20px} + h2 {margin:2em 0 0em;font-size:18px;color:#3589B1} + table.results {margin-top:.5em;border-collapse:collapse;font-size:90%} + table.results th {background:#eee} + table.results th,table.results td {border-left:1px solid #bbb;padding:.4em 2em} + table.results tr:nth-child(odd) td {background:#f8f8f8} +<% end + +local function format(v) + v = v .. '' + return gsub( v, [[(\d+\.\d{1})\d+]], '$1' ) +end + +function M.html(test_names,tests,results) %> +<!DOCTYPE html> +<html lang="en"> + <head> + <title>A/B Test Results</title> + <style><% basic_style() %></style> + </head> + <body> + <h1>A/B Test Results</h1> + <% + for _, test_name in ipairs(test_names) do + local test = tests[test_name] + local result = results[test_name] + local n = #test.values + %> + <h2><%=test_name%></h2> + <table class="results"> + <tr> + <th>Event</th> + <th class="top" colspan="<%=n%>">Count</th> + <th class="top" colspan="<%=n%>">% of total</th> + <th class="top" colspan="<%=n%>">% of prev</th> + </tr> + <tr> + <th></th> + <% + for _ in range(1,3) do + for _, value in ipairs(test.values) do + %><th class="top"><%=value%></th><% + end + end + %> + </tr> + <% + for _, event in ipairs(test.events) do + local event_values = result[event] + %> + <tr> + <td><%=event%></td> + <% + for _, value in ipairs(test.values) do + %><td><%=format(event_values[value].count)%></th><% + end + for _, value in ipairs(test.values) do + %><td><%=format(event_values[value].pct_of_total)%></th><% + end + for _, value in ipairs(test.values) do + %><td><%=format(event_values[value].pct_of_prev)%></th><% + end + %> + </tr> + <% + end + %> + </table> + <% + end + %> + </body> +</html> +<% end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/Lucene.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,161 @@ +java() +local Luan = require "luan:Luan.luan" +local error = Luan.error +local ipairs = Luan.ipairs or error() +local type = Luan.type or error() +local Html = require "luan:Html.luan" +local Io = require "luan:Io.luan" +local uri = Io.uri or error() +local String = require "luan:String.luan" +local matches = String.matches or error() +local Rpc = require "luan:Rpc.luan" +local LuceneIndex = require "java:luan.modules.lucene.LuceneIndex" +local NumberFieldParser = require "java:luan.modules.lucene.queryparser.NumberFieldParser" +local StringFieldParser = require "java:luan.modules.lucene.queryparser.StringFieldParser" +local SaneQueryParser = require "java:luan.modules.lucene.queryparser.SaneQueryParser" +local Version = require "java:org.apache.lucene.util.Version" +local EnglishAnalyzer = require "java:org.apache.lucene.analysis.en.EnglishAnalyzer" + + +local M = {} + +M.instances = {} + +M.type = { + string = LuceneIndex.STRING_FIELD_PARSER; + integer = NumberFieldParser.INT; + long = NumberFieldParser.LONG; + double = NumberFieldParser.DOUBLE; + + english = StringFieldParser.new(EnglishAnalyzer.new(Version.LUCENE_CURRENT)) +} + +M.literal = SaneQueryParser.literal + +function M.index(index_dir,default_type,default_fields) + local index = {} + index.dir = index_dir + local java_index = LuceneIndex.new(index_dir,default_type,default_fields) + index.indexed_fields = java_index.indexedFieldsMeta.newTable() + + -- index.indexed_only_fields[type][field] = fn(doc) + index.indexed_only_fields = java_index.indexed_only_fields + + index.to_string = java_index.to_string +-- index.backup = java_index.backup + index.snapshot = java_index.snapshot + index.advanced_search = java_index.advanced_search + index.search_in_transaction = java_index.search_in_transaction + index.delete_all = java_index.delete_all + index.delete = java_index.delete + index.save = java_index.save + index.update_in_transaction = java_index.update_in_transaction +-- index.close = java_index.close + index.ensure_open = java_index.ensure_open + index.next_id = java_index.nextId + index.highlighter = java_index.highlighter + + M.instances[index] = true + + function index.close() + M.instances[index] = nil + java_index.close() + end + + function index.search(query, from, to, sort) + from or error "missing 'from' parameter" + to or error "missing 'to' parameter" + local results = {} + local function fn(i,doc_fn) + if i >= from then + results[#results+1] = doc_fn() + end + end + local total_hits = index.advanced_search(query,fn,to,sort) + return results, total_hits + end + + function index.get_document(query) + local doc + local function fn(_,doc_fn) + doc = doc_fn() + end + local total_hits = index.advanced_search(query,fn,1) + total_hits <= 1 or error( "found " .. total_hits .. " documents" ) + return doc + end + + function index.count(query) + return index.advanced_search(query) + end + + function index.html_highlighter(query,formatter,container_tags) + local highlighter = index.highlighter(query,formatter) + return function(html) + local list = Html.parse(html,container_tags) + local result = {} + for _, obj in ipairs(list) do + if type(obj) == "string" then + obj = highlighter(obj) + end + result[#result+1] = obj + end + return Html.to_string(result) + end + end + + function index.zip(zip_file) + index.snapshot( function(dir_path,file_names) + zip_file.delete() + local zip_path = zip_file.canonical().to_string() + local dir = uri("file:"..dir_path) + local dir_name = dir.name() + local options = {dir=dir.parent()} + for _, file_name in ipairs(file_names) do + local cmd = "zip "..zip_path.." "..dir_name.."/"..file_name + Io.uri("os:"..cmd,options).read_text() + end + end ) + end + + function index.restore(zip_file) + java_index.run_in_lock( function() + local lucene_dir = uri("file:"..index.dir) + local before_restore = lucene_dir.parent().child("before_restore.zip") + index.zip(before_restore) + java_index.close() + lucene_dir.delete() + Io.uri("os:unzip "..zip_file.canonical().to_string(),{dir=lucene_dir.parent()}).read_text() + java_index.reopen() + end ) + end + + local function multi_error() + error "multiple lucene instances" + end + + if Rpc.functions.backup == nil then + + function Rpc.functions.lucene_backup(password) + Io.password == password or error "wrong password" + local zip_file = uri("file:"..index.dir).parent().child("backup.zip") + index.zip(zip_file) + return zip_file + end + + function Rpc.functions.lucene_restore(password,zip_file) + Io.password == password or error "wrong password" + local backup_zip = uri("file:"..index.dir).parent().child("backup.zip") + backup_zip.write(zip_file) + index.restore(backup_zip) + end + + else + Rpc.functions.lucene_backup = multi_error + Rpc.functions.lucene_restore = multi_error + end + + return index +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/LuceneIndex.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,631 @@ +package luan.modules.lucene; + +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.zip.ZipOutputStream; +import java.util.zip.ZipEntry; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.core.KeywordAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.StoredField; +import org.apache.lucene.document.StringField; +import org.apache.lucene.document.TextField; +import org.apache.lucene.document.IntField; +import org.apache.lucene.document.LongField; +import org.apache.lucene.document.DoubleField; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.index.SnapshotDeletionPolicy; +import org.apache.lucene.index.IndexCommit; +import org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; +import org.apache.lucene.util.Version; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.NumericUtils; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.TotalHitCountCollector; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.highlight.Formatter; +import org.apache.lucene.search.highlight.Highlighter; +import org.apache.lucene.search.highlight.InvalidTokenOffsetsException; +import org.apache.lucene.search.highlight.NullFragmenter; +import org.apache.lucene.search.highlight.QueryScorer; +import org.apache.lucene.search.highlight.TokenGroup; +import luan.modules.lucene.queryparser.SaneQueryParser; +import luan.modules.lucene.queryparser.FieldParser; +import luan.modules.lucene.queryparser.MultiFieldParser; +import luan.modules.lucene.queryparser.StringFieldParser; +import luan.modules.lucene.queryparser.NumberFieldParser; +import luan.modules.lucene.queryparser.ParseException; +import luan.modules.Utils; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanMeta; +import luan.LuanRuntimeException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public final class LuceneIndex implements Closeable { + private static final Logger logger = LoggerFactory.getLogger(LuceneIndex.class); + + private static final String FLD_NEXT_ID = "nextId"; + public static final StringFieldParser STRING_FIELD_PARSER = new StringFieldParser(new KeywordAnalyzer()); + + private static final Version version = Version.LUCENE_4_9; + private final ReentrantLock writeLock = new ReentrantLock(); + private final File indexDir; + private SnapshotDeletionPolicy snapshotDeletionPolicy; + private IndexWriter writer; + private DirectoryReader reader; + private IndexSearcher searcher; + private final ThreadLocal<IndexSearcher> threadLocalSearcher = new ThreadLocal<IndexSearcher>(); + private boolean isClosed = true; + private final MultiFieldParser mfp; + public final LuanTable indexed_only_fields = new LuanTable(); + private final Analyzer analyzer; + + private static ConcurrentMap<File,AtomicInteger> globalWriteCounters = new ConcurrentHashMap<File,AtomicInteger>(); + private File fileDir; + private int writeCount; + + public LuceneIndex(LuanState luan,String indexDirStr,FieldParser defaultFieldParser,String[] defaultFields) throws LuanException, IOException { + mfp = defaultFieldParser==null ? new MultiFieldParser() : new MultiFieldParser(defaultFieldParser,defaultFields); + mfp.fields.put( "type", STRING_FIELD_PARSER ); + mfp.fields.put( "id", NumberFieldParser.LONG ); + File indexDir = new File(indexDirStr); + this.indexDir = indexDir; + Analyzer analyzer = STRING_FIELD_PARSER.analyzer; + if( defaultFieldParser instanceof StringFieldParser ) { + StringFieldParser sfp = (StringFieldParser)defaultFieldParser; + analyzer = sfp.analyzer; + } + this.analyzer = analyzer; + luan.onClose(this); + reopen(); + } + + public void reopen() throws LuanException, IOException { + if( !isClosed ) throw new RuntimeException(); + isClosed = false; + IndexWriterConfig conf = new IndexWriterConfig(version,analyzer); + snapshotDeletionPolicy = new SnapshotDeletionPolicy(conf.getIndexDeletionPolicy()); + conf.setIndexDeletionPolicy(snapshotDeletionPolicy); + FSDirectory dir = FSDirectory.open(indexDir); + fileDir = dir.getDirectory(); + globalWriteCounters.putIfAbsent(fileDir,new AtomicInteger()); + writer = new IndexWriter(dir,conf); + writer.commit(); // commit index creation + reader = DirectoryReader.open(dir); + searcher = new IndexSearcher(reader); + initId(); + } + + private int globalWriteCount() { + return globalWriteCounters.get(fileDir).get(); + } + + private void wrote() { + globalWriteCounters.get(fileDir).incrementAndGet(); + } + + public void delete_all() throws IOException { + boolean commit = !writeLock.isHeldByCurrentThread(); + writeLock.lock(); + try { + writer.deleteAll(); + id = idLim = 0; + if(commit) writer.commit(); + } finally { + wrote(); + writeLock.unlock(); + } + } + + private static Term term(String key,long value) { + BytesRef br = new BytesRef(); + NumericUtils.longToPrefixCoded(value,0,br); + return new Term(key,br); + } + + public void delete(LuanState luan,String queryStr) throws LuanException, IOException, ParseException { + Query query = SaneQueryParser.parseQuery(mfp,queryStr); + + boolean commit = !writeLock.isHeldByCurrentThread(); + writeLock.lock(); + try { + writer.deleteDocuments(query); + if(commit) writer.commit(); + } finally { + wrote(); + writeLock.unlock(); + } + } + + public void save(LuanState luan,LuanTable doc) throws LuanException, IOException { + Set indexedOnlySet = new HashSet(); + Object typeObj = doc.get(luan,"type"); + if( typeObj==null ) + throw new LuanException("missing 'type' field"); + if( !(typeObj instanceof String) ) + throw new LuanException("type must be string"); + String type = (String)typeObj; + Object indexedOnlyObj = indexed_only_fields.get(luan,type); + if( indexedOnlyObj != null ) { + if( !(indexedOnlyObj instanceof LuanTable) ) + throw new LuanException("indexed_only_fields elements must be tables"); + LuanTable indexedOnly = (LuanTable)indexedOnlyObj; + for( Map.Entry<Object,Object> entry : indexedOnly.iterable(luan) ) { + Object key = entry.getKey(); + if( !(key instanceof String) ) + throw new LuanException("indexed_only_fields."+type+" entries must be strings"); + String name = (String)key; + Object value = entry.getValue(); + if( !(value instanceof LuanFunction) ) + throw new LuanException("indexed_only_fields."+type+" values must be functions"); + LuanFunction fn = (LuanFunction)value; + value = Luan.first(fn.call(luan,new Object[]{doc})); + doc.put(luan, name, value ); + indexedOnlySet.add(name); + } + } + Object obj = doc.get(luan,"id"); + Long id; + try { + id = (Long)obj; + } catch(ClassCastException e) { + throw new LuanException("id should be Long but is "+obj.getClass().getSimpleName()); + } + + boolean commit = !writeLock.isHeldByCurrentThread(); + writeLock.lock(); + try { + if( id == null ) { + id = nextId(luan); + doc.put(luan,"id",id); + writer.addDocument(toLucene(luan,doc,indexedOnlySet)); + } else { + writer.updateDocument( term("id",id), toLucene(luan,doc,indexedOnlySet) ); + } + if(commit) writer.commit(); + } finally { + wrote(); + writeLock.unlock(); + } + } + + public void update_in_transaction(LuanState luan,LuanFunction fn) throws IOException, LuanException { + boolean commit = !writeLock.isHeldByCurrentThread(); + writeLock.lock(); + try { + fn.call(luan); + if(commit) writer.commit(); + } finally { + wrote(); + writeLock.unlock(); + } + } + + public void run_in_lock(LuanState luan,LuanFunction fn) throws IOException, LuanException { + if( writeLock.isHeldByCurrentThread() ) + throw new RuntimeException(); + writeLock.lock(); + try { + synchronized(this) { + fn.call(luan); + } + } finally { + wrote(); + writeLock.unlock(); + } + } + + + private long id; + private long idLim; + private final int idBatch = 10; + + private void initId() throws LuanException, IOException { + TopDocs td = searcher.search(new TermQuery(new Term("type","next_id")),1); + switch(td.totalHits) { + case 0: + id = 0; + idLim = 0; + break; + case 1: + idLim = (Long)searcher.doc(td.scoreDocs[0].doc).getField(FLD_NEXT_ID).numericValue(); + id = idLim; + break; + default: + throw new RuntimeException(); + } + } + + public synchronized long nextId(LuanState luan) throws LuanException, IOException { + if( ++id > idLim ) { + idLim += idBatch; + LuanTable doc = new LuanTable(); + doc.rawPut( "type", "next_id" ); + doc.rawPut( FLD_NEXT_ID, idLim ); + writer.updateDocument(new Term("type","next_id"),toLucene(luan,doc,Collections.EMPTY_SET)); + wrote(); + } + return id; + } + +/* + public void backup(String zipFile) throws LuanException, IOException { + if( !zipFile.endsWith(".zip") ) + throw new LuanException("file "+zipFile+" doesn't end with '.zip'"); + IndexCommit ic = snapshotDeletionPolicy.snapshot(); + try { + ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile)); + for( String fileName : ic.getFileNames() ) { + out.putNextEntry(new ZipEntry(fileName)); + FileInputStream in = new FileInputStream(new File(indexDir,fileName)); + Utils.copyAll(in,out); + in.close(); + out.closeEntry(); + } + out.close(); + } finally { + snapshotDeletionPolicy.release(ic); + } + } +*/ + public Object snapshot(LuanState luan,LuanFunction fn) throws LuanException, IOException { + IndexCommit ic = snapshotDeletionPolicy.snapshot(); + try { + String dir = fileDir.toString(); + LuanTable fileNames = new LuanTable(new ArrayList(ic.getFileNames())); + return fn.call(luan,new Object[]{dir,fileNames}); + } finally { + snapshotDeletionPolicy.release(ic); + } + } + + + + public String to_string() { + return writer.getDirectory().toString(); + } + + public void close() throws IOException { + if( !isClosed ) { + writer.close(); + reader.close(); + isClosed = true; + } + } + + protected void finalize() throws Throwable { + if( !isClosed ) { + logger.error("not closed"); + close(); + } + super.finalize(); + } + + + + private static class DocFn extends LuanFunction { + final IndexSearcher searcher; + int docID; + + DocFn(IndexSearcher searcher) { + this.searcher = searcher; + } + + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + return toTable(searcher.doc(docID)); + } catch(IOException e) { + throw new LuanException(e); + } + } + } + + private static abstract class MyCollector extends Collector { + int docBase; + int i = 0; + + @Override public void setScorer(Scorer scorer) {} + @Override public void setNextReader(AtomicReaderContext context) { + this.docBase = context.docBase; + } + @Override public boolean acceptsDocsOutOfOrder() { + return true; + } + } + + private synchronized IndexSearcher openSearcher() throws IOException { + int gwc = globalWriteCount(); + if( writeCount != gwc ) { + writeCount = gwc; + DirectoryReader newReader = DirectoryReader.openIfChanged(reader); + if( newReader != null ) { + reader.decRef(); + reader = newReader; + searcher = new IndexSearcher(reader); + } + } + reader.incRef(); + return searcher; + } + + // call in finally block + private static void close(IndexSearcher searcher) throws IOException { + searcher.getIndexReader().decRef(); + } + + public void ensure_open() throws IOException { + close(openSearcher()); + } + + public int advanced_search( final LuanState luan, String queryStr, LuanFunction fn, Integer n, String sortStr ) throws LuanException, IOException, ParseException { + Utils.checkNotNull(queryStr); + Query query = SaneQueryParser.parseQuery(mfp,queryStr); + IndexSearcher searcher = threadLocalSearcher.get(); + boolean inTransaction = searcher != null; + if( !inTransaction ) + searcher = openSearcher(); + try { + if( fn!=null && n==null ) { + if( sortStr != null ) + throw new LuanException("sort must be nil when n is nil"); + final DocFn docFn = new DocFn(searcher); + MyCollector col = new MyCollector() { + @Override public void collect(int doc) { + try { + docFn.docID = docBase + doc; + fn.call(luan,new Object[]{++i,docFn}); + } catch(LuanException e) { + throw new LuanRuntimeException(e); + } + } + }; + try { + searcher.search(query,col); + } catch(LuanRuntimeException e) { + throw (LuanException)e.getCause(); + } + return col.i; + } + if( fn==null || n==0 ) { + TotalHitCountCollector thcc = new TotalHitCountCollector(); + searcher.search(query,thcc); + return thcc.getTotalHits(); + } + Sort sort = sortStr==null ? null : SaneQueryParser.parseSort(mfp,sortStr); + TopDocs td = sort==null ? searcher.search(query,n) : searcher.search(query,n,sort); + final ScoreDoc[] scoreDocs = td.scoreDocs; + DocFn docFn = new DocFn(searcher); + for( int i=0; i<scoreDocs.length; i++ ) { + docFn.docID = scoreDocs[i].doc; + fn.call(luan,new Object[]{i+1,docFn}); + } + return td.totalHits; + } finally { + if( !inTransaction ) + close(searcher); + } + } + + public Object search_in_transaction(LuanState luan,LuanFunction fn) throws LuanException, IOException { + if( threadLocalSearcher.get() != null ) + throw new LuanException("can't nest search_in_transaction calls"); + IndexSearcher searcher = openSearcher(); + threadLocalSearcher.set(searcher); + try { + return fn.call(luan); + } finally { + threadLocalSearcher.set(null); + close(searcher); + } + } + + + + public final LuanMeta indexedFieldsMeta = new LuanMeta() { + + @Override public boolean canNewindex() { + return true; + } + + @Override public Object __index(LuanState luan,LuanTable tbl,Object key) { + return mfp.fields.get(key); + } + + @Override public void __new_index(LuanState luan,LuanTable tbl,Object key,Object value) throws LuanException { + if( !(key instanceof String) ) + throw new LuanException("key must be string"); + String field = (String)key; + if( value==null ) { // delete + mfp.fields.remove(field); + return; + } + if( !(value instanceof FieldParser) ) + throw new LuanException("value must be FieldParser like the values of Lucene.type"); + FieldParser parser = (FieldParser)value; + mfp.fields.put( field, parser ); + } + + @Override public final Iterator keys(LuanTable tbl) { + return mfp.fields.keySet().iterator(); + } + + @Override protected String type(LuanTable tbl) { + return "lucene-indexed-fields"; + } + + }; + + + + private IndexableField newField(String name,Object value,Field.Store store,Set<String> indexed) + throws LuanException + { + if( value instanceof String ) { + String s = (String)value; + FieldParser fp = mfp.fields.get(name); + if( fp != null ) { + if( fp instanceof StringFieldParser && fp != STRING_FIELD_PARSER ) { + return new TextField(name, s, store); + } else { + return new StringField(name, s, store); + } + } else { + return new StoredField(name, s); + } + } else if( value instanceof Integer ) { + int i = (Integer)value; + if( indexed.contains(name) ) { + return new IntField(name, i, store); + } else { + return new StoredField(name, i); + } + } else if( value instanceof Long ) { + long i = (Long)value; + if( indexed.contains(name) ) { + return new LongField(name, i, store); + } else { + return new StoredField(name, i); + } + } else if( value instanceof Double ) { + double i = (Double)value; + if( indexed.contains(name) ) { + return new DoubleField(name, i, store); + } else { + return new StoredField(name, i); + } + } else if( value instanceof byte[] ) { + byte[] b = (byte[])value; + return new StoredField(name, b); + } else + throw new LuanException("invalid value type "+value.getClass()+"' for '"+name+"'"); + } + + private Document toLucene(LuanState luan,LuanTable table,Set indexOnly) throws LuanException { + Set<String> indexed = mfp.fields.keySet(); + Document doc = new Document(); + for( Map.Entry<Object,Object> entry : table.iterable(luan) ) { + Object key = entry.getKey(); + if( !(key instanceof String) ) + throw new LuanException("key must be string"); + String name = (String)key; + Object value = entry.getValue(); + Field.Store store = indexOnly.contains(name) ? Field.Store.NO : Field.Store.YES; + if( !(value instanceof LuanTable) ) { + doc.add(newField(name, value, store, indexed)); + } else { // list + LuanTable list = (LuanTable)value; + for( Object el : list.asList() ) { + doc.add(newField(name, el, store, indexed)); + } + } + } + return doc; + } + + private static Object getValue(IndexableField ifld) throws LuanException { + BytesRef br = ifld.binaryValue(); + if( br != null ) + return br.bytes; + Number n = ifld.numericValue(); + if( n != null ) + return n; + String s = ifld.stringValue(); + if( s != null ) + return s; + throw new LuanException("invalid field type for "+ifld); + } + + private static LuanTable toTable(Document doc) throws LuanException { + if( doc==null ) + return null; + LuanTable table = new LuanTable(); + for( IndexableField ifld : doc ) { + String name = ifld.name(); + Object value = getValue(ifld); + Object old = table.rawGet(name); + if( old == null ) { + table.rawPut(name,value); + } else { + LuanTable list; + if( old instanceof LuanTable ) { + list = (LuanTable)old; + } else { + list = new LuanTable(); + list.rawPut(1,old); + table.rawPut(name,list); + } + list.rawPut(list.rawLength()+1,value); + } + } + return table; + } + + + public LuanFunction highlighter(LuanState luan,String queryStr,LuanFunction formatter) throws ParseException { + Query query = SaneQueryParser.parseQuery(mfp,queryStr); + Formatter fmt = new Formatter() { + public String highlightTerm(String originalText,TokenGroup tokenGroup) { + if( tokenGroup.getTotalScore() <= 0 ) + return originalText; + try { + return (String)Luan.first(formatter.call(luan,new Object[]{originalText})); + } catch(LuanException e) { + throw new LuanRuntimeException(e); + } + } + }; + Highlighter hl = new Highlighter( fmt, new QueryScorer(query) ); + hl.setTextFragmenter( new NullFragmenter() ); + return new LuanFunction() { + @Override public String call(LuanState luan,Object[] args) throws LuanException { + String text = (String)args[0]; + try { + String s = hl.getBestFragment(analyzer,null,text); + return s!=null ? s : text; + } catch(LuanRuntimeException e) { + throw (LuanException)e.getCause(); + } catch(IOException e) { + throw new RuntimeException(e); + } catch(InvalidTokenOffsetsException e) { + throw new RuntimeException(e); + } + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/Versioning.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,56 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local pairs = Luan.pairs or error() +local Number = require "luan:Number.luan" +local integer = Number.integer or error() +local long = Number.long or error() +local String = require "luan:String.luan" +local matches = String.matches or error() +local sub = String.sub or error() +local string_to_number = String.to_number or error() +local Table = require "luan:Table.luan" +local copy = Table.copy or error() +local Lucene = require "luan:lucene/Lucene.luan" +require "luan:logging/init.luan" +local Logging = require "luan:logging/Logging.luan" + +local logger = Logging.logger "lucene versioning" + + +local M = {} + +function M.update(db,steps,version) + local doc = db.get_document"type:version" or { type="version", version=integer(0) } + while doc.version < version do + doc.version = integer(doc.version + 1) + logger.error("step "..doc.version) + db.update_in_transaction( function() + local step = steps[doc.version] + step and step(db) + db.save(doc) + end ) + end +end + + +-- hack to deal with latest changes +function M.a_big_step(db) + db.indexed_fields["id index"] = Lucene.type.string + db.advanced_search( Lucene.literal"id index" .. ":*", function(_,doc_fn) + local doc = doc_fn() + for field, value in pairs(copy(doc)) do + if matches(field," index$") then + local new_field = sub(field,1,-7) + db.indexed_fields[new_field] or error("field '"..new_field.."' not indexed") + doc[new_field] = value + doc[field] = nil + end + end + doc.id = long(string_to_number(doc.id)) + db.save(doc) + end ) + db.indexed_fields["type index"] = Lucene.type.string + db.delete( Lucene.literal"type index" .. ":*" ) +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/Web_search.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,189 @@ + local Luan = require "luan:Luan.luan" +local error = Luan.error +local pairs = Luan.pairs or error() +local ipairs = Luan.ipairs or error() +local range = Luan.range or error() +local to_string = Luan.to_string or error() +local Io = require "luan:Io.luan" +local repr = Io.repr or error() +local Http = require "luan:http/Http.luan" +local String = require "luan:String.luan" +local string_to_number = String.to_number or error() +local Html = require "luan:Html.luan" + + +local M = {} + +local function style() %> + body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + margin: 2em 5%; + } + h2 { + margin-bottom: .5em; + } + label { + text-align: right; + min-width: 6em; + display: inline-block; + margin-right: .5em; + } +<% +end + +local function form() %> +<html> + <head> + <title>Lucene Query</title> + <style> + <% style() %> + input { + margin-top: 1em; + } + input[type="text"] { + font: inherit; + padding: .5em .8em; + border-radius: 8px; + border-style: groove; + } + input[type="text"]:focus { + border-color: #66afe9; + outline: none; + } + span[tip] { + color: #888; + font-size: smaller; + margin-left: .5em; + } + input[type="submit"] { + color: white; + background: #337ab7; + border-color: #337ab7; + font: inherit; + padding: .5em; + border-radius: 4px; + } + input[type="submit"]:hover { + background: #236aa7 !important; + } + </style> + </head> + <body> + <h2>Lucene Query</h2> + <form horizontal name="form0" method="post"> + <div> + <label>Query:</label> + <input type=text name="query" size="80" autofocus /> + </div> + <div> + <label></label> + <span tip>Query examples: <i>type:user</i> or <i>+type:user +name:Joe"</i></span> + </div> + <div> + <label>Max Rows:</label> + <input type=text name="rows" value="100" size="3" maxlength="5" /> + </div> + <div> + <label>Sort:</label> + <input type=text name="sort" size="60" /> + </div> + <div> + <label></label> + <span tip>Sort examples: <i>name, id</i></span> + </div> + <div> + <label></label> + <input type="submit" /> + </div> + </form> + </body> +</html> +<% end + + +local function result(query,sort,headers,table) %> +<html> + <head> + <title>Lucene Query</title> + <style> + <% style() %> + table { + border-collapse: collapse; + width: 100%; + font-size: smaller; + } + th, td { + text-align: left; + padding: .5em; + border: solid 1px #ddd; + } + </style> + </head> + <body> + <h2>Lucene Query Results</h2> + <p><label>Query:</label> <b><%=Html.encode(to_string(query))%></b></p> + <p><label>Sort:</label> <b><%=Html.encode(to_string(sort))%></b></p> + <table> + <tr> + <th></th> + <% for _, header in ipairs(headers) do %> + <th><%=header%></th> + <% end %> + </tr> + <% for i, row in ipairs(table) do %> + <tr> + <td><%=i%></td> + <% + for col in range(1, #headers) do + local val = row[col] + %><td><%= val and repr(val) or "" %></td><% + end + %> + </tr> + <% end %> + </table> + </body> +</html> +<% end + + +local function index_of(tbl,val) + for i, v in ipairs(tbl) do + if v == val then + return i + end + end + local n = #tbl + 1 + tbl[n] = val + return n +end + + +function M.of(index) + index or error "index is nil" + + return function() + Io.stdout = Http.response.text_writer() + local query = Http.request.parameter.query + if query == nil then + form() + return + end + local rows = string_to_number(Http.request.parameter.rows) + local sort = Http.request.parameter.sort + local results = index.search(query,1,rows,sort) + local headers = {} + local table = {} + for _, doc in ipairs(results) do + local row = {} + for field, value in pairs(doc) do + row[index_of(headers,field)] = value + end + table[#table+1] = row + end + result(query,sort,headers,table) + end + +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/queryparser/FieldParser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,11 @@ +package luan.modules.lucene.queryparser; + +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SortField; + + +public interface FieldParser { + public Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException; + public Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException; + public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) throws ParseException; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/queryparser/MultiFieldParser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,85 @@ +package luan.modules.lucene.queryparser; + +import java.util.Map; +import java.util.HashMap; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.SortField; + + +public class MultiFieldParser implements FieldParser { + + /** + * maps field name to FieldParser + */ + public final Map<String,FieldParser> fields = new HashMap<String,FieldParser>(); + public boolean allowUnspecifiedFields = false; + private final FieldParser defaultFieldParser; + private final String[] defaultFields; + + public MultiFieldParser() { + this.defaultFieldParser = null; + this.defaultFields = null; + } + + public MultiFieldParser(FieldParser defaultFieldParser,String... defaultFields) { + this.defaultFieldParser = defaultFieldParser; + this.defaultFields = defaultFields; + for( String field : defaultFields ) { + fields.put(field,defaultFieldParser); + } + } + + @Override public Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException { + if( field == null ) { + if( defaultFieldParser == null ) + throw new ParseException(qp,"no defaults were specified, so a field is required"); + if( defaultFields.length == 1 ) + return defaultFieldParser.getQuery(qp,defaultFields[0],query); + BooleanQuery bq = new BooleanQuery(); + for( String f : defaultFields ) { + bq.add( defaultFieldParser.getQuery(qp,f,query), BooleanClause.Occur.SHOULD ); + } + return bq; + } else { + FieldParser fp = fields.get(field); + if( fp != null ) + return fp.getQuery(qp,field,query); + if( allowUnspecifiedFields ) + return defaultFieldParser.getQuery(qp,field,query); + throw new ParseException(qp,"unrecognized field '"+field+"'"); + } + } + + @Override public Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException { + if( field == null ) { + if( defaultFieldParser == null ) + throw new ParseException(qp,"no defaults were specified, so a field is required"); + if( defaultFields.length == 1 ) + return defaultFieldParser.getRangeQuery(qp,defaultFields[0],minQuery,maxQuery,includeMin,includeMax); + BooleanQuery bq = new BooleanQuery(); + for( String f : defaultFields ) { + bq.add( defaultFieldParser.getRangeQuery(qp,f,minQuery,maxQuery,includeMin,includeMax), BooleanClause.Occur.SHOULD ); + } + return bq; + } else { + FieldParser fp = fields.get(field); + if( fp != null ) + return fp.getRangeQuery(qp,field,minQuery,maxQuery,includeMin,includeMax); + if( allowUnspecifiedFields ) + return defaultFieldParser.getRangeQuery(qp,field,minQuery,maxQuery,includeMin,includeMax); + throw new ParseException(qp,"field '"+field+"' not specified"); + } + } + + @Override public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) throws ParseException { + FieldParser fp = fields.get(field); + if( fp != null ) + return fp.getSortField(qp,field,reverse); + if( allowUnspecifiedFields ) + return defaultFieldParser.getSortField(qp,field,reverse); + throw new ParseException(qp,"field '"+field+"' not specified"); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/queryparser/NumberFieldParser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,83 @@ +package luan.modules.lucene.queryparser; + +import org.apache.lucene.search.Query; +import org.apache.lucene.search.NumericRangeQuery; +import org.apache.lucene.search.SortField; + + +public abstract class NumberFieldParser implements FieldParser { + + @Override public final Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException { + return getRangeQuery(qp,field,query,query,true,true); + } + + @Override public final Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException { + try { + return getRangeQuery(field,minQuery,maxQuery,includeMin,includeMax); + } catch(NumberFormatException e) { + throw new ParseException(qp,e); + } + } + + abstract protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax); + + @Override public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) { + return new SortField( field, sortType(), reverse ); + } + + abstract protected SortField.Type sortType(); + + + public static final FieldParser INT = new NumberFieldParser() { + + @Override protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) { + int min = Integer.parseInt(minQuery); + int max = Integer.parseInt(maxQuery); + return NumericRangeQuery.newIntRange(field,min,max,includeMin,includeMax); + } + + @Override protected SortField.Type sortType() { + return SortField.Type.INT; + } + }; + + public static final FieldParser LONG = new NumberFieldParser() { + + @Override protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) { + long min = Long.parseLong(minQuery); + long max = Long.parseLong(maxQuery); + return NumericRangeQuery.newLongRange(field,min,max,includeMin,includeMax); + } + + @Override protected SortField.Type sortType() { + return SortField.Type.LONG; + } + }; + + public static final FieldParser FLOAT = new NumberFieldParser() { + + @Override protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) { + float min = Float.parseFloat(minQuery); + float max = Float.parseFloat(maxQuery); + return NumericRangeQuery.newFloatRange(field,min,max,includeMin,includeMax); + } + + @Override protected SortField.Type sortType() { + return SortField.Type.FLOAT; + } + }; + + public static final FieldParser DOUBLE = new NumberFieldParser() { + + @Override protected Query getRangeQuery(String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) { + double min = Double.parseDouble(minQuery); + double max = Double.parseDouble(maxQuery); + return NumericRangeQuery.newDoubleRange(field,min,max,includeMin,includeMax); + } + + @Override protected SortField.Type sortType() { + return SortField.Type.DOUBLE; + } + }; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/queryparser/ParseException.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,33 @@ +package luan.modules.lucene.queryparser; + + +public final class ParseException extends Exception { + public final String text; + public final int errorIndex; + public final int highIndex; + + public ParseException(SaneQueryParser qp) { + this(qp,"Invalid input"); + } + + public ParseException(SaneQueryParser qp,String msg) { + super(msg+" at position "+(qp.parser.errorIndex()+1)); + Parser parser = qp.parser; + this.text = parser.text; + this.errorIndex = parser.errorIndex(); + this.highIndex = parser.highIndex(); + } + + public ParseException(SaneQueryParser qp,Exception cause) { + this(qp,cause.getMessage(),cause); + } + + public ParseException(SaneQueryParser qp,String msg,Exception cause) { + super(msg+" at position "+(qp.parser.errorIndex()+1),cause); + Parser parser = qp.parser; + this.text = parser.text; + this.errorIndex = parser.errorIndex(); + this.highIndex = parser.highIndex(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/queryparser/Parser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,155 @@ +package luan.modules.lucene.queryparser; + +/** + * A general parser utility class. + * This class is not needed to use SaneQueryParser. + */ +public class Parser { + public final String text; + private final int len; + private int[] stack = new int[256]; + private int frame = 0; + private int iHigh; + + Parser(String text) { + this.text = text; + this.len = text.length(); + } + + private int i() { + return stack[frame]; + } + + private void i(int i) { + stack[frame] += i; + if( iHigh < stack[frame] ) + iHigh = stack[frame]; + } + + public int begin() { + frame++; + if( frame == stack.length ) { + int[] a = new int[2*frame]; + System.arraycopy(stack,0,a,0,frame); + stack = a; + } + stack[frame] = stack[frame-1]; + return i(); + } + + public void rollback() { + stack[frame] = stack[frame-1]; + } + + public <T> T success(T t) { + success(); + return t; + } + + public boolean success() { + frame--; + stack[frame] = stack[frame+1]; + return true; + } + + public <T> T failure(T t) { + failure(); + return t; + } + + public boolean failure() { + frame--; + return false; + } + + public int currentIndex() { + return i(); + } + + public int errorIndex() { + return frame > 0 ? stack[frame-1] : 0; + } + + public int highIndex() { + return iHigh; + } + + public char lastChar() { + return text.charAt(i()-1); + } + + public char currentChar() { + return text.charAt(i()); + } + + public boolean endOfInput() { + return i() >= len; + } + + public boolean match(char c) { + if( endOfInput() || text.charAt(i()) != c ) + return false; + i(1); + return true; + } + + public boolean match(String s) { + int n = s.length(); + if( !text.regionMatches(i(),s,0,n) ) + return false; + i(n); + return true; + } + + public boolean matchIgnoreCase(String s) { + int n = s.length(); + if( !text.regionMatches(true,i(),s,0,n) ) + return false; + i(n); + return true; + } + + public boolean anyOf(String s) { + if( endOfInput() || s.indexOf(text.charAt(i())) == -1 ) + return false; + i(1); + return true; + } + + public boolean noneOf(String s) { + if( endOfInput() || s.indexOf(text.charAt(i())) != -1 ) + return false; + i(1); + return true; + } + + public boolean inCharRange(char cLow, char cHigh) { + if( endOfInput() ) + return false; + char c = text.charAt(i()); + if( !(cLow <= c && c <= cHigh) ) + return false; + i(1); + return true; + } + + public boolean anyChar() { + if( endOfInput() ) + return false; + i(1); + return true; + } + + public boolean test(char c) { + return !endOfInput() && text.charAt(i()) == c; + } + + public boolean test(String s) { + return text.regionMatches(i(),s,0,s.length()); + } + + public String textFrom(int start) { + return text.substring(start,i()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/queryparser/SaneQueryParser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,247 @@ +package luan.modules.lucene.queryparser; + +import java.util.List; +import java.util.ArrayList; +import java.util.regex.Pattern; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; + + +public class SaneQueryParser { + + public static Query parseQuery(FieldParser fieldParser,String query) throws ParseException { + return new SaneQueryParser(fieldParser,query).parseQuery(); + } + + private static Pattern specialChar = Pattern.compile("[ \\t\\r\\n\":\\[\\]{}^+\\-(),?*\\\\]"); + + public static String literal(String s) { + return specialChar.matcher(s).replaceAll("\\\\$0"); + } + + public static Sort parseSort(FieldParser fieldParser,String sort) throws ParseException { + return new SaneQueryParser(fieldParser,sort).parseSort(); + } + + + private static final String NOT_IN_TERM = " \t\r\n\":[]{}^+-()"; + private static final String NOT_IN_FIELD = NOT_IN_TERM + ","; + private final FieldParser fieldParser; + final Parser parser; + + private SaneQueryParser(FieldParser fieldParser,String query) { + this.fieldParser = fieldParser; + this.parser = new Parser(query); + } + + private Query parseQuery() throws ParseException { + Spaces(); + BooleanQuery bq = new BooleanQuery(); + while( !parser.endOfInput() ) { + bq.add( Term(null) ); + } + BooleanClause[] clauses = bq.getClauses(); + switch( clauses.length ) { + case 0: + return new MatchAllDocsQuery(); + case 1: + { + BooleanClause bc = clauses[0]; + if( bc.getOccur() != BooleanClause.Occur.MUST_NOT ) + return bc.getQuery(); + } + default: + return bq; + } + } + + private BooleanClause Term(String defaultField) throws ParseException { + BooleanClause.Occur occur; + if( parser.match('+') ) { + occur = BooleanClause.Occur.MUST; + Spaces(); + } else if( parser.match('-') ) { + occur = BooleanClause.Occur.MUST_NOT; + Spaces(); + } else { + occur = BooleanClause.Occur.SHOULD; + } + String field = QueryField(); + if( field == null ) + field = defaultField; + Query query = NestedTerm(field); + if( query == null ) + query = RangeTerm(field); + if( query == null ) { + parser.begin(); + String match = SimpleTerm(); + query = fieldParser.getQuery(this,field,match); + parser.success(); + } + if( parser.match('^') ) { + Spaces(); + int start = parser.begin(); + try { + while( parser.anyOf("0123456789.") ); + String match = parser.textFrom(start); + float boost = Float.parseFloat(match); + query.setBoost(boost); + } catch(NumberFormatException e) { + throw new ParseException(this,e); + } + parser.success(); + Spaces(); + } + BooleanClause bc = new BooleanClause(query,occur); + return bc; + } + + private Query NestedTerm(String field) throws ParseException { + parser.begin(); + if( !parser.match('(') ) + return parser.failure(null); + BooleanQuery bq = new BooleanQuery(); + while( !parser.match(')') ) { + if( parser.endOfInput() ) + throw new ParseException(this,"unclosed parentheses"); + bq.add( Term(field) ); + } + Spaces(); + BooleanClause[] clauses = bq.getClauses(); + switch( clauses.length ) { + case 0: + throw new ParseException(this,"empty parentheses"); + case 1: + { + BooleanClause bc = clauses[0]; + if( bc.getOccur() != BooleanClause.Occur.MUST_NOT ) + return parser.success(bc.getQuery()); + } + default: + return parser.success(bq); + } + } + + private Query RangeTerm(String field) throws ParseException { + parser.begin(); + if( !parser.anyOf("[{") ) + return parser.failure(null); + boolean includeMin = parser.lastChar() == '['; + Spaces(); + String minQuery = SimpleTerm(); + TO(); + String maxQuery = SimpleTerm(); + if( !parser.anyOf("]}") ) + throw new ParseException(this,"unclosed range"); + boolean includeMax = parser.lastChar() == ']'; + Spaces(); + Query query = fieldParser.getRangeQuery(this,field,minQuery,maxQuery,includeMin,includeMax); + return parser.success(query); + } + + private void TO() throws ParseException { + parser.begin(); + if( !(parser.match("TO") && Space()) ) + throw new ParseException(this,"'TO' expected"); + Spaces(); + parser.success(); + } + + private String SimpleTerm() throws ParseException { + parser.begin(); + String match; + if( parser.match('"') ) { + int start = parser.currentIndex() - 1; + while( !parser.match('"') ) { + if( parser.endOfInput() ) + throw new ParseException(this,"unclosed quotes"); + parser.anyChar(); + checkEscape(); + } + match = parser.textFrom(start); + Spaces(); + } else { + match = Unquoted(NOT_IN_TERM); + } + if( match.length() == 0 ) + throw new ParseException(this); + return parser.success(match); + } + + private String QueryField() throws ParseException { + parser.begin(); + String match = Field(); + if( match==null || !parser.match(':') ) + return parser.failure((String)null); + Spaces(); + return parser.success(match); + } + + private String Field() throws ParseException { + parser.begin(); + String match = Unquoted(NOT_IN_FIELD); + if( match.length()==0 ) + return parser.failure((String)null); + match = StringFieldParser.escape(this,match); + return parser.success(match); + } + + private String Unquoted(String exclude) throws ParseException { + int start = parser.begin(); + while( parser.noneOf(exclude) ) { + checkEscape(); + } + String match = parser.textFrom(start); + Spaces(); + return parser.success(match); + } + + private void checkEscape() { + if( parser.lastChar() == '\\' ) + parser.anyChar(); + } + + private void Spaces() { + while( Space() ); + } + + private boolean Space() { + return parser.anyOf(" \t\r\n"); + } + + + // sort + + private Sort parseSort() throws ParseException { + Spaces(); + if( parser.endOfInput() ) + return null; + List<SortField> list = new ArrayList<SortField>(); + list.add( SortField() ); + while( !parser.endOfInput() ) { + parser.begin(); + if( !parser.match(',') ) + throw new ParseException(this,"',' expected"); + Spaces(); + parser.success(); + list.add( SortField() ); + } + return new Sort(list.toArray(new SortField[0])); + } + + private SortField SortField() throws ParseException { + parser.begin(); + String field = Field(); + if( field==null ) + throw new ParseException(this); + boolean reverse = !parser.matchIgnoreCase("asc") && parser.matchIgnoreCase("desc"); + Spaces(); + SortField sf = fieldParser.getSortField(this,field,reverse); + return parser.success(sf); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/queryparser/StringFieldParser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,112 @@ +package luan.modules.lucene.queryparser; + +import java.io.StringReader; +import java.io.IOException; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TermRangeQuery; +import org.apache.lucene.search.PhraseQuery; +import org.apache.lucene.search.WildcardQuery; +import org.apache.lucene.search.PrefixQuery; +import org.apache.lucene.search.SortField; +import org.apache.lucene.index.Term; + + +public class StringFieldParser implements FieldParser { + public int slop = 0; + public final Analyzer analyzer; + + public StringFieldParser(Analyzer analyzer) { + this.analyzer = analyzer; + } + + @Override public Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException { + String wildcard = wildcard(qp,query); + if( wildcard != null ) + return new WildcardQuery(new Term(field,wildcard)); + if( query.endsWith("*") && !query.endsWith("\\*") ) + return new PrefixQuery(new Term(field,query.substring(0,query.length()-1))); + query = escape(qp,query); + PhraseQuery pq = new PhraseQuery(); + try { + TokenStream ts = analyzer.tokenStream(field,new StringReader(query)); + CharTermAttribute termAttr = ts.addAttribute(CharTermAttribute.class); + PositionIncrementAttribute posAttr = ts.addAttribute(PositionIncrementAttribute.class); + ts.reset(); + int pos = -1; + while( ts.incrementToken() ) { + pos += posAttr.getPositionIncrement(); + pq.add( new Term(field,termAttr.toString()), pos ); + } + ts.end(); + ts.close(); + } catch(IOException e) { + throw new RuntimeException(e); + } + Term[] terms = pq.getTerms(); + if( terms.length==1 && pq.getPositions()[0]==0 ) + return new TermQuery(terms[0]); + return pq; + } + + @Override public Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException { + minQuery = escape(qp,minQuery); + maxQuery = escape(qp,maxQuery); + return TermRangeQuery.newStringRange(field,minQuery,maxQuery,includeMin,includeMax); + } + + static String escape(SaneQueryParser qp,String s) throws ParseException { + final char[] a = s.toCharArray(); + int i, n; + if( a[0] == '"' ) { + if( a[a.length-1] != '"' ) throw new RuntimeException(); + i = 1; + n = a.length - 1; + } else { + i = 0; + n = a.length; + } + StringBuilder sb = new StringBuilder(); + for( ; i<n; i++ ) { + char c = a[i]; + if( c == '\\' ) { + if( ++i == a.length ) + throw new ParseException(qp,"ends with '\\'"); + c = a[i]; + } + sb.append(c); + } + return sb.toString(); + } + + private static String wildcard(SaneQueryParser qp,String s) throws ParseException { + final char[] a = s.toCharArray(); + if( a[0] == '"' ) + return null; + boolean hasWildcard = false; + StringBuilder sb = new StringBuilder(); + for( int i=0; i<a.length; i++ ) { + char c = a[i]; + if( c=='?' || c=='*' && i<a.length-1 ) + hasWildcard = true; + if( c == '\\' ) { + if( ++i == a.length ) + throw new ParseException(qp,"ends with '\\'"); + c = a[i]; + if( c=='?' || c=='*' ) + sb.append('\\'); + } + sb.append(c); + } + return hasWildcard ? sb.toString() : null; + } + + @Override public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) { + return new SortField( field, SortField.Type.STRING, reverse ); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/lucene/queryparser/SynonymParser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,42 @@ +package luan.modules.lucene.queryparser; + +import java.util.Map; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.SortField; + + +public class SynonymParser implements FieldParser { + private final FieldParser fp; + private final Map<String,String[]> synonymMap; + + public SynonymParser(FieldParser fp,Map<String,String[]> synonymMap) { + this.fp = fp; + this.synonymMap = synonymMap; + } + + protected String[] getSynonyms(String query) { + return synonymMap.get(query); + } + + public Query getQuery(SaneQueryParser qp,String field,String query) throws ParseException { + String[] synonyms = getSynonyms(query); + if( synonyms == null ) + return fp.getQuery(qp,field,query); + BooleanQuery bq = new BooleanQuery(); + bq.add( fp.getQuery(qp,field,query), BooleanClause.Occur.SHOULD ); + for( String s : synonyms ) { + bq.add( fp.getQuery(qp,field,s), BooleanClause.Occur.SHOULD ); + } + return bq; + } + + public Query getRangeQuery(SaneQueryParser qp,String field,String minQuery,String maxQuery,boolean includeMin,boolean includeMax) throws ParseException { + return fp.getRangeQuery(qp,field,minQuery,maxQuery,includeMin,includeMax); + } + + public SortField getSortField(SaneQueryParser qp,String field,boolean reverse) throws ParseException { + return fp.getSortField(qp,field,reverse); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/mail/Mail.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,17 @@ +java() +local Luan = require "luan:Luan.luan" +local assert_table = Luan.assert_table +local System = require "java:java.lang.System" +local SmtpCon = require "java:luan.modules.mail.SmtpCon" + +local M = {} + +System.setProperty( "mail.mime.charset", "UTF-8" ) + +function M.Sender(params) + assert_table(params) + local smtpCon = SmtpCon.new(params) + return { send = smtpCon.send } +end + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/mail/SmtpCon.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,187 @@ +package luan.modules.mail; + +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; +import java.util.Properties; +import javax.mail.Authenticator; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Part; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; +import javax.mail.internet.MimeBodyPart; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanException; + + +public final class SmtpCon { + private final Session session; + + public SmtpCon(LuanState luan,LuanTable paramsTbl) throws LuanException { + Map<Object,Object> params = new HashMap<Object,Object>(paramsTbl.asMap(luan)); + Properties props = new Properties(System.getProperties()); + + String host = getString(params,"host"); + if( host==null ) + throw new LuanException( "parameter 'host' is required" ); + props.setProperty("mail.smtp.host",host); + + Object port = params.remove("port"); + if( port != null ) { + String s; + if( port instanceof String ) { + s = (String)port; + } else if( port instanceof Number ) { + Integer i = Luan.asInteger(port); + if( i == null ) + throw new LuanException( "parameter 'port' must be an integer" ); + s = i.toString(); + } else { + throw new LuanException( "parameter 'port' must be an integer" ); + } + props.setProperty("mail.smtp.socketFactory.port", s); + props.setProperty("mail.smtp.port", s); + } + + String username = getString(params,"username"); + if( username == null ) { + session = Session.getInstance(props); + } else { + String password = getString(params,"password"); + if( password==null ) + throw new LuanException( "parameter 'password' is required with 'username'" ); + props.setProperty("mail.smtp.auth","true"); + final PasswordAuthentication pa = new PasswordAuthentication(username,password); + Authenticator auth = new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return pa; + } + }; + session = Session.getInstance(props,auth); + } + + if( !params.isEmpty() ) + throw new LuanException( "unrecognized parameters: "+params ); + } + + private String getString(Map<Object,Object> params,String key) throws LuanException { + Object val = params.remove(key); + if( val!=null && !(val instanceof String) ) + throw new LuanException( "parameter '"+key+"' must be a string" ); + return (String)val; + } + + + public void send(LuanState luan,LuanTable mailTbl) throws LuanException { + try { + Map<Object,Object> mailParams = new HashMap<Object,Object>(mailTbl.asMap(luan)); + MimeMessage msg = new MimeMessage(session); + + String from = getString(mailParams,"from"); + if( from != null ) + msg.setFrom(from); + + String to = getString(mailParams,"to"); + if( to != null ) + msg.setRecipients(Message.RecipientType.TO,to); + + String cc = getString(mailParams,"cc"); + if( cc != null ) + msg.setRecipients(Message.RecipientType.CC,cc); + + String subject = getString(mailParams,"subject"); + if( subject != null ) + msg.setSubject(subject); + + Object body = mailParams.remove("body"); + Object attachments = mailParams.remove("attachments"); + Part bodyPart = attachments==null ? msg : new MimeBodyPart(); + + if( body != null ) { + if( body instanceof String ) { + bodyPart.setText((String)body); + } else if( body instanceof LuanTable ) { + LuanTable bodyTbl = (LuanTable)body; + Map<Object,Object> map = new HashMap<Object,Object>(bodyTbl.asMap(luan)); + MimeMultipart mp = new MimeMultipart("alternative"); + String text = (String)map.remove("text"); + if( text != null ) { + MimeBodyPart part = new MimeBodyPart(); + part.setText(text); + mp.addBodyPart(part); + } + String html = (String)map.remove("html"); + if( html != null ) { + MimeBodyPart part = new MimeBodyPart(); + part.setContent(html,"text/html"); + mp.addBodyPart(part); + } + if( !map.isEmpty() ) + throw new LuanException( "invalid body types: " + map ); + bodyPart.setContent(mp); + } else + throw new LuanException( "parameter 'body' must be a string or table" ); + } + + if( attachments != null ) { + if( !(attachments instanceof LuanTable) ) + throw new LuanException( "parameter 'attachments' must be a table" ); + LuanTable attachmentsTbl = (LuanTable)attachments; + if( !attachmentsTbl.isList() ) + throw new LuanException( "parameter 'attachments' must be a list" ); + MimeMultipart mp = new MimeMultipart("mixed"); + if( body != null ) + mp.addBodyPart((MimeBodyPart)bodyPart); + for( Object attachment : attachmentsTbl.asList() ) { + if( !(attachment instanceof LuanTable) ) + throw new LuanException( "each attachment must be a table" ); + Map<Object,Object> attachmentMap = new HashMap<Object,Object>(((LuanTable)attachment).asMap(luan)); + Object obj; + + obj = attachmentMap.remove("filename"); + if( obj==null ) + throw new LuanException( "an attachment is missing 'filename'" ); + if( !(obj instanceof String) ) + throw new LuanException( "an attachment filename must be a string" ); + String filename = (String)obj; + + obj = attachmentMap.remove("content_type"); + if( obj==null ) + throw new LuanException( "an attachment is missing 'content_type'" ); + if( !(obj instanceof String) ) + throw new LuanException( "an attachment content_type must be a string" ); + String content_type = (String)obj; + + Object content = attachmentMap.remove("content"); + if( content==null ) + throw new LuanException( "an attachment is missing 'content'" ); + if( content_type.startsWith("text/") && content instanceof byte[] ) + content = new String((byte[])content); + + if( !attachmentMap.isEmpty() ) + throw new LuanException( "unrecognized attachment parameters: "+attachmentMap ); + + MimeBodyPart part = new MimeBodyPart(); + part.setContent(content,content_type); + part.setFileName(filename); + mp.addBodyPart(part); + } + msg.setContent(mp); + } + + if( !mailParams.isEmpty() ) + throw new LuanException( "unrecognized parameters: "+mailParams ); + + Transport.send(msg); + } catch(MessagingException e) { + throw new LuanException(e); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/mmake.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,59 @@ +local Luan = require "luan:Luan.luan" +local ipairs = Luan.ipairs +local Table = require "luan:Table.luan" +local Io = require "luan:Io.luan" +local print = Io.print +local String = require "luan:String.luan" +local Time = require "luan:Time.luan" + + +local compiler = Table.concat( { "javac -g -encoding UTF8", ... }, " " ) + + +local function header() + return %> +# Makefile created on <%=Time.format(Time.now())%> by Mmake + +.SUFFIXES: .java .class + +.java.class: + <%=compiler%> '$<' + +all: <% +end + + +local function mmake(dir) + local javas = {} + local dirs = {} + for _, file in ipairs(dir.children()) do + local name = file.name() + if String.matches(name,[[\.java$]]) then + javas[#javas+1] = String.sub(name,1,-6) + end + if file.is_directory() and mmake(file) then + dirs[#dirs+1] = name + end + end + if #javas == 0 and #dirs == 0 then + return false; + end + local out = dir.child("Makefile").text_writer() + out.write( header() ) + for _, s in ipairs(javas) do + s = String.gsub(s,[[\$]],[[\$\$]]) + out.write( "\\\n\t\t", s , ".class" ) + end + for _, s in ipairs(dirs) do + out.write( "\n\tcd ", s, "; make all" ) + end + out.write "\n\nclean:\n\trm -f *.class\n" + for _, s in ipairs(dirs) do + out.write( "\tcd ", s, "; make clean\n" ) + end + out.close() + print(dir.to_string()) + return true +end + +mmake(Io.schemes.file ".")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/BBCode.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,311 @@ +package luan.modules.parsers; + +import java.util.List; +import java.util.ArrayList; +import luan.Luan; +import luan.LuanState; +import luan.LuanFunction; +import luan.LuanException; +import luan.modules.Utils; + + +public final class BBCode { + + public static String toHtml(LuanState luan,String bbcode,LuanFunction quoter) throws LuanException { + return new BBCode(luan,bbcode,quoter,true).parse(); + } + + public static String toText(LuanState luan,String bbcode,LuanFunction quoter) throws LuanException { + return new BBCode(luan,bbcode,quoter,false).parse(); + } + + private final LuanState luan; + private final Parser parser; + private final LuanFunction quoter; + private final boolean toHtml; + + private BBCode(LuanState luan,String text,LuanFunction quoter,boolean toHtml) throws LuanException { + Utils.checkNotNull(text,1); + Utils.checkNotNull(quoter,2); + this.luan = luan; + this.parser = new Parser(text); + this.quoter = quoter; + this.toHtml = toHtml; + } + + private String parse() throws LuanException { + StringBuilder sb = new StringBuilder(); + while( !parser.endOfInput() ) { + String block = parseBlock(); + if( block != null ) + sb.append(block); + else { + sb.append( parser.currentChar() ); + parser.anyChar(); + } + } + return sb.toString(); + } + + private String parseWellFormed() throws LuanException { + StringBuilder sb = new StringBuilder(); + while( !parser.endOfInput() ) { + String block = parseBlock(); + if( block != null ) { + sb.append(block); + continue; + } + if( couldBeTag() ) + break; + sb.append( parser.currentChar() ); + parser.anyChar(); + } + return sb.toString(); + } + + private boolean couldBeTag() { + if( parser.currentChar() != '[' ) + return false; + return parser.testIgnoreCase("[b]") + || parser.testIgnoreCase("[/b]") + || parser.testIgnoreCase("[i]") + || parser.testIgnoreCase("[/i]") + || parser.testIgnoreCase("[u]") + || parser.testIgnoreCase("[/u]") + || parser.testIgnoreCase("[url]") + || parser.testIgnoreCase("[url=") + || parser.testIgnoreCase("[/url]") + || parser.testIgnoreCase("[code]") + || parser.testIgnoreCase("[/code]") + || parser.testIgnoreCase("[img]") + || parser.testIgnoreCase("[/img]") + || parser.testIgnoreCase("[color=") + || parser.testIgnoreCase("[/color]") + || parser.testIgnoreCase("[size=") + || parser.testIgnoreCase("[/size]") + || parser.testIgnoreCase("[youtube]") + || parser.testIgnoreCase("[/youtube]") + || parser.testIgnoreCase("[quote]") + || parser.testIgnoreCase("[quote=") + || parser.testIgnoreCase("[/quote]") + ; + } + + private String parseBlock() throws LuanException { + if( parser.currentChar() != '[' ) + return null; + String s; + s = parseB(); if(s!=null) return s; + s = parseI(); if(s!=null) return s; + s = parseU(); if(s!=null) return s; + s = parseUrl1(); if(s!=null) return s; + s = parseUrl2(); if(s!=null) return s; + s = parseCode(); if(s!=null) return s; + s = parseImg(); if(s!=null) return s; + s = parseColor(); if(s!=null) return s; + s = parseSize(); if(s!=null) return s; + s = parseYouTube(); if(s!=null) return s; + s = parseQuote1(); if(s!=null) return s; + s = parseQuote2(); if(s!=null) return s; + return null; + } + + private String parseB() throws LuanException { + parser.begin(); + if( !parser.matchIgnoreCase("[b]") ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/b]") ) + return parser.failure(null); + String rtn = toHtml ? "<b>"+content+"</b>" : content; + return parser.success(rtn); + } + + private String parseI() throws LuanException { + parser.begin(); + if( !parser.matchIgnoreCase("[i]") ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/i]") ) + return parser.failure(null); + String rtn = toHtml ? "<i>"+content+"</i>" : content; + return parser.success(rtn); + } + + private String parseU() throws LuanException { + parser.begin(); + if( !parser.matchIgnoreCase("[u]") ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/u]") ) + return parser.failure(null); + String rtn = toHtml ? "<u>"+content+"</u>" : content; + return parser.success(rtn); + } + + private String parseUrl1() { + parser.begin(); + if( !parser.matchIgnoreCase("[url]") ) + return parser.failure(null); + String url = parseRealUrl(); + if( !parser.matchIgnoreCase("[/url]") ) + return parser.failure(null); + String rtn = toHtml ? "<a href='"+url+"'>"+url+"</u>" : url; + return parser.success(rtn); + } + + private String parseUrl2() throws LuanException { + parser.begin(); + if( !parser.matchIgnoreCase("[url=") ) + return parser.failure(null); + String url = parseRealUrl(); + if( !parser.match(']') ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/url]") ) + return parser.failure(null); + String rtn = toHtml ? "<a href='"+url+"'>"+content+"</u>" : content; + return parser.success(rtn); + } + + private String parseRealUrl() { + parser.begin(); + while( parser.match(' ') ); + int start = parser.currentIndex(); + if( !parser.matchIgnoreCase("http") ) + return parser.failure(null); + parser.matchIgnoreCase("s"); + if( !parser.matchIgnoreCase("://") ) + return parser.failure(null); + while( parser.noneOf(" []'") ); + String url = parser.textFrom(start); + while( parser.match(' ') ); + return parser.success(url); + } + + private String parseCode() { + parser.begin(); + if( !parser.matchIgnoreCase("[code]") ) + return parser.failure(null); + int start = parser.currentIndex(); + while( !parser.testIgnoreCase("[/code]") ) { + if( !parser.anyChar() ) + return parser.failure(null); + } + String content = parser.textFrom(start); + if( !parser.matchIgnoreCase("[/code]") ) throw new RuntimeException(); + String rtn = toHtml ? "<code>"+content+"</code>" : content; + return parser.success(rtn); + } + + private String parseImg() { + parser.begin(); + if( !parser.matchIgnoreCase("[img]") ) + return parser.failure(null); + String url = parseRealUrl(); + if( !parser.matchIgnoreCase("[/img]") ) + return parser.failure(null); + String rtn = toHtml ? "<img src='"+url+"'>" : ""; + return parser.success(rtn); + } + + private String parseColor() throws LuanException { + parser.begin(); + if( !parser.matchIgnoreCase("[color=") ) + return parser.failure(null); + int start = parser.currentIndex(); + parser.match('#'); + while( parser.inCharRange('0','9') + || parser.inCharRange('a','z') + || parser.inCharRange('A','Z') + ); + String color = parser.textFrom(start); + if( !parser.match(']') ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/color]") ) + return parser.failure(null); + String rtn = toHtml ? "<span style='color: "+color+"'>"+content+"</span>" : content; + return parser.success(rtn); + } + + private String parseSize() throws LuanException { + parser.begin(); + if( !parser.matchIgnoreCase("[size=") ) + return parser.failure(null); + int start = parser.currentIndex(); + while( parser.match('.') || parser.inCharRange('0','9') ); + String size = parser.textFrom(start); + if( !parser.match(']') ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/size]") ) + return parser.failure(null); + String rtn = toHtml ? "<span style='font-size: "+size+"em'>"+content+"</span>" : content; + return parser.success(rtn); + } + + private String parseYouTube() { + parser.begin(); + if( !parser.matchIgnoreCase("[youtube]") ) + return parser.failure(null); + int start = parser.currentIndex(); + while( parser.inCharRange('0','9') + || parser.inCharRange('a','z') + || parser.inCharRange('A','Z') + || parser.match('-') + || parser.match('_') + ); + String id = parser.textFrom(start); + if( id.length()==0 || !parser.matchIgnoreCase("[/youtube]") ) + return parser.failure(null); + String rtn = toHtml ? "<iframe width='420' height='315' src='https://www.youtube.com/embed/"+id+"' frameborder='0' allowfullscreen></iframe>" : ""; + return parser.success(rtn); + } + + private String quote(Object... args) throws LuanException { + Object obj = quoter.call(luan,args); + if( !(obj instanceof String) ) + throw new LuanException("BBCode quoter function returned "+Luan.type(obj)+" but string required"); + return (String)obj; + } + + private String parseQuote1() throws LuanException { + parser.begin(); + if( !parser.matchIgnoreCase("[quote]") ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/quote]") ) + return parser.failure(null); + String rtn = quote(content); + return parser.success(rtn); + } + + private String parseQuote2() throws LuanException { + parser.begin(); + if( !parser.matchIgnoreCase("[quote=") ) + return parser.failure(null); + List args = new ArrayList(); + int start = parser.currentIndex(); + while( parser.noneOf("[];") ); + String name = parser.textFrom(start).trim(); + if( name.length() == 0 ) + return parser.failure(null); + args.add(name); + while( parser.match(';') ) { + start = parser.currentIndex(); + while( parser.noneOf("[];'") ); + String src = parser.textFrom(start).trim(); + args.add(src); + } + if( !parser.match(']') ) + return parser.failure(null); + String content = parseWellFormed(); + args.add(0,content); + if( !parser.matchIgnoreCase("[/quote]") ) + return parser.failure(null); + String rtn = quote(args.toArray()); + return parser.success(rtn); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/Csv.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,61 @@ +package luan.modules.parsers; + +import luan.LuanTable; + + +public final class Csv { + + public static LuanTable toList(String line) throws ParseException { + return new Csv(line).parse(); + } + + private final Parser parser; + + private Csv(String line) { + this.parser = new Parser(line); + } + + private ParseException exception(String msg) { + return new ParseException(parser,msg); + } + + private LuanTable parse() throws ParseException { + LuanTable list = new LuanTable(); + while(true) { + Spaces(); + String field = parseField(); + list.rawPut(list.rawLength()+1,field); + Spaces(); + if( parser.endOfInput() ) + return list; + if( !parser.match(',') ) + throw exception("unexpected char"); + } + } + + private String parseField() throws ParseException { + parser.begin(); + String rtn; + if( parser.match('"') ) { + int start = parser.currentIndex(); + do { + if( parser.endOfInput() ) { + parser.failure(); + throw exception("unclosed quote"); + } + } while( parser.noneOf("\"") ); + rtn = parser.textFrom(start); + parser.match('"'); + } else { + int start = parser.currentIndex(); + while( !parser.endOfInput() && parser.noneOf(",") ); + rtn = parser.textFrom(start).trim(); + } + return parser.success(rtn); + } + + private void Spaces() { + while( parser.anyOf(" \t") ); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/Html.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,197 @@ +package luan.modules.parsers; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import luan.LuanTable; + + +public final class Html { + + public static LuanTable toList(String text,LuanTable containerTagsTbl) throws ParseException { + return new Html(text,containerTagsTbl).parse(); + } + + private final Parser parser; + private final Set<String> containerTags = new HashSet<String>(); + + private Html(String text,LuanTable containerTagsTbl) { + this.parser = new Parser(text); + for( Object v : containerTagsTbl.asList() ) { + containerTags.add((String)v); + } + } + + private LuanTable parse() throws ParseException { + List list = new ArrayList(); + StringBuilder sb = new StringBuilder(); + while( !parser.endOfInput() ) { + if( parser.test('<') ) { + LuanTable tbl = parseTag(); + if( tbl != null ) { + String tagName = (String)tbl.rawGet("name"); + if( containerTags.contains(tagName) ) { + LuanTable container = parseContainer(tbl); + if( container != null ) + tbl = container; + } + if( tbl != null + || (tbl = parseComment()) != null + || (tbl = parseCdata()) != null + ) { + if( sb.length() > 0 ) { + list.add(sb.toString()); + sb.setLength(0); + } + list.add(tbl); + continue; + } + } + } + sb.append( parser.currentChar() ); + parser.anyChar(); + } + if( sb.length() > 0 ) + list.add(sb.toString()); + return new LuanTable(list); + } + + private LuanTable parseComment() { + parser.begin(); + if( !parser.match("<!--") ) + return parser.failure(null); + int start = parser.currentIndex(); + while( !parser.test("-->") ) { + if( !parser.anyChar() ) + return parser.failure(null); + } + String text = parser.textFrom(start); + LuanTable tbl = new LuanTable(); + tbl.rawPut("type","comment"); + tbl.rawPut("text",text); + return parser.success(tbl); + } + + private LuanTable parseCdata() { + parser.begin(); + if( !parser.match("<![CDATA[") ) + return parser.failure(null); + int start = parser.currentIndex(); + while( !parser.test("]]>") ) { + if( !parser.anyChar() ) + return parser.failure(null); + } + String text = parser.textFrom(start); + LuanTable tbl = new LuanTable(); + tbl.rawPut("type","cdata"); + tbl.rawPut("text",text); + return parser.success(tbl); + } + + private LuanTable parseContainer(LuanTable tag) { + String endTagName = '/' + (String)tag.rawGet("name"); + int start = parser.begin(); + int end; + while(true) { + if( parser.test('<') ) { + end = parser.currentIndex(); + LuanTable tag2 = parseTag(); + String s = (String)tag2.rawGet("name"); + if( s.equals(endTagName) ) + break; + } + if( !parser.anyChar() ) + return parser.failure(null); + } + String text = parser.text.substring(start,end); + LuanTable tbl = new LuanTable(); + tbl.rawPut("type","container"); + tbl.rawPut("tag",tag); + tbl.rawPut("text",text); + return parser.success(tbl); + } + + private LuanTable parseTag() { + parser.begin(); + if( !parser.match('<') ) + return parser.failure(null); + int start = parser.currentIndex(); + parser.match('/'); + if( !matchNameChar() ) + return parser.failure(null); + while( matchNameChar() ); + String name = parser.textFrom(start).toLowerCase(); + LuanTable attributes = new LuanTable(); + String attrName; + while( (attrName = parseAttrName()) != null ) { + String attrValue = parseAttrValue(); + attributes.rawPut( attrName, attrValue!=null ? attrValue : true ); + } + while( matchSpace() ); + boolean isEmpty = parser.match('/'); + if( !parser.match('>') ) + return parser.failure(null); + LuanTable tbl = new LuanTable(); + tbl.rawPut("type","tag"); + tbl.rawPut("name",name); + tbl.rawPut("attributes",attributes); + tbl.rawPut("is_empty",isEmpty); + return parser.success(tbl); + } + + private String parseAttrName() { + parser.begin(); + if( !matchSpace() ) + return parser.failure(null); + while( matchSpace() ); + int start = parser.currentIndex(); + if( !matchNameChar() ) + return parser.failure(null); + while( matchNameChar() ); + String name = parser.textFrom(start); + return parser.success(name); + } + + private String parseAttrValue() { + parser.begin(); + while( matchSpace() ); + if( !parser.match('=') ) + return parser.failure(null); + while( matchSpace() ); + if( parser.anyOf("\"'") ) { + char quote = parser.lastChar(); + int start = parser.currentIndex(); + while( !parser.test(quote) ) { + if( !parser.anyChar() ) + return parser.failure(null); + } + String value = parser.textFrom(start); + parser.match(quote); + return parser.success(value); + } + int start = parser.currentIndex(); + if( !matchValueChar() ) + return parser.failure(null); + while( matchValueChar() ); + String value = parser.textFrom(start); + return parser.success(value); + } + + private boolean matchNameChar() { + return parser.inCharRange('a','z') + || parser.inCharRange('A','Z') + || parser.inCharRange('0','9') + || parser.anyOf("_.-:") + ; + } + + private boolean matchValueChar() { + return parser.noneOf(" \t\r\n\"'>/="); + } + + private boolean matchSpace() { + return parser.anyOf(" \t\r\n"); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/Json.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,313 @@ +package luan.modules.parsers; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.LinkedHashMap; +import java.util.Iterator; +import luan.LuanTable; +import luan.LuanException; + + +public final class Json { + + public static Object parse(String text) throws ParseException { + return new Json(text).parse(); + } + + private final Parser parser; + + private Json(String text) { + this.parser = new Parser(text); + } + + private ParseException exception(String msg) { + return new ParseException(parser,msg); + } + + private Object parse() throws ParseException { + spaces(); + Object value = value(); + spaces(); + if( !parser.endOfInput() ) + throw exception("unexpected text"); + return value; + } + + private Object value() throws ParseException { + if( parser.match("null") ) + return null; + if( parser.match("true") ) + return Boolean.TRUE; + if( parser.match("false") ) + return Boolean.FALSE; + String s = string(); + if( s != null ) + return s; + Number n = number(); + if( n != null ) + return n; + LuanTable a = array(); + if( a != null ) + return a; + LuanTable o = object(); + if( o != null ) + return o; + throw exception("invalid value"); + } + + private String string() throws ParseException { + parser.begin(); + if( !parser.match('"') ) + return parser.failure(null); + StringBuilder sb = new StringBuilder(); + while( parser.anyChar() ) { + char c = parser.lastChar(); + switch(c) { + case '"': + return parser.success(sb.toString()); + case '\\': + if( parser.anyChar() ) { + c = parser.lastChar(); + switch(c) { + case '"': + case '\\': + case '/': + sb.append(c); + continue; + case 'b': + sb.append('\b'); + continue; + case 'f': + sb.append('\f'); + continue; + case 'n': + sb.append('\n'); + continue; + case 'r': + sb.append('\r'); + continue; + case 't': + sb.append('\t'); + continue; + case 'u': + int n = 0; + for( int i=0; i<4; i++ ) { + int d; + if( parser.inCharRange('0','9') ) { + d = parser.lastChar() - '0'; + } else if( parser.inCharRange('a','f') ) { + d = parser.lastChar() - 'a' + 10; + } else if( parser.inCharRange('A','F') ) { + d = parser.lastChar() - 'A' + 10; + } else { + throw exception("invalid hex digit"); + } + n = 16*n + d; + } + sb.append((char)n); + continue; + } + } + throw exception("invalid escape char"); + default: + sb.append(c); + } + } + parser.failure(); + throw exception("unclosed string"); + } + + private Number number() { + int start = parser.begin(); + boolean isFloat = false; + parser.match('-'); + if( !parser.match('0') ) { + if( !parser.inCharRange('1','9') ) + return parser.failure(null); + while( parser.inCharRange('0','9') ); + } + if( parser.match('.') ) { + if( !parser.inCharRange('0','9') ) + return parser.failure(null); + while( parser.inCharRange('0','9') ); + isFloat = true; + } + if( parser.anyOf("eE") ) { + parser.anyOf("+-"); + if( !parser.inCharRange('0','9') ) + return parser.failure(null); + while( parser.inCharRange('0','9') ); + isFloat = true; + } + String s = parser.textFrom(start); + Number n; + if(isFloat) + n = Double.valueOf(s); + else + n = Long.valueOf(s); + return parser.success(n); + } + + private LuanTable array() throws ParseException { + parser.begin(); + if( !parser.match('[') ) + return parser.failure(null); + spaces(); + if( parser.match(']') ) + return parser.success(new LuanTable()); + List list = new ArrayList(); + list.add( value() ); + spaces(); + while( parser.match(',') ) { + spaces(); + list.add( value() ); + spaces(); + } + if( parser.match(']') ) + return parser.success(new LuanTable(list)); + if( parser.endOfInput() ) { + parser.failure(); + throw exception("unclosed array"); + } + throw exception("unexpected text in array"); + } + + private LuanTable object() throws ParseException { + parser.begin(); + if( !parser.match('{') ) + return parser.failure(null); + spaces(); + if( parser.match('}') ) + return parser.success(new LuanTable()); + Map map = new LinkedHashMap(); + addEntry(map); + while( parser.match(',') ) { + spaces(); + addEntry(map); + } + if( parser.match('}') ) + return parser.success(new LuanTable(map)); + if( parser.endOfInput() ) { + parser.failure(); + throw exception("unclosed object"); + } + throw exception("unexpected text in object"); + } + + private void addEntry(Map map) throws ParseException { + String key = string(); + if( key==null ) + throw exception("invalid object key"); + spaces(); + if( !parser.match(':') ) + throw exception("':' expected"); + spaces(); + Object value = value(); + spaces(); + map.put(key,value); + } + + private void spaces() { + while( parser.anyOf(" \t\r\n") ); + } + + + + + + + + + + public static String toString(Object obj) throws LuanException { + StringBuilder sb = new StringBuilder(); + toString(obj,sb); + return sb.toString(); + } + + private static void toString(Object obj,StringBuilder sb) throws LuanException { + if( obj == null || obj instanceof Boolean || obj instanceof Number ) { + sb.append(obj); + return; + } + if( obj instanceof String ) { + toString((String)obj,sb); + return; + } + if( obj instanceof LuanTable ) { + toString((LuanTable)obj,sb); + return; + } + throw new LuanException("can't handle type "+obj.getClass().getName()); + } + + private static void toString(final String s,StringBuilder sb) { + sb.append('"'); + for( int i=0; i<s.length(); i++ ) { + char c = s.charAt(i); + switch(c) { + case '"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: + sb.append(c); + } + } + sb.append('"'); + } + + private static void toString(LuanTable t,StringBuilder sb) throws LuanException { + if( t.isList() ) { + final List list = t.asList(); + if( list.isEmpty() ) { + sb.append("{}"); + return; + } + sb.append('['); + toString(list.get(0),sb); + for( int i=1; i<list.size(); i++ ) { + sb.append(','); + toString(list.get(i),sb); + } + sb.append(']'); + return; + } + sb.append('{'); + Iterator<Map.Entry<Object,Object>> i = t.rawIterator(); + toString(i.next(),sb); + while( i.hasNext() ) { + sb.append(','); + toString(i.next(),sb); + } + sb.append('}'); + } + + private static void toString(Map.Entry<Object,Object> entry,StringBuilder sb) throws LuanException { + Object key = entry.getKey(); + if( !(key instanceof String) ) + throw new LuanException("table keys must be strings"); + toString((String)key,sb); + sb.append(':'); + toString(entry.getValue(),sb); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/ParseException.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,51 @@ +package luan.modules.parsers; + + +public final class ParseException extends Exception { + public final String text; + public final int errorIndex; + public final int highIndex; + + ParseException(Parser parser,String msg) { + super(msg); + this.text = parser.text; + this.errorIndex = parser.currentIndex(); + this.highIndex = parser.highIndex(); + } + + private class Location { + final int line; + final int pos; + + Location(int index) { + int line = 0; + int i = -1; + while(true) { + int j = text.indexOf('\n',i+1); + if( j == -1 || j >= index ) + break; + i = j; + line++; + } + this.line = line; + this.pos = index - i - 1; + } + } + + private String[] lines() { + return text.split("\n",-1); + } + + @Override public String getMessage() { + Location loc = new Location(errorIndex); + String line = lines()[loc.line]; + String msg = super.getMessage() + " (line " + (loc.line+1) + ", pos " + (loc.pos+1) + ")\n"; + StringBuilder sb = new StringBuilder(msg); + sb.append( line + "\n" ); + for( int i=0; i<loc.pos; i++ ) { + sb.append( line.charAt(i)=='\t' ? '\t' : ' ' ); + } + sb.append("^\n"); + return sb.toString(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/Parser.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,156 @@ +package luan.modules.parsers; + + +public class Parser { + public final String text; + private final int len; + private int[] stack = new int[256]; + private int frame = 0; + private int iHigh; + + Parser(String text) { + this.text = text; + this.len = text.length(); + } + + private int i() { + return stack[frame]; + } + + private void i(int i) { + stack[frame] += i; + if( iHigh < stack[frame] ) + iHigh = stack[frame]; + } + + public int begin() { + frame++; + if( frame == stack.length ) { + int[] a = new int[2*frame]; + System.arraycopy(stack,0,a,0,frame); + stack = a; + } + stack[frame] = stack[frame-1]; + return i(); + } + + public void rollback() { + stack[frame] = stack[frame-1]; + } + + public <T> T success(T t) { + success(); + return t; + } + + public boolean success() { + frame--; + stack[frame] = stack[frame+1]; + return true; + } + + public <T> T failure(T t) { + failure(); + return t; + } + + public boolean failure() { + frame--; + return false; + } + + public int currentIndex() { + return i(); + } +/* + public int errorIndex() { + return frame > 0 ? stack[frame-1] : 0; + } +*/ + public int highIndex() { + return iHigh; + } + + public char lastChar() { + return text.charAt(i()-1); + } + + public char currentChar() { + return text.charAt(i()); + } + + public boolean endOfInput() { + return i() >= len; + } + + public boolean match(char c) { + if( endOfInput() || text.charAt(i()) != c ) + return false; + i(1); + return true; + } + + public boolean match(String s) { + int n = s.length(); + if( !text.regionMatches(i(),s,0,n) ) + return false; + i(n); + return true; + } + + public boolean matchIgnoreCase(String s) { + int n = s.length(); + if( !text.regionMatches(true,i(),s,0,n) ) + return false; + i(n); + return true; + } + + public boolean anyOf(String s) { + if( endOfInput() || s.indexOf(text.charAt(i())) == -1 ) + return false; + i(1); + return true; + } + + public boolean noneOf(String s) { + if( endOfInput() || s.indexOf(text.charAt(i())) != -1 ) + return false; + i(1); + return true; + } + + public boolean inCharRange(char cLow, char cHigh) { + if( endOfInput() ) + return false; + char c = text.charAt(i()); + if( !(cLow <= c && c <= cHigh) ) + return false; + i(1); + return true; + } + + public boolean anyChar() { + if( endOfInput() ) + return false; + i(1); + return true; + } + + public boolean test(char c) { + return !endOfInput() && text.charAt(i()) == c; + } + + public boolean test(String s) { + return text.regionMatches(i(),s,0,s.length()); + } + + public boolean testIgnoreCase(String s) { + return text.regionMatches(true,i(),s,0,s.length()); + } + + public String textFrom(int start) { + return text.substring(start,i()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/Theme.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,202 @@ +package luan.modules.parsers; + +import luan.LuanException; + + +public final class Theme { + + public static String toLuan(String source) throws LuanException { + try { + return new Theme(source).parse(); + } catch(ParseException e) { + throw new LuanException(e.getMessage()); + } + } + + private final Parser parser; + + private Theme(String source) { + this.parser = new Parser(source); + } + + private ParseException exception(String msg) { +// parser.failure(); + return new ParseException(parser,msg); + } + + private String parse() throws ParseException { + StringBuilder stmts = new StringBuilder(); + stmts.append( "local M = {}; " ); + while( !parser.endOfInput() ) { + String def = parseDef(); + if( def != null ) { + stmts.append(def); + } else { +// parser.anyChar(); + stmts.append(parsePadding()); + } + } + stmts.append( "\n\nreturn M\n" ); + return stmts.toString(); + } + + private String parsePadding() throws ParseException { + int start = parser.currentIndex(); + if( parser.match("--") ) { + while( parser.noneOf("\r\n") ); + } else if( !parser.anyOf(" \t\r\n") ) { + throw exception("unexpected text"); + } + return parser.textFrom(start); + } + + private String parseDef() throws ParseException { + int start = parser.begin(); + if( !parser.match('{') ) + return parser.failure(null); + spaces(); + if( !parser.match("define:") ) + return parser.failure(null); + String name = parseName(); + if( name==null ) + throw exception("invalid block name"); + spaces(); + if( !parser.match('}') ) + throw exception("unclosed define tag"); + String block = parseBody("define:"+name); + String rtn = "function M." + name + "(env) " + block + " end; "; + return parser.success(rtn); + } + + private String parseBody(String tagName) throws ParseException { + StringBuilder stmts = new StringBuilder(); + int start = parser.currentIndex(); + int end = start; + while( !matchEndTag(tagName) ) { + if( parser.endOfInput() ) { + parser.failure(); + throw exception("unclosed block"); + } + String block = parseBlock(); + if( block != null ) { + addText(start,end,stmts); + start = parser.currentIndex(); + stmts.append(block); + continue; + } + String simpleTag = parseSimpleTag(); + if( simpleTag != null ) { + addText(start,end,stmts); + start = parser.currentIndex(); + stmts.append(simpleTag); + continue; + } + if( parser.match("<%") ) { + addText(start,end,stmts); + start = parser.currentIndex(); + stmts.append("%><%='<%'%><%"); + continue; + } + parser.anyChar(); + end = parser.currentIndex(); + } + addText(start,end,stmts); + return stmts.toString(); + } + + private boolean matchEndTag(String tagName) { + parser.begin(); + if( !parser.match('{') ) + return parser.failure(); + spaces(); + if( !(parser.match('/') && parser.match(tagName)) ) + return parser.failure(); + spaces(); + if( !parser.match('}') ) + return parser.failure(); + return parser.success(); + } + + private void addText(int start,int end,StringBuilder stmts) { + if( start < end ) { + stmts.append( "%>" ).append( parser.text.substring(start,end) ).append( "<%" ); + } + } + + private String parseBlock() throws ParseException { + int start = parser.begin(); + if( !parser.match('{') ) + return parser.failure(null); + spaces(); + if( !parser.match("block:") ) + return parser.failure(null); + String name = parseName(); + if( name==null ) { + parser.failure(); + throw exception("invalid block name"); + } + spaces(); + if( !parser.match('}') ) + return parser.failure(null); + String block = parseBody("block:"+name); + String rtn = " env."+ name + "( env, function(env) " + block + "end); "; +// String rtn = "<% env." + tag.name + "(" + (tag.attrs.isEmpty() ? "nil" : table(tag.attrs)) + ",env,function(env) %>" + block + "<% end) %>"; + return parser.success(rtn); + } + + private String parseSimpleTag() throws ParseException { + int start = parser.begin(); + if( !parser.match('{') ) + return parser.failure(null); + spaces(); + String name = parseName(); + if( name==null ) + return parser.failure(null); + spaces(); + if( !parser.match('}') ) + return parser.failure(null); +// rtn = "<% env." + name + (attrs.isEmpty() ? "()" : table(attrs)) + " %>"; + String rtn = " env." + name + "(env); "; + return parser.success(rtn); + } + + private boolean BlankLine() { + parser.begin(); + while( parser.anyOf(" \t") ); + return EndOfLine() ? parser.success() : parser.failure(); + } + + private boolean EndOfLine() { + return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' ); + } + + private String parseName() throws ParseException { + int start = parser.begin(); + if( parser.match('/') ) { + parser.failure(); + throw exception("bad closing tag"); + } + if( parser.match("define:") ) { + parser.failure(); + throw exception("unexpected definition"); + } + if( !FirstNameChar() ) + return parser.failure(null); + while( NameChar() ); + String match = parser.textFrom(start); + return parser.success(match); + } + + private boolean FirstNameChar() { + return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_'); + } + + private boolean NameChar() { + return FirstNameChar() || parser.inCharRange('0', '9'); + } + + private void spaces() { + while( parser.anyOf(" \t") ); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/theme_to_luan.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,4 @@ +local Io = require "luan:Io.luan" +local Parsers = require "luan:Parsers.luan" + +Io.stdout.write( Parsers.theme_to_luan( Io.stdin.read_text(), "stdin" ) )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/url/LuanUrl.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,302 @@ +package luan.modules.url; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLConnection; +import java.net.HttpURLConnection; +import java.net.URLEncoder; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.Base64; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanJavaFunction; +import luan.LuanException; +import luan.modules.IoLuan; +import luan.modules.Utils; + + +public final class LuanUrl extends IoLuan.LuanIn { + + private static enum Method { GET, POST, DELETE } + + private URL url; + private Method method = Method.GET; + private Map headers; + private String content = null; + private MultipartClient multipart = null; + private int timeout = 0; + + public LuanUrl(LuanState luan,URL url,LuanTable options) throws LuanException { + this.url = url; + if( options != null ) { + Map map = options.asMap(luan); + String methodStr = getString(map,"method"); + if( methodStr != null ) { + methodStr = methodStr.toUpperCase(); + try { + this.method = Method.valueOf(methodStr); + } catch(IllegalArgumentException e) { + throw new LuanException( "invalid method: "+methodStr ); + } + } + Map headerMap = getMap(luan,map,"headers"); + if( headerMap != null ) { + headers = new HashMap(); + for( Object hack : headerMap.entrySet() ) { + Map.Entry entry = (Map.Entry)hack; + String key = (String)entry.getKey(); + Object val = entry.getValue(); + String name = toHttpHeaderName(key); + if( val instanceof String ) { + headers.put(name,val); + } else { + if( !(val instanceof LuanTable) ) + throw new LuanException( "header '"+key+"' must be string or table" ); + LuanTable t = (LuanTable)val; + if( !t.isList() ) + throw new LuanException( "header '"+key+"' table must be list" ); + headers.put(name,t.asList()); + } + } + } + Map auth = getMap(luan,map,"authorization"); + if( auth != null ) { + if( headers!=null && headers.containsKey("Authorization") ) + throw new LuanException( "can't define authorization with header 'Authorization' defined" ); + String user = getString(auth,"user"); + String password = getString(auth,"password"); + if( !auth.isEmpty() ) + throw new LuanException( "unrecognized authorization options: "+auth ); + StringBuilder sb = new StringBuilder(); + if( user != null ) + sb.append(user); + sb.append(':'); + if( password != null ) + sb.append(password); + String val = "Basic " + Base64.getEncoder().encodeToString(sb.toString().getBytes()); + if( headers == null ) + headers = new HashMap(); + headers.put("Authorization",val); + } + Map params = getMap(luan,map,"parameters"); + String enctype = getString(map,"enctype"); + if( enctype != null ) { + if( !enctype.equals("multipart/form-data") ) + throw new LuanException( "unrecognized enctype: "+enctype ); + if( this.method!=Method.POST ) + throw new LuanException( "multipart/form-data can only be used with POST" ); + if( params==null ) + throw new LuanException( "multipart/form-data requires parameters" ); + if( params.isEmpty() ) + throw new LuanException( "multipart/form-data parameters can't be empty" ); + multipart = new MultipartClient(params); + } + else if( params != null ) { + StringBuilder sb = new StringBuilder(); + for( Object hack : params.entrySet() ) { + Map.Entry entry = (Map.Entry)hack; + String key = (String)entry.getKey(); + Object val = entry.getValue(); + String keyEnc = encode(key); + if( val instanceof String ) { + and(sb); + sb.append( keyEnc ).append( '=' ).append( encode((String)val) ); + } else { + if( !(val instanceof LuanTable) ) + throw new LuanException( "parameter '"+key+"' must be string or table" ); + LuanTable t = (LuanTable)val; + if( !t.isList() ) + throw new LuanException( "parameter '"+key+"' table must be list" ); + for( Object obj : t.asList() ) { + if( !(obj instanceof String) ) + throw new LuanException( "parameter '"+key+"' values must be strings" ); + and(sb); + sb.append( keyEnc ).append( '=' ).append( encode((String)obj) ); + } + } + } + if( this.method==Method.DELETE ) + throw new LuanException( "the DELETE method cannot take parameters" ); + if( this.method==Method.POST ) { + content = sb.toString(); + } else { // GET + String urlS = this.url.toString(); + if( urlS.indexOf('?') == -1 ) { + urlS += '?'; + } else { + urlS += '&'; + } + urlS += sb; + try { + this.url = new URL(urlS); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + } + Integer timeout = getInt(map,"time_out"); + if( timeout != null ) + this.timeout = timeout; + if( !map.isEmpty() ) + throw new LuanException( "unrecognized options: "+map ); + } + } + + public static String toHttpHeaderName(String luanName) { + luanName = luanName.toLowerCase(); + StringBuilder buf = new StringBuilder(); + boolean capitalize = true; + char[] a = luanName.toCharArray(); + for( int i=0; i<a.length; i++ ) { + char c = a[i]; + if( c == '_' || c == '-' ) { + a[i] = '-'; + capitalize = true; + } else if( capitalize ) { + a[i] = Character.toUpperCase(c); + capitalize = false; + } + } + return String.valueOf(a); + } + + private static void and(StringBuilder sb) { + if( sb.length() > 0 ) + sb.append('&'); + } + + private static String encode(String s) { + try { + return URLEncoder.encode(s,"UTF-8"); + } catch(UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private static String getString(Map map,String key) throws LuanException { + Object val = map.remove(key); + if( val!=null && !(val instanceof String) ) + throw new LuanException( "parameter '"+key+"' must be a string" ); + return (String)val; + } + + private static Integer getInt(Map map,String key) throws LuanException { + Object val = map.remove(key); + if( val==null ) + return null; + Integer i = Luan.asInteger(val); + if( i==null ) + throw new LuanException( "parameter '"+key+"' must be an integer" ); + return i; + } + + private static LuanTable getTable(Map map,String key) throws LuanException { + Object val = map.remove(key); + if( val!=null && !(val instanceof LuanTable) ) + throw new LuanException( "parameter '"+key+"' must be a table" ); + return (LuanTable)val; + } + + private static Map getMap(LuanState luan,Map map,String key) throws LuanException { + LuanTable t = getTable(map,key); + return t==null ? null : t.asMap(luan); + } + + @Override public InputStream inputStream() throws IOException, LuanException { + URLConnection con = url.openConnection(); + if( timeout != 0 ) { + con.setConnectTimeout(timeout); + con.setReadTimeout(timeout); + } + if( headers != null ) { + for( Object hack : headers.entrySet() ) { + Map.Entry entry = (Map.Entry)hack; + String key = (String)entry.getKey(); + Object val = entry.getValue(); + if( val instanceof String ) { + con.addRequestProperty(key,(String)val); + } else { + List list = (List)val; + for( Object obj : list ) { + con.addRequestProperty(key,(String)obj); + } + } + } + } + if( method==Method.GET ) { + return con.getInputStream(); + } + + HttpURLConnection httpCon = (HttpURLConnection)con; + + if( method==Method.DELETE ) { + httpCon.setRequestMethod("DELETE"); + return httpCon.getInputStream(); + } + + // POST + +// httpCon.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); + httpCon.setDoOutput(true); + httpCon.setRequestMethod("POST"); + + OutputStream out; + if( multipart != null ) { + out = multipart.write(httpCon); + } else { + byte[] post = content.getBytes(); +// httpCon.setRequestProperty("Content-Length",Integer.toString(post.length)); + out = httpCon.getOutputStream(); + out.write(post); + } + out.flush(); + try { + try { + return httpCon.getInputStream(); + } catch(IOException e) { + InputStream is = httpCon.getErrorStream(); + if( is == null ) + throw e; + Reader in = new InputStreamReader(is); + String msg = Utils.readAll(in); + in.close(); + throw new LuanException(msg,e); + } + } finally { + out.close(); + } + } + + @Override public String to_string() { + return url.toString(); + } + + @Override public String to_uri_string() { + return url.toString(); + } +/* + public String post(String postS) throws IOException { + return new UrlCall(url).post(postS); + } + + @Override public LuanTable table() { + LuanTable tbl = super.table(); + try { + tbl.rawPut( "post", new LuanJavaFunction( + LuanUrl.class.getMethod( "post", String.class ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } +*/ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/url/MultiPartOutputStream.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,146 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// +// This horrible broken code from jetty is just here for me to look at. It isn't used. -fschmidt + +package luan.modules.url; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + + +/* ================================================================ */ +/** Handle a multipart MIME response. + * + * + * +*/ +public class MultiPartOutputStream extends FilterOutputStream +{ + /* ------------------------------------------------------------ */ + private static final byte[] __CRLF={'\r','\n'}; + private static final byte[] __DASHDASH={'-','-'}; + + public static String MULTIPART_MIXED="multipart/mixed"; + public static String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace"; + public static final String __ISO_8859_1="ISO-8859-1"; + + public static String newBoundary(Object obj) { + return "jetty"+System.identityHashCode(obj)+ + Long.toString(System.currentTimeMillis(),36); + } + + /* ------------------------------------------------------------ */ + private final String boundary; + private final byte[] boundaryBytes; + + /* ------------------------------------------------------------ */ + private boolean inPart=false; + + /* ------------------------------------------------------------ */ + public MultiPartOutputStream(OutputStream out,String boundary) + throws IOException + { + super(out); + + this.boundary = boundary; + boundaryBytes=boundary.getBytes(__ISO_8859_1); + + inPart=false; + } + + + + /* ------------------------------------------------------------ */ + /** End the current part. + * @exception IOException IOException + */ + @Override + public void close() + throws IOException + { + if (inPart) + out.write(__CRLF); + out.write(__DASHDASH); + out.write(boundaryBytes); + out.write(__DASHDASH); + out.write(__CRLF); + inPart=false; + super.close(); + } + + /* ------------------------------------------------------------ */ + public String getBoundary() + { + return boundary; + } + + public OutputStream getOut() {return out;} + + /* ------------------------------------------------------------ */ + /** Start creation of the next Content. + */ + public void startPart(String contentType) + throws IOException + { + if (inPart) + out.write(__CRLF); + inPart=true; + out.write(__DASHDASH); + out.write(boundaryBytes); + out.write(__CRLF); + if (contentType != null) + out.write(("Content-Type: "+contentType).getBytes(__ISO_8859_1)); + out.write(__CRLF); + out.write(__CRLF); + } + + /* ------------------------------------------------------------ */ + /** Start creation of the next Content. + */ + public void startPart(String contentType, String[] headers) + throws IOException + { + if (inPart) + out.write(__CRLF); + inPart=true; + out.write(__DASHDASH); + out.write(boundaryBytes); + out.write(__CRLF); + if (contentType != null) + out.write(("Content-Type: "+contentType).getBytes(__ISO_8859_1)); + out.write(__CRLF); + for (int i=0;headers!=null && i<headers.length;i++) + { + out.write(headers[i].getBytes(__ISO_8859_1)); + out.write(__CRLF); + } + out.write(__CRLF); + } + + /* ------------------------------------------------------------ */ + @Override + public void write(byte[] b, int off, int len) throws IOException + { + out.write(b,off,len); + } +} + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/url/MultipartClient.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,73 @@ +package luan.modules.url; + +import java.io.OutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import luan.LuanTable; +import luan.LuanException; + + +public final class MultipartClient { + private static final byte[] __CRLF = {'\r','\n'}; + private static final byte[] __DASHDASH = {'-','-'}; + private static final String __ISO_8859_1 = "ISO-8859-1"; + + private final Map params = new HashMap(); + + MultipartClient(Map params) throws LuanException { + for( Object hack : params.entrySet() ) { + Map.Entry entry = (Map.Entry)hack; + String key = (String)entry.getKey(); + Object val = entry.getValue(); + List list = new ArrayList(); + if( val instanceof String ) { + list.add(val); + } else { + if( !(val instanceof LuanTable) ) + throw new LuanException( "parameter '"+key+"' must be string or table" ); + LuanTable t = (LuanTable)val; + if( !t.isList() ) + throw new LuanException( "parameter '"+key+"' table must be list" ); + for( Object obj : t.asList() ) { + if( !(obj instanceof String) ) + throw new LuanException( "parameter '"+key+"' values must be strings" ); + list.add(obj); + } + } + this.params.put(key,list); + } + } + + public OutputStream write(HttpURLConnection httpCon) throws IOException { + String boundary = "luan" + System.identityHashCode(this) + Long.toString(System.currentTimeMillis(),36); + byte[] boundaryBytes = boundary.getBytes(__ISO_8859_1); + + httpCon.setRequestProperty("Content-Type","multipart/form-data; boundary="+boundary); + OutputStream out = httpCon.getOutputStream(); + for( Object hack : params.entrySet() ) { + Map.Entry entry = (Map.Entry)hack; + String name = (String)entry.getKey(); + List list = (List)entry.getValue(); + for( Object obj : list ) { + String val = (String)obj; + out.write(__DASHDASH); + out.write(boundaryBytes); + out.write(__CRLF); + out.write(("Content-Disposition: form-data; name=\""+name+"\"").getBytes(__ISO_8859_1)); + out.write(__CRLF); + out.write(__CRLF); + out.write(val.getBytes()); + out.write(__CRLF); + } + } + out.write(__DASHDASH); + out.write(boundaryBytes); + out.write(__DASHDASH); + out.write(__CRLF); + return out; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/url/UrlCall.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,84 @@ +// not used, just for reference + +package luan.modules.url; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.IOException; +import java.net.URLConnection; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Map; +import java.util.HashMap; +import luan.modules.Utils; + + +public final class UrlCall { + public final URLConnection connection; + + public UrlCall(String url) throws IOException { + this(new URL(url)); + } + + public UrlCall(URL url) throws IOException { + connection = url.openConnection(); + } + + public void acceptJson() { + connection.setRequestProperty("accept","application/json"); + } + + public String get() throws IOException { + Reader in = new InputStreamReader(connection.getInputStream()); + String rtn = Utils.readAll(in); + in.close(); + return rtn; + } + + public String post(String content,String contentType) throws IOException { + HttpURLConnection connection = (HttpURLConnection)this.connection; + + connection.setRequestProperty("Content-type",contentType); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + + byte[] post = content.getBytes(); + connection.setRequestProperty("Content-Length",Integer.toString(post.length)); + OutputStream out = connection.getOutputStream(); + out.write(post); + out.flush(); + + Reader in; + try { + in = new InputStreamReader(connection.getInputStream()); + } catch(IOException e) { + InputStream is = connection.getErrorStream(); + if( is == null ) + throw e; + in = new InputStreamReader(is); + String msg = Utils.readAll(in); + in.close(); + throw new UrlCallException(msg,e); + } + String rtn = Utils.readAll(in); + in.close(); + out.close(); + return rtn; + } + + public String post(String content) throws IOException { + return post(content,"application/x-www-form-urlencoded"); + } + + public String postJson(String content) throws IOException { + return post(content,"application/json"); + } + + public static final class UrlCallException extends IOException { + UrlCallException(String msg,IOException e) { + super(msg,e); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/which.luan Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,9 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local values = Luan.values or error() +local Which_mod = require "luan:Which_mod.luan" +local which = Which_mod.which or error() + +for name in values(...) do + which(name) +end