Mercurial Hosting > luan
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() ); |