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

Last change on this file since 5577 was 5577, checked in by bastiK, 11 years ago

fixed #8187 - eval(#colour) doesn't result in #colour value suitable for color

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