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

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

mapcss: minor improvements, fixes #5937 - MapCSS matching of piste:type=downhill, fixes #5938 - matching of relation members

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