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

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

applied #6150 - mapcss - improve parent_tag (based on patch by Gubaer)

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