source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/parser/MapCSSParser.jj@ 3893

Last change on this file since 3893 was 3893, checked in by bastiK, 14 years ago

mapcss: some rework of Error Handling, (Multi)Cascade and icon loading

File size: 13.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2options {
3 STATIC = false;
4}
5
6PARSER_BEGIN(MapCSSParser)
7package org.openstreetmap.josm.gui.mappaint.mapcss.parser;
8
9import java.awt.Color;
10import java.util.ArrayList;
11import java.util.List;
12
13import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
14import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
15import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
16import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
17import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
18import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
19import org.openstreetmap.josm.gui.mappaint.mapcss.Expression.FunctionExpression;
20import org.openstreetmap.josm.gui.mappaint.mapcss.Expression.LiteralExpression;
21import org.openstreetmap.josm.tools.Pair;
22
23public class MapCSSParser {
24 MapCSSStyleSource sheet;
25}
26PARSER_END(MapCSSParser)
27
28/*************
29 * Token definitions
30 */
31
32<DEFAULT>
33TOKEN:
34{
35 < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
36| < UINT: ["1"-"9"] ( ["0"-"9"] )* >
37| < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? >
38| < STRING: "\"" ( [" ","!","#"-"[","]"-"~","\u0080"-"\uFFFF"] | "\\\"" | "\\\\" )* "\"" >
39| < #REGEX_CHAR_WITHOUT_STAR: [" "-")","+"-".","0"-"[","]"-"~","\u0080"-"\uFFFF"] | "\\/" | "\\\\" >
40| < REGEX: "/" <REGEX_CHAR_WITHOUT_STAR> ( <REGEX_CHAR_WITHOUT_STAR> | "*" )* "/" >
41| < #H: ["0"-"9","a"-"f","A"-"F"] >
42| < HEXCOLOR: "#" ( <H><H><H><H><H><H> | <H><H><H> ) >
43| < S: ( " " | "\t" | "\n" | "\r" | "\f" )+ >
44| < STAR: "*" >
45| < SLASH: "/" >
46| < LBRACE: "{" >
47| < RBRACE: "}" >
48| < LSQUARE: "[" >
49| < RSQUARE: "]" >
50| < LPAR: "(" >
51| < RPAR: ")" >
52| < GREATER_EQUAL: ">=" >
53| < LESS_EQUAL: "<=" >
54| < GREATER: ">" >
55| < LESS: "<" >
56| < EQUAL: "=" >
57| < EXCLAMATION: "!" >
58| < TILDE: "~" >
59| < COLON: ":" >
60| < DCOLON: "::" >
61| < SEMICOLON: ";" >
62| < COMMA: "," >
63| < PIPE: "|" >
64| < PIPE_Z: "|z" >
65| < PLUS: "+" >
66| < MINUS: "-" >
67| < AMPERSAND: "&" >
68| < QUESTION: "?" >
69| < DOLLAR: "$" >
70| < CARET: "^" >
71| < COMMENT_START: "/*" > : COMMENT
72| < UNEXPECTED_CHAR : ~[] > // avoid TokenMgrErrors because they are hard to recover from
73}
74
75<COMMENT>
76TOKEN:
77{
78 < COMMENT_END: "*/" > : DEFAULT
79}
80
81<COMMENT>
82SKIP:
83{
84 < ~[] >
85}
86
87/*************
88 * Parser definitions
89 *
90 * rule
91 * _______________________|______________________________
92 * | |
93 * selector declaration
94 * _________|___________________ _________|____________
95 * | | | |
96 *
97 * way|z11-12[highway=residential] { color: red; width: 3 }
98 *
99 * |_____||___________________| |_________|
100 * | | |
101 * zoom condition instruction
102 *
103 * more general:
104 *
105 * way|z13-[a=b][c=d]::subpart, way|z-3[u=v]:closed::subpart2 { p1 : val; p2 : val; }
106 *
107 * 'val' can be a literal, or an expression like "prop(width, default) + 0.8".
108 *
109 */
110
111int uint() :
112{
113 Token i;
114}
115{
116 i=<UINT> { return Integer.parseInt(i.image); }
117}
118
119float ufloat() :
120{
121 Token f;
122}
123{
124 ( f=<UFLOAT> | f=<UINT> )
125 { return Float.parseFloat(f.image); }
126}
127
128float float_() :
129{
130 float f;
131}
132{
133 <MINUS> f=ufloat() { return -f; } | f=ufloat() { return f; }
134}
135
136String string() :
137{
138 Token t;
139}
140{
141 t=<STRING>
142 { return t.image.substring(1, t.image.length() - 1).replace("\\\"", "\"").replace("\\\\", "\\"); }
143}
144
145String string_or_ident() :
146{
147 Token t;
148 String s;
149}
150{
151 t=<IDENT> { return t.image; } | s=string() { return s; }
152}
153
154String regex() :
155{
156 Token t;
157}
158{
159 t=<REGEX>
160 { return t.image.substring(1, t.image.length() - 1); }
161}
162
163/**
164 * white-space
165 */
166void s() :
167{
168}
169{
170 ( <S> )?
171}
172
173/**
174 * mix of white-space and comments
175 */
176void w() :
177{
178}
179{
180 ( <S> | <COMMENT_START> <COMMENT_END> )*
181}
182
183/**
184 * comma delimited list of floats (at least 2, all >= 0)
185 */
186List<Float> float_array() :
187{
188 float f;
189 List<Float> fs = new ArrayList<Float>();
190}
191{
192 f=ufloat() { fs.add(f); }
193 (
194 <COMMA> s()
195 f=ufloat() { fs.add(f); }
196 )+
197 {
198 return fs;
199 }
200}
201
202/**
203 * root
204 */
205void sheet(MapCSSStyleSource sheet):
206{
207 MapCSSRule r;
208 Token com = null;
209}
210{
211 { this.sheet = sheet; }
212 w()
213 (
214 try {
215 r=rule() { if (r != null) { sheet.rules.add(r); } } w()
216 } catch (ParseException ex) {
217 error_skipto(RBRACE);
218 w();
219 }
220 )*
221 <EOF>
222}
223
224MapCSSRule rule():
225{
226 List<Selector> selectors = new ArrayList<Selector>();
227 Selector sel;
228 List<Instruction> decl;
229}
230{
231 sel=selector() { selectors.add(sel); } w()
232 (
233 <COMMA> w()
234 sel=selector() { selectors.add(sel); } w()
235 )*
236 decl=declaration()
237 { return new MapCSSRule(selectors, decl); }
238}
239
240Selector selector() :
241{
242 Token base;
243 Condition c;
244 Pair<Integer, Integer> r = null;
245 List<Condition> conditions = new ArrayList<Condition>();
246 String sub = null;
247}
248{
249 ( base=<IDENT> | base=<STAR> )
250 ( r=zoom() )?
251 ( ( c=condition() | c=pseudoclass() ) { conditions.add(c); } )*
252 ( sub=subpart() )?
253 { return new Selector(base.image, r, conditions, sub); }
254}
255
256Pair<Integer, Integer> zoom() :
257{
258 Integer min = 0;
259 Integer max = Integer.MAX_VALUE;
260}
261{
262 <PIPE_Z>
263 (
264 <MINUS> max=uint()
265 |
266 min=uint() ( <MINUS> ( max=uint() )? )?
267 )
268 { return new Pair<Integer, Integer>(min, max); }
269}
270
271Condition condition() :
272{
273 Condition c;
274 Expression e;
275}
276{
277 <LSQUARE>
278 (
279 LOOKAHEAD(3)
280 c=simple_key_condition() <RSQUARE> { return c; }
281 |
282 LOOKAHEAD(5)
283 c=simple_key_value_condition() <RSQUARE> { return c; }
284 |
285 e=expression() <RSQUARE> { return new Condition.ExpressionCondition(e); }
286 )
287}
288
289Condition simple_key_condition() :
290{
291 boolean not = false;
292 boolean yes = false;
293 String key;
294}
295{
296 ( <EXCLAMATION> { not = true; } )?
297 key=string_or_ident()
298 ( <QUESTION> { yes = true; } )?
299 { return new Condition.KeyCondition(key, not, yes); }
300}
301
302Condition simple_key_value_condition() :
303{
304 String key;
305 String val;
306 float f;
307 Condition.Op op;
308}
309{
310 key=string_or_ident()
311 (
312 (
313 <EXCLAMATION> <EQUAL> { op=Condition.Op.NEQ; } val=string_or_ident()
314 |
315 <EQUAL> { op=Condition.Op.EQ; }
316 (
317 <TILDE> { op=Condition.Op.REGEX; }
318 val=regex()
319 |
320 val=string_or_ident()
321 )
322 |
323 <TILDE> <EQUAL> { op=Condition.Op.ONE_OF; } val=string_or_ident()
324 |
325 <CARET> <EQUAL> { op=Condition.Op.BEGINS_WITH; } val=string_or_ident()
326 |
327 <DOLLAR> <EQUAL> { op=Condition.Op.ENDS_WITH; } val=string_or_ident()
328 |
329 <STAR> <EQUAL> { op=Condition.Op.CONTAINS; } val=string_or_ident()
330 )
331 { return new Condition.KeyValueCondition(key, val, op); }
332 |
333 (
334 <GREATER_EQUAL> { op=Condition.Op.GREATER_OR_EQUAL; }
335 |
336 <GREATER> { op=Condition.Op.GREATER; }
337 |
338 <LESS_EQUAL> { op=Condition.Op.LESS_OR_EQUAL; }
339 |
340 <LESS> { op=Condition.Op.LESS; }
341 )
342 f=float_()
343 { return new Condition.KeyValueCondition(key, Float.toString(f), op); }
344 )
345}
346
347Condition pseudoclass() :
348{
349 Token t;
350 boolean not = false;
351}
352{
353 ( <EXCLAMATION> { not = true; } )?
354 <COLON>
355 t=<IDENT>
356 { return new Condition.PseudoClassCondition(t.image, not); }
357}
358
359String subpart() :
360{
361 Token t;
362}
363{
364 <DCOLON>
365 ( t=<IDENT> | t=<STAR> )
366 { return t.image; }
367}
368
369List<Instruction> declaration() :
370{
371 List<Instruction> ins = new ArrayList<Instruction>();
372 Instruction i;
373 Token key;
374 Object val;
375}
376{
377 <LBRACE> w()
378 (
379 key=<IDENT> w() <COLON> w()
380 (
381 LOOKAHEAD( float_array() w() ( <SEMICOLON> | <RBRACE> ) )
382 val=float_array()
383 { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
384 w()
385 ( <RBRACE> { return ins; } | <SEMICOLON> w() )
386 |
387 LOOKAHEAD( expression() ( <SEMICOLON> | <RBRACE> ) )
388 val=expression()
389 { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
390 ( <RBRACE> { return ins; } | <SEMICOLON> w() )
391 |
392 val=readRaw() w() { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
393 )
394 )*
395 <RBRACE>
396 { return ins; }
397}
398
399Expression expression():
400{
401 List<Expression> args = new ArrayList<Expression>();
402 Expression e;
403 String op = null;
404}
405{
406 (
407 <EXCLAMATION> { op = "not"; } w() e=primary() { args.add(e); } w()
408 |
409 <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w()
410 |
411
412 (
413 e=primary() { args.add(e); } w()
414 (
415 ( <PLUS> { op = "plus"; } w() e=primary() { args.add(e); } w() )+
416 |
417 ( <STAR> { op = "times"; } w() e=primary() { args.add(e); } w() )+
418 |
419 ( <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w() )+
420 |
421 ( <SLASH> { op = "divided_by"; } w() e=primary() { args.add(e); } w() )+
422 |
423 <GREATER_EQUAL> { op = "greater_equal"; } w() e=primary() { args.add(e); } w()
424 |
425 <LESS_EQUAL> { op = "less_equal"; } w() e=primary() { args.add(e); } w()
426 |
427 <GREATER> { op = "greater"; } w() e=primary() { args.add(e); } w()
428 |
429 <EQUAL> ( <EQUAL> )? { op = "equal"; } w() e=primary() { args.add(e); } w()
430 |
431 <LESS> { op = "less"; } w() e=primary() { args.add(e); } w()
432 |
433 <AMPERSAND> <AMPERSAND> { op = "and"; } w() e=primary() { args.add(e); } w()
434 |
435 <PIPE> <PIPE> { op = "or"; } w() e=primary() { args.add(e); } w()
436 |
437 <QUESTION> { op = "cond"; } w() e=primary() { args.add(e); } w() <COLON> w() e=primary() { args.add(e); } w()
438 )?
439 )
440 )
441 {
442 if (op == null)
443 return args.get(0);
444 return new FunctionExpression(op, args);
445 }
446}
447
448Expression primary() :
449{
450 Expression nested;
451 FunctionExpression fn;
452 Object lit;
453}
454{
455 LOOKAHEAD(2) // both function and identifier start with an identifier
456 fn=function() { return fn; }
457 |
458 lit=literal() { return new LiteralExpression(lit); }
459 |
460 <LPAR> w() nested=expression() <RPAR> { return nested; }
461}
462
463FunctionExpression function() :
464{
465 Token tmp;
466 Expression arg;
467 String name;
468 List<Expression> args = new ArrayList<Expression>();
469}
470{
471 tmp=<IDENT> { name = tmp.image; } w()
472 <LPAR> w()
473 (
474 arg=expression() { args.add(arg); }
475 ( <COMMA> w() arg=expression() { args.add(arg); } )*
476 )?
477 <RPAR>
478 { return new FunctionExpression(name, args); }
479}
480
481Object literal() :
482{
483 Object val;
484 Token t;
485 float f;
486}
487{
488 val=string_or_ident() { return val; }
489 |
490 <PLUS> f=ufloat() { return new Instruction.RelativeFloat(f); }
491 |
492 f=ufloat() { return f; }
493 |
494 t=<HEXCOLOR>
495 {
496 String clr = t.image.substring(1);
497 if (clr.length() == 3) {
498 clr = new String(new char[] {clr.charAt(0),clr.charAt(0),clr.charAt(1),clr.charAt(1),clr.charAt(2),clr.charAt(2)});
499 }
500 if (clr.length() != 6)
501 throw new AssertionError();
502 return new Color(Integer.parseInt(clr, 16));
503 }
504}
505
506JAVACODE
507void error_skipto(int kind) {
508 if (token.kind == EOF)
509 throw new ParseException("Reached end of file while parsing");
510 ParseException e = generateParseException();
511 System.err.println("Skipping to the next rule, because of an error:");
512 System.err.println(e);
513 if (sheet != null) {
514 sheet.logError(new ParseException(e.getMessage()));
515 }
516 Token t;
517 do {
518 t = getNextToken();
519 } while (t.kind != kind && t.kind != EOF);
520 if (t.kind == EOF)
521 throw new ParseException("Reached end of file while parsing");
522}
523
524JAVACODE
525/**
526 * read everything to the next semicolon
527 */
528String readRaw() {
529 Token t;
530 StringBuilder s = new StringBuilder();
531 while (true) {
532 t = getNextToken();
533 if ((t.kind == S || t.kind == STRING || t.kind == UNEXPECTED_CHAR) &&
534 t.image.contains("\n")) {
535 ParseException e = new ParseException(String.format("Warning: end of line while reading an unquoted string at line %s column %s.", t.beginLine, t.beginColumn));
536 System.err.println(e);
537 if (sheet != null) {
538 sheet.logError(e);
539 }
540 }
541 if (t.kind == SEMICOLON || t.kind == EOF)
542 break;
543 s.append(t.image);
544 }
545 if (t.kind == EOF)
546 throw new ParseException("Reached end of file while parsing");
547 return s.toString();
548}
549
Note: See TracBrowser for help on using the repository browser.