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

Last change on this file since 7057 was 7057, checked in by bastiK, 10 years ago

reverting [7056] because it won't work for execution in multiple threads (which will be more useful)

File size: 22.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2options {
3 STATIC = false;
4 OUTPUT_DIRECTORY = "parsergen";
5}
6
7PARSER_BEGIN(MapCSSParser)
8package org.openstreetmap.josm.gui.mappaint.mapcss.parsergen;
9
10import java.io.InputStream;
11import java.util.ArrayList;
12import java.util.List;
13
14import org.openstreetmap.josm.gui.mappaint.Keyword;
15import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
16import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Context;
17import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
18import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
19import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
20import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
21import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
22import org.openstreetmap.josm.gui.mappaint.mapcss.ExpressionFactory;
23import org.openstreetmap.josm.gui.mappaint.mapcss.LiteralExpression;
24import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSException;
25import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector;
26import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
27import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.LinkSelector;
28import org.openstreetmap.josm.tools.ColorHelper;
29import org.openstreetmap.josm.tools.Pair;
30import org.openstreetmap.josm.Main;
31
32/**
33 * MapCSS parser.
34 *
35 * Contains two independent grammars:
36 * (a) the preprocessor and (b) the main mapcss parser.
37 *
38 * The preprocessor handles @media syntax. Basically this allows
39 * to write one style for different versions of JOSM (or different editors).
40 * When the @media condition is not fulfilled, it should simply skip over
41 * the whole section and not attempt to parse the possibly unknown
42 * grammar. It preserves whitespace and comments, in order to keep the
43 * line and column numbers in the error messages correct for the second pass.
44 *
45 */
46
47public class MapCSSParser {
48 MapCSSStyleSource sheet;
49 StringBuilder sb;
50
51 /**
52 * Nicer way to refer to a lexical state.
53 */
54 public static enum LexicalState {
55 PREPROCESSOR(0), /* the preprocessor */
56 DEFAULT(2); /* the main parser */
57
58 int idx; // the integer, which javacc assigns to this state
59
60 LexicalState(int idx) {
61 if (!this.name().equals(MapCSSParserTokenManager.lexStateNames[idx])) {
62 throw new RuntimeException();
63 }
64 this.idx = idx;
65 }
66 };
67
68 /**
69 * Constructor which initializes the parser with a certain lexical state.
70 */
71 public MapCSSParser(InputStream in, String encoding, LexicalState initState) {
72 this(createTokenManager(in, encoding, initState));
73 }
74
75 protected static MapCSSParserTokenManager createTokenManager(InputStream in, String encoding, LexicalState initState) {
76 SimpleCharStream scs;
77 try {
78 scs = new SimpleCharStream(in, encoding, 1, 1);
79 } catch(java.io.UnsupportedEncodingException e) {
80 throw new RuntimeException(e);
81 }
82 return new MapCSSParserTokenManager(scs, initState.idx);
83 }
84}
85PARSER_END(MapCSSParser)
86
87/*************
88 * Token definitions
89 *
90 * Lexical states for the preprocessor: <PREPROCESSOR>, <PP_COMMENT>
91 * Lexical states for the main parser: <DEFAULT>, <COMMENT>
92 */
93
94<PREPROCESSOR>
95TOKEN:
96{
97 < PP_AND: "and" >
98| < PP_NOT: "not" >
99| < PP_MEDIA: "@media" >
100| < PP_NEWLINECHAR: "\n" | "\r" | "\f" >
101| < PP_WHITESPACE: " " | "\t" >
102| < PP_COMMENT_START: "/*" > : PP_COMMENT
103}
104
105<PP_COMMENT>
106TOKEN:
107{
108 < PP_COMMENT_END: "*/" > : PREPROCESSOR
109}
110
111<PP_COMMENT>
112MORE:
113{
114 < ~[] >
115}
116
117<DEFAULT>
118TOKEN [IGNORE_CASE]:
119{
120 /* Special keyword in some contexts, ordinary identifier in other contexts.
121 Use the parsing rule <code>ident()</code> to refer to a general
122 identifier, including "set". */
123 < SET: "set" >
124}
125
126<DEFAULT,PREPROCESSOR>
127TOKEN:
128{
129 < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
130| < UINT: ["1"-"9"] ( ["0"-"9"] )* >
131| < STRING: "\"" ( [" ","!","#"-"[","]"-"~","\u0080"-"\uFFFF"] | "\\\"" | "\\\\" )* "\"" >
132| < #PREDEFINED: "\\" ["d","D","s","S","w","W"] >
133| < #REGEX_CHAR_WITHOUT_STAR: [" "-")","+"-".","0"-"[","]"-"~","\u0080"-"\uFFFF"] | "\\/" | "\\\\" | "\\[" | "\\]" | "\\+" | "\\." | "\\'" | "\\\"" | <PREDEFINED> >
134| < REGEX: "/" <REGEX_CHAR_WITHOUT_STAR> ( <REGEX_CHAR_WITHOUT_STAR> | "*" )* "/" >
135| < LBRACE: "{" >
136| < RBRACE: "}" >
137| < LPAR: "(" >
138| < RPAR: ")" >
139| < COLON: ":" >
140}
141
142<PREPROCESSOR>
143TOKEN:
144{
145 < PP_SOMETHING_ELSE : ~[] >
146}
147
148<DEFAULT>
149TOKEN:
150{
151 < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? >
152| < #H: ["0"-"9","a"-"f","A"-"F"] >
153| < HEXCOLOR: "#" ( <H><H><H><H><H><H><H><H> | <H><H><H><H><H><H> | <H><H><H> ) >
154| < S: ( " " | "\t" | "\n" | "\r" | "\f" )+ >
155| < STAR: "*" >
156| < SLASH: "/" >
157| < LSQUARE: "[" >
158| < RSQUARE: "]" >
159| < GREATER_EQUAL: ">=" >
160| < LESS_EQUAL: "<=" >
161| < GREATER: ">" >
162| < LESS: "<" >
163| < EQUAL: "=" >
164| < EXCLAMATION: "!" >
165| < TILDE: "~" >
166| < DCOLON: "::" >
167| < SEMICOLON: ";" >
168| < COMMA: "," >
169| < PIPE: "|" >
170| < PIPE_Z: "|z" >
171| < PLUS: "+" >
172| < MINUS: "-" >
173| < AMPERSAND: "&" >
174| < QUESTION: "?" >
175| < DOLLAR: "$" >
176| < CARET: "^" >
177| < FULLSTOP: "." >
178| < ELEMENT_OF: "∈" >
179| < CROSSING: "⧉" >
180| < COMMENT_START: "/*" > : COMMENT
181| < UNEXPECTED_CHAR : ~[] > // avoid TokenMgrErrors because they are hard to recover from
182}
183
184<COMMENT>
185TOKEN:
186{
187 < COMMENT_END: "*/" > : DEFAULT
188}
189
190<COMMENT>
191SKIP:
192{
193 < ~[] >
194}
195
196
197/*************
198 *
199 * Preprocessor parser definitions:
200 *
201 * <pre>
202 *
203 * {@literal @media} { ... } queries are supported, following http://www.w3.org/TR/css3-mediaqueries/#syntax
204 *
205 * media_query
206 * ___________________________|_______________________________
207 * | |
208 * {@literal @media} all and (min-josm-version: 7789) and (max-josm-version: 7790), all and (user-agent: xyz) { ... }
209 * |______________________|
210 * |
211 * media_expression
212 * </pre>
213 */
214
215
216/**
217 * root method for the preprocessor.
218 */
219String pp_root(MapCSSStyleSource sheet):
220{
221}
222{
223 { sb = new StringBuilder(); this.sheet = sheet; }
224 pp_black_box(true) <EOF>
225 { return sb.toString(); }
226}
227
228/**
229 * Parse any unknown grammar (black box).
230 *
231 * Only stop when "@media" is encountered and keep track of correct number of
232 * opening and closing curly brackets.
233 *
234 * @param write false if this content should be skipped (@pp_media condition is not fulfilled), true otherwise
235 */
236void pp_black_box(boolean write):
237{
238 Token t;
239}
240{
241 (
242 (t=<PP_AND> | t=<PP_NOT> | t=<UINT> | t=<STRING> | t=<REGEX> | t=<LPAR> | t=<RPAR> | t=<COLON> | t=<IDENT> | t=<PP_SOMETHING_ELSE>) { if (write) sb.append(t.image); }
243 |
244 pp_w1()
245 |
246 pp_media()
247 |
248 t=<LBRACE> { if (write) sb.append(t.image); } pp_black_box(write) t=<RBRACE> { if (write) sb.append(t.image); }
249 )*
250}
251
252void pp_media():
253{
254 boolean pass = false;
255 boolean q;
256 boolean empty = true;
257}
258{
259 <PP_MEDIA> pp_w()
260 ( q=pp_media_query() { pass = pass || q; empty = false; }
261 ( <COMMA> pp_w() q=pp_media_query() { pass = pass || q; } )*
262 )?
263 <LBRACE>
264 pp_black_box(empty || pass)
265 <RBRACE>
266}
267
268boolean pp_media_query():
269{
270 Token t;
271 String mediatype = "all";
272 boolean pass = true;
273 boolean invert = false;
274 boolean e;
275}
276{
277 ( <PP_NOT> { invert = true; } pp_w() )?
278 (
279 t=<IDENT> { mediatype = t.image.toLowerCase(); } pp_w()
280 ( <PP_AND> pp_w() e=pp_media_expression() { pass = pass && e; } pp_w() )*
281 |
282 e=pp_media_expression() { pass = pass && e; } pp_w()
283 ( <PP_AND> pp_w() e=pp_media_expression() { pass = pass && e; } pp_w() )*
284 )
285 {
286 if (!"all".equals(mediatype)) {
287 pass = false;
288 }
289 return invert ? (!pass) : pass;
290 }
291}
292
293/**
294 * Parse an @media expression.
295 *
296 * The parsing rule {@link #literal()} from the main mapcss parser is reused here.
297 *
298 * @return true if the condition is fulfilled
299 */
300boolean pp_media_expression():
301{
302 Token t;
303 String feature;
304 Object val = null;
305}
306{
307 <LPAR> pp_w() t=<IDENT> { feature = t.image; } pp_w() ( <COLON> pp_w() val=literal() )? <RPAR>
308 { return this.sheet.evalMediaExpression(feature, val); }
309}
310
311void pp_w1():
312{
313 Token t;
314}
315{
316 t=<PP_NEWLINECHAR> { sb.append(t.image); }
317 |
318 t=<PP_WHITESPACE> { sb.append(t.image); }
319 |
320 t=<PP_COMMENT_START> { sb.append(t.image); } t=<PP_COMMENT_END> { sb.append(t.image); }
321}
322
323void pp_w():
324{
325}
326{
327 ( pp_w1() )*
328}
329
330/*************
331 *
332 * Parser definition for the main MapCSS parser:
333 *
334 * <pre>
335 *
336 * rule
337 * _______________________|______________________________
338 * | |
339 * selector declaration
340 * _________|___________________ _________|____________
341 * | | | |
342 *
343 * way|z11-12[highway=residential] { color: red; width: 3 }
344 *
345 * |_____||___________________| |_________|
346 * | | |
347 * zoom condition instruction
348 *
349 * more general:
350 *
351 * way|z13-[a=b][c=d]::subpart, way|z-3[u=v]:closed::subpart2 { p1 : val; p2 : val; }
352 *
353 * 'val' can be a literal, or an expression like "prop(width, default) + 0.8".
354 *
355 * </pre>
356 */
357
358int uint() :
359{
360 Token i;
361}
362{
363 i=<UINT> { return Integer.parseInt(i.image); }
364}
365
366int int_() :
367{
368 int i;
369}
370{
371 <MINUS> i=uint() { return -i; } | i=uint() { return i; }
372}
373
374float ufloat() :
375{
376 Token f;
377}
378{
379 ( f=<UFLOAT> | f=<UINT> )
380 { return Float.parseFloat(f.image); }
381}
382
383float float_() :
384{
385 float f;
386}
387{
388 <MINUS> f=ufloat() { return -f; } | f=ufloat() { return f; }
389}
390
391String string() :
392{
393 Token t;
394}
395{
396 t=<STRING>
397 { return t.image.substring(1, t.image.length() - 1).replace("\\\"", "\"").replace("\\\\", "\\"); }
398}
399
400String ident():
401{
402 Token t;
403 String s;
404}
405{
406 ( t=<IDENT> | t=<SET> ) { return t.image; }
407}
408
409String string_or_ident() :
410{
411 Token t;
412 String s;
413}
414{
415 ( s=ident() | s=string() ) { return s; }
416}
417
418String regex() :
419{
420 Token t;
421}
422{
423 t=<REGEX>
424 { return t.image.substring(1, t.image.length() - 1); }
425}
426
427/**
428 * white-space
429 */
430void s() :
431{
432}
433{
434 ( <S> )?
435}
436
437/**
438 * mix of white-space and comments
439 */
440void w() :
441{
442}
443{
444 ( <S> | <COMMENT_START> <COMMENT_END> )*
445}
446
447/**
448 * comma delimited list of floats (at least 2, all &gt;= 0)
449 */
450List<Float> float_array() :
451{
452 float f;
453 List<Float> fs = new ArrayList<Float>();
454}
455{
456 f=ufloat() { fs.add(f); }
457 (
458 <COMMA> s()
459 f=ufloat() { fs.add(f); }
460 )+
461 {
462 return fs;
463 }
464}
465
466/**
467 * entry point for the main parser
468 */
469void sheet(MapCSSStyleSource sheet):
470{
471 MapCSSRule r;
472}
473{
474 { this.sheet = sheet; }
475 w()
476 (
477 try {
478 r=rule() { if (r != null) { sheet.rules.add(r); } } w()
479 } catch (MapCSSException mex) {
480 error_skipto(RBRACE, mex);
481 w();
482 } catch (ParseException ex) {
483 error_skipto(RBRACE, null);
484 w();
485 }
486 )*
487 <EOF>
488}
489
490MapCSSRule rule():
491{
492 List<Selector> selectors = new ArrayList<Selector>();
493 Selector sel;
494 List<Instruction> decl;
495}
496{
497 sel=child_selector() { selectors.add(sel); }
498 (
499 <COMMA> w()
500 sel=child_selector() { selectors.add(sel); }
501 )*
502 decl=declaration()
503 { return new MapCSSRule(selectors, decl); }
504}
505
506Selector child_selector() :
507{
508 Selector.ChildOrParentSelectorType type = null;
509 Condition c;
510 List<Condition> conditions = new ArrayList<Condition>();
511 Selector selLeft;
512 LinkSelector selLink = null;
513 Selector selRight = null;
514}
515{
516 selLeft=selector() w()
517 (
518 (
519 (
520 <GREATER> { type = Selector.ChildOrParentSelectorType.CHILD; }
521 |
522 <LESS> { type = Selector.ChildOrParentSelectorType.PARENT; }
523 |
524 <PLUS> { type = Selector.ChildOrParentSelectorType.SIBLING; }
525 )
526 ( ( c=condition(Context.LINK) | c=class_or_pseudoclass(Context.LINK) ) { conditions.add(c); } )*
527 |
528 <ELEMENT_OF> { type = Selector.ChildOrParentSelectorType.ELEMENT_OF; }
529 |
530 <CROSSING> { type = Selector.ChildOrParentSelectorType.CROSSING; }
531 )
532 { selLink = new LinkSelector(conditions); }
533 w()
534 selRight=selector() w()
535 )?
536 { return selRight != null ? new ChildOrParentSelector(selLeft, selLink, selRight, type) : selLeft; }
537}
538
539Selector selector() :
540{
541 Token base;
542 Condition c;
543 Pair<Integer, Integer> r = null;
544 List<Condition> conditions = new ArrayList<Condition>();
545 String sub = null;
546}
547{
548 ( base=<IDENT> | base=<STAR> )
549 ( r=zoom() )?
550 ( ( c=condition(Context.PRIMITIVE) | c=class_or_pseudoclass(Context.PRIMITIVE) ) { conditions.add(c); } )*
551 ( sub=subpart() )?
552 { return new GeneralSelector(base.image, r, conditions, sub); }
553}
554
555Pair<Integer, Integer> zoom() :
556{
557 Integer min = 0;
558 Integer max = Integer.MAX_VALUE;
559}
560{
561 <PIPE_Z>
562 (
563 <MINUS> max=uint()
564 |
565 LOOKAHEAD(2)
566 min=uint() <MINUS> ( max=uint() )?
567 |
568 min=uint() { max = min; }
569 )
570 { return new Pair<Integer, Integer>(min, max); }
571}
572
573Condition condition(Context context) :
574{
575 Condition c;
576 Expression e;
577}
578{
579 <LSQUARE> s()
580 (
581 LOOKAHEAD( simple_key_condition(context) s() <RSQUARE> )
582 c=simple_key_condition(context) s() <RSQUARE> { return c; }
583 |
584 LOOKAHEAD( simple_key_value_condition(context) s() <RSQUARE> )
585 c=simple_key_value_condition(context) s() <RSQUARE> { return c; }
586 |
587 e=expression() <RSQUARE> { return Condition.createExpressionCondition(e, context); }
588 )
589}
590
591String tag_key() :
592{
593 String s, s2;
594 Token t;
595}
596{
597 s=string() { return s; }
598 |
599 s=ident() ( <COLON> s2=ident() { s += ':' + s2; } )* { return s; }
600}
601
602Condition simple_key_condition(Context context) :
603{
604 boolean not = false;
605 Condition.KeyMatchType matchType = null;;
606 String key;
607}
608{
609 ( <EXCLAMATION> { not = true; } )?
610 (
611 { matchType = Condition.KeyMatchType.REGEX; } key = regex()
612 |
613 key = tag_key()
614 )
615 ( LOOKAHEAD(2) <QUESTION> <EXCLAMATION> { matchType = Condition.KeyMatchType.FALSE; } )?
616 ( <QUESTION> { matchType = Condition.KeyMatchType.TRUE; } )?
617 { return Condition.createKeyCondition(key, not, matchType, context); }
618}
619
620Condition simple_key_value_condition(Context context) :
621{
622 String key;
623 String val;
624 float f;
625 int i;
626 Condition.Op op;
627 boolean considerValAsKey = false;
628}
629{
630 key=tag_key() s()
631 (
632 LOOKAHEAD(3)
633 (
634 <EQUAL> <TILDE> { op=Condition.Op.REGEX; }
635 |
636 <EXCLAMATION> <TILDE> { op=Condition.Op.NREGEX; }
637 )
638 s()
639 ( <STAR> { considerValAsKey=true; } )?
640 val=regex()
641 |
642 (
643 <EXCLAMATION> <EQUAL> { op=Condition.Op.NEQ; }
644 |
645 <EQUAL> { op=Condition.Op.EQ; }
646 |
647 <TILDE> <EQUAL> { op=Condition.Op.ONE_OF; }
648 |
649 <CARET> <EQUAL> { op=Condition.Op.BEGINS_WITH; }
650 |
651 <DOLLAR> <EQUAL> { op=Condition.Op.ENDS_WITH; }
652 |
653 <STAR> <EQUAL> { op=Condition.Op.CONTAINS; }
654 )
655 s()
656 ( <STAR> { considerValAsKey=true; } )?
657 (
658 LOOKAHEAD(2)
659 i=int_() { val=Integer.toString(i); }
660 |
661 f=float_() { val=Float.toString(f); }
662 |
663 val=string_or_ident()
664 )
665 |
666 (
667 <GREATER_EQUAL> { op=Condition.Op.GREATER_OR_EQUAL; }
668 |
669 <GREATER> { op=Condition.Op.GREATER; }
670 |
671 <LESS_EQUAL> { op=Condition.Op.LESS_OR_EQUAL; }
672 |
673 <LESS> { op=Condition.Op.LESS; }
674 )
675 s()
676 f=float_() { val=Float.toString(f); }
677 )
678 { return Condition.createKeyValueCondition(key, val, op, context, considerValAsKey); }
679}
680
681Condition class_or_pseudoclass(Context context) :
682{
683 String s;
684 boolean not = false;
685 boolean pseudo;
686}
687{
688 ( <EXCLAMATION> { not = true; } )?
689 (
690 <FULLSTOP> { pseudo = false; }
691 |
692 <COLON> { pseudo = true; }
693 )
694 s=ident()
695 { return pseudo
696 ? Condition.createPseudoClassCondition(s, not, context)
697 : Condition.createClassCondition(s, not, context); }
698}
699
700String subpart() :
701{
702 String s;
703}
704{
705 <DCOLON>
706 ( s=ident() { return s; } | <STAR> { return "*"; } )
707}
708
709List<Instruction> declaration() :
710{
711 List<Instruction> ins = new ArrayList<Instruction>();
712 Instruction i;
713 Token key;
714 Object val = null;
715}
716{
717 <LBRACE> w()
718 (
719 (
720 <SET> w()
721 (<FULLSTOP>)? // specification allows "set .class" to set "class". we also support "set class"
722 key=<IDENT> w()
723 ( <EQUAL> val=expression() )?
724 { ins.add(new Instruction.AssignmentInstruction(key.image, val == null ? true : val, true)); }
725 ( <RBRACE> { return ins; } | <SEMICOLON> w() )
726 )
727 |
728 key=<IDENT> w() <COLON> w()
729 (
730 LOOKAHEAD( float_array() w() ( <SEMICOLON> | <RBRACE> ) )
731 val=float_array()
732 { ins.add(new Instruction.AssignmentInstruction(key.image, val, false)); }
733 w()
734 ( <RBRACE> { return ins; } | <SEMICOLON> w() )
735 |
736 LOOKAHEAD( expression() ( <SEMICOLON> | <RBRACE> ) )
737 val=expression()
738 { ins.add(new Instruction.AssignmentInstruction(key.image, val, false)); }
739 ( <RBRACE> { return ins; } | <SEMICOLON> w() )
740 |
741 val=readRaw() w() { ins.add(new Instruction.AssignmentInstruction(key.image, val, false)); }
742 )
743 )*
744 <RBRACE>
745 { return ins; }
746}
747
748Expression expression():
749{
750 List<Expression> args = new ArrayList<Expression>();
751 Expression e;
752 String op = null;
753}
754{
755 (
756 <EXCLAMATION> { op = "not"; } w() e=primary() { args.add(e); } w()
757 |
758 <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w()
759 |
760
761 (
762 e=primary() { args.add(e); } w()
763 (
764 ( <PLUS> { op = "plus"; } w() e=primary() { args.add(e); } w() )+
765 |
766 ( <STAR> { op = "times"; } w() e=primary() { args.add(e); } w() )+
767 |
768 ( <MINUS> { op = "minus"; } w() e=primary() { args.add(e); } w() )+
769 |
770 ( <SLASH> { op = "divided_by"; } w() e=primary() { args.add(e); } w() )+
771 |
772 <GREATER_EQUAL> { op = "greater_equal"; } w() e=primary() { args.add(e); } w()
773 |
774 <LESS_EQUAL> { op = "less_equal"; } w() e=primary() { args.add(e); } w()
775 |
776 <GREATER> { op = "greater"; } w() e=primary() { args.add(e); } w()
777 |
778 <EQUAL> ( <EQUAL> )? { op = "equal"; } w() e=primary() { args.add(e); } w()
779 |
780 <LESS> { op = "less"; } w() e=primary() { args.add(e); } w()
781 |
782 <AMPERSAND> <AMPERSAND> { op = "and"; } w() e=primary() { args.add(e); } w()
783 |
784 <PIPE> <PIPE> { op = "or"; } w() e=primary() { args.add(e); } w()
785 |
786 <QUESTION> { op = "cond"; } w() e=primary() { args.add(e); } w() <COLON> w() e=primary() { args.add(e); } w()
787 )?
788 )
789 )
790 {
791 if (op == null)
792 return args.get(0);
793 return ExpressionFactory.createFunctionExpression(op, args);
794 }
795}
796
797Expression primary() :
798{
799 Expression nested;
800 Expression fn;
801 Object lit;
802}
803{
804 LOOKAHEAD(3) // both function and identifier start with an identifier (+ optional whitespace)
805 fn=function() { return fn; }
806 |
807 lit=literal() { return new LiteralExpression(lit); }
808 |
809 <LPAR> w() nested=expression() <RPAR> { return nested; }
810}
811
812Expression function() :
813{
814 Expression arg;
815 String name;
816 List<Expression> args = new ArrayList<Expression>();
817}
818{
819 name=ident() w()
820 <LPAR> w()
821 (
822 arg=expression() { args.add(arg); }
823 ( <COMMA> w() arg=expression() { args.add(arg); } )*
824 )?
825 <RPAR>
826 { return ExpressionFactory.createFunctionExpression(name, args); }
827}
828
829Object literal() :
830{
831 String val, pref;
832 Token t;
833 float f;
834}
835{
836 LOOKAHEAD(2)
837 pref=ident() t=<HEXCOLOR>
838 { return Main.pref.getColor("mappaint." + (sheet == null ? "MapCSS" : sheet.title) + "." + pref, ColorHelper.html2color(t.image)); }
839 |
840 t=<IDENT> { return new Keyword(t.image); }
841 |
842 val=string() { return val; }
843 |
844 <PLUS> f=ufloat() { return new Instruction.RelativeFloat(f); }
845 |
846 f=ufloat() { return f; }
847 |
848 t=<HEXCOLOR> { return ColorHelper.html2color(t.image); }
849}
850
851JAVACODE
852void error_skipto(int kind, MapCSSException me) {
853 if (token.kind == EOF)
854 throw new ParseException("Reached end of file while parsing");
855
856 Exception e = null;
857 ParseException pe = generateParseException();
858
859 if (me != null) {
860 me.setLine(pe.currentToken.next.beginLine);
861 me.setColumn(pe.currentToken.next.beginColumn);
862 e = me;
863 } else {
864 e = new ParseException(pe.getMessage()); // prevent memory leak
865 }
866
867 Main.error("Skipping to the next rule, because of an error:");
868 Main.error(e);
869 if (sheet != null) {
870 sheet.logError(e);
871 }
872 Token t;
873 do {
874 t = getNextToken();
875 } while (t.kind != kind && t.kind != EOF);
876 if (t.kind == EOF)
877 throw new ParseException("Reached end of file while parsing");
878}
879
880JAVACODE
881/**
882 * read everything to the next semicolon
883 */
884String readRaw() {
885 Token t;
886 StringBuilder s = new StringBuilder();
887 while (true) {
888 t = getNextToken();
889 if ((t.kind == S || t.kind == STRING || t.kind == UNEXPECTED_CHAR) &&
890 t.image.contains("\n")) {
891 ParseException e = new ParseException(String.format("Warning: end of line while reading an unquoted string at line %s column %s.", t.beginLine, t.beginColumn));
892 Main.error(e);
893 if (sheet != null) {
894 sheet.logError(e);
895 }
896 }
897 if (t.kind == SEMICOLON || t.kind == EOF)
898 break;
899 s.append(t.image);
900 }
901 if (t.kind == EOF)
902 throw new ParseException("Reached end of file while parsing");
903 return s.toString();
904}
905
Note: See TracBrowser for help on using the repository browser.