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

Last change on this file since 3902 was 3902, checked in by bastiK, 9 years ago

mapcss: minor improvements, fixes #5937 - MapCSS matching of piste:type=downhill, fixes #5938 - matching of relation members

File size: 14.0 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.gui.mappaint.mapcss.Selector.DescendentSelector;
22import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
23import org.openstreetmap.josm.tools.Pair;
24
25public class MapCSSParser {
26    MapCSSStyleSource sheet;
27}
28PARSER_END(MapCSSParser)
29
30/*************
31 * Token definitions
32 */
33
34<DEFAULT>
35TOKEN:
36{
37    < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
38|   < UINT: ["1"-"9"] ( ["0"-"9"] )* >
39|   < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? >
40|   < STRING: "\"" ( [" ","!","#"-"[","]"-"~","\u0080"-"\uFFFF"] | "\\\"" | "\\\\" )*  "\"" >
41|   < #REGEX_CHAR_WITHOUT_STAR: [" "-")","+"-".","0"-"[","]"-"~","\u0080"-"\uFFFF"] | "\\/" | "\\\\" >
42|   < REGEX: "/" <REGEX_CHAR_WITHOUT_STAR> ( <REGEX_CHAR_WITHOUT_STAR> | "*" )*  "/" >
43|   < #H: ["0"-"9","a"-"f","A"-"F"] >
44|   < HEXCOLOR: "#" ( <H><H><H><H><H><H> | <H><H><H> ) >
45|   < S: ( " " | "\t" | "\n" | "\r" | "\f" )+ >
46|   < STAR: "*" >
47|   < SLASH: "/" >
48|   < LBRACE: "{" >
49|   < RBRACE: "}" >
50|   < LSQUARE: "[" >
51|   < RSQUARE: "]" >
52|   < LPAR: "(" >
53|   < RPAR: ")" >
54|   < GREATER_EQUAL: ">=" >
55|   < LESS_EQUAL: "<=" >
56|   < GREATER: ">" >
57|   < LESS: "<" >
58|   < EQUAL: "=" >
59|   < EXCLAMATION: "!" >
60|   < TILDE: "~" >
61|   < COLON: ":" >
62|   < DCOLON: "::" >
63|   < SEMICOLON: ";" >
64|   < COMMA: "," >
65|   < PIPE: "|" >
66|   < PIPE_Z: "|z" >
67|   < PLUS: "+" >
68|   < MINUS: "-" >
69|   < AMPERSAND: "&" >
70|   < QUESTION: "?" >
71|   < DOLLAR: "$" >
72|   < CARET: "^" >
73|   < COMMENT_START: "/*" > : COMMENT
74|   < UNEXPECTED_CHAR : ~[] > // avoid TokenMgrErrors because they are hard to recover from
75}
76
77<COMMENT>
78TOKEN:
79{
80    < COMMENT_END: "*/" > : DEFAULT
81}
82
83<COMMENT>
84SKIP:
85{
86    < ~[] >
87}
88
89/*************
90 * Parser definitions
91 *
92 *                       rule
93 *  _______________________|______________________________
94 * |                                                      |
95 *        selector                      declaration
96 *  _________|___________________   _________|____________
97 * |                             | |                      |
98 *
99 * way|z11-12[highway=residential] { color: red; width: 3 }
100 *
101 *    |_____||___________________|   |_________|
102 *       |            |                   |
103 *     zoom       condition          instruction
104 *
105 * more general:
106 *
107 * way|z13-[a=b][c=d]::subpart, way|z-3[u=v]:closed::subpart2 { p1 : val; p2 : val; }
108 *
109 * 'val' can be a literal, or an expression like "prop(width, default) + 0.8".
110 *
111 */
112
113int uint() :
114{
115    Token i;
116}
117{
118    i=<UINT> { return Integer.parseInt(i.image); }
119}
120
121float ufloat() :
122{
123    Token f;
124}
125{
126    ( f=<UFLOAT> | f=<UINT> )
127    { return Float.parseFloat(f.image); }
128}
129
130float float_() :
131{
132    float f;
133}
134{
135    <MINUS> f=ufloat() { return -f; } | f=ufloat() { return f; }
136}
137
138String string() :
139{
140    Token t;
141}
142{
143    t=<STRING>
144    { return t.image.substring(1, t.image.length() - 1).replace("\\\"", "\"").replace("\\\\", "\\"); }
145}
146
147String string_or_ident() :
148{
149    Token t;
150    String s;
151}
152{
153    t=<IDENT> { return t.image; } | s=string() { return s; }
154}
155
156String regex() :
157{
158    Token t;
159}
160{
161    t=<REGEX>
162    { return t.image.substring(1, t.image.length() - 1); }
163}
164
165/**
166 * white-space
167 */
168void s() :
169{
170}
171{
172    ( <S> )?
173}
174
175/**
176 * mix of white-space and comments
177 */
178void w() :
179{
180}
181{
182    ( <S> | <COMMENT_START> <COMMENT_END> )*
183}
184
185/**
186 * comma delimited list of floats (at least 2, all >= 0)
187 */
188List<Float> float_array() :
189{
190    float f;
191    List<Float> fs = new ArrayList<Float>();
192}
193{
194    f=ufloat() { fs.add(f); }
195    (
196        <COMMA> s()
197        f=ufloat() { fs.add(f); }
198    )+
199    {
200        return fs;
201    }
202}
203
204/**
205 * root
206 */
207void sheet(MapCSSStyleSource sheet):
208{
209    MapCSSRule r;
210    Token com = null;
211}
212{
213    { this.sheet = sheet; }
214    w()
215    (
216        try {
217            r=rule() { if (r != null) { sheet.rules.add(r); } } w()
218        } catch (ParseException ex) {
219            error_skipto(RBRACE);
220            w();
221        }
222    )*
223    <EOF>
224}
225
226MapCSSRule rule():
227{
228    List<Selector> selectors = new ArrayList<Selector>();
229    Selector sel;
230    List<Instruction> decl;
231}
232{
233    sel=child_selector() { selectors.add(sel); }
234    (
235        <COMMA> w()
236        sel=child_selector() { selectors.add(sel); }
237    )*
238    decl=declaration()
239    { return new MapCSSRule(selectors, decl); }
240}
241
242Selector child_selector() :
243{
244    boolean child = false;
245    Selector sel1, sel2 = null;
246}
247{
248    sel1=selector() w()
249    (
250        ( <GREATER> { child = true; } | <LESS> { child = false; } ) w()
251        sel2=selector() w()
252    )?
253    { return sel2 != null ? new DescendentSelector(sel1, sel2, child) : sel1; }
254}
255
256Selector selector() :
257{
258    Token base;
259    Condition c;
260    Pair<Integer, Integer> r = null;
261    List<Condition> conditions = new ArrayList<Condition>();
262    String sub = null;
263}
264{
265    ( base=<IDENT> | base=<STAR> )
266    ( r=zoom() )?
267    ( ( c=condition() | c=pseudoclass() ) { conditions.add(c); } )*
268    ( sub=subpart() )?
269    { return new GeneralSelector(base.image, r, conditions, sub); }
270}
271
272Pair<Integer, Integer> zoom() :
273{
274    Integer min = 0;
275    Integer max = Integer.MAX_VALUE;
276}
277{
278    <PIPE_Z>
279    (
280            <MINUS> max=uint()
281        |
282            min=uint() ( <MINUS> ( max=uint() )? )?
283    )
284    { return new Pair<Integer, Integer>(min, max); }
285}
286
287Condition condition() :
288{
289    Condition c;
290    Expression e;
291}
292{
293    <LSQUARE>
294    (
295        LOOKAHEAD( simple_key_condition() <RSQUARE> )
296            c=simple_key_condition() <RSQUARE> { return c; }
297        |
298        LOOKAHEAD( simple_key_value_condition() <RSQUARE> )
299            c=simple_key_value_condition() <RSQUARE> { return c; }
300        |
301            e=expression() <RSQUARE> { return new Condition.ExpressionCondition(e); }
302    )
303}
304
305String tag_key() :
306{
307    String s;
308    Token t;
309}
310{
311        s=string() { return s; }
312    |
313        t=<IDENT> { s = t.image; } ( <COLON> t=<IDENT> { s += ':' + t.image; } )* { return s; }
314}
315
316Condition simple_key_condition() :
317{
318    boolean not = false;
319    boolean yes = false;
320    String key;
321}
322{
323    ( <EXCLAMATION> { not = true; } )?
324    key=tag_key()
325    ( <QUESTION> { yes = true; } )?
326    { return new Condition.KeyCondition(key, not, yes); }
327}
328
329Condition simple_key_value_condition() :
330{
331    String key;
332    String val;
333    float f;
334    Condition.Op op;
335}
336{
337    key=tag_key()
338    (
339            (
340                    <EXCLAMATION> <EQUAL> { op=Condition.Op.NEQ; } val=string_or_ident()
341                |
342                    <EQUAL> { op=Condition.Op.EQ; }
343                    (
344                            <TILDE> { op=Condition.Op.REGEX; }
345                            val=regex()
346                        |
347                            val=string_or_ident()
348                    )
349                |
350                    <TILDE> <EQUAL> { op=Condition.Op.ONE_OF; } val=string_or_ident()
351                |
352                    <CARET> <EQUAL> { op=Condition.Op.BEGINS_WITH; } val=string_or_ident()
353                |
354                    <DOLLAR> <EQUAL> { op=Condition.Op.ENDS_WITH; } val=string_or_ident()
355                |
356                    <STAR> <EQUAL> { op=Condition.Op.CONTAINS; } val=string_or_ident()
357            )
358            { return new Condition.KeyValueCondition(key, val, op); }
359        |
360            (
361                    <GREATER_EQUAL> { op=Condition.Op.GREATER_OR_EQUAL; }
362                |
363                    <GREATER> { op=Condition.Op.GREATER; }
364                |
365                    <LESS_EQUAL> { op=Condition.Op.LESS_OR_EQUAL; }
366                |
367                    <LESS> { op=Condition.Op.LESS; }
368            )
369            f=float_()
370            { return new Condition.KeyValueCondition(key, Float.toString(f), op); }
371    )
372}
373
374Condition pseudoclass() :
375{
376    Token t;
377    boolean not = false;
378}
379{
380    ( <EXCLAMATION> { not = true; } )?
381    <COLON>
382    t=<IDENT>
383    { return new Condition.PseudoClassCondition(t.image, not); }
384}
385
386String subpart() :
387{
388    Token t;
389}
390{
391    <DCOLON>
392    ( t=<IDENT> | t=<STAR> )
393    { return t.image; }
394}
395
396List<Instruction> declaration() :
397{
398    List<Instruction> ins = new ArrayList<Instruction>();
399    Instruction i;
400    Token key;
401    Object val;
402}
403{
404    <LBRACE> w()
405    (
406        key=<IDENT> w() <COLON> w()
407        (
408            LOOKAHEAD( float_array() w() ( <SEMICOLON> | <RBRACE> ) )
409                val=float_array()
410                { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
411                w()
412                ( <RBRACE> { return ins; } | <SEMICOLON> w() )
413            |
414            LOOKAHEAD( expression() ( <SEMICOLON> | <RBRACE> ) )
415                val=expression()
416                { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
417                ( <RBRACE> { return ins; } | <SEMICOLON> w() )
418            |
419                val=readRaw() w() { ins.add(new Instruction.AssignmentInstruction(key.image, val)); }
420        )
421    )*
422    <RBRACE>
423    { return ins; }
424}
425
426Expression expression():
427{
428    List<Expression> args = new ArrayList<Expression>();
429    Expression e;
430    String op = null;
431}
432{
433    (
434        <EXCLAMATION> { op = "not"; } w() e=primary() { args.add(e); } w()
435    |
436        <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w()
437    |
438
439        (
440            e=primary() { args.add(e); } w()
441            (
442                    ( <PLUS> { op = "plus"; } w() e=primary() { args.add(e); } w() )+
443                |
444                    ( <STAR> { op = "times"; } w() e=primary() { args.add(e); } w() )+
445                |
446                    ( <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w() )+
447                |
448                    ( <SLASH> { op = "divided_by"; } w() e=primary() { args.add(e); } w() )+
449                |
450                    <GREATER_EQUAL> { op = "greater_equal"; } w() e=primary() { args.add(e); } w()
451                |
452                    <LESS_EQUAL> { op = "less_equal"; } w() e=primary() { args.add(e); } w()
453                |
454                    <GREATER> { op = "greater"; } w() e=primary() { args.add(e); } w()
455                |
456                    <EQUAL> ( <EQUAL> )? { op = "equal"; } w() e=primary() { args.add(e); } w()
457                |
458                    <LESS> { op = "less"; } w() e=primary() { args.add(e); } w()
459                |
460                    <AMPERSAND> <AMPERSAND> { op = "and"; } w() e=primary() { args.add(e); } w()
461                |
462                    <PIPE> <PIPE> { op = "or"; } w() e=primary() { args.add(e); } w()
463                |
464                    <QUESTION> { op = "cond"; } w() e=primary() { args.add(e); } w() <COLON> w() e=primary() { args.add(e); } w()
465            )?
466        )
467    )
468    {
469        if (op == null)
470            return args.get(0);
471        return new FunctionExpression(op, args);
472    }
473}
474
475Expression primary() :
476{
477    Expression nested;
478    FunctionExpression fn;
479    Object lit;
480}
481{
482    LOOKAHEAD(2) // both function and identifier start with an identifier
483        fn=function() { return fn; }
484    |
485        lit=literal() { return new LiteralExpression(lit); }
486    |
487        <LPAR> w() nested=expression() <RPAR> { return nested; }
488}
489
490FunctionExpression function() :
491{
492    Token tmp;
493    Expression arg;
494    String name;
495    List<Expression> args = new ArrayList<Expression>();
496}
497{
498    tmp=<IDENT> { name = tmp.image; } w()
499    <LPAR> w()
500    (
501        arg=expression() { args.add(arg); }
502        ( <COMMA> w() arg=expression() { args.add(arg); } )*
503    )?
504    <RPAR>
505    { return new FunctionExpression(name, args); }
506}
507
508Object literal() :
509{
510    Object val;
511    Token t;
512    float f;
513}
514{
515        val=string_or_ident() { return val; }
516    |
517        <PLUS> f=ufloat() { return new Instruction.RelativeFloat(f); }
518    |
519        f=ufloat() { return f; }
520    |
521        t=<HEXCOLOR>
522            {
523                String clr = t.image.substring(1);
524                if (clr.length() == 3) {
525                    clr = new String(new char[] {clr.charAt(0),clr.charAt(0),clr.charAt(1),clr.charAt(1),clr.charAt(2),clr.charAt(2)});
526                }
527                if (clr.length() != 6)
528                    throw new AssertionError();
529                return new Color(Integer.parseInt(clr, 16));
530            }
531}
532
533JAVACODE
534void error_skipto(int kind) {
535    if (token.kind == EOF)
536        throw new ParseException("Reached end of file while parsing");
537    ParseException e = generateParseException();
538    System.err.println("Skipping to the next rule, because of an error:");
539    System.err.println(e);
540    if (sheet != null) {
541        sheet.logError(new ParseException(e.getMessage()));
542    }
543    Token t;
544    do {
545        t = getNextToken();
546    } while (t.kind != kind && t.kind != EOF);
547    if (t.kind == EOF)
548        throw new ParseException("Reached end of file while parsing");
549}
550
551JAVACODE
552/**
553 * read everything to the next semicolon
554 */
555String readRaw() {
556    Token t;
557    StringBuilder s = new StringBuilder();
558    while (true) {
559        t = getNextToken();
560        if ((t.kind == S || t.kind == STRING || t.kind == UNEXPECTED_CHAR) &&
561                t.image.contains("\n")) {
562            ParseException e = new ParseException(String.format("Warning: end of line while reading an unquoted string at line %s column %s.", t.beginLine, t.beginColumn));
563            System.err.println(e);
564            if (sheet != null) {
565                sheet.logError(e);
566            }
567        }
568        if (t.kind == SEMICOLON || t.kind == EOF)
569            break;
570        s.append(t.image);
571    }
572    if (t.kind == EOF)
573        throw new ParseException("Reached end of file while parsing");
574    return s.toString();
575}
576
Note: See TracBrowser for help on using the repository browser.