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

Last change on this file since 3876 was 3876, checked in by bastiK, 15 years ago

mapcss: support for more exotic constructs; make parser more robust: if parsing as an expression fails, read everything between colon and semicolon as string, e.g. 'icon-image: images/img.png;' where the path should be in quotes, but it isn't

File size: 12.7 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 String key;
293}
294{
295 ( <EXCLAMATION> { not = true; } )?
296 key=string_or_ident()
297 { return new Condition.KeyCondition(key, not); }
298}
299
300Condition simple_key_value_condition() :
301{
302 String key;
303 String val;
304 float f;
305 Condition.Op op;
306}
307{
308 key=string_or_ident()
309 (
310 (
311 <EXCLAMATION> <EQUAL> { op=Condition.Op.NEQ; } val=string_or_ident()
312 |
313 <EQUAL> { op=Condition.Op.EQ; }
314 (
315 <TILDE> { op=Condition.Op.REGEX; }
316 val=regex()
317 |
318 val=string_or_ident()
319 )
320 |
321 <TILDE> <EQUAL> { op=Condition.Op.ONE_OF; } val=string_or_ident()
322 |
323 <CARET> <EQUAL> { op=Condition.Op.BEGINS_WITH; } val=string_or_ident()
324 |
325 <DOLLAR> <EQUAL> { op=Condition.Op.ENDS_WITH; } val=string_or_ident()
326 |
327 <STAR> <EQUAL> { op=Condition.Op.CONTAINS; } val=string_or_ident()
328 )
329 { return new Condition.KeyValueCondition(key, val, op); }
330 |
331 (
332 <GREATER_EQUAL> { op=Condition.Op.GREATER_OR_EQUAL; }
333 |
334 <GREATER> { op=Condition.Op.GREATER; }
335 |
336 <LESS_EQUAL> { op=Condition.Op.LESS_OR_EQUAL; }
337 |
338 <LESS> { op=Condition.Op.LESS; }
339 )
340 f=float_()
341 { return new Condition.KeyValueCondition(key, Float.toString(f), op); }
342 )
343}
344
345Condition pseudoclass() :
346{
347 Token t;
348 boolean not = false;
349}
350{
351 <COLON>
352 ( <EXCLAMATION> { not = true; } )? t=<IDENT>
353 { return new Condition.PseudoClassCondition(t.image, not); }
354}
355
356String subpart() :
357{
358 Token t;
359}
360{
361 <DCOLON>
362 ( t=<IDENT> | t=<STAR> )
363 { return t.image; }
364}
365
366List<Instruction> declaration() :
367{
368 List<Instruction> ins = new ArrayList<Instruction>();
369 Instruction i;
370 Token key;
371 Object val;
372}
373{
374 <LBRACE> w()
375 (
376 key=<IDENT> w() <COLON> w()
377 (
378 LOOKAHEAD( float_array() w() ( <SEMICOLON> | <RBRACE> ) )
379 val=float_array()
380 { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
381 w()
382 ( <RBRACE> { return ins; } | <SEMICOLON> w() )
383 |
384 LOOKAHEAD( expression() ( <SEMICOLON> | <RBRACE> ) )
385 val=expression()
386 { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
387 ( <RBRACE> { return ins; } | <SEMICOLON> w() )
388 |
389 val=readRaw() w() { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
390 )
391 )*
392 <RBRACE>
393 { return ins; }
394}
395
396Expression expression():
397{
398 List<Expression> args = new ArrayList<Expression>();
399 Expression e;
400 String op = null;
401}
402{
403 (
404 <EXCLAMATION> { op = "not"; } w() e=primary() { args.add(e); } w()
405 |
406 <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w()
407 |
408
409 (
410 e=primary() { args.add(e); } w()
411 (
412 ( <PLUS> { op = "plus"; } w() e=primary() { args.add(e); } w() )+
413 |
414 ( <STAR> { op = "times"; } w() e=primary() { args.add(e); } w() )+
415 |
416 ( <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w() )+
417 |
418 ( <SLASH> { op = "divided_by"; } w() e=primary() { args.add(e); } w() )+
419 |
420 <GREATER_EQUAL> { op = "greater_equal"; } w() e=primary() { args.add(e); } w()
421 |
422 <LESS_EQUAL> { op = "less_equal"; } w() e=primary() { args.add(e); } w()
423 |
424 <GREATER> { op = "greater"; } w() e=primary() { args.add(e); } w()
425 |
426 <EQUAL> ( <EQUAL> )? { op = "equal"; } w() e=primary() { args.add(e); } w()
427 |
428 <LESS> { op = "less"; } w() e=primary() { args.add(e); } w()
429 |
430 <AMPERSAND> <AMPERSAND> { op = "and"; } w() e=primary() { args.add(e); } w()
431 |
432 <PIPE> <PIPE> { op = "or"; } w() e=primary() { args.add(e); } w()
433 |
434 <QUESTION> { op = "cond"; } w() e=primary() { args.add(e); } w() <COLON> w() e=primary() { args.add(e); } w()
435 )?
436 )
437 )
438 {
439 if (op == null)
440 return args.get(0);
441 return new FunctionExpression(op, args);
442 }
443}
444
445Expression primary() :
446{
447 Expression nested;
448 FunctionExpression fn;
449 Object lit;
450}
451{
452 LOOKAHEAD(2) // both function and identifier start with an identifier
453 fn=function() { return fn; }
454 |
455 lit=literal() { return new LiteralExpression(lit); }
456 |
457 <LPAR> w() nested=expression() <RPAR> { return nested; }
458}
459
460FunctionExpression function() :
461{
462 Token tmp;
463 Expression arg;
464 String name;
465 List<Expression> args = new ArrayList<Expression>();
466}
467{
468 tmp=<IDENT> { name = tmp.image; } w()
469 <LPAR> w()
470 (
471 arg=expression() { args.add(arg); }
472 ( <COMMA> w() arg=expression() { args.add(arg); } )*
473 )?
474 <RPAR>
475 { return new FunctionExpression(name, args); }
476}
477
478Object literal() :
479{
480 Object val;
481 Token t;
482 float f;
483}
484{
485 val=string_or_ident() { return val; }
486 |
487 <PLUS> f=ufloat() { return new Instruction.RelativeFloat(f); }
488 |
489 f=ufloat() { return f; }
490 |
491 t=<HEXCOLOR>
492 {
493 String clr = t.image.substring(1);
494 if (clr.length() == 3) {
495 clr = new String(new char[] {clr.charAt(0),clr.charAt(0),clr.charAt(1),clr.charAt(1),clr.charAt(2),clr.charAt(2)});
496 }
497 if (clr.length() != 6)
498 throw new AssertionError();
499 return new Color(Integer.parseInt(clr, 16));
500 }
501}
502
503JAVACODE
504void error_skipto(int kind) {
505 ParseException e = generateParseException();
506 System.err.println("Skipping to the next rule, because of an error:");
507 System.err.println(e);
508 if (sheet != null) {
509 sheet.logError(e);
510 }
511 Token t;
512 do {
513 t = getNextToken();
514 } while (t.kind != kind && t.kind != EOF);
515 if (t.kind == EOF)
516 throw new ParseException("Reached end of file while parsing");
517}
518
519JAVACODE
520/**
521 * read everything to the next semicolon
522 */
523String readRaw() {
524 Token t;
525 StringBuilder s = new StringBuilder();
526 while (true) {
527 t = getNextToken();
528 if (t.kind == SEMICOLON || t.kind == EOF)
529 break;
530 s.append(t.image);
531 }
532 if (t.kind == EOF)
533 throw new ParseException("Reached end of file while parsing");
534 return s.toString();
535}
536
Note: See TracBrowser for help on using the repository browser.