Mercurial Hosting > luan
view core/src/luan/impl/ThemeParser.java @ 587:fa281ee942c8
minor
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 24 Aug 2015 04:43:55 -0600 |
parents | a140be489a72 |
children | 92c9fa5e39e6 |
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 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); parser.match('\r'); parser.match('\n'); // ignore newline 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( !NameChar(parser) ) return parser.failure(null); while( NameChar(parser) ); String match = parser.textFrom(start); return parser.success(match); } private static boolean NameChar(Parser parser) { return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.inCharRange('0', '9') || parser.anyOf("-_"); } }