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

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

improve mapcss support

File size: 9.5 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}
25PARSER_END(MapCSSParser)
26
27/*************
28 * Token definitions
29 */
30
31<DEFAULT>
32TOKEN:
33{
34 < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
35| < UINT: ["1"-"9"] ( ["0"-"9"] )* >
36| < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? >
37| < STRING: "\"" ( [" ","!","#"-"&","("-"[","]"-"~","\u0080"-"\uFFFF"] | "\\\"" | "\\\\" )* "\"" >
38| < #H: ["0"-"9","a"-"f","A"-"F"] >
39| < HEXCOLOR: "#" ( <H><H><H><H><H><H> | <H><H><H> ) >
40| < S: ( " " | "\t" | "\n" | "\r" | "\f" )+ >
41| < STAR: "*" >
42| < SLASH: "/" >
43| < LBRACE: "{" >
44| < RBRACE: "}" >
45| < LSQUARE: "[" >
46| < RSQUARE: "]" >
47| < LPAR: "(" >
48| < RPAR: ")" >
49| < EQUAL: "=" >
50| < EXCLAMATION: "!" >
51| < EXCLAMATION_EQUAL: "!=" >
52| < COLON: ":" >
53| < DCOLON: "::" >
54| < SEMICOLON: ";" >
55| < COMMA: "," >
56| < PIPE: "|" >
57| < PIPE_Z: "|z" >
58| < PLUS: "+" >
59| < MINUS: "-" >
60| < AMPERSAND: "&" >
61| < QUESTION: "?" >
62| < COMMENT_START: "/*" > : COMMENT
63| < UNEXPECTED_CHAR : ~[] > // avoid TokenMgrErrors because they are hard to recover from
64}
65
66<COMMENT>
67TOKEN:
68{
69 < COMMENT_END: "*/" > : DEFAULT
70}
71
72<COMMENT>
73SKIP:
74{
75 < ~[] >
76}
77
78/*************
79 * Parser definitions
80 *
81 * rule
82 * _______________________|______________________________
83 * | |
84 * selector declaration
85 * _________|___________________ _________|____________
86 * | | | |
87 *
88 * way|z11-12[highway=residential] { color: red; width: 3 }
89 *
90 * |_____||___________________| |_________|
91 * | | |
92 * zoom condition instruction
93 *
94 * more general:
95 *
96 * way|z13-[a=b][c=d]::subpart, way|z-3[u=v]:closed::subpart2 { p1 : val; p2 : val; }
97 *
98 * 'val' can be a literal, or an expression like "prop(width, default) + 0.8".
99 *
100 */
101
102int uint() :
103{
104 Token i;
105}
106{
107 i=<UINT> { return Integer.parseInt(i.image); }
108}
109
110float ufloat() :
111{
112 Token f;
113}
114{
115 ( f=<UFLOAT> | f=<UINT> )
116 { return Float.parseFloat(f.image); }
117}
118
119String string() :
120{
121 Token t;
122}
123{
124 t=<STRING>
125 {
126 return t.image.substring(1, t.image.length() - 1).replace("\\\"", "\"").replace("\\\\", "\\");
127 }
128}
129
130String string_or_ident() :
131{
132 Token t;
133 String s;
134}
135{
136 t=<IDENT> { return t.image; } | s=string() { return s; }
137}
138
139/**
140 * white-space
141 */
142void s() :
143{
144}
145{
146 ( <S> )?
147}
148
149/**
150 * mix of white-space and comments
151 */
152void w() :
153{
154}
155{
156 ( <S> | ( <COMMENT_START> <COMMENT_END> ) )*
157}
158
159/**
160 * comma delimited list of floats (at least 2, all >= 0)
161 */
162List<Float> float_array() :
163{
164 float f;
165 List<Float> fs = new ArrayList<Float>();
166}
167{
168 f=ufloat() { fs.add(f); }
169 (
170 <COMMA> s()
171 f=ufloat() { fs.add(f); }
172 )+
173 {
174 return fs;
175 }
176}
177
178/**
179 * root
180 */
181void sheet(MapCSSStyleSource sheet):
182{
183 MapCSSRule r;
184 Token com = null;
185}
186{
187 w()
188 ( r=rule() { sheet.rules.add(r); } w() )*
189 <EOF>
190}
191
192MapCSSRule rule():
193{
194 List<Selector> selectors = new ArrayList<Selector>();
195 Selector sel;
196 List<Instruction> decl;
197}
198{
199 sel=selector() { selectors.add(sel); } w()
200 (
201 <COMMA> w()
202 sel=selector() { selectors.add(sel); } w()
203 )*
204 decl=declaration()
205 { return new MapCSSRule(selectors, decl); }
206}
207
208Selector selector() :
209{
210 Token base;
211 Condition c;
212 Pair<Integer, Integer> r = null;
213 List<Condition> conditions = new ArrayList<Condition>();
214 String sub = null;
215}
216{
217 ( base=<IDENT> | base=<STAR> )
218 ( r=zoom() )?
219 ( ( c=condition() | c=pseudoclass() ) { conditions.add(c); } )*
220 ( sub=subpart() )?
221 { return new Selector(base.image, r, conditions, sub); }
222}
223
224Pair<Integer, Integer> zoom() :
225{
226 Integer min = 0;
227 Integer max = Integer.MAX_VALUE;
228}
229{
230 <PIPE_Z>
231 (
232 ( <MINUS> max=uint() ) |
233 ( min=uint() ( <MINUS> ( max=uint() )? )? )
234 )
235 { return new Pair<Integer, Integer>(min, max); }
236}
237
238Condition condition() :
239{
240 Condition c;
241 Expression e;
242}
243{
244 <LSQUARE>
245 (
246 LOOKAHEAD(2)
247 ( c=simple_key_condition() <RSQUARE> { return c; } )
248 |
249 LOOKAHEAD(3)
250 c=simple_key_value_condition()
251 |
252 e=expression() { c = new Condition.ExpressionCondition(e); }
253 )
254 <RSQUARE>
255 { return c; }
256}
257
258Condition simple_key_condition() :
259{
260 boolean not = false;
261 String key;
262}
263{
264 ( <EXCLAMATION> { not = true; } )?
265 key=string_or_ident()
266 { return new Condition.KeyCondition(key, not); }
267}
268
269Condition simple_key_value_condition() :
270{
271 boolean not = false;
272 String key;
273 String val;
274}
275{
276 key=string_or_ident()
277 ( <EXCLAMATION_EQUAL> { not = true; } | <EQUAL> )
278 val=string_or_ident()
279 { return new Condition.KeyValueCondition(key, val, not ? Condition.Op.NEQ : Condition.Op.EQ); }
280}
281
282Condition pseudoclass() :
283{
284 Token t;
285 boolean not = false;
286}
287{
288 <COLON>
289 ( <EXCLAMATION> { not = true; } )? t=<IDENT>
290 { return new Condition.PseudoClassCondition(t.image, not); }
291}
292
293String subpart() :
294{
295 Token t;
296}
297{
298 <DCOLON>
299 ( t=<IDENT> | t=<STAR> )
300 { return t.image; }
301}
302
303List<Instruction> declaration() :
304{
305 List<Instruction> ins = new ArrayList<Instruction>();
306 Instruction i;
307}
308{
309 <LBRACE> w()
310 ( <RBRACE> { return ins; } )?
311 try {
312 i=instruction() { if (i != null) ins.add(i); }
313 (
314 <SEMICOLON> w()
315 ( <RBRACE> { return ins; } )?
316 i=instruction() { if (i != null) ins.add(i); }
317 )*
318 <RBRACE> { return ins; }
319 } catch (ParseException ex) {
320 error_skipto(RBRACE);
321 return ins;
322 }
323}
324
325Instruction instruction() :
326{
327 Token key;
328 Object val;
329}
330{
331 key=<IDENT> w()
332 <COLON> w()
333 (
334 LOOKAHEAD(2) // both number and float array start with a number
335 ( val=float_array() w() )
336 |
337 val=expression()
338 )
339 {
340 if (val instanceof LiteralExpression)
341 return new Instruction.AssignmentInstruction(key.image, ((LiteralExpression) val).evaluate(null));
342 else
343 return new Instruction.AssignmentInstruction(key.image, val);
344 }
345}
346
347Expression expression():
348{
349 List<Expression> args = new ArrayList<Expression>();
350 Expression e;
351 String op = null;
352}
353{
354 e=primary() { args.add(e); } w()
355 (
356 ( <PLUS> { op = "plus"; } w() e=primary() { args.add(e); } w() )+
357 |
358 ( <STAR> { op = "times"; } w() e=primary() { args.add(e); } w() )+
359 |
360 ( <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w() )+
361 |
362 ( <SLASH> { op = "divided_by"; } w() e=primary() { args.add(e); } w() )+
363 |
364 ( <AMPERSAND> <AMPERSAND> { op = "and"; } w() e=primary() { args.add(e); } w() )
365 |
366 ( <PIPE> <PIPE> { op = "or"; } w() e=primary() { args.add(e); } w() )
367 |
368 ( <QUESTION> { op = "cond"; } w() e=primary() { args.add(e); } w() <COLON> w() e=primary() { args.add(e); } w() )
369 )?
370 {
371 if (op == null)
372 return args.get(0);
373 return new FunctionExpression(op, args);
374 }
375}
376
377Expression primary() :
378{
379 Expression nested;
380 FunctionExpression fn;
381 Object lit;
382}
383{
384 LOOKAHEAD(2) // both function and identifier start with an identifier
385 fn=function() { return fn; }
386 |
387 lit=literal() { return new LiteralExpression(lit); }
388 |
389 ( <LPAR> w() nested=expression() <RPAR> ) { return nested; }
390}
391
392FunctionExpression function() :
393{
394 Token tmp;
395 Expression arg;
396 String name;
397 List<Expression> args = new ArrayList<Expression>();
398}
399{
400 tmp=<IDENT> { name = tmp.image; } w()
401 <LPAR> w()
402 (
403 arg=expression() { args.add(arg); }
404 ( <COMMA> w() arg=expression() { args.add(arg); } )*
405 )?
406 <RPAR>
407 { return new FunctionExpression(name, args); }
408}
409
410Object literal() :
411{
412 Object val;
413 Token t;
414 float f;
415}
416{
417 val=string_or_ident() { return val; }
418 |
419 ( <PLUS> f=ufloat() ) { return new Instruction.RelativeFloat(f); }
420 |
421 ( <MINUS> f=ufloat() ) { return -f; }
422 |
423 f=ufloat() { return f; }
424 |
425 t=<HEXCOLOR>
426 {
427 String clr = t.image.substring(1);
428 if (clr.length() == 3) {
429 clr = new String(new char[] {clr.charAt(0),clr.charAt(0),clr.charAt(1),clr.charAt(1),clr.charAt(2),clr.charAt(2)});
430 }
431 if (clr.length() != 6)
432 throw new AssertionError();
433 return new Color(Integer.parseInt(clr, 16));
434 }
435}
436
437JAVACODE
438void error_skipto(int kind) {
439 ParseException e = generateParseException();
440 System.err.println(e);
441 Token t;
442 do {
443 t = getNextToken();
444 } while (t.kind != kind);
445}
446
Note: See TracBrowser for help on using the repository browser.