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, 10 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.