changeset 584:0742ac78fa69

add Luan.load_theme
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 12 Aug 2015 05:21:21 -0600
parents 1368ca798ccc
children bb3818249dfb
files core/src/luan/impl/GetLocalVar.java core/src/luan/impl/GetUpVar.java core/src/luan/impl/LuanParser.java core/src/luan/impl/ThemeParser.java core/src/luan/modules/BasicLuan.java core/src/luan/modules/Luan.luan
diffstat 6 files changed, 409 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/core/src/luan/impl/GetLocalVar.java	Fri Jul 31 19:19:42 2015 -0600
+++ b/core/src/luan/impl/GetLocalVar.java	Wed Aug 12 05:21:21 2015 -0600
@@ -8,6 +8,7 @@
 
 	GetLocalVar(LuanElement se,int index) {
 		super(se);
+		if( index < 0 )  throw new RuntimeException();
 		this.index = index;
 	}
 
--- a/core/src/luan/impl/GetUpVar.java	Fri Jul 31 19:19:42 2015 -0600
+++ b/core/src/luan/impl/GetUpVar.java	Wed Aug 12 05:21:21 2015 -0600
@@ -8,6 +8,7 @@
 
 	GetUpVar(LuanElement se,int index) {
 		super(se);
+		if( index < 0 )  throw new RuntimeException();
 		this.index = index;
 	}
 
--- a/core/src/luan/impl/LuanParser.java	Fri Jul 31 19:19:42 2015 -0600
+++ b/core/src/luan/impl/LuanParser.java	Wed Aug 12 05:21:21 2015 -0600
@@ -256,7 +256,6 @@
 			|| (stmt=ForStmt()) != null
 			|| (stmt=DoStmt()) != null
 			|| (stmt=WhileStmt()) != null
-			|| (stmt=FunctionStmt()) != null
 			|| (stmt=RepeatStmt()) != null
 			|| (stmt=IfStmt()) != null
 			|| (stmt=SetStmt()) != null
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/ThemeParser.java	Wed Aug 12 05:21:21 2015 -0600
@@ -0,0 +1,397 @@
+package luan.impl;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import luan.LuanSource;
+import luan.LuanTable;
+import luan.LuanElement;
+import luan.LuanState;
+import luan.LuanFunction;
+import luan.LuanException;
+import luan.modules.PackageLuan;
+
+
+public final class ThemeParser {
+
+	public static LuanFunction compile(LuanState luan,LuanSource source) throws LuanException {
+		try {
+			FnDef fnDef = new ThemeParser(source).parse();
+			final LuanStateImpl luanImpl = (LuanStateImpl)luan;
+			return new Closure(luanImpl,fnDef);
+		} catch(ParseException e) {
+//e.printStackTrace();
+			throw new LuanException(luan, e.getFancyMessage() );
+		}
+	}
+
+	private static final class Frame {
+		final Frame parent;
+		final List<String> symbols = new ArrayList<String>();
+		int stackSize = 0;
+		final boolean isVarArg;
+		final List<String> upValueSymbols = new ArrayList<String>();
+		final List<UpValue.Getter> upValueGetters = new ArrayList<UpValue.Getter>();
+
+		Frame() {
+			this.parent = null;
+			isVarArg = true;
+		}
+
+		Frame(Frame parent) {
+			this.parent = parent;
+			isVarArg = false;
+			if( upValueIndex(MOD) != 0 )
+				throw new RuntimeException();
+		}
+
+		int stackIndex(String name) {
+			int i = symbols.size();
+			while( --i >= 0 ) {
+				if( symbols.get(i).equals(name) )
+					return i;
+			}
+			return -1;
+		}
+
+		int upValueIndex(String name) {
+			int i = upValueSymbols.size();
+			while( --i >= 0 ) {
+				if( upValueSymbols.get(i).equals(name) )
+					return i;
+			}
+			if( parent==null )
+				return -1;
+			i = parent.stackIndex(name);
+			if( i != -1 ) {
+				upValueGetters.add(new UpValue.StackGetter(i));
+			} else {
+				i = parent.upValueIndex(name);
+				if( i == -1 )
+					return -1;
+				upValueGetters.add(new UpValue.NestedGetter(i));
+			}
+			upValueSymbols.add(name);
+			return upValueSymbols.size() - 1;
+		}
+
+		void addUpValueGetter(String name,UpValue.Getter upValueGetter) {
+			upValueSymbols.add(name);
+			upValueGetters.add(upValueGetter);
+		}
+	}
+
+	private static final String IO = "-IO-";
+	private static final String MOD = "-MOD-";
+	private static final String ENV = "-ENV-";
+	private static final UpValue.Getter[] NO_UP_VALUE_GETTERS = new UpValue.Getter[0];
+
+	private final LuanSource source;
+	private final Parser parser;
+	private Frame frame = new Frame();
+
+	private ThemeParser(LuanSource source) {
+		this.source = source;
+		this.parser = new Parser(this.source);
+	}
+
+	private LuanElement se(int start) {
+		return se(start,null);
+	}
+
+	private LuanElement se(int start,String text) {
+		return new LuanElement(source,start,parser.currentIndex(),text);
+	}
+
+	private int symbolsSize() {
+		return frame.symbols.size();
+	}
+
+	private void addSymbol(String name) {
+		frame.symbols.add(name);
+		if( frame.stackSize < symbolsSize() )
+			frame.stackSize = symbolsSize();
+	}
+
+	private FnDef newFnDef(int start,Stmt stmt) {
+		return new FnDef( se(start), stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) );
+	}
+
+	private int stackIndex(String name) {
+		return frame.stackIndex(name);
+	}
+
+	private int upValueIndex(String name) {
+		return frame.upValueIndex(name);
+	}
+
+	private ParseException exception(String msg) {
+		parser.failure();
+		return parser.exception(msg);
+	}
+
+	private Expr env() {
+		return new GetLocalVar(null,stackIndex(ENV));
+	}
+
+	private FnDef parse() throws ParseException {
+		List<Stmt> stmts = new ArrayList<Stmt>();
+		int stackStart = symbolsSize();
+		{
+			addSymbol(IO);
+			LuanElement se = se(0,"require 'luan:Io'");
+			FnCall requireCall = new FnCall( se, new ConstExpr(se,PackageLuan.requireFn), new ConstExpr(se,"luan:Io") );
+			SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(IO)), new ExpressionsExpr(requireCall) );
+			stmts.add(setStmt);
+		}
+		{
+			addSymbol(MOD);
+			LuanElement se = se(0,"local M = {}");
+			TableExpr tableExpr = new TableExpr( se, new TableExpr.Field[0], ExpList.emptyExpList );
+			SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(MOD)), tableExpr );
+			stmts.add(setStmt);
+		}
+		while( !parser.endOfInput() ) {
+			Stmt def = parseDef();
+			if( def != null ) {
+				stmts.add(def);
+			} else {
+				parser.anyChar();
+			}
+		}
+		stmts.add( new ReturnStmt(null,new GetLocalVar(null,stackIndex(MOD))) );
+		Stmt block = new Block( stmts.toArray(new Stmt[0]), stackStart, symbolsSize() );
+		FnDef fnDef = newFnDef(0,block);
+		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();
+		Tag tag = parseBlockTag();
+		if( tag == null )
+			return parser.failure(null);
+		Stmt rtn = null;
+		if( tag.name.equals("Set") ) {
+			String name = tag.attrs.remove("name");
+			if( name == null )
+				throw exception("block:Set missing required attribute 'name'");
+			if( !tag.attrs.isEmpty() )
+				throw exception("block:Set has unrecognized attributes: "+tag.attrs);
+			if( !validateName(name) )
+				throw exception("invalid Set name: "+name);
+			addSymbol( name );
+			frame = new Frame(frame);
+			addSymbol(ENV);
+			Stmt block = parseBody(tag);
+			FnDef fnDef = newFnDef(start,block);
+			frame = frame.parent;
+			rtn = new SetStmt( new SetLocalVar(symbolsSize()-1), fnDef );
+		} else {
+			if( !tag.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));
+			frame = new Frame(frame);
+			addSymbol(ENV);
+			Stmt block = parseBody(tag);
+			FnDef fnDef = newFnDef(start,block);
+			frame = frame.parent;
+			rtn = new SetStmt(fnName,fnDef);
+		}
+		return parser.success(rtn);
+	}
+
+	private Stmt parseBody(Tag tag) throws ParseException {
+		String endTag = "{/block:" + tag.name + "}";
+		List<Stmt> stmts = new ArrayList<Stmt>();
+		int stackStart = symbolsSize();
+		StringBuilder sb = new StringBuilder();
+		int start = -1;
+		while( !parser.match(endTag) ) {
+			if( parser.endOfInput() )
+				throw exception("unclosed block");
+			Stmt block = parseBlock();
+			if( block != null ) {
+				addText(start,stmts,sb);
+				stmts.add(block);
+				continue;
+			}
+			Stmt simpleTag = parseSimpleTag();
+			if( simpleTag != null ) {
+				addText(start,stmts,sb);
+				stmts.add(simpleTag);
+				continue;
+			}
+			if( sb.length() == 0 )
+				start = parser.currentIndex();
+			sb.append( parser.currentChar() );
+			parser.anyChar();
+		}
+		addText(start,stmts,sb);
+		Stmt block = new Block( stmts.toArray(new Stmt[0]), 0, symbolsSize() );
+		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 Stmt parseBlock() throws ParseException {
+		int start = parser.begin();
+		Tag tag = parseBlockTag();
+		if( tag == null )
+			return parser.failure(null);
+		if( tag.name.equals("Set") )
+			throw exception("block:Set not allowed here");
+		frame = new Frame(frame);
+		addSymbol(ENV);
+		Stmt block = parseBody(tag);
+		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) );
+		List<Expressions> args = new ArrayList<Expressions>();
+		args.add( tag.attrs.isEmpty() ? new ConstExpr(null,null) : table(tag.attrs) );
+		args.add( env );
+		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 {
+		parser.begin();
+		if( !parser.match("{block:") )
+			return parser.failure(null);
+		String name = parseName();
+		if( name==null )
+			throw exception("invalid block name");
+		Map<String,String> attrs = parseAttrs();
+		if( !parser.match("}") )
+			return parser.failure(null);
+		Tag tag = new Tag(name,attrs);
+		return parser.success(tag);
+	}
+
+	private Stmt parseSimpleTag() throws ParseException {
+		int start = parser.begin();
+		if( !parser.match("{") )
+			return parser.failure(null);
+		String name = parseName();
+		if( name==null )
+			return parser.failure(null);
+		Map<String,String> attrs = parseAttrs();
+		if( !parser.match("}") )
+			return parser.failure(null);
+		FnCall fnCall;
+		if( name.equals("Get") ) {
+			name = attrs.remove("name");
+			if( name == null )
+				throw exception("Get missing required attribute 'name'");
+			if( !attrs.isEmpty() )
+				throw exception("Get has unrecognized attributes: "+attrs);
+			if( !validateName(name) )
+				throw exception("invalid Get name: "+name);
+//			rtn = "<% " + name + "(env) %>";
+			int index = upValueIndex(name);
+			if( index == -1 )
+				throw exception("name '"+name+"' not defined");
+			Expr fn = new GetUpVar(se(start,name),index);
+			fnCall = new FnCall( se(start), fn, env() );
+		} else {
+//			rtn = "<% env." + name + (attrs.isEmpty() ? "()" : table(attrs)) + " %>";
+			Expr fn = new IndexExpr( se(start,name), env(), new ConstExpr(null,name) );
+			Expressions args = attrs.isEmpty() ? ExpList.emptyExpList : table(attrs);
+			fnCall = new FnCall( se(start), fn, args );
+		}
+		Stmt rtn = new ExpressionsStmt(fnCall);
+		return parser.success(rtn);
+	}
+
+	private TableExpr table(Map<String,String> attrs) {
+		List<TableExpr.Field> fields = new ArrayList<TableExpr.Field>();
+		for( Map.Entry<String,String> entry : attrs.entrySet() ) {
+			ConstExpr key = new ConstExpr(null,entry.getKey());
+			ConstExpr value = new ConstExpr(null,entry.getValue());
+			fields.add( new TableExpr.Field(key,value) );
+		}
+		return new TableExpr( null, fields.toArray(new TableExpr.Field[0]), ExpList.emptyExpList );
+	}
+
+	private Map<String,String> parseAttrs() {
+		Map<String,String> attrs = new HashMap<String,String>();
+		while( parseAttr(attrs) );
+		Spaces();
+		return attrs;
+	}
+
+	private boolean parseAttr(Map<String,String> attrs) {
+		parser.begin();
+		Spaces();
+		String name = parseName();
+		if( name==null )
+			return parser.failure();
+		Spaces();
+		if( !parser.match('=') )
+			return parser.failure();
+		Spaces();
+		if( !parser.match('"') )
+			return parser.failure();
+		int start = parser.currentIndex();
+		while( parser.noneOf("\"}") );
+		String val = parser.textFrom(start);
+		if( !parser.match('"') )
+			return parser.failure();
+		attrs.put(name,val);
+		return parser.success();
+	}
+
+	private void Spaces() {
+		while( parser.anyOf(" \t\r\n") );
+	}
+
+	private String parseName() {
+		return parseName(parser);
+	}
+
+	private static boolean validateName(String name) {
+		return name.equals(parseName(new Parser(new LuanSource("NAME",name))));
+	}
+
+	private static String parseName(Parser parser) {
+		int start = parser.begin();
+		if( !NameFirstChar(parser) )
+			return parser.failure(null);
+		while( NameChar(parser) );
+		String match = parser.textFrom(start);
+		return parser.success(match);
+	}
+
+	private static boolean NameChar(Parser parser) {
+		return NameFirstChar(parser) || parser.inCharRange('0', '9');
+	}
+
+	private static boolean NameFirstChar(Parser parser) {
+		return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_');
+	}
+
+}
--- a/core/src/luan/modules/BasicLuan.java	Fri Jul 31 19:19:42 2015 -0600
+++ b/core/src/luan/modules/BasicLuan.java	Wed Aug 12 05:21:21 2015 -0600
@@ -18,6 +18,7 @@
 import luan.LuanMethod;
 import luan.LuanMeta;
 import luan.impl.LuanCompiler;
+import luan.impl.ThemeParser;
 
 
 public final class BasicLuan {
@@ -34,6 +35,14 @@
 		return LuanCompiler.compile(luan,new LuanSource(sourceName,text),env,allowExpr);
 	}
 
+	public static LuanFunction load_theme(LuanState luan,String text,String sourceName)
+		throws LuanException
+	{
+		if( sourceName==null )
+			sourceName = "THEME";
+		return ThemeParser.compile(luan,new LuanSource(sourceName,text));
+	}
+
 	public static LuanFunction load_file(LuanState luan,String fileName,Boolean addExtension) throws LuanException {
 		if( fileName == null )
 			fileName = "stdin:";
--- a/core/src/luan/modules/Luan.luan	Fri Jul 31 19:19:42 2015 -0600
+++ b/core/src/luan/modules/Luan.luan	Wed Aug 12 05:21:21 2015 -0600
@@ -12,6 +12,7 @@
 M.ipairs = BasicLuan.ipairs
 M.load = BasicLuan.load
 M.load_file = BasicLuan.load_file
+M.load_theme = BasicLuan.load_theme
 M.new_error = BasicLuan.new_error
 M.pairs = BasicLuan.pairs
 M.pcall = BasicLuan.pcall