comparison core/src/luan/impl/ThemeParser.java @ 594:e91e476186c7

theme indentation
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 03 Sep 2015 12:23:53 -0600
parents 92c9fa5e39e6
children 8370c4009cce
comparison
equal deleted inserted replaced
593:92c9fa5e39e6 594:e91e476186c7
83 } 83 }
84 84
85 private static final String IO = "-IO-"; 85 private static final String IO = "-IO-";
86 private static final String MOD = "-MOD-"; 86 private static final String MOD = "-MOD-";
87 private static final String ENV = "-ENV-"; 87 private static final String ENV = "-ENV-";
88 private static final String INDENT = "-INDENT-";
88 private static final UpValue.Getter[] NO_UP_VALUE_GETTERS = new UpValue.Getter[0]; 89 private static final UpValue.Getter[] NO_UP_VALUE_GETTERS = new UpValue.Getter[0];
89 90
90 private final LuanSource source; 91 private final LuanSource source;
91 private final Parser parser; 92 private final Parser parser;
92 private Frame frame = new Frame(); 93 private Frame frame = new Frame();
118 return new FnDef( se(start), stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) ); 119 return new FnDef( se(start), stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) );
119 } 120 }
120 121
121 private int stackIndex(String name) { 122 private int stackIndex(String name) {
122 return frame.stackIndex(name); 123 return frame.stackIndex(name);
124 }
125
126 private void popSymbols(int n) {
127 List<String> symbols = frame.symbols;
128 while( n-- > 0 ) {
129 symbols.remove(symbols.size()-1);
130 }
123 } 131 }
124 132
125 private int upValueIndex(String name) { 133 private int upValueIndex(String name) {
126 return frame.upValueIndex(name); 134 return frame.upValueIndex(name);
127 } 135 }
146 stmts.add(setStmt); 154 stmts.add(setStmt);
147 } 155 }
148 { 156 {
149 addSymbol(MOD); 157 addSymbol(MOD);
150 LuanElement se = se(0,"local M = {}"); 158 LuanElement se = se(0,"local M = {}");
151 TableExpr tableExpr = new TableExpr( se, new TableExpr.Field[0], ExpList.emptyExpList ); 159 TableExpr.Field indent = new TableExpr.Field(new ConstExpr(null,INDENT),new ConstExpr(null,""));
160 TableExpr tableExpr = new TableExpr( se, new TableExpr.Field[]{indent}, ExpList.emptyExpList );
152 SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(MOD)), tableExpr ); 161 SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(MOD)), tableExpr );
153 stmts.add(setStmt); 162 stmts.add(setStmt);
154 } 163 }
155 while( !parser.endOfInput() ) { 164 while( !parser.endOfInput() ) {
156 Stmt def = parseDef(); 165 Stmt def = parseDef();
164 Stmt block = new Block( stmts.toArray(new Stmt[0]), stackStart, symbolsSize() ); 173 Stmt block = new Block( stmts.toArray(new Stmt[0]), stackStart, symbolsSize() );
165 FnDef fnDef = newFnDef(0,block); 174 FnDef fnDef = newFnDef(0,block);
166 return fnDef; 175 return fnDef;
167 } 176 }
168 177
169 private static class Tag {
170 final String name;
171 final Map<String,String> attrs;
172
173 Tag(String name,Map<String,String> attrs) {
174 this.name = name;
175 this.attrs = attrs;
176 }
177 }
178
179 private Stmt parseDef() throws ParseException { 178 private Stmt parseDef() throws ParseException {
180 int start = parser.begin(); 179 int start = parser.begin();
181 if( !parser.match("{define:") ) 180 if( !parser.match("{define:") )
182 return parser.failure(null); 181 return parser.failure(null);
183 Tag tag = parseBlockTag(); 182 String name = parseName();
184 if( tag == null ) 183 if( name==null )
185 return parser.failure(null); 184 throw exception("invalid block name");
186 parser.match('\r'); parser.match('\n'); // ignore newline 185 Map<String,String> attrs = parseAttrs();
187 if( !tag.attrs.isEmpty() ) 186 String spaces = "";
187 if( BlankLine() ) {
188 while( BlankLine() );
189 int startSpaces = parser.currentIndex();
190 InlineSpaces();
191 spaces = parser.textFrom(startSpaces);
192 }
193 if( !parser.match("}") )
194 return null;
195 if( !attrs.isEmpty() )
188 throw exception("this block should have no attributes"); 196 throw exception("this block should have no attributes");
189 Expr table = new GetLocalVar(null,stackIndex(MOD)); 197 Expr table = new GetLocalVar(null,stackIndex(MOD));
190 Settable fnName = new SetTableEntry(se(start),table,new ConstExpr(null,tag.name)); 198 Settable fnName = new SetTableEntry(se(start),table,new ConstExpr(null,name));
191 frame = new Frame(frame); 199 frame = new Frame(frame);
192 addSymbol(ENV); 200 addSymbol(ENV);
193 Stmt block = parseBody("define",tag); 201 Stmt block = parseBody("define:"+name,spaces,EndOfLine());
194 FnDef fnDef = newFnDef(start,block); 202 FnDef fnDef = newFnDef(start,block);
195 frame = frame.parent; 203 frame = frame.parent;
196 Stmt rtn = new SetStmt(fnName,fnDef); 204 Stmt rtn = new SetStmt(fnName,fnDef);
197 return parser.success(rtn); 205 return parser.success(rtn);
198 } 206 }
199 207
200 private Stmt parseBody(String type,Tag tag) throws ParseException { 208 private Stmt parseBody(String tagName,String spaces,boolean initIndent) throws ParseException {
201 String endTag = "{/"+type+":" + tag.name + "}";
202 List<Stmt> stmts = new ArrayList<Stmt>(); 209 List<Stmt> stmts = new ArrayList<Stmt>();
203 int stackStart = symbolsSize(); 210 int stackStart = symbolsSize();
204 StringBuilder sb = new StringBuilder(); 211 int start = parser.currentIndex();
205 int start = -1; 212 {
206 while( !parser.match(endTag) ) { 213 addSymbol(INDENT);
214 final Expr env = env();
215 Expr exp = new ExprImpl(se(start,"indent")) {
216 @Override public Object eval(LuanStateImpl luan) throws LuanException {
217 LuanTable tbl = (LuanTable)env.eval(luan);
218 String indent = (String)tbl.get(luan,INDENT);
219 if( indent==null ) throw new NullPointerException();
220 return indent;
221 }
222 };
223 // Expr exp = new IndexExpr( se(start,"indent"), env(), new ConstExpr(null,INDENT) );
224 SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(INDENT)), exp );
225 stmts.add(setStmt);
226 }
227 boolean afterIndent = false;
228 if( initIndent && parser.match(spaces) ) {
229 addText(start,start,stmts,true);
230 start = parser.currentIndex();
231 afterIndent = true;
232 }
233 int end = start;
234 while( !matchEndTag(tagName) ) {
207 if( parser.endOfInput() ) 235 if( parser.endOfInput() )
208 throw exception("unclosed block"); 236 throw exception("unclosed block");
209 Stmt block = parseBlock(); 237 Stmt block = parseBlock(spaces);
210 if( block != null ) { 238 if( block != null ) {
211 addText(start,stmts,sb); 239 addText(start,end,stmts,false);
240 start = parser.currentIndex();
212 stmts.add(block); 241 stmts.add(block);
242 afterIndent = false;
213 continue; 243 continue;
214 } 244 }
215 Stmt simpleTag = parseSimpleTag(); 245 {
216 if( simpleTag != null ) { 246 String extraSpaces = null;
217 addText(start,stmts,sb); 247 if( afterIndent ) {
218 stmts.add(simpleTag); 248 int startSpaces = parser.currentIndex();
249 InlineSpaces();
250 extraSpaces = parser.textFrom(startSpaces);
251 end = parser.currentIndex();
252 }
253 Stmt simpleTag = parseSimpleTag(extraSpaces);
254 if( simpleTag != null ) {
255 addText(start,end,stmts,false);
256 start = parser.currentIndex();
257 stmts.add(simpleTag);
258 afterIndent = false;
259 continue;
260 }
261 if( extraSpaces!=null && extraSpaces.length() > 0 )
262 continue;
263 }
264 if( EndOfLine() ) {
265 end = parser.currentIndex();
266 afterIndent = false;
267 if( parser.match(spaces) ) {
268 addText(start,end,stmts,true);
269 start = parser.currentIndex();
270 afterIndent = true;
271 }
219 continue; 272 continue;
220 } 273 }
221 if( sb.length() == 0 )
222 start = parser.currentIndex();
223 sb.append( parser.currentChar() );
224 parser.anyChar(); 274 parser.anyChar();
225 } 275 end = parser.currentIndex();
226 addText(start,stmts,sb); 276 afterIndent = false;
227 Stmt block = new Block( stmts.toArray(new Stmt[0]), 0, symbolsSize() ); 277 }
278 addText(start,end,stmts,false);
279 Stmt block = new Block( stmts.toArray(new Stmt[0]), stackStart, symbolsSize() );
280 popSymbols(1);
228 return block; 281 return block;
229 } 282 }
230 283
231 private void addText(int start,List<Stmt> stmts,StringBuilder sb) { 284 private boolean matchEndTag(String tagName) {
232 if( sb.length() == 0 ) 285 parser.begin();
233 return; 286 if( !parser.match('{') )
234 Expr io = new GetUpVar(null,upValueIndex(IO)); 287 return parser.failure();
235 Expr stdoutExp = new IndexExpr( se(start,"stdout"), io, new ConstExpr(null,"stdout") ); 288 Spaces();
236 Expr writeExp = new IndexExpr( se(start,"write"), stdoutExp, new ConstExpr(null,"write") ); 289 if( !(parser.match('/') && parser.match(tagName)) )
237 FnCall writeCall = new FnCall( se(start), writeExp, new ConstExpr(null,sb.toString()) ); 290 return parser.failure();
238 stmts.add( new ExpressionsStmt(writeCall) ); 291 Spaces();
239 sb.setLength(0); 292 if( !parser.match('}') )
240 } 293 return parser.failure();
241 294 return parser.success();
242 private Stmt parseBlock() throws ParseException { 295 }
296
297 private void addText(int start,int end,List<Stmt> stmts,boolean indent) {
298 List<Expressions> args = new ArrayList<Expressions>();
299 if( start < end ) {
300 String text = parser.text.substring(start,end);
301 args.add( new ConstExpr(null,text) );
302 }
303 if( indent ) {
304 args.add( new GetLocalVar(null,stackIndex(INDENT)) );
305 }
306 if( !args.isEmpty() ) {
307 Expr io = new GetUpVar(null,upValueIndex(IO));
308 Expr stdoutExp = new IndexExpr( se(start,"stdout"), io, new ConstExpr(null,"stdout") );
309 Expr writeExp = new IndexExpr( se(start,"write"), stdoutExp, new ConstExpr(null,"write") );
310 FnCall writeCall = new FnCall( se(start), writeExp, ExpList.build(args) );
311 stmts.add( new ExpressionsStmt(writeCall) );
312 }
313 }
314
315 private Stmt parseBlock(String spaces) throws ParseException {
243 int start = parser.begin(); 316 int start = parser.begin();
244 if( !parser.match("{block:") ) 317 if( !parser.match("{block:") )
245 return parser.failure(null); 318 return parser.failure(null);
246 Tag tag = parseBlockTag();
247 if( tag == null )
248 return parser.failure(null);
249 if( tag.name.equals("Set") )
250 throw exception("block:Set not allowed here");
251 frame = new Frame(frame);
252 addSymbol(ENV);
253 Stmt block = parseBody("block",tag);
254 FnDef fnDef = newFnDef(start,block);
255 frame = frame.parent;
256 // String rtn = "<% env." + tag.name + "(" + (tag.attrs.isEmpty() ? "nil" : table(tag.attrs)) + ",env,function(env) %>" + block + "<% end) %>";
257 Expr env = env();
258 Expr fn = new IndexExpr( se(start,"block:"+tag.name), env, new ConstExpr(null,tag.name) );
259 List<Expressions> args = new ArrayList<Expressions>();
260 args.add( env );
261 args.add( tag.attrs.isEmpty() ? new ConstExpr(null,null) : table(tag.attrs) );
262 args.add( fnDef );
263 FnCall fnCall = new FnCall( se(start), fn, ExpList.build(args) );
264 Stmt rtn = new ExpressionsStmt(fnCall);
265 return parser.success(rtn);
266 }
267
268 private Tag parseBlockTag() throws ParseException {
269 String name = parseName(); 319 String name = parseName();
270 if( name==null ) 320 if( name==null )
271 throw exception("invalid block name"); 321 throw exception("invalid block name");
272 Map<String,String> attrs = parseAttrs(); 322 Map<String,String> attrs = parseAttrs();
273 if( !parser.match("}") ) 323 if( !parser.match("}") )
274 return null; 324 return null;
275 Tag tag = new Tag(name,attrs); 325 frame = new Frame(frame);
276 return tag; 326 addSymbol(ENV);
277 } 327 Stmt block = parseBody("block:"+name,spaces,false);
278 328 FnDef fnDef = newFnDef(start,block);
279 private Stmt parseSimpleTag() throws ParseException { 329 frame = frame.parent;
330 // String rtn = "<% env." + tag.name + "(" + (tag.attrs.isEmpty() ? "nil" : table(tag.attrs)) + ",env,function(env) %>" + block + "<% end) %>";
331 Expr env = env();
332 Expr fn = new IndexExpr( se(start,"block:"+name), env, new ConstExpr(null,name) );
333 List<Expressions> args = new ArrayList<Expressions>();
334 args.add( env );
335 args.add( attrs.isEmpty() ? new ConstExpr(null,null) : table(attrs) );
336 args.add( fnDef );
337 FnCall fnCall = new FnCall( se(start), fn, ExpList.build(args) );
338 Stmt rtn = new ExpressionsStmt(fnCall);
339 return parser.success(rtn);
340 }
341
342 private Stmt parseSimpleTag(String spaces) throws ParseException {
280 int start = parser.begin(); 343 int start = parser.begin();
281 if( !parser.match("{") ) 344 if( !parser.match("{") )
282 return parser.failure(null); 345 return parser.failure(null);
283 String name = parseName(); 346 String name = parseName();
284 if( name==null ) 347 if( name==null )
285 return parser.failure(null); 348 return parser.failure(null);
286 Map<String,String> attrs = parseAttrs(); 349 Map<String,String> attrs = parseAttrs();
350 Spaces();
287 if( !parser.match("}") ) 351 if( !parser.match("}") )
288 return parser.failure(null); 352 return parser.failure(null);
289 // rtn = "<% env." + name + (attrs.isEmpty() ? "()" : table(attrs)) + " %>"; 353 // rtn = "<% env." + name + (attrs.isEmpty() ? "()" : table(attrs)) + " %>";
290 Expr env = env(); 354 Expr env = env();
291 Expr fn = new IndexExpr( se(start,name), env, new ConstExpr(null,name) ); 355 Expr fn = new IndexExpr( se(start,name), env, new ConstExpr(null,name) );
356 if( spaces!=null && spaces.length() > 0 ) {
357 final Expr oldEnv = env;
358 final Expr oldIndentExpr = new GetLocalVar(null,stackIndex(INDENT));
359 final String addSpaces = spaces;
360 env = new ExprImpl(se(start,"indent_env")) {
361 @Override public Object eval(LuanStateImpl luan) throws LuanException {
362 LuanTable mt = new LuanTable();
363 mt.rawPut("__index",oldEnv.eval(luan));
364 LuanTable tbl = new LuanTable();
365 tbl.setMetatable(mt);
366 String oldIndent = (String)oldIndentExpr.eval(luan);
367 String newIndent = oldIndent + addSpaces;
368 tbl.rawPut(INDENT,newIndent);
369 return tbl;
370 }
371 };
372 }
292 List<Expressions> args = new ArrayList<Expressions>(); 373 List<Expressions> args = new ArrayList<Expressions>();
293 args.add( env ); 374 args.add( env );
294 if( !attrs.isEmpty() ) 375 if( !attrs.isEmpty() )
295 args.add( table(attrs) ); 376 args.add( table(attrs) );
296 FnCall fnCall = new FnCall( se(start), fn, ExpList.build(args) ); 377 FnCall fnCall = new FnCall( se(start), fn, ExpList.build(args) );
309 } 390 }
310 391
311 private Map<String,String> parseAttrs() { 392 private Map<String,String> parseAttrs() {
312 Map<String,String> attrs = new HashMap<String,String>(); 393 Map<String,String> attrs = new HashMap<String,String>();
313 while( parseAttr(attrs) ); 394 while( parseAttr(attrs) );
314 Spaces();
315 return attrs; 395 return attrs;
316 } 396 }
317 397
318 private boolean parseAttr(Map<String,String> attrs) { 398 private boolean parseAttr(Map<String,String> attrs) {
319 parser.begin(); 399 parser.begin();
338 418
339 private void Spaces() { 419 private void Spaces() {
340 while( parser.anyOf(" \t\r\n") ); 420 while( parser.anyOf(" \t\r\n") );
341 } 421 }
342 422
423 private void InlineSpaces() {
424 while( parser.anyOf(" \t") );
425 }
426
427 private void BlankLines() {
428 while( BlankLine() );
429 }
430
431 private boolean BlankLine() {
432 parser.begin();
433 while( parser.anyOf(" \t") );
434 return EndOfLine() ? parser.success() : parser.failure();
435 }
436
437 private boolean EndOfLine() {
438 return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' );
439 }
440
343 private String parseName() { 441 private String parseName() {
344 int start = parser.begin(); 442 int start = parser.begin();
345 if( !NameChar() ) 443 if( !NameChar() )
346 return parser.failure(null); 444 return parser.failure(null);
347 while( NameChar() ); 445 while( NameChar() );