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

Last change on this file since 3863 was 3863, checked in by bastiK, 16 years ago

extended mappaint style dialog

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