Mercurial Hosting > luan
comparison core/src/luan/impl/ThemeParser.java @ 584:0742ac78fa69
add Luan.load_theme
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 12 Aug 2015 05:21:21 -0600 |
parents | |
children | a140be489a72 |
comparison
equal
deleted
inserted
replaced
583:1368ca798ccc | 584:0742ac78fa69 |
---|---|
1 package luan.impl; | |
2 | |
3 import java.util.Map; | |
4 import java.util.HashMap; | |
5 import java.util.List; | |
6 import java.util.ArrayList; | |
7 import luan.LuanSource; | |
8 import luan.LuanTable; | |
9 import luan.LuanElement; | |
10 import luan.LuanState; | |
11 import luan.LuanFunction; | |
12 import luan.LuanException; | |
13 import luan.modules.PackageLuan; | |
14 | |
15 | |
16 public final class ThemeParser { | |
17 | |
18 public static LuanFunction compile(LuanState luan,LuanSource source) throws LuanException { | |
19 try { | |
20 FnDef fnDef = new ThemeParser(source).parse(); | |
21 final LuanStateImpl luanImpl = (LuanStateImpl)luan; | |
22 return new Closure(luanImpl,fnDef); | |
23 } catch(ParseException e) { | |
24 //e.printStackTrace(); | |
25 throw new LuanException(luan, e.getFancyMessage() ); | |
26 } | |
27 } | |
28 | |
29 private static final class Frame { | |
30 final Frame parent; | |
31 final List<String> symbols = new ArrayList<String>(); | |
32 int stackSize = 0; | |
33 final boolean isVarArg; | |
34 final List<String> upValueSymbols = new ArrayList<String>(); | |
35 final List<UpValue.Getter> upValueGetters = new ArrayList<UpValue.Getter>(); | |
36 | |
37 Frame() { | |
38 this.parent = null; | |
39 isVarArg = true; | |
40 } | |
41 | |
42 Frame(Frame parent) { | |
43 this.parent = parent; | |
44 isVarArg = false; | |
45 if( upValueIndex(MOD) != 0 ) | |
46 throw new RuntimeException(); | |
47 } | |
48 | |
49 int stackIndex(String name) { | |
50 int i = symbols.size(); | |
51 while( --i >= 0 ) { | |
52 if( symbols.get(i).equals(name) ) | |
53 return i; | |
54 } | |
55 return -1; | |
56 } | |
57 | |
58 int upValueIndex(String name) { | |
59 int i = upValueSymbols.size(); | |
60 while( --i >= 0 ) { | |
61 if( upValueSymbols.get(i).equals(name) ) | |
62 return i; | |
63 } | |
64 if( parent==null ) | |
65 return -1; | |
66 i = parent.stackIndex(name); | |
67 if( i != -1 ) { | |
68 upValueGetters.add(new UpValue.StackGetter(i)); | |
69 } else { | |
70 i = parent.upValueIndex(name); | |
71 if( i == -1 ) | |
72 return -1; | |
73 upValueGetters.add(new UpValue.NestedGetter(i)); | |
74 } | |
75 upValueSymbols.add(name); | |
76 return upValueSymbols.size() - 1; | |
77 } | |
78 | |
79 void addUpValueGetter(String name,UpValue.Getter upValueGetter) { | |
80 upValueSymbols.add(name); | |
81 upValueGetters.add(upValueGetter); | |
82 } | |
83 } | |
84 | |
85 private static final String IO = "-IO-"; | |
86 private static final String MOD = "-MOD-"; | |
87 private static final String ENV = "-ENV-"; | |
88 private static final UpValue.Getter[] NO_UP_VALUE_GETTERS = new UpValue.Getter[0]; | |
89 | |
90 private final LuanSource source; | |
91 private final Parser parser; | |
92 private Frame frame = new Frame(); | |
93 | |
94 private ThemeParser(LuanSource source) { | |
95 this.source = source; | |
96 this.parser = new Parser(this.source); | |
97 } | |
98 | |
99 private LuanElement se(int start) { | |
100 return se(start,null); | |
101 } | |
102 | |
103 private LuanElement se(int start,String text) { | |
104 return new LuanElement(source,start,parser.currentIndex(),text); | |
105 } | |
106 | |
107 private int symbolsSize() { | |
108 return frame.symbols.size(); | |
109 } | |
110 | |
111 private void addSymbol(String name) { | |
112 frame.symbols.add(name); | |
113 if( frame.stackSize < symbolsSize() ) | |
114 frame.stackSize = symbolsSize(); | |
115 } | |
116 | |
117 private FnDef newFnDef(int start,Stmt stmt) { | |
118 return new FnDef( se(start), stmt, frame.stackSize, symbolsSize(), frame.isVarArg, frame.upValueGetters.toArray(NO_UP_VALUE_GETTERS) ); | |
119 } | |
120 | |
121 private int stackIndex(String name) { | |
122 return frame.stackIndex(name); | |
123 } | |
124 | |
125 private int upValueIndex(String name) { | |
126 return frame.upValueIndex(name); | |
127 } | |
128 | |
129 private ParseException exception(String msg) { | |
130 parser.failure(); | |
131 return parser.exception(msg); | |
132 } | |
133 | |
134 private Expr env() { | |
135 return new GetLocalVar(null,stackIndex(ENV)); | |
136 } | |
137 | |
138 private FnDef parse() throws ParseException { | |
139 List<Stmt> stmts = new ArrayList<Stmt>(); | |
140 int stackStart = symbolsSize(); | |
141 { | |
142 addSymbol(IO); | |
143 LuanElement se = se(0,"require 'luan:Io'"); | |
144 FnCall requireCall = new FnCall( se, new ConstExpr(se,PackageLuan.requireFn), new ConstExpr(se,"luan:Io") ); | |
145 SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(IO)), new ExpressionsExpr(requireCall) ); | |
146 stmts.add(setStmt); | |
147 } | |
148 { | |
149 addSymbol(MOD); | |
150 LuanElement se = se(0,"local M = {}"); | |
151 TableExpr tableExpr = new TableExpr( se, new TableExpr.Field[0], ExpList.emptyExpList ); | |
152 SetStmt setStmt = new SetStmt( new SetLocalVar(stackIndex(MOD)), tableExpr ); | |
153 stmts.add(setStmt); | |
154 } | |
155 while( !parser.endOfInput() ) { | |
156 Stmt def = parseDef(); | |
157 if( def != null ) { | |
158 stmts.add(def); | |
159 } else { | |
160 parser.anyChar(); | |
161 } | |
162 } | |
163 stmts.add( new ReturnStmt(null,new GetLocalVar(null,stackIndex(MOD))) ); | |
164 Stmt block = new Block( stmts.toArray(new Stmt[0]), stackStart, symbolsSize() ); | |
165 FnDef fnDef = newFnDef(0,block); | |
166 return fnDef; | |
167 } | |
168 | |
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 { | |
180 int start = parser.begin(); | |
181 Tag tag = parseBlockTag(); | |
182 if( tag == null ) | |
183 return parser.failure(null); | |
184 Stmt rtn = null; | |
185 if( tag.name.equals("Set") ) { | |
186 String name = tag.attrs.remove("name"); | |
187 if( name == null ) | |
188 throw exception("block:Set missing required attribute 'name'"); | |
189 if( !tag.attrs.isEmpty() ) | |
190 throw exception("block:Set has unrecognized attributes: "+tag.attrs); | |
191 if( !validateName(name) ) | |
192 throw exception("invalid Set name: "+name); | |
193 addSymbol( name ); | |
194 frame = new Frame(frame); | |
195 addSymbol(ENV); | |
196 Stmt block = parseBody(tag); | |
197 FnDef fnDef = newFnDef(start,block); | |
198 frame = frame.parent; | |
199 rtn = new SetStmt( new SetLocalVar(symbolsSize()-1), fnDef ); | |
200 } else { | |
201 if( !tag.attrs.isEmpty() ) | |
202 throw exception("this block should have no attributes"); | |
203 Expr table = new GetLocalVar(null,stackIndex(MOD)); | |
204 Settable fnName = new SetTableEntry(se(start),table,new ConstExpr(null,tag.name)); | |
205 frame = new Frame(frame); | |
206 addSymbol(ENV); | |
207 Stmt block = parseBody(tag); | |
208 FnDef fnDef = newFnDef(start,block); | |
209 frame = frame.parent; | |
210 rtn = new SetStmt(fnName,fnDef); | |
211 } | |
212 return parser.success(rtn); | |
213 } | |
214 | |
215 private Stmt parseBody(Tag tag) throws ParseException { | |
216 String endTag = "{/block:" + tag.name + "}"; | |
217 List<Stmt> stmts = new ArrayList<Stmt>(); | |
218 int stackStart = symbolsSize(); | |
219 StringBuilder sb = new StringBuilder(); | |
220 int start = -1; | |
221 while( !parser.match(endTag) ) { | |
222 if( parser.endOfInput() ) | |
223 throw exception("unclosed block"); | |
224 Stmt block = parseBlock(); | |
225 if( block != null ) { | |
226 addText(start,stmts,sb); | |
227 stmts.add(block); | |
228 continue; | |
229 } | |
230 Stmt simpleTag = parseSimpleTag(); | |
231 if( simpleTag != null ) { | |
232 addText(start,stmts,sb); | |
233 stmts.add(simpleTag); | |
234 continue; | |
235 } | |
236 if( sb.length() == 0 ) | |
237 start = parser.currentIndex(); | |
238 sb.append( parser.currentChar() ); | |
239 parser.anyChar(); | |
240 } | |
241 addText(start,stmts,sb); | |
242 Stmt block = new Block( stmts.toArray(new Stmt[0]), 0, symbolsSize() ); | |
243 return block; | |
244 } | |
245 | |
246 private void addText(int start,List<Stmt> stmts,StringBuilder sb) { | |
247 if( sb.length() == 0 ) | |
248 return; | |
249 Expr io = new GetUpVar(null,upValueIndex(IO)); | |
250 Expr stdoutExp = new IndexExpr( se(start,"stdout"), io, new ConstExpr(null,"stdout") ); | |
251 Expr writeExp = new IndexExpr( se(start,"write"), stdoutExp, new ConstExpr(null,"write") ); | |
252 FnCall writeCall = new FnCall( se(start), writeExp, new ConstExpr(null,sb.toString()) ); | |
253 stmts.add( new ExpressionsStmt(writeCall) ); | |
254 sb.setLength(0); | |
255 } | |
256 | |
257 private Stmt parseBlock() throws ParseException { | |
258 int start = parser.begin(); | |
259 Tag tag = parseBlockTag(); | |
260 if( tag == null ) | |
261 return parser.failure(null); | |
262 if( tag.name.equals("Set") ) | |
263 throw exception("block:Set not allowed here"); | |
264 frame = new Frame(frame); | |
265 addSymbol(ENV); | |
266 Stmt block = parseBody(tag); | |
267 FnDef fnDef = newFnDef(start,block); | |
268 frame = frame.parent; | |
269 // String rtn = "<% env." + tag.name + "(" + (tag.attrs.isEmpty() ? "nil" : table(tag.attrs)) + ",env,function(env) %>" + block + "<% end) %>"; | |
270 Expr env = env(); | |
271 Expr fn = new IndexExpr( se(start,"block:"+tag.name), env, new ConstExpr(null,tag.name) ); | |
272 List<Expressions> args = new ArrayList<Expressions>(); | |
273 args.add( tag.attrs.isEmpty() ? new ConstExpr(null,null) : table(tag.attrs) ); | |
274 args.add( env ); | |
275 args.add( fnDef ); | |
276 FnCall fnCall = new FnCall( se(start), fn, ExpList.build(args) ); | |
277 Stmt rtn = new ExpressionsStmt(fnCall); | |
278 return parser.success(rtn); | |
279 } | |
280 | |
281 private Tag parseBlockTag() throws ParseException { | |
282 parser.begin(); | |
283 if( !parser.match("{block:") ) | |
284 return parser.failure(null); | |
285 String name = parseName(); | |
286 if( name==null ) | |
287 throw exception("invalid block name"); | |
288 Map<String,String> attrs = parseAttrs(); | |
289 if( !parser.match("}") ) | |
290 return parser.failure(null); | |
291 Tag tag = new Tag(name,attrs); | |
292 return parser.success(tag); | |
293 } | |
294 | |
295 private Stmt parseSimpleTag() throws ParseException { | |
296 int start = parser.begin(); | |
297 if( !parser.match("{") ) | |
298 return parser.failure(null); | |
299 String name = parseName(); | |
300 if( name==null ) | |
301 return parser.failure(null); | |
302 Map<String,String> attrs = parseAttrs(); | |
303 if( !parser.match("}") ) | |
304 return parser.failure(null); | |
305 FnCall fnCall; | |
306 if( name.equals("Get") ) { | |
307 name = attrs.remove("name"); | |
308 if( name == null ) | |
309 throw exception("Get missing required attribute 'name'"); | |
310 if( !attrs.isEmpty() ) | |
311 throw exception("Get has unrecognized attributes: "+attrs); | |
312 if( !validateName(name) ) | |
313 throw exception("invalid Get name: "+name); | |
314 // rtn = "<% " + name + "(env) %>"; | |
315 int index = upValueIndex(name); | |
316 if( index == -1 ) | |
317 throw exception("name '"+name+"' not defined"); | |
318 Expr fn = new GetUpVar(se(start,name),index); | |
319 fnCall = new FnCall( se(start), fn, env() ); | |
320 } else { | |
321 // rtn = "<% env." + name + (attrs.isEmpty() ? "()" : table(attrs)) + " %>"; | |
322 Expr fn = new IndexExpr( se(start,name), env(), new ConstExpr(null,name) ); | |
323 Expressions args = attrs.isEmpty() ? ExpList.emptyExpList : table(attrs); | |
324 fnCall = new FnCall( se(start), fn, args ); | |
325 } | |
326 Stmt rtn = new ExpressionsStmt(fnCall); | |
327 return parser.success(rtn); | |
328 } | |
329 | |
330 private TableExpr table(Map<String,String> attrs) { | |
331 List<TableExpr.Field> fields = new ArrayList<TableExpr.Field>(); | |
332 for( Map.Entry<String,String> entry : attrs.entrySet() ) { | |
333 ConstExpr key = new ConstExpr(null,entry.getKey()); | |
334 ConstExpr value = new ConstExpr(null,entry.getValue()); | |
335 fields.add( new TableExpr.Field(key,value) ); | |
336 } | |
337 return new TableExpr( null, fields.toArray(new TableExpr.Field[0]), ExpList.emptyExpList ); | |
338 } | |
339 | |
340 private Map<String,String> parseAttrs() { | |
341 Map<String,String> attrs = new HashMap<String,String>(); | |
342 while( parseAttr(attrs) ); | |
343 Spaces(); | |
344 return attrs; | |
345 } | |
346 | |
347 private boolean parseAttr(Map<String,String> attrs) { | |
348 parser.begin(); | |
349 Spaces(); | |
350 String name = parseName(); | |
351 if( name==null ) | |
352 return parser.failure(); | |
353 Spaces(); | |
354 if( !parser.match('=') ) | |
355 return parser.failure(); | |
356 Spaces(); | |
357 if( !parser.match('"') ) | |
358 return parser.failure(); | |
359 int start = parser.currentIndex(); | |
360 while( parser.noneOf("\"}") ); | |
361 String val = parser.textFrom(start); | |
362 if( !parser.match('"') ) | |
363 return parser.failure(); | |
364 attrs.put(name,val); | |
365 return parser.success(); | |
366 } | |
367 | |
368 private void Spaces() { | |
369 while( parser.anyOf(" \t\r\n") ); | |
370 } | |
371 | |
372 private String parseName() { | |
373 return parseName(parser); | |
374 } | |
375 | |
376 private static boolean validateName(String name) { | |
377 return name.equals(parseName(new Parser(new LuanSource("NAME",name)))); | |
378 } | |
379 | |
380 private static String parseName(Parser parser) { | |
381 int start = parser.begin(); | |
382 if( !NameFirstChar(parser) ) | |
383 return parser.failure(null); | |
384 while( NameChar(parser) ); | |
385 String match = parser.textFrom(start); | |
386 return parser.success(match); | |
387 } | |
388 | |
389 private static boolean NameChar(Parser parser) { | |
390 return NameFirstChar(parser) || parser.inCharRange('0', '9'); | |
391 } | |
392 | |
393 private static boolean NameFirstChar(Parser parser) { | |
394 return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') || parser.match('_'); | |
395 } | |
396 | |
397 } |