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

Last change on this file since 3888 was 3888, checked in by bastiK, 13 years ago

mapcss: several small fixes

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