changeset 21:c93d8c781853

add functions git-svn-id: https://luan-java.googlecode.com/svn/trunk@22 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Tue, 04 Dec 2012 09:16:03 +0000
parents d85510d92eee
children 1e37f22a34c8
files src/luan/CmdLine.java src/luan/LuaClosure.java src/luan/LuaJavaFunction.java src/luan/LuaState.java src/luan/interp/Block.java src/luan/interp/Chunk.java src/luan/interp/LuaParser.java src/luan/interp/ReturnException.java src/luan/interp/ReturnStmt.java src/luan/interp/SetStmt.java src/luan/lib/BasicLib.java
diffstat 11 files changed, 264 insertions(+), 101 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/CmdLine.java	Sun Dec 02 10:51:18 2012 +0000
+++ b/src/luan/CmdLine.java	Tue Dec 04 09:16:03 2012 +0000
@@ -27,7 +27,7 @@
 		} else {
 			String file = args[i++];
 			try {
-				LuaFunction fn = BasicLib.loadFile(file);
+				LuaFunction fn = BasicLib.loadFile(lua,file);
 				fn.call(lua);
 			} catch(LuaException e) {
 //				System.out.println(e.getMessage());
@@ -44,7 +44,7 @@
 			System.out.print("> ");
 			String input = new Scanner(System.in).nextLine();
 			try {
-				LuaFunction fn = BasicLib.load(input);
+				LuaFunction fn = BasicLib.load(lua,input);
 				Object[] rtn = fn.call(lua);
 				if( rtn.length > 0 )
 					BasicLib.print(rtn);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/LuaClosure.java	Tue Dec 04 09:16:03 2012 +0000
@@ -0,0 +1,29 @@
+package luan;
+
+import luan.interp.Chunk;
+import luan.interp.ReturnException;
+
+
+public final class LuaClosure extends LuaFunction {
+	private final Chunk chunk;
+
+	public LuaClosure(Chunk chunk,LuaState lua) {
+		this.chunk = chunk;
+	}
+
+	public Object[] call(LuaState lua,Object... args) throws LuaException {
+		Object[] stack = lua.newStack(chunk.stackSize);
+		final int n = Math.min(args.length,chunk.numArgs);
+		for( int i=0; i<n; i++ ) {
+			stack[i] = args[i];
+		}
+		try {
+			chunk.block.eval(lua);
+		} catch(ReturnException e) {
+		} finally {
+			lua.popStack();
+		}
+		return lua.returnValues;
+	}
+
+}
--- a/src/luan/LuaJavaFunction.java	Sun Dec 02 10:51:18 2012 +0000
+++ b/src/luan/LuaJavaFunction.java	Tue Dec 04 09:16:03 2012 +0000
@@ -9,6 +9,7 @@
 	private final Method method;
 	private final Object obj;
 	private final RtnConverter rtnConverter;
+	private final boolean takesLuaState;
 	private final ArgConverter[] argConverters;
 	private final Class<?> varArgCls;
 
@@ -16,7 +17,8 @@
 		this.method = method;
 		this.obj = obj;
 		this.rtnConverter = getRtnConverter(method);
-		this.argConverters = getArgConverters(method);
+		this.takesLuaState = takesLuaState(method);
+		this.argConverters = getArgConverters(takesLuaState,method);
 		if( method.isVarArgs() ) {
 			Class<?>[] paramTypes = method.getParameterTypes();
 			this.varArgCls = paramTypes[paramTypes.length-1].getComponentType();
@@ -26,7 +28,7 @@
 	}
 
 	@Override public Object[] call(LuaState lua,Object... args) {
-		args = fixArgs(args);
+		args = fixArgs(lua,args);
 		Object rtn;
 		try {
 			rtn = method.invoke(obj,args);
@@ -38,37 +40,39 @@
 		return rtnConverter.convert(rtn);
 	}
 
-	private Object[] fixArgs(Object[] args) {
-		if( varArgCls==null ) {
-			if( args.length != argConverters.length ) {
-				Object[] t = new Object[argConverters.length];
-				System.arraycopy(args,0,t,0,Math.min(args.length,t.length));
-				args = t;
-			}
-			for( int i=0; i<args.length; i++ ) {
-				args[i] = argConverters[i].convert(args[i]);
+	private Object[] fixArgs(LuaState lua,Object[] args) {
+		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++] = lua;
 			}
-			return args;
-		} else {  // varargs
-			Object[] rtn = new Object[argConverters.length];
-			int n = argConverters.length - 1;
-			if( args.length < argConverters.length ) {
-				System.arraycopy(args,0,rtn,0,args.length);
-				rtn[rtn.length-1] = Array.newInstance(varArgCls,0);
-			} else {
-				System.arraycopy(args,0,rtn,0,n);
-				Object[] varArgs = (Object[])Array.newInstance(varArgCls,args.length-n);
-				ArgConverter ac = argConverters[n];
-				for( int i=0; i<varArgs.length; i++ ) {
-					varArgs[i] = ac.convert(args[n+i]);
+			n = argConverters.length;
+			if( varArgCls != null ) {
+				n--;
+				if( args.length < argConverters.length ) {
+					rtn[rtn.length-1] = Array.newInstance(varArgCls,0);
+				} else {
+					Object[] varArgs = (Object[])Array.newInstance(varArgCls,args.length-n);
+					ArgConverter ac = argConverters[n];
+					for( int i=0; i<varArgs.length; i++ ) {
+						varArgs[i] = ac.convert(args[n+i]);
+					}
+					rtn[rtn.length-1] = varArgs;
 				}
-				rtn[rtn.length-1] = varArgs;
 			}
-			for( int i=0; i<n; i++ ) {
-				rtn[i] = argConverters[i].convert(rtn[i]);
-			}
-			return rtn;
+			System.arraycopy(args,0,rtn,start,Math.min(args.length,n));
 		}
+		for( int i=0; i<n; i++ ) {
+			rtn[start+i] = argConverters[i].convert(rtn[start+i]);
+		}
+		return rtn;
 	}
 
 
@@ -218,9 +222,19 @@
 		}
 	};
 
-	private static ArgConverter[] getArgConverters(Method m) {
+	private static boolean takesLuaState(Method m) {
+		Class<?>[] paramTypes = m.getParameterTypes();
+		return paramTypes.length > 0 && paramTypes[0].equals(LuaState.class);
+	}
+
+	private static ArgConverter[] getArgConverters(boolean takesLuaState,Method 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];
--- a/src/luan/LuaState.java	Sun Dec 02 10:51:18 2012 +0000
+++ b/src/luan/LuaState.java	Tue Dec 04 09:16:03 2012 +0000
@@ -20,12 +20,15 @@
 	}
 
 	private LuaStack stack = null;
+	public Object[] returnValues;
 
-	public void newStack(int stackSize) {
+	Object[] newStack(int stackSize) {
+		returnValues = LuaFunction.EMPTY_RTN;
 		stack = new LuaStack(stack,stackSize);
+		return stack.a;
 	}
 
-	public void popStack() {
+	void popStack() {
 		stack = stack.previousStack;
 	}
 
--- a/src/luan/interp/Block.java	Sun Dec 02 10:51:18 2012 +0000
+++ b/src/luan/interp/Block.java	Tue Dec 04 09:16:03 2012 +0000
@@ -5,7 +5,7 @@
 
 
 final class Block implements Stmt {
-	private final Stmt[] stmts;
+	final Stmt[] stmts;
 	private final int stackStart;
 	private final int stackEnd;
 
--- a/src/luan/interp/Chunk.java	Sun Dec 02 10:51:18 2012 +0000
+++ b/src/luan/interp/Chunk.java	Tue Dec 04 09:16:03 2012 +0000
@@ -2,24 +2,37 @@
 
 import luan.LuaState;
 import luan.LuaException;
+import luan.LuaClosure;
 
 
-final class Chunk implements Stmt {
-	private final Stmt block;
-	private final int stackSize;
+public final class Chunk implements Expr {
+	public final Stmt block;
+	public final int stackSize;
+	public final int numArgs;
 
-	Chunk(Stmt block,int stackSize) {
+	Chunk(Stmt block,int stackSize,int numArgs) {
 		this.block = block;
 		this.stackSize = stackSize;
-	}
-
-	@Override public void eval(LuaState lua) throws LuaException {
-		lua.newStack(stackSize);
-		try {
-			block.eval(lua);
-		} finally {
-			lua.popStack();
+		this.numArgs = numArgs;
+		Stmt stmt = block;
+		while( stmt instanceof Block ) {
+			Block b = (Block)stmt;
+			if( b.stmts.length==0 )
+				break;
+			stmt = b.stmts[b.stmts.length-1];
+		}
+		if( stmt instanceof ReturnStmt ) {
+			ReturnStmt rs = (ReturnStmt)stmt;
+			rs.throwReturnException = false;
 		}
 	}
 
+	public LuaClosure newClosure(LuaState lua) {
+		return new LuaClosure(this,lua);
+	}
+
+	@Override public Object eval(LuaState lua) {
+		return newClosure(lua);
+	}
+
 }
--- a/src/luan/interp/LuaParser.java	Sun Dec 02 10:51:18 2012 +0000
+++ b/src/luan/interp/LuaParser.java	Tue Dec 04 09:16:03 2012 +0000
@@ -22,11 +22,21 @@
 
 
 public class LuaParser extends BaseParser<Object> {
+
+	static final class Frame {
+		final Frame parent;
+		final List<String> symbols = new ArrayList<String>();
+		int stackSize = 0;
+		int loops = 0;
+
+		Frame(Frame parent) {
+			this.parent = parent;
+		}
+	}
+
 	int nEquals;
 	int parens = 0;
-	List<String> symbols = new ArrayList<String>();
-	int stackSize = 0;
-	int loops = 0;
+	Frame frame = new Frame(null);
 
 	boolean nEquals(int n) {
 		nEquals = n;
@@ -43,7 +53,23 @@
 		return true;
 	}
 
+	List<String> symbols() {
+		return frame.symbols;
+	}
+
+	int symbolsSize() {
+		return frame.symbols.size();
+	}
+
+	boolean addSymbol(String name) {
+		frame.symbols.add(name);
+		if( frame.stackSize < symbolsSize() )
+			frame.stackSize = symbolsSize();
+		return true;
+	}
+
 	int index(String name) {
+		List<String> symbols = frame.symbols;
 		int i = symbols.size();
 		while( --i >= 0 ) {
 			if( symbols.get(i).equals(name) )
@@ -53,6 +79,7 @@
 	}
 
 	boolean popSymbols(int n) {
+		List<String> symbols = frame.symbols;
 		while( n-- > 0 ) {
 			symbols.remove(symbols.size()-1);
 		}
@@ -60,12 +87,12 @@
 	}
 
 	boolean incLoops() {
-		loops++;
+		frame.loops++;
 		return true;
 	}
 
 	boolean decLoops() {
-		loops--;
+		frame.loops--;
 		return true;
 	}
 
@@ -75,21 +102,18 @@
 			Spaces(),
 			FirstOf(
 				Sequence( ExpList(), EOI ),
-				Sequence( Chunk(), EOI )
+				Sequence(
+					Block(),
+					EOI,
+					push( new Chunk( (Stmt)pop(), frame.stackSize, 0 ) )
+				)
 			)
 		);
 	}
 
-	Rule Chunk() {
-		return Sequence(
-			Block(),
-			push( new Chunk( (Stmt)pop(), stackSize ) )
-		);
-	}
-
 	Rule Block() {
 		Var<List<Stmt>> stmts = new Var<List<Stmt>>(new ArrayList<Stmt>());
-		Var<Integer> stackStart = new Var<Integer>(symbols.size());
+		Var<Integer> stackStart = new Var<Integer>(symbolsSize());
 		return Sequence(
 			Optional( Stmt(stmts) ),
 			ZeroOrMore(
@@ -101,9 +125,7 @@
 	}
 
 	Stmt newBlock(List<Stmt> stmts,int stackStart) {
-		if( stackSize < symbols.size() )
-			stackSize = symbols.size();
-		int stackEnd = symbols.size();
+		int stackEnd = symbolsSize();
 		popSymbols( stackEnd - stackStart );
 		if( stmts.isEmpty() )
 			return Stmt.EMPTY;
@@ -134,6 +156,9 @@
 			LocalStmt(stmts),
 			Sequence(
 				FirstOf(
+					ReturnStmt(),
+					FunctionStmt(),
+					LocalFunctionStmt(),
 					BreakStmt(),
 					GenericForStmt(),
 					NumericForStmt(),
@@ -149,20 +174,54 @@
 		);
 	}
 
+	Rule ReturnStmt() {
+		return Sequence(
+			Keyword("return"), Expressions(),
+			push( new ReturnStmt( (Expressions)pop() ) )
+		);
+	}
+
+	Rule FunctionStmt() {
+		return Sequence(
+			Keyword("function"), FnName(), Function(),
+			push( new SetStmt( (Settable)pop(1), expr(pop()) ) )
+		);
+	}
+
+	Rule FnName() {
+		return Sequence(
+			push(null),  // marker
+			Name(),
+			ZeroOrMore(
+				'.', Spaces(),
+				makeVarExp(),
+				NameExpr()
+			),
+			makeSettableVar()
+		);
+	}
+
+	Rule LocalFunctionStmt() {
+		return Sequence(
+			Keyword("local"), Keyword("function"), LocalName(), Function(),
+			push( new SetStmt( new SetLocalVar(symbolsSize()-1), expr(pop()) ) )
+		);
+	}
+
 	Rule BreakStmt() {
 		return Sequence(
 			Keyword("break"),
-			loops > 0,
+			frame.loops > 0,
 			push( new BreakStmt() )
 		);
 	}
 
 	Rule GenericForStmt() {
-		Var<Integer> stackStart = new Var<Integer>(symbols.size());
+		Var<Integer> stackStart = new Var<Integer>(symbolsSize());
 		return Sequence(
 			Keyword("for"), NameList(), Keyword("in"), Expr(), Keyword("do"), LoopBlock(), Keyword("end"),
-			push( new GenericForStmt( stackStart.get(), symbols.size() - stackStart.get(), expr(pop(1)), (Stmt)pop() ) ),
-			popSymbols( symbols.size() - stackStart.get() )
+			push( new GenericForStmt( stackStart.get(), symbolsSize() - stackStart.get(), expr(pop(1)), (Stmt)pop() ) ),
+			popSymbols( symbolsSize() - stackStart.get() )
 		);
 	}
 
@@ -175,9 +234,9 @@
 				drop(),
 				Expr()
 			),
-			symbols.add( (String)pop(3) ),  // add "for" var to symbols
+			addSymbol( (String)pop(3) ),  // add "for" var to symbols
 			Keyword("do"), LoopBlock(), Keyword("end"),
-			push( new NumericForStmt( symbols.size()-1, expr(pop(3)), expr(pop(2)), expr(pop(1)), (Stmt)pop() ) ),
+			push( new NumericForStmt( symbolsSize()-1, expr(pop(3)), expr(pop(2)), expr(pop(1)), (Stmt)pop() ) ),
 			popSymbols(1)
 		);
 	}
@@ -189,7 +248,7 @@
 	}
 
 	Rule LocalStmt(Var<List<Stmt>> stmts) {
-		Var<Integer> stackStart = new Var<Integer>(symbols.size());
+		Var<Integer> stackStart = new Var<Integer>(symbolsSize());
 		return Sequence(
 			Keyword("local"), NameList(),
 			Optional(
@@ -201,18 +260,23 @@
 
 	Rule NameList() {
 		return Sequence(
-			Name(),
-			symbols.add( (String)pop() ),
+			LocalName(),
 			ZeroOrMore(
-				',', Spaces(), Name(),
-				symbols.add( (String)pop() )
+				',', Spaces(), LocalName()
 			)
 		);
 	}
 
+	Rule LocalName() {
+		return Sequence(
+			Name(),
+			addSymbol( (String)pop() )
+		);
+	}
+
 	SetStmt newSetLocalStmt(int stackStart) {
 		Expressions values = (Expressions)pop();
-		SetLocalVar[] vars = new SetLocalVar[symbols.size()-stackStart];
+		SetLocalVar[] vars = new SetLocalVar[symbolsSize()-stackStart];
 		for( int i=0; i<vars.length; i++ ) {
 			vars[i] = new SetLocalVar(stackStart+i);
 		}
@@ -289,36 +353,39 @@
 	}
 
 	Rule VarList() {
+		Var<List<Settable>> vars = new Var<List<Settable>>(new ArrayList<Settable>());
 		return Sequence(
-			push(new ArrayList<Settable>()),
-			Var(),
-			addToVarList(),
+			SettableVar(),
+			vars.get().add( (Settable)pop() ),
 			ZeroOrMore(
-				',', Spaces(), Var(),
-				addToVarList()
-			)
+				',', Spaces(), SettableVar(),
+				vars.get().add( (Settable)pop() )
+			),
+			push(vars.get())
 		);
 	}
 
-	boolean addToVarList() {
+	Rule SettableVar() {
+		return Sequence( Var(), makeSettableVar() );
+	}
+
+	boolean makeSettableVar() {
 		Object obj2 = pop();
 		if( obj2==null )
 			return false;
 		Object obj1 = pop();
-		@SuppressWarnings("unchecked")
-		List<Settable> vars = (List<Settable>)peek();
 		if( obj1==null ) {
 			String name = (String)obj2;
 			int index = index(name);
 			if( index == -1 ) {
-				vars.add( new SetTableEntry( EnvExpr.INSTANCE, new ConstExpr(name) ) );
+				push( new SetTableEntry( EnvExpr.INSTANCE, new ConstExpr(name) ) );
 			} else {
-				vars.add( new SetLocalVar(index) );
+				push( new SetLocalVar(index) );
 			}
 		} else {
 			Expr key = expr(obj2);
 			Expr table = expr(obj1);
-			vars.add( new SetTableEntry(table,key) );
+			push( new SetTableEntry(table,key) );
 		}
 		return true;
 	}
@@ -407,12 +474,26 @@
 
 	Rule SingleExpr() {
 		return FirstOf(
+			FunctionExpr(),
 			TableExpr(),
 			VarExp(),
 			LiteralExpr()
 		);
 	}
 
+	Rule FunctionExpr() {
+		return Sequence( "function", Spaces(), Function() );
+	}
+
+	Rule Function() {
+		return Sequence(
+			action( frame = new Frame(frame) ),
+			'(', incParens(), Spaces(), Optional(NameList()), ')', decParens(), Spaces(), Block(), Keyword("end"),
+			push( new Chunk( (Stmt)pop(), frame.stackSize, symbolsSize() ) ),
+			action( frame = frame.parent )
+		);
+	}
+
 	Rule TableExpr() {
 		return Sequence(
 			'{', incParens(), Spaces(),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/ReturnException.java	Tue Dec 04 09:16:03 2012 +0000
@@ -0,0 +1,4 @@
+package luan.interp;
+
+
+public final class ReturnException extends RuntimeException {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/ReturnStmt.java	Tue Dec 04 09:16:03 2012 +0000
@@ -0,0 +1,20 @@
+package luan.interp;
+
+import luan.LuaState;
+import luan.LuaException;
+
+
+final class ReturnStmt implements Stmt {
+	private final Expressions expressions;
+	boolean throwReturnException = true;
+
+	ReturnStmt(Expressions expressions) {
+		this.expressions = expressions;
+	}
+
+	@Override public void eval(LuaState lua) throws LuaException {
+		lua.returnValues = expressions.eval(lua);
+		if( throwReturnException )
+			throw new ReturnException();
+	}
+}
--- a/src/luan/interp/SetStmt.java	Sun Dec 02 10:51:18 2012 +0000
+++ b/src/luan/interp/SetStmt.java	Tue Dec 04 09:16:03 2012 +0000
@@ -9,6 +9,10 @@
 	private final Settable[] vars;
 	private final Expressions expressions;
 
+	SetStmt(Settable var,Expr expr) {
+		this( new Settable[]{var}, new ExpList.SingleExpList(expr) );
+	}
+
 	SetStmt(Settable[] vars,Expressions expressions) {
 		this.vars = vars;
 		this.expressions = expressions;
--- a/src/luan/lib/BasicLib.java	Sun Dec 02 10:51:18 2012 +0000
+++ b/src/luan/lib/BasicLib.java	Tue Dec 04 09:16:03 2012 +0000
@@ -19,7 +19,7 @@
 import luan.LuaException;
 import luan.interp.LuaParser;
 import luan.interp.Expressions;
-import luan.interp.Stmt;
+import luan.interp.Chunk;
 
 
 public class BasicLib {
@@ -28,8 +28,8 @@
 		LuaTable t = lua.env();
 		add( t, "print", new Object[0].getClass() );
 		add( t, "type", Object.class );
-		add( t, "load", String.class );
-		add( t, "loadFile", String.class );
+		add( t, "load", LuaState.class, String.class );
+		add( t, "loadFile", LuaState.class, String.class );
 		add( t, "pairs", LuaTable.class );
 		add( t, "ipairs", LuaTable.class );
 	}
@@ -55,7 +55,7 @@
 		return Lua.type(obj);
 	}
 
-	public static LuaFunction load(String ld) throws LuaException {
+	public static LuaFunction load(LuaState lua,String ld) throws LuaException {
 		LuaParser parser = Parboiled.createParser(LuaParser.class);
 		ParsingResult<?> result = new ReportingParseRunner(parser.Target()).run(ld);
 //		ParsingResult<?> result = new TracingParseRunner(parser.Target()).run(ld);
@@ -70,13 +70,8 @@
 				}
 			};
 		}
-		final Stmt stmt = (Stmt)resultValue;
-		return new LuaFunction() {
-			public Object[] call(LuaState lua,Object... args) throws LuaException {
-				stmt.eval(lua);
-				return LuaFunction.EMPTY_RTN;
-			}
-		};
+		Chunk chunk = (Chunk)resultValue;
+		return chunk.newClosure(lua);
 	}
 
 	public static String readFile(String fileName) throws IOException {
@@ -90,8 +85,8 @@
 		return sb.toString();
 	}
 
-	public static LuaFunction loadFile(String fileName) throws LuaException,IOException {
-		return load(readFile(fileName));
+	public static LuaFunction loadFile(LuaState lua,String fileName) throws LuaException,IOException {
+		return load(lua,readFile(fileName));
 	}
 
 	private static class TableIter {