Mercurial Hosting > luan
view core/src/luan/impl/ThemeParser.java @ 609:24b05963ba62
shell "clear" button now clears the session
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 09 Dec 2015 18:12:16 -0700 |
parents | d2e0855b3105 |
children | 56b0b9018319 |
line wrap: on
line source
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 String INDENT = "-INDENT-"; 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 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); } 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 = {['-INDENT-']=''}"); 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); } 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 Stmt parseDef() throws ParseException { int start = parser.currentIndex(); if( !parser.match("{define:") ) return null; String name = parseName(); if( name==null ) throw parser.exception("invalid block name"); if( !parser.match("}") ) throw parser.exception("unclosed define tag"); String spaces = ""; boolean indent = BlankLine(); if( indent ) { int startSpaces = parser.currentIndex(); InlineSpaces(); spaces = parser.textFrom(startSpaces); } Expr table = new GetLocalVar(null,stackIndex(MOD)); Settable fnName = new SetTableEntry(se(start),table,new ConstExpr(null,name)); frame = new Frame(frame); addSymbol(ENV); Stmt block = parseBody("define:"+name,spaces,indent); FnDef fnDef = newFnDef(start,block); frame = frame.parent; Stmt rtn = new SetStmt(fnName,fnDef); return rtn; } private Stmt parseBody(String tagName,final String spaces,boolean afterIndent) throws ParseException { List<Stmt> stmts = new ArrayList<Stmt>(); int stackStart = symbolsSize(); 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); } int end = start; while( !matchEndTag(tagName) ) { if( parser.endOfInput() ) throw exception("unclosed block"); Stmt block = parseBlock(spaces,afterIndent); if( block != null ) { addText(start,end,stmts,false); start = parser.currentIndex(); stmts.add(block); afterIndent = false; continue; } { 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; } parser.anyChar(); end = parser.currentIndex(); afterIndent = false; } addText(start,end,stmts,false); Stmt block = new Block( stmts.toArray(new Stmt[0]), stackStart, symbolsSize() ); popSymbols(1); return block; } private boolean matchEndTag(String tagName) { parser.begin(); /* if( tagName.startsWith("define:") ) EndOfLine(); */ if( EndOfLine() ) InlineSpaces(); return parser.match("{/") && parser.match(tagName) && parser.match('}') ? parser.success() : parser.failure(); } 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,boolean afterIndent) throws ParseException { int start = parser.begin(); String tagSpaces = null; if( afterIndent ) { tagSpaces = spaces; } else if( EndOfLine() ) { int startSpaces = parser.currentIndex(); InlineSpaces(); tagSpaces = parser.textFrom(startSpaces); } if( !parser.match("{block:") ) return parser.failure(null); String name = parseName(); if( name==null ) throw exception("invalid block name"); if( !parser.match("}") ) return null; if( tagSpaces != null ) { parser.begin(); InlineSpaces(); if( EndOfLine() ) { int startSpaces = parser.currentIndex(); InlineSpaces(); String line1Spaces = parser.textFrom(startSpaces); if( line1Spaces.startsWith(tagSpaces) ) { String newSpaces = spaces + line1Spaces.substring(tagSpaces.length()); if( line1Spaces.startsWith(newSpaces) ) spaces = newSpaces; } } parser.failure(); // rollback } frame = new Frame(frame); addSymbol(ENV); 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:"+name), env, new ConstExpr(null,name) ); List<Expressions> args = new ArrayList<Expressions>(); 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 Stmt parseSimpleTag(String spaces) throws ParseException { int start = parser.begin(); if( !parser.match("{") ) return parser.failure(null); String name = parseName(); if( name==null ) return parser.failure(null); 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; } }; } FnCall fnCall = new FnCall( se(start), fn, env ); Stmt rtn = new ExpressionsStmt(fnCall); return parser.success(rtn); } private void InlineSpaces() { while( parser.anyOf(" \t") ); } 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() ) return parser.failure(null); while( NameChar() ); String match = parser.textFrom(start); return parser.success(match); } private boolean NameChar() { return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.inCharRange('0', '9') || parser.anyOf("-_"); } }