Mercurial Hosting > luan
annotate core/src/luan/impl/ThemeParser.java @ 593:92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 31 Aug 2015 10:16:57 -0600 |
parents | fa281ee942c8 |
children | e91e476186c7 |
rev | line source |
---|---|
584 | 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(); | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
181 if( !parser.match("{define:") ) |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
182 return parser.failure(null); |
584 | 183 Tag tag = parseBlockTag(); |
184 if( tag == null ) | |
185 return parser.failure(null); | |
586 | 186 parser.match('\r'); parser.match('\n'); // ignore newline |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
187 if( !tag.attrs.isEmpty() ) |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
188 throw exception("this block should have no attributes"); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
189 Expr table = new GetLocalVar(null,stackIndex(MOD)); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
190 Settable fnName = new SetTableEntry(se(start),table,new ConstExpr(null,tag.name)); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
191 frame = new Frame(frame); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
192 addSymbol(ENV); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
193 Stmt block = parseBody("define",tag); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
194 FnDef fnDef = newFnDef(start,block); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
195 frame = frame.parent; |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
196 Stmt rtn = new SetStmt(fnName,fnDef); |
584 | 197 return parser.success(rtn); |
198 } | |
199 | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
200 private Stmt parseBody(String type,Tag tag) throws ParseException { |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
201 String endTag = "{/"+type+":" + tag.name + "}"; |
584 | 202 List<Stmt> stmts = new ArrayList<Stmt>(); |
203 int stackStart = symbolsSize(); | |
204 StringBuilder sb = new StringBuilder(); | |
205 int start = -1; | |
206 while( !parser.match(endTag) ) { | |
207 if( parser.endOfInput() ) | |
208 throw exception("unclosed block"); | |
209 Stmt block = parseBlock(); | |
210 if( block != null ) { | |
211 addText(start,stmts,sb); | |
212 stmts.add(block); | |
213 continue; | |
214 } | |
215 Stmt simpleTag = parseSimpleTag(); | |
216 if( simpleTag != null ) { | |
217 addText(start,stmts,sb); | |
218 stmts.add(simpleTag); | |
219 continue; | |
220 } | |
221 if( sb.length() == 0 ) | |
222 start = parser.currentIndex(); | |
223 sb.append( parser.currentChar() ); | |
224 parser.anyChar(); | |
225 } | |
226 addText(start,stmts,sb); | |
227 Stmt block = new Block( stmts.toArray(new Stmt[0]), 0, symbolsSize() ); | |
228 return block; | |
229 } | |
230 | |
231 private void addText(int start,List<Stmt> stmts,StringBuilder sb) { | |
232 if( sb.length() == 0 ) | |
233 return; | |
234 Expr io = new GetUpVar(null,upValueIndex(IO)); | |
235 Expr stdoutExp = new IndexExpr( se(start,"stdout"), io, new ConstExpr(null,"stdout") ); | |
236 Expr writeExp = new IndexExpr( se(start,"write"), stdoutExp, new ConstExpr(null,"write") ); | |
237 FnCall writeCall = new FnCall( se(start), writeExp, new ConstExpr(null,sb.toString()) ); | |
238 stmts.add( new ExpressionsStmt(writeCall) ); | |
239 sb.setLength(0); | |
240 } | |
241 | |
242 private Stmt parseBlock() throws ParseException { | |
243 int start = parser.begin(); | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
244 if( !parser.match("{block:") ) |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
245 return parser.failure(null); |
584 | 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); | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
253 Stmt block = parseBody("block",tag); |
584 | 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>(); | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
260 args.add( env ); |
584 | 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(); | |
270 if( name==null ) | |
271 throw exception("invalid block name"); | |
272 Map<String,String> attrs = parseAttrs(); | |
273 if( !parser.match("}") ) | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
274 return null; |
584 | 275 Tag tag = new Tag(name,attrs); |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
276 return tag; |
584 | 277 } |
278 | |
279 private Stmt parseSimpleTag() throws ParseException { | |
280 int start = parser.begin(); | |
281 if( !parser.match("{") ) | |
282 return parser.failure(null); | |
283 String name = parseName(); | |
284 if( name==null ) | |
285 return parser.failure(null); | |
286 Map<String,String> attrs = parseAttrs(); | |
287 if( !parser.match("}") ) | |
288 return parser.failure(null); | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
289 // rtn = "<% env." + name + (attrs.isEmpty() ? "()" : table(attrs)) + " %>"; |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
290 Expr env = env(); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
291 Expr fn = new IndexExpr( se(start,name), env, new ConstExpr(null,name) ); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
292 List<Expressions> args = new ArrayList<Expressions>(); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
293 args.add( env ); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
294 if( !attrs.isEmpty() ) |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
295 args.add( table(attrs) ); |
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
296 FnCall fnCall = new FnCall( se(start), fn, ExpList.build(args) ); |
584 | 297 Stmt rtn = new ExpressionsStmt(fnCall); |
298 return parser.success(rtn); | |
299 } | |
300 | |
301 private TableExpr table(Map<String,String> attrs) { | |
302 List<TableExpr.Field> fields = new ArrayList<TableExpr.Field>(); | |
303 for( Map.Entry<String,String> entry : attrs.entrySet() ) { | |
304 ConstExpr key = new ConstExpr(null,entry.getKey()); | |
305 ConstExpr value = new ConstExpr(null,entry.getValue()); | |
306 fields.add( new TableExpr.Field(key,value) ); | |
307 } | |
308 return new TableExpr( null, fields.toArray(new TableExpr.Field[0]), ExpList.emptyExpList ); | |
309 } | |
310 | |
311 private Map<String,String> parseAttrs() { | |
312 Map<String,String> attrs = new HashMap<String,String>(); | |
313 while( parseAttr(attrs) ); | |
314 Spaces(); | |
315 return attrs; | |
316 } | |
317 | |
318 private boolean parseAttr(Map<String,String> attrs) { | |
319 parser.begin(); | |
320 Spaces(); | |
321 String name = parseName(); | |
322 if( name==null ) | |
323 return parser.failure(); | |
324 Spaces(); | |
325 if( !parser.match('=') ) | |
326 return parser.failure(); | |
327 Spaces(); | |
328 if( !parser.match('"') ) | |
329 return parser.failure(); | |
330 int start = parser.currentIndex(); | |
331 while( parser.noneOf("\"}") ); | |
332 String val = parser.textFrom(start); | |
333 if( !parser.match('"') ) | |
334 return parser.failure(); | |
335 attrs.put(name,val); | |
336 return parser.success(); | |
337 } | |
338 | |
339 private void Spaces() { | |
340 while( parser.anyOf(" \t\r\n") ); | |
341 } | |
342 | |
343 private String parseName() { | |
344 int start = parser.begin(); | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
345 if( !NameChar() ) |
584 | 346 return parser.failure(null); |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
347 while( NameChar() ); |
584 | 348 String match = parser.textFrom(start); |
349 return parser.success(match); | |
350 } | |
351 | |
593
92c9fa5e39e6
remove theme "Get" and "Set", and add "define"
Franklin Schmidt <fschmidt@gmail.com>
parents:
587
diff
changeset
|
352 private boolean NameChar() { |
587 | 353 return parser.inCharRange('a', 'z') || parser.inCharRange('A', 'Z') |
354 || parser.inCharRange('0', '9') || parser.anyOf("-_"); | |
584 | 355 } |
356 | |
357 } |