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 }