Mercurial Hosting > luan
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