changeset 17:09d41f7490a8

add local variables git-svn-id: https://luan-java.googlecode.com/svn/trunk@18 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Fri, 30 Nov 2012 11:46:34 +0000
parents 2a30281ef47c
children 3971113699b8
files src/luan/LuaState.java src/luan/interp/Block.java src/luan/interp/Chunk.java src/luan/interp/GetLocalVar.java src/luan/interp/LuaParser.java src/luan/interp/SetLocalVar.java src/luan/interp/SetStmt.java src/luan/interp/SetTableEntry.java src/luan/interp/Settable.java
diffstat 9 files changed, 313 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/LuaState.java	Thu Nov 29 10:36:38 2012 +0000
+++ b/src/luan/LuaState.java	Fri Nov 30 11:46:34 2012 +0000
@@ -8,4 +8,29 @@
 		return env;
 	}
 
+
+	private static class LuaStack {
+		final LuaStack previousStack;
+		final Object[] a;
+
+		LuaStack( LuaStack previousStack, int stackSize) {
+			this.previousStack = previousStack;
+			this.a = new Object[stackSize];
+		}
+	}
+
+	private LuaStack stack = null;
+
+	public void newStack(int stackSize) {
+		stack = new LuaStack(stack,stackSize);
+	}
+
+	public void popStack() {
+		stack = stack.previousStack;
+	}
+
+	public Object[] stack() {
+		return stack.a;
+	}
+
 }
--- a/src/luan/interp/Block.java	Thu Nov 29 10:36:38 2012 +0000
+++ b/src/luan/interp/Block.java	Fri Nov 30 11:46:34 2012 +0000
@@ -6,14 +6,25 @@
 
 final class Block implements Stmt {
 	private final Stmt[] stmts;
+	private final int stackStart;
+	private final int stackEnd;
 
-	Block(Stmt[] stmts) {
+	Block(Stmt[] stmts,int stackStart,int stackEnd) {
 		this.stmts = stmts;
+		this.stackStart = stackStart;
+		this.stackEnd = stackEnd;
 	}
 
 	@Override public void eval(LuaState lua) throws LuaException {
-		for( Stmt stmt : stmts ) {
-			stmt.eval(lua);
+		try {
+			for( Stmt stmt : stmts ) {
+				stmt.eval(lua);
+			}
+		} finally {
+			Object[] stack = lua.stack();
+			for( int i=stackStart; i<stackEnd; i++ ) {
+				stack[i] = null;
+			}
 		}
 	}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/Chunk.java	Fri Nov 30 11:46:34 2012 +0000
@@ -0,0 +1,25 @@
+package luan.interp;
+
+import luan.LuaState;
+import luan.LuaException;
+
+
+final class Chunk implements Stmt {
+	private final Stmt block;
+	private final int stackSize;
+
+	Chunk(Stmt block,int stackSize) {
+		this.block = block;
+		this.stackSize = stackSize;
+	}
+
+	@Override public void eval(LuaState lua) throws LuaException {
+		lua.newStack(stackSize);
+		try {
+			block.eval(lua);
+		} finally {
+			lua.popStack();
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/GetLocalVar.java	Fri Nov 30 11:46:34 2012 +0000
@@ -0,0 +1,16 @@
+package luan.interp;
+
+import luan.LuaState;
+
+
+final class GetLocalVar implements Expr {
+	private final int index;
+
+	GetLocalVar(int index) {
+		this.index = index;
+	}
+
+	@Override public Object eval(LuaState lua) {
+		return lua.stack()[index];
+	}
+}
--- a/src/luan/interp/LuaParser.java	Thu Nov 29 10:36:38 2012 +0000
+++ b/src/luan/interp/LuaParser.java	Fri Nov 30 11:46:34 2012 +0000
@@ -13,6 +13,7 @@
 import org.parboiled.support.ParsingResult;
 import org.parboiled.support.Var;
 import org.parboiled.support.StringVar;
+import org.parboiled.support.StringBuilderVar;
 import org.parboiled.support.ValueStack;
 import org.parboiled.errors.ErrorUtils;
 import luan.Lua;
@@ -23,6 +24,8 @@
 public class LuaParser extends BaseParser<Object> {
 	int nEquals;
 	int parens = 0;
+	List<String> symbols = new ArrayList<String>();
+	int stackSize = 0;
 
 	boolean nEquals(int n) {
 		nEquals = n;
@@ -39,6 +42,15 @@
 		return true;
 	}
 
+	int index(String name) {
+		int i = symbols.size();
+		while( --i >= 0 ) {
+			if( symbols.get(i).equals(name) )
+				return i;
+		}
+		return -1;
+	}
+
 	public Rule Target() {
 		return Sequence(
 			Spaces(),
@@ -51,35 +63,36 @@
 
 	Rule Chunk() {
 		return Sequence(
-			push(new ArrayList<Stmt>()),
-			Optional( Stmt() ),
-			ZeroOrMore(
-				StmtSep(),
-				Optional( Stmt() )
-			),
-			push( newChunk() )
+			Block(),
+			push( new Chunk( (Stmt)pop(), stackSize ) )
 		);
 	}
 
-	boolean addStmt() {
-		Stmt stmt = (Stmt)pop();
-		@SuppressWarnings("unchecked")
-		List<Stmt> stmts = (List<Stmt>)peek();
-		stmts.add(stmt);
-		return true;
+	Rule Block() {
+		Var<List<Stmt>> stmts = new Var<List<Stmt>>(new ArrayList<Stmt>());
+		return Sequence(
+			push(0),  // stackCount
+			Optional( Stmt(stmts) ),
+			ZeroOrMore(
+				StmtSep(),
+				Optional( Stmt(stmts) )
+			),
+			push( newBlock(stmts.get()) )
+		);
 	}
 
-	Stmt newChunk() {
-		@SuppressWarnings("unchecked")
-		List<Stmt> stmts = (List<Stmt>)pop();
-		switch( stmts.size() ) {
-		case 0:
+	Stmt newBlock(List<Stmt> stmts) {
+		if( stackSize < symbols.size() )
+			stackSize = symbols.size();
+		int stackN = (Integer)pop();
+		for( int i=0; i<stackN; i++ ) {
+			symbols.remove(symbols.size()-1);  // pop
+		}
+		if( stmts.isEmpty() )
 			return Stmt.EMPTY;
-		case 1:
+		if( stmts.size()==1 && stackN==0 )
 			return stmts.get(0);
-		default:
-			return new Block(stmts.toArray(new Stmt[0]));
-		}
+		return new Block( stmts.toArray(new Stmt[0]), symbols.size(), symbols.size()+stackN );
 	}
 
 	Rule StmtSep() {
@@ -99,29 +112,74 @@
 		return FirstOf("\r\n", '\r', '\n');
 	}
 
-	Rule Stmt() {
+	Rule Stmt(Var<List<Stmt>> stmts) {
+		return FirstOf(
+			LocalStmt(stmts),
+			Sequence(
+				FirstOf(
+					DoStmt(),
+					WhileStmt(),
+					RepeatStmt(),
+					IfStmt(),
+					SetStmt(),
+					ExpressionsStmt()
+				),
+				stmts.get().add( (Stmt)pop() )
+			)
+		);
+	}
+
+	Rule DoStmt() {
+		return Sequence(
+			Keyword("do"), Block(), Keyword("end")
+		);
+	}
+
+	Rule LocalStmt(Var<List<Stmt>> stmts) {
+		Var<List<String>> names = new Var<List<String>>(new ArrayList<String>());
 		return Sequence(
-			FirstOf(
-				WhileStmt(),
-				RepeatStmt(),
-				IfStmt(),
-				SetStmt(),
-				ExpressionsStmt()
+			Keyword("local"),
+			Name(),
+			newName(names.get()),
+			ZeroOrMore(
+				',', Spaces(), Name(),
+				newName(names.get())
 			),
-			addStmt()
+			Optional(
+				'=', Spaces(),
+				ExpList(),
+				stmts.get().add( newSetLocalStmt(names.get()) )
+			)
 		);
 	}
 
+	boolean newName(List<String> names) {
+		String name = (String)pop();
+		names.add(name);
+		symbols.add(name);
+		push( ((Integer)pop()) + 1 );
+		return true;
+	}
+
+	SetStmt newSetLocalStmt(List<String> names) {
+		Expressions values = (Expressions)pop();
+		SetLocalVar[] vars = new SetLocalVar[names.size()];
+		for( int i=0; i<vars.length; i++ ) {
+			vars[i] = new SetLocalVar(index(names.get(i)));
+		}
+		return new SetStmt( vars, values );
+	}
+
 	Rule WhileStmt() {
 		return Sequence(
-			"while", Spaces(), Expr(), "do", Spaces(), Chunk(), "end", Spaces(),
+			Keyword("while"), Expr(), Keyword("do"), Block(), Keyword("end"),
 			push( new WhileStmt( expr(pop(1)), (Stmt)pop() ) )
 		);
 	}
 
 	Rule RepeatStmt() {
 		return Sequence(
-			"repeat", Spaces(), Chunk(), "until", Spaces(), Expr(),
+			Keyword("repeat"), Block(), Keyword("until"), Expr(),
 			push( new RepeatStmt( (Stmt)pop(1), expr(pop()) ) )
 		);
 	}
@@ -129,17 +187,17 @@
 	Rule IfStmt() {
 		Var<Integer> n = new Var<Integer>(1);
 		return Sequence(
-			"if", Spaces(), Expr(), "then", Spaces(), Chunk(),
+			Keyword("if"), Expr(), Keyword("then"), Block(),
 			push(Stmt.EMPTY),
 			ZeroOrMore(
-				"elseif", Spaces(), drop(), Expr(), "then", Spaces(), Chunk(),
+				Keyword("elseif"), drop(), Expr(), Keyword("then"), Block(),
 				push(Stmt.EMPTY),
 				n.set(n.get()+1)
 			),
 			Optional(
-				"else", Spaces(), drop(), Chunk()
+				Keyword("else"), drop(), Block()
 			),
-			"end", Spaces(),
+			Keyword("end"),
 			buildIfStmt(n.get())
 		);
 	}
@@ -173,13 +231,13 @@
 	SetStmt newSetStmt() {
 		Expressions values = (Expressions)pop();
 		@SuppressWarnings("unchecked")
-		List<SetStmt.Var> vars = (List<SetStmt.Var>)pop();
-		return new SetStmt( vars.toArray(new SetStmt.Var[0]), values );
+		List<Settable> vars = (List<Settable>)pop();
+		return new SetStmt( vars.toArray(new Settable[0]), values );
 	}
 
 	Rule VarList() {
 		return Sequence(
-			push(new ArrayList<SetStmt.Var>()),
+			push(new ArrayList<Settable>()),
 			Var(),
 			addToVarList(),
 			ZeroOrMore(
@@ -190,14 +248,25 @@
 	}
 
 	boolean addToVarList() {
-		Object obj = pop();
-		if( obj==null )
+		Object obj2 = pop();
+		if( obj2==null )
 			return false;
-		Expr key = expr(obj);
-		Expr table = expr(pop());
+		Object obj1 = pop();
 		@SuppressWarnings("unchecked")
-		List<SetStmt.Var> vars = (List<SetStmt.Var>)peek();
-		vars.add( new SetStmt.Var(table,key) );
+		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) ) );
+			} else {
+				vars.add( new SetLocalVar(index) );
+			}
+		} else {
+			Expr key = expr(obj2);
+			Expr table = expr(obj1);
+			vars.add( new SetTableEntry(table,key) );
+		}
 		return true;
 	}
 
@@ -324,7 +393,7 @@
 	Rule Field() {
 		return FirstOf(
 			Sequence(
-				FirstOf( SubExpr(), Name() ),
+				FirstOf( SubExpr(), NameExpr() ),
 				'=', Spaces(), Expr(),
 				addField()
 			),
@@ -376,7 +445,7 @@
 					push(null)  // marker
 				),
 				Sequence(
-					push(EnvExpr.INSTANCE),
+					push(null),  // marker
 					Name()
 				)
 			),
@@ -384,7 +453,7 @@
 				makeVarExp(),
 				FirstOf(
 					SubExpr(),
-					Sequence( '.', Spaces(), Name() ),
+					Sequence( '.', Spaces(), NameExpr() ),
 					Sequence(
 						Args(),
 						push(null)  // marker
@@ -395,10 +464,20 @@
 	}
 
 	boolean makeVarExp() {
-		Object obj = pop();
-		if( obj==null )
+		Object obj2 = pop();
+		if( obj2==null )
 			return true;
-		return push( new GetExpr( expr(pop()), expr(obj) ) );
+		Object obj1 = pop();
+		if( obj1==null ) {
+			String name = (String)obj2;
+			int index = index(name);
+			if( index == -1 ) {
+				return push( new GetExpr( EnvExpr.INSTANCE, new ConstExpr(name) ) );
+			} else {
+				return push( new GetLocalVar(index) );
+			}
+		}
+		return push( new GetExpr( expr(obj1), expr(obj2) ) );
 	}
 
 	// function should be on top of the stack
@@ -429,21 +508,20 @@
 	}
 
 	Rule ExpList() {
+		Var<ExpList.Builder> builder = new Var<ExpList.Builder>(new ExpList.Builder());
 		return Sequence(
-			push(new ExpList.Builder()),
 			Expr(),
-			addToExpList(),
+			addToExpList(builder.get()),
 			ZeroOrMore(
 				',', Spaces(), Expr(),
-				addToExpList()
+				addToExpList(builder.get())
 			),
-			push( ((ExpList.Builder)pop()).build() )
+			push( builder.get().build() )
 		);
 	}
 
-	boolean addToExpList() {
+	boolean addToExpList(ExpList.Builder bld) {
 		Object obj = pop();
-		ExpList.Builder bld = (ExpList.Builder)peek();
 		if( obj instanceof Expressions ) {
 			bld.add( (Expressions)obj );
 		} else {
@@ -452,24 +530,35 @@
 		return true;
 	}
 
+
 	Rule SubExpr() {
 		return Sequence( '[', incParens(), Spaces(), Expr(), ']', decParens(), Spaces() );
 	}
 
+	Rule NameExpr() {
+		return Sequence(
+			Name(),
+			push( new ConstExpr((String)pop()) )
+		);
+	}
+
 	Rule Name() {
 		return Sequence(
 			Sequence(
-				NameStart(),
-				ZeroOrMore(
-					FirstOf( NameStart(), Digit() )
-				)
+				NameFirstChar(),
+				ZeroOrMore( NameChar() )
 			),
-			pushNameExpr(),
+			!keywords.contains(match()),
+			push(match()),
 			Spaces()
 		);
 	}
 
-	Rule NameStart() {
+	Rule NameChar() {
+		return FirstOf( NameFirstChar(), Digit() );
+	}
+
+	Rule NameFirstChar() {
 		return FirstOf(
 			CharRange('a', 'z'),
 			CharRange('A', 'Z'),
@@ -477,14 +566,15 @@
 		);
 	}
 
-	boolean pushNameExpr() {
-		String name = match();
-		if( keywords.contains(name) )
-			return false;
-		return push( new ConstExpr(name) );
+	Rule Keyword(String keyword) {
+		return Sequence(
+			keyword,
+			TestNot( NameChar() ),
+			Spaces()
+		);
 	}
 
-	private static final Set<String> keywords = new HashSet<String>(Arrays.asList(
+	static final Set<String> keywords = new HashSet<String>(Arrays.asList(
 		"and",
 		"break",
 		"do",
@@ -635,7 +725,7 @@
 
 	Rule LongString() {
 		return Sequence(
-			"[",
+			'[',
 			ZeroOrMore('='),
 			nEquals(matchLength()),
 			'[',
@@ -649,41 +739,41 @@
 	}
 
 	Rule QuotedString(char quote) {
+		StringBuilderVar buf = new StringBuilderVar();
 		return Sequence(
 			quote,
-			push(new StringBuffer()),
 			ZeroOrMore(
 				FirstOf(
 					Sequence(
 						NoneOf("\\\n"+quote),
-						append(matchedChar())
+						buf.append(matchedChar())
 					),
-					EscSeq()
+					EscSeq(buf)
 				)
 			),
 			quote,
-			push(((StringBuffer)pop()).toString())
+			push( buf.getString() )
 		);
 	}
 
-	Rule EscSeq() {
+	Rule EscSeq(StringBuilderVar buf) {
 		return Sequence(
 			'\\',
 			FirstOf(
-				Sequence( 'a', append('\u0007') ),
-				Sequence( 'b', append('\b') ),
-				Sequence( 'f', append('\f') ),
-				Sequence( 'n', append('\n') ),
-				Sequence( 'r', append('\r') ),
-				Sequence( 't', append('\t') ),
-				Sequence( 'v', append('\u000b') ),
-				Sequence( '\\', append('\\') ),
-				Sequence( '"', append('"') ),
-				Sequence( '\'', append('\'') ),
+				Sequence( 'a', buf.append('\u0007') ),
+				Sequence( 'b', buf.append('\b') ),
+				Sequence( 'f', buf.append('\f') ),
+				Sequence( 'n', buf.append('\n') ),
+				Sequence( 'r', buf.append('\r') ),
+				Sequence( 't', buf.append('\t') ),
+				Sequence( 'v', buf.append('\u000b') ),
+				Sequence( '\\', buf.append('\\') ),
+				Sequence( '"', buf.append('"') ),
+				Sequence( '\'', buf.append('\'') ),
 				Sequence(
 					'x',
 					Sequence( HexDigit(), HexDigit() ),
-					append( (char)Integer.parseInt(match(),16) )
+					buf.append( (char)Integer.parseInt(match(),16) )
 				),
 				Sequence(
 					Sequence(
@@ -695,18 +785,12 @@
 							)
 						)
 					),
-					append( (char)Integer.parseInt(match()) )
+					buf.append( (char)Integer.parseInt(match()) )
 				)
 			)
 		);
 	}
 
-	boolean append(char ch) {
-		StringBuffer sb = (StringBuffer)peek();
-		sb.append(ch);
-		return true;
-	}
-
 	Rule Spaces() {
 		return ZeroOrMore(
 			FirstOf(
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/SetLocalVar.java	Fri Nov 30 11:46:34 2012 +0000
@@ -0,0 +1,16 @@
+package luan.interp;
+
+import luan.LuaState;
+
+
+final class SetLocalVar implements Settable {
+	private final int index;
+
+	SetLocalVar(int index) {
+		this.index = index;
+	}
+
+	@Override public void set(LuaState lua,Object value) {
+		lua.stack()[index] = value;
+	}
+}
--- a/src/luan/interp/SetStmt.java	Thu Nov 29 10:36:38 2012 +0000
+++ b/src/luan/interp/SetStmt.java	Fri Nov 30 11:46:34 2012 +0000
@@ -1,28 +1,15 @@
 package luan.interp;
 
-import java.util.List;
 import luan.Lua;
 import luan.LuaState;
 import luan.LuaException;
-import luan.LuaTable;
 
 
 final class SetStmt implements Stmt {
-
-	static class Var {
-		final Expr table;
-		final Expr key;
-
-		Var(Expr table,Expr key) {
-			this.table = table;
-			this.key = key;
-		}
-	}
-
-	private final Var[] vars;
+	private final Settable[] vars;
 	private final Expressions expressions;
 
-	SetStmt(Var[] vars,Expressions expressions) {
+	SetStmt(Settable[] vars,Expressions expressions) {
 		this.vars = vars;
 		this.expressions = expressions;
 	}
@@ -30,14 +17,9 @@
 	@Override public void eval(LuaState lua) throws LuaException {
 		final Object[] vals = expressions.eval(lua);
 		for( int i=0; i<vars.length; i++ ) {
-			Var var = vars[i];
-			Object t = var.table.eval(lua);
-			if( !(t instanceof LuaTable) )
-				throw new LuaException( "attempt to index a " + Lua.type(t) + " value" );
-			LuaTable tbl = (LuaTable)t;
-			Object key = var.key.eval(lua);
+			Settable var = vars[i];
 			Object val = i < vals.length ? vals[i] : null;
-			tbl.set(key,val);
+			var.set(lua,val);
 		}
 	}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/SetTableEntry.java	Fri Nov 30 11:46:34 2012 +0000
@@ -0,0 +1,27 @@
+package luan.interp;
+
+import luan.LuaState;
+import luan.LuaException;
+import luan.LuaTable;
+import luan.Lua;
+
+
+final class SetTableEntry implements Settable {
+	private final Expr tableExpr;
+	private final Expr keyExpr;
+
+	SetTableEntry(Expr tableExpr,Expr keyExpr) {
+		this.tableExpr = tableExpr;
+		this.keyExpr = keyExpr;
+	}
+
+	@Override public void set(LuaState lua,Object value) throws LuaException {
+		Object t = tableExpr.eval(lua);
+		if( !(t instanceof LuaTable) )
+			throw new LuaException( "attempt to index a " + Lua.type(t) + " value" );
+		LuaTable table = (LuaTable)t;
+		Object key = keyExpr.eval(lua);
+		table.set(key,value);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/interp/Settable.java	Fri Nov 30 11:46:34 2012 +0000
@@ -0,0 +1,9 @@
+package luan.interp;
+
+import luan.LuaState;
+import luan.LuaException;
+
+
+interface Settable {
+	public void set(LuaState lua,Object value) throws LuaException;
+}