changeset 594:e91e476186c7

theme indentation
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 03 Sep 2015 12:23:53 -0600
parents 92c9fa5e39e6
children 8370c4009cce
files core/src/luan/impl/ExprImpl.java core/src/luan/impl/SetLocalVar.java core/src/luan/impl/ThemeParser.java
diffstat 3 files changed, 175 insertions(+), 65 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ExprImpl.java	Thu Sep 03 12:23:53 2015 -0600
@@ -0,0 +1,11 @@
+package luan.impl;
+
+import luan.LuanElement;
+
+
+abstract class ExprImpl extends CodeImpl implements Expr {
+
+	ExprImpl(LuanElement el) {
+		super(el);
+	}
+}
--- a/core/src/luan/impl/SetLocalVar.java	Mon Aug 31 10:16:57 2015 -0600
+++ b/core/src/luan/impl/SetLocalVar.java	Thu Sep 03 12:23:53 2015 -0600
@@ -5,6 +5,7 @@
 	private final int index;
 
 	SetLocalVar(int index) {
+		if( index < 0 )  throw new RuntimeException();
 		this.index = index;
 	}
 
--- a/core/src/luan/impl/ThemeParser.java	Mon Aug 31 10:16:57 2015 -0600
+++ b/core/src/luan/impl/ThemeParser.java	Thu Sep 03 12:23:53 2015 -0600
@@ -85,6 +85,7 @@
 	private static final String IO = "-IO-";
 	private static final String MOD = "-MOD-";
 	private static final String ENV = "-ENV-";
+	private static final String INDENT = "-INDENT-";
 	private static final UpValue.Getter[] NO_UP_VALUE_GETTERS = new UpValue.Getter[0];
 
 	private final LuanSource source;
@@ -122,6 +123,13 @@
 		return frame.stackIndex(name);
 	}
 
+	private void popSymbols(int n) {
+		List<String> symbols = frame.symbols;
+		while( n-- > 0 ) {
+			symbols.remove(symbols.size()-1);
+		}
+	}
+
 	private int upValueIndex(String name) {
 		return frame.upValueIndex(name);
 	}
@@ -148,7 +156,8 @@
 		{
 			addSymbol(MOD);
 			LuanElement se = se(0,"local M = {}");
-			TableExpr tableExpr = new TableExpr( se, new TableExpr.Field[0], ExpList.emptyExpList );
+			TableExpr.Field indent = new TableExpr.Field(new ConstExpr(null,INDENT),new ConstExpr(null,""));
+			TableExpr tableExpr = new TableExpr( se, new TableExpr.Field[]{indent}, ExpList.emptyExpList );
 			SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(MOD)), tableExpr );
 			stmts.add(setStmt);
 		}
@@ -166,117 +175,171 @@
 		return fnDef;
 	}
 
-	private static class Tag {
-		final String name;
-		final Map<String,String> attrs;
-
-		Tag(String name,Map<String,String> attrs) {
-			this.name = name;
-			this.attrs = attrs;
-		}
-	}
-
 	private Stmt parseDef() throws ParseException {
 		int start = parser.begin();
 		if( !parser.match("{define:") )
 			return parser.failure(null);
-		Tag tag = parseBlockTag();
-		if( tag == null )
-			return parser.failure(null);
-		parser.match('\r');  parser.match('\n');  // ignore newline
-		if( !tag.attrs.isEmpty() )
+		String name = parseName();
+		if( name==null )
+			throw exception("invalid block name");
+		Map<String,String> attrs = parseAttrs();
+		String spaces = "";
+		if( BlankLine() ) {
+			while( BlankLine() );
+			int startSpaces = parser.currentIndex();
+			InlineSpaces();
+			spaces = parser.textFrom(startSpaces);
+		}
+		if( !parser.match("}") )
+			return null;
+		if( !attrs.isEmpty() )
 			throw exception("this block should have no attributes");
 		Expr table = new GetLocalVar(null,stackIndex(MOD));
-		Settable fnName = new SetTableEntry(se(start),table,new ConstExpr(null,tag.name));
+		Settable fnName = new SetTableEntry(se(start),table,new ConstExpr(null,name));
 		frame = new Frame(frame);
 		addSymbol(ENV);
-		Stmt block = parseBody("define",tag);
+		Stmt block = parseBody("define:"+name,spaces,EndOfLine());
 		FnDef fnDef = newFnDef(start,block);
 		frame = frame.parent;
 		Stmt rtn = new SetStmt(fnName,fnDef);
 		return parser.success(rtn);
 	}
 
-	private Stmt parseBody(String type,Tag tag) throws ParseException {
-		String endTag = "{/"+type+":" + tag.name + "}";
+	private Stmt parseBody(String tagName,String spaces,boolean initIndent) throws ParseException {
 		List<Stmt> stmts = new ArrayList<Stmt>();
 		int stackStart = symbolsSize();
-		StringBuilder sb = new StringBuilder();
-		int start = -1;
-		while( !parser.match(endTag) ) {
+		int start = parser.currentIndex();
+		{
+			addSymbol(INDENT);
+			final Expr env = env();
+			Expr exp = new ExprImpl(se(start,"indent")) {
+				@Override public Object eval(LuanStateImpl luan) throws LuanException {
+					LuanTable tbl = (LuanTable)env.eval(luan);
+					String indent = (String)tbl.get(luan,INDENT);
+					if( indent==null )  throw new NullPointerException();
+					return indent;
+				}
+			};
+//			Expr exp = new IndexExpr( se(start,"indent"), env(), new ConstExpr(null,INDENT) );
+			SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(INDENT)), exp );
+			stmts.add(setStmt);
+		}
+		boolean afterIndent = false;
+		if( initIndent && parser.match(spaces) ) {
+			addText(start,start,stmts,true);
+			start = parser.currentIndex();
+			afterIndent = true;
+		}
+		int end = start;
+		while( !matchEndTag(tagName) ) {
 			if( parser.endOfInput() )
 				throw exception("unclosed block");
-			Stmt block = parseBlock();
+			Stmt block = parseBlock(spaces);
 			if( block != null ) {
-				addText(start,stmts,sb);
+				addText(start,end,stmts,false);
+				start = parser.currentIndex();
 				stmts.add(block);
+				afterIndent = false;
 				continue;
 			}
-			Stmt simpleTag = parseSimpleTag();
-			if( simpleTag != null ) {
-				addText(start,stmts,sb);
-				stmts.add(simpleTag);
+			{
+				String extraSpaces = null;
+				if( afterIndent ) {
+					int startSpaces = parser.currentIndex();
+					InlineSpaces();
+					extraSpaces = parser.textFrom(startSpaces);
+					end = parser.currentIndex();
+				}
+				Stmt simpleTag = parseSimpleTag(extraSpaces);
+				if( simpleTag != null ) {
+					addText(start,end,stmts,false);
+					start = parser.currentIndex();
+					stmts.add(simpleTag);
+					afterIndent = false;
+					continue;
+				}
+				if( extraSpaces!=null && extraSpaces.length() > 0 )
+					continue;
+			}
+			if( EndOfLine() ) {
+				end = parser.currentIndex();
+				afterIndent = false;
+				if( parser.match(spaces) ) {
+					addText(start,end,stmts,true);
+					start = parser.currentIndex();
+					afterIndent = true;
+				}
 				continue;
 			}
-			if( sb.length() == 0 )
-				start = parser.currentIndex();
-			sb.append( parser.currentChar() );
 			parser.anyChar();
+			end = parser.currentIndex();
+			afterIndent = false;
 		}
-		addText(start,stmts,sb);
-		Stmt block = new Block( stmts.toArray(new Stmt[0]), 0, symbolsSize() );
+		addText(start,end,stmts,false);
+		Stmt block = new Block( stmts.toArray(new Stmt[0]), stackStart, symbolsSize() );
+		popSymbols(1);
 		return block;
 	}
 
-	private void addText(int start,List<Stmt> stmts,StringBuilder sb) {
-		if( sb.length() == 0 )
-			return;
-		Expr io = new GetUpVar(null,upValueIndex(IO));
-		Expr stdoutExp = new IndexExpr( se(start,"stdout"), io, new ConstExpr(null,"stdout") );
-		Expr writeExp = new IndexExpr( se(start,"write"), stdoutExp, new ConstExpr(null,"write") );
-		FnCall writeCall = new FnCall( se(start), writeExp, new ConstExpr(null,sb.toString()) );
-		stmts.add( new ExpressionsStmt(writeCall) );
-		sb.setLength(0);
+	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 Stmt parseBlock() throws ParseException {
+	private void addText(int start,int end,List<Stmt> stmts,boolean indent) {
+		List<Expressions> args = new ArrayList<Expressions>();
+		if( start < end ) {
+			String text = parser.text.substring(start,end);
+			args.add( new ConstExpr(null,text) );
+		}
+		if( indent ) {
+			args.add( new GetLocalVar(null,stackIndex(INDENT)) );
+		}
+		if( !args.isEmpty() ) {
+			Expr io = new GetUpVar(null,upValueIndex(IO));
+			Expr stdoutExp = new IndexExpr( se(start,"stdout"), io, new ConstExpr(null,"stdout") );
+			Expr writeExp = new IndexExpr( se(start,"write"), stdoutExp, new ConstExpr(null,"write") );
+			FnCall writeCall = new FnCall( se(start), writeExp, ExpList.build(args) );
+			stmts.add( new ExpressionsStmt(writeCall) );
+		}
+	}
+
+	private Stmt parseBlock(String spaces) throws ParseException {
 		int start = parser.begin();
 		if( !parser.match("{block:") )
 			return parser.failure(null);
-		Tag tag = parseBlockTag();
-		if( tag == null )
-			return parser.failure(null);
-		if( tag.name.equals("Set") )
-			throw exception("block:Set not allowed here");
+		String name = parseName();
+		if( name==null )
+			throw exception("invalid block name");
+		Map<String,String> attrs = parseAttrs();
+		if( !parser.match("}") )
+			return null;
 		frame = new Frame(frame);
 		addSymbol(ENV);
-		Stmt block = parseBody("block",tag);
+		Stmt block = parseBody("block:"+name,spaces,false);
 		FnDef fnDef = newFnDef(start,block);
 		frame = frame.parent;
 //		String rtn = "<% env." + tag.name + "(" + (tag.attrs.isEmpty() ? "nil" : table(tag.attrs)) + ",env,function(env) %>" + block + "<% end) %>";
 		Expr env = env();
-		Expr fn = new IndexExpr( se(start,"block:"+tag.name), env, new ConstExpr(null,tag.name) );
+		Expr fn = new IndexExpr( se(start,"block:"+name), env, new ConstExpr(null,name) );
 		List<Expressions> args = new ArrayList<Expressions>();
 		args.add( env );
-		args.add( tag.attrs.isEmpty() ? new ConstExpr(null,null) : table(tag.attrs) );
+		args.add( attrs.isEmpty() ? new ConstExpr(null,null) : table(attrs) );
 		args.add( fnDef );
 		FnCall fnCall = new FnCall( se(start), fn, ExpList.build(args) );
 		Stmt rtn = new ExpressionsStmt(fnCall);
 		return parser.success(rtn);
 	}
 
-	private Tag parseBlockTag() throws ParseException {
-		String name = parseName();
-		if( name==null )
-			throw exception("invalid block name");
-		Map<String,String> attrs = parseAttrs();
-		if( !parser.match("}") )
-			return null;
-		Tag tag = new Tag(name,attrs);
-		return tag;
-	}
-
-	private Stmt parseSimpleTag() throws ParseException {
+	private Stmt parseSimpleTag(String spaces) throws ParseException {
 		int start = parser.begin();
 		if( !parser.match("{") )
 			return parser.failure(null);
@@ -284,11 +347,29 @@
 		if( name==null )
 			return parser.failure(null);
 		Map<String,String> attrs = parseAttrs();
+		Spaces();
 		if( !parser.match("}") )
 			return parser.failure(null);
 //		rtn = "<% env." + name + (attrs.isEmpty() ? "()" : table(attrs)) + " %>";
 		Expr env = env();
 		Expr fn = new IndexExpr( se(start,name), env, new ConstExpr(null,name) );
+		if( spaces!=null && spaces.length() > 0 ) {
+			final Expr oldEnv = env;
+			final Expr oldIndentExpr = new GetLocalVar(null,stackIndex(INDENT));
+			final String addSpaces = spaces;
+			env = new ExprImpl(se(start,"indent_env")) {
+				@Override public Object eval(LuanStateImpl luan) throws LuanException {
+					LuanTable mt = new LuanTable();
+					mt.rawPut("__index",oldEnv.eval(luan));
+					LuanTable tbl = new LuanTable();
+					tbl.setMetatable(mt);
+					String oldIndent = (String)oldIndentExpr.eval(luan);
+					String newIndent = oldIndent + addSpaces;
+					tbl.rawPut(INDENT,newIndent);
+					return tbl;
+				}
+			};
+		}
 		List<Expressions> args = new ArrayList<Expressions>();
 		args.add( env );
 		if( !attrs.isEmpty() )
@@ -311,7 +392,6 @@
 	private Map<String,String> parseAttrs() {
 		Map<String,String> attrs = new HashMap<String,String>();
 		while( parseAttr(attrs) );
-		Spaces();
 		return attrs;
 	}
 
@@ -340,6 +420,24 @@
 		while( parser.anyOf(" \t\r\n") );
 	}
 
+	private void InlineSpaces() {
+		while( parser.anyOf(" \t") );
+	}
+
+	private void BlankLines() {
+		while( BlankLine() );
+	}
+
+	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() {
 		int start = parser.begin();
 		if( !NameChar() )