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

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

mapcss: parser improvements

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