WebCore:
[WebKit-https.git] / WebCore / css / CSSGrammar.y
1 %{
2
3 /*
4  *  Copyright (C) 2002-2003 Lars Knoll (knoll@kde.org)
5  *  Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #include "config.h"
25
26 #include "CSSMediaRule.h"
27 #include "CSSParser.h"
28 #include "CSSRuleList.h"
29 #include "CSSSelector.h"
30 #include "CSSStyleSheet.h"
31 #include "Document.h"
32 #include "HTMLNames.h"
33 #include "MediaList.h"
34 #include <stdlib.h>
35 #include <string.h>
36
37 using namespace WebCore;
38 using namespace HTMLNames;
39
40 #define YYENABLE_NLS 0
41 #define YYLTYPE_IS_TRIVIAL 1
42 #define YYMAXDEPTH 10000
43 #define YYDEBUG 0
44
45 // FIXME: Replace with %parse-param { CSSParser* parser } once we can depend on bison 2.x
46 #define YYPARSE_PARAM parser
47
48 %}
49
50 %pure_parser
51
52 %union {
53     bool boolean;
54     char character;
55     int integer;
56     double number;
57     ParseString string;
58
59     CSSRule* rule;
60     CSSRuleList* ruleList;
61     CSSSelector* selector;
62     CSSSelector::Relation relation;
63     MediaList* mediaList;
64     MediaQuery* mediaQuery;
65     MediaQuery::Restrictor mediaQueryRestrictor;
66     MediaQueryExp* mediaQueryExp;
67     Value value;
68     ValueList* valueList;
69     Vector<MediaQueryExp*>* mediaQueryExpList;
70 }
71
72 %{
73
74 static inline int cssyyerror(const char*) { return 1; }
75 static int cssyylex(YYSTYPE* yylval) { return CSSParser::current()->lex(yylval); }
76
77 %}
78
79 %expect 41
80
81 %left UNIMPORTANT_TOK
82
83 %token WHITESPACE SGML_CD
84
85 %token INCLUDES
86 %token DASHMATCH
87 %token BEGINSWITH
88 %token ENDSWITH
89 %token CONTAINS
90
91 %token <string> STRING
92 %right <string> IDENT
93 %token <string> NTH
94
95 %nonassoc <string> HEX
96 %nonassoc <string> IDSEL
97 %nonassoc ':'
98 %nonassoc '.'
99 %nonassoc '['
100 %nonassoc <string> '*'
101 %nonassoc error
102 %left '|'
103
104 %token IMPORT_SYM
105 %token PAGE_SYM
106 %token MEDIA_SYM
107 %token FONT_FACE_SYM
108 %token CHARSET_SYM
109 %token NAMESPACE_SYM
110 %token WEBKIT_RULE_SYM
111 %token WEBKIT_DECLS_SYM
112 %token WEBKIT_VALUE_SYM
113 %token WEBKIT_MEDIAQUERY_SYM
114
115 %token IMPORTANT_SYM
116 %token MEDIA_ONLY
117 %token MEDIA_NOT
118 %token MEDIA_AND
119
120 %token <number> QEMS
121 %token <number> EMS
122 %token <number> EXS
123 %token <number> PXS
124 %token <number> CMS
125 %token <number> MMS
126 %token <number> INS
127 %token <number> PTS
128 %token <number> PCS
129 %token <number> DEGS
130 %token <number> RADS
131 %token <number> GRADS
132 %token <number> MSECS
133 %token <number> SECS
134 %token <number> HERZ
135 %token <number> KHERZ
136 %token <string> DIMEN
137 %token <number> PERCENTAGE
138 %token <number> FLOATTOKEN
139 %token <number> INTEGER
140
141 %token <string> URI
142 %token <string> FUNCTION
143 %token <string> NOTFUNCTION
144
145 %token <string> UNICODERANGE
146
147 %type <relation> combinator
148
149 %type <rule> charset
150 %type <rule> ruleset
151 %type <rule> valid_rule_or_import
152 %type <rule> media
153 %type <rule> import
154 %type <rule> page
155 %type <rule> font_face
156 %type <rule> invalid_rule
157 %type <rule> invalid_at
158 %type <rule> invalid_import
159 %type <rule> rule
160 %type <rule> valid_rule
161
162 %type <string> maybe_ns_prefix
163
164 %type <string> namespace_selector
165
166 %type <string> string_or_uri
167 %type <string> ident_or_string
168 %type <string> medium
169 %type <string> hexcolor
170
171 %type <string> media_feature
172 %type <mediaList> media_list
173 %type <mediaList> maybe_media_list
174 %type <mediaQuery> media_query
175 %type <mediaQueryRestrictor> maybe_media_restrictor
176 %type <valueList> maybe_media_value
177 %type <mediaQueryExp> media_query_exp
178 %type <mediaQueryExpList> media_query_exp_list
179 %type <mediaQueryExpList> maybe_media_query_exp_list
180
181 %type <ruleList> ruleset_list
182
183 %type <integer> property
184
185 %type <selector> specifier
186 %type <selector> specifier_list
187 %type <selector> simple_selector
188 %type <selector> selector
189 %type <selector> selector_list
190 %type <selector> selector_with_trailing_whitespace
191 %type <selector> class
192 %type <selector> attrib
193 %type <selector> pseudo
194
195 %type <boolean> declaration_list
196 %type <boolean> decl_list
197 %type <boolean> declaration
198
199 %type <boolean> prio
200
201 %type <integer> match
202 %type <integer> unary_operator
203 %type <character> operator
204
205 %type <valueList> expr
206 %type <value> term
207 %type <value> unary_term
208 %type <value> function
209
210 %type <string> element_name
211 %type <string> attr_name
212
213 %%
214
215 stylesheet:
216     maybe_charset maybe_sgml import_list namespace_list rule_list
217   | webkit_rule maybe_space
218   | webkit_decls maybe_space
219   | webkit_value maybe_space
220   | webkit_mediaquery maybe_space
221   ;
222
223 valid_rule_or_import:
224     valid_rule
225   | import
226   ;
227
228 webkit_rule:
229     WEBKIT_RULE_SYM '{' maybe_space valid_rule_or_import maybe_space '}' {
230         static_cast<CSSParser*>(parser)->rule = $4;
231     }
232 ;
233
234 webkit_decls:
235     WEBKIT_DECLS_SYM '{' maybe_space declaration_list '}' {
236         /* can be empty */
237     }
238 ;
239
240 webkit_value:
241     WEBKIT_VALUE_SYM '{' maybe_space expr '}' {
242         CSSParser* p = static_cast<CSSParser*>(parser);
243         if ($4) {
244             p->valueList = p->sinkFloatingValueList($4);
245             int oldParsedProperties = p->numParsedProperties;
246             if (!p->parseValue(p->id, p->important))
247                 p->rollbackLastProperties(p->numParsedProperties - oldParsedProperties);
248             delete p->valueList;
249             p->valueList = 0;
250         }
251     }
252 ;
253
254 webkit_mediaquery:
255      WEBKIT_MEDIAQUERY_SYM WHITESPACE maybe_space media_query '}' {
256          CSSParser* p = static_cast<CSSParser*>(parser);
257          p->mediaQuery = p->sinkFloatingMediaQuery($4);
258      }
259 ;
260
261 maybe_space:
262     /* empty */ %prec UNIMPORTANT_TOK
263   | maybe_space WHITESPACE
264   ;
265
266 maybe_sgml:
267     /* empty */
268   | maybe_sgml SGML_CD
269   | maybe_sgml WHITESPACE
270   ;
271
272 maybe_charset:
273    /* empty */
274   | charset {
275   }
276 ;
277
278 charset:
279   CHARSET_SYM maybe_space STRING maybe_space ';' {
280      CSSParser* p = static_cast<CSSParser*>(parser);
281      $$ = static_cast<CSSParser*>(parser)->createCharsetRule($3);
282      if ($$ && p->styleElement && p->styleElement->isCSSStyleSheet())
283          p->styleElement->append($$);
284   }
285   | CHARSET_SYM error invalid_block {
286   }
287   | CHARSET_SYM error ';' {
288   }
289 ;
290
291 import_list:
292  /* empty */
293  | import_list import maybe_sgml {
294      CSSParser* p = static_cast<CSSParser*>(parser);
295      if ($2 && p->styleElement && p->styleElement->isCSSStyleSheet())
296          p->styleElement->append($2);
297  }
298  ;
299
300 namespace_list:
301 /* empty */
302 | namespace_list namespace maybe_sgml
303 ;
304
305 rule_list:
306    /* empty */
307  | rule_list rule maybe_sgml {
308      CSSParser* p = static_cast<CSSParser*>(parser);
309      if ($2 && p->styleElement && p->styleElement->isCSSStyleSheet())
310          p->styleElement->append($2);
311  }
312  ;
313
314 valid_rule:
315     ruleset
316   | media
317   | page
318   | font_face
319   ;
320
321 rule:
322     valid_rule
323   | invalid_rule
324   | invalid_at
325   | invalid_import
326   ;
327
328 import:
329     IMPORT_SYM maybe_space string_or_uri maybe_space maybe_media_list ';' {
330         $$ = static_cast<CSSParser*>(parser)->createImportRule($3, $5);
331     }
332   | IMPORT_SYM error invalid_block {
333         $$ = 0;
334     }
335   | IMPORT_SYM error ';' {
336         $$ = 0;
337     }
338   ;
339
340 namespace:
341 NAMESPACE_SYM maybe_space maybe_ns_prefix string_or_uri maybe_space ';' {
342     CSSParser* p = static_cast<CSSParser*>(parser);
343     if (p->styleElement && p->styleElement->isCSSStyleSheet())
344         static_cast<CSSStyleSheet*>(p->styleElement)->addNamespace(p, $3, $4);
345 }
346 | NAMESPACE_SYM error invalid_block
347 | NAMESPACE_SYM error ';'
348 ;
349
350 maybe_ns_prefix:
351 /* empty */ { $$.characters = 0; }
352 | IDENT WHITESPACE { $$ = $1; }
353 ;
354
355 string_or_uri:
356 STRING
357 | URI
358 ;
359
360 media_feature:
361     IDENT maybe_space {
362         $$ = $1;
363     }
364     ;
365
366 maybe_media_value:
367     /*empty*/ {
368         $$ = 0;
369     }
370     | ':' maybe_space expr maybe_space {
371         $$ = $3;
372     }
373     ;
374
375 media_query_exp:
376     MEDIA_AND maybe_space '(' maybe_space media_feature maybe_space maybe_media_value ')' maybe_space {
377         $5.lower();
378         $$ = static_cast<CSSParser*>(parser)->createFloatingMediaQueryExp($5, $7);
379     }
380     ;
381
382 media_query_exp_list:
383     media_query_exp {
384         CSSParser* p = static_cast<CSSParser*>(parser);
385         $$ = p->createFloatingMediaQueryExpList();
386         $$->append(p->sinkFloatingMediaQueryExp($1));
387     }
388     | media_query_exp_list media_query_exp {
389         $$ = $1;
390         $$->append(static_cast<CSSParser*>(parser)->sinkFloatingMediaQueryExp($2));
391     }
392     ;
393
394 maybe_media_query_exp_list:
395     /*empty*/ {
396         $$ = static_cast<CSSParser*>(parser)->createFloatingMediaQueryExpList();
397     }
398     | media_query_exp_list
399     ;
400
401 maybe_media_restrictor:
402     /*empty*/ {
403         $$ = MediaQuery::None;
404     }
405     | MEDIA_ONLY {
406         $$ = MediaQuery::Only;
407     }
408     | MEDIA_NOT {
409         $$ = MediaQuery::Not;
410     }
411     ;
412
413 media_query:
414     maybe_media_restrictor maybe_space medium maybe_media_query_exp_list {
415         CSSParser* p = static_cast<CSSParser*>(parser);
416         $3.lower();
417         $$ = p->createFloatingMediaQuery($1, $3, p->sinkFloatingMediaQueryExpList($4));
418     }
419     ;
420
421 maybe_media_list:
422      /* empty */ {
423         $$ = static_cast<CSSParser*>(parser)->createMediaList();
424      }
425      | media_list
426      ;
427
428 media_list:
429     media_query {
430         CSSParser* p = static_cast<CSSParser*>(parser);
431         $$ = p->createMediaList();
432         $$->appendMediaQuery(p->sinkFloatingMediaQuery($1));
433     }
434     | media_list ',' maybe_space media_query {
435         $$ = $1;
436         if ($$)
437             $$->appendMediaQuery(static_cast<CSSParser*>(parser)->sinkFloatingMediaQuery($4));
438     }
439     | media_list error {
440         $$ = 0;
441     }
442     ;
443
444 media:
445     MEDIA_SYM maybe_space media_list '{' maybe_space ruleset_list '}' {
446         $$ = static_cast<CSSParser*>(parser)->createMediaRule($3, $6);
447     }
448     | MEDIA_SYM maybe_space '{' maybe_space ruleset_list '}' {
449         $$ = static_cast<CSSParser*>(parser)->createMediaRule(0, $5);
450     }
451     ;
452
453 ruleset_list:
454     /* empty */ { $$ = 0; }
455     | ruleset_list ruleset maybe_space {
456         $$ = $1;
457         if ($2) {
458             if (!$$)
459                 $$ = static_cast<CSSParser*>(parser)->createRuleList();
460             $$->append($2);
461         }
462     }
463     ;
464
465 medium:
466   IDENT maybe_space {
467       $$ = $1;
468   }
469   ;
470
471 /*
472 page:
473     PAGE_SYM maybe_space IDENT? pseudo_page? maybe_space
474     '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space
475   ;
476
477 pseudo_page
478   : ':' IDENT
479   ;
480 */
481
482 page:
483     PAGE_SYM error invalid_block {
484       $$ = 0;
485     }
486   | PAGE_SYM error ';' {
487       $$ = 0;
488     }
489     ;
490
491 font_face:
492     FONT_FACE_SYM maybe_space
493     '{' maybe_space declaration_list '}'  maybe_space {
494         $$ = static_cast<CSSParser*>(parser)->createFontFaceRule();
495     }
496     | FONT_FACE_SYM error invalid_block {
497       $$ = 0;
498     }
499     | FONT_FACE_SYM error ';' {
500       $$ = 0;
501     }
502 ;
503
504 combinator:
505     '+' maybe_space { $$ = CSSSelector::DirectAdjacent; }
506   | '~' maybe_space { $$ = CSSSelector::IndirectAdjacent; }
507   | '>' maybe_space { $$ = CSSSelector::Child; }
508   ;
509
510 unary_operator:
511     '-' { $$ = -1; }
512   | '+' { $$ = 1; }
513   ;
514
515 ruleset:
516     selector_list '{' maybe_space declaration_list '}' {
517         $$ = static_cast<CSSParser*>(parser)->createStyleRule($1);
518     }
519   ;
520
521 selector_list:
522     selector %prec UNIMPORTANT_TOK {
523         $$ = $1;
524     }
525     | selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK {
526         if ($1 && $4) {
527             CSSParser* p = static_cast<CSSParser*>(parser);
528             $$ = $1;
529             $$->append(p->sinkFloatingSelector($4));
530         } else
531             $$ = 0;
532     }
533   | selector_list error {
534         $$ = 0;
535     }
536    ;
537
538 selector_with_trailing_whitespace:
539     selector WHITESPACE {
540         $$ = $1;
541     }
542     ;
543
544 selector:
545     simple_selector {
546         $$ = $1;
547     }
548     | selector_with_trailing_whitespace
549     {
550         $$ = $1;
551     }
552     | selector_with_trailing_whitespace simple_selector
553     {
554         $$ = $2;
555         if (!$1)
556             $$ = 0;
557         else if ($$) {
558             CSSParser* p = static_cast<CSSParser*>(parser);
559             CSSSelector* end = $$;
560             while (end->m_tagHistory)
561                 end = end->m_tagHistory;
562             end->m_relation = CSSSelector::Descendant;
563             end->m_tagHistory = p->sinkFloatingSelector($1);
564             if (Document* doc = p->document())
565                 doc->setUsesDescendantRules(true);
566         }
567     }
568     | selector combinator simple_selector {
569         $$ = $3;
570         if (!$1)
571             $$ = 0;
572         else if ($$) {
573             CSSParser* p = static_cast<CSSParser*>(parser);
574             CSSSelector* end = $$;
575             while (end->m_tagHistory)
576                 end = end->m_tagHistory;
577             end->m_relation = $2;
578             end->m_tagHistory = p->sinkFloatingSelector($1);
579             if ($2 == CSSSelector::Child) {
580                 if (Document* doc = p->document())
581                     doc->setUsesDescendantRules(true);
582             } else if ($2 == CSSSelector::DirectAdjacent || $2 == CSSSelector::IndirectAdjacent) {
583                 if (Document* doc = p->document())
584                     doc->setUsesSiblingRules(true);
585             }
586         }
587     }
588     | selector error {
589         $$ = 0;
590     }
591     ;
592
593 namespace_selector:
594     /* empty */ '|' { $$.characters = 0; $$.length = 0; }
595     | '*' '|' { static UChar star = '*'; $$.characters = &star; $$.length = 1; }
596     | IDENT '|' { $$ = $1; }
597 ;
598     
599 simple_selector:
600     element_name {
601         CSSParser* p = static_cast<CSSParser*>(parser);
602         $$ = p->createFloatingSelector();
603         $$->m_tag = QualifiedName(nullAtom, $1, p->defaultNamespace);
604     }
605     | element_name specifier_list {
606         $$ = $2;
607         if ($$) {
608             CSSParser* p = static_cast<CSSParser*>(parser);
609             $$->m_tag = QualifiedName(nullAtom, $1, p->defaultNamespace);
610         }
611     }
612     | specifier_list {
613         $$ = $1;
614         CSSParser* p = static_cast<CSSParser*>(parser);
615         if ($$ && p->defaultNamespace != starAtom)
616             $$->m_tag = QualifiedName(nullAtom, starAtom, p->defaultNamespace);
617     }
618     | namespace_selector element_name {
619         AtomicString namespacePrefix = $1;
620         CSSParser* p = static_cast<CSSParser*>(parser);
621         $$ = p->createFloatingSelector();
622         if (p->styleElement && p->styleElement->isCSSStyleSheet()) {
623             $$->m_tag = QualifiedName(namespacePrefix, $2,
624                                       static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
625         } else // FIXME: Shouldn't this case be an error?
626             $$->m_tag = QualifiedName(nullAtom, $2, p->defaultNamespace);
627     }
628     | namespace_selector element_name specifier_list {
629         $$ = $3;
630         if ($$) {
631             AtomicString namespacePrefix = $1;
632             CSSParser* p = static_cast<CSSParser*>(parser);
633             if (p->styleElement && p->styleElement->isCSSStyleSheet()) {
634                 $$->m_tag = QualifiedName(namespacePrefix, $2,
635                                           static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
636             } else // FIXME: Shouldn't this case be an error?
637                 $$->m_tag = QualifiedName(nullAtom, $2, p->defaultNamespace);
638         }
639     }
640     | namespace_selector specifier_list {
641         $$ = $2;
642         if ($$) {
643             AtomicString namespacePrefix = $1;
644             CSSParser* p = static_cast<CSSParser*>(parser);
645             if (p->styleElement && p->styleElement->isCSSStyleSheet())
646                 $$->m_tag = QualifiedName(namespacePrefix,
647                                           starAtom,
648                                           static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
649         }
650     }
651   ;
652
653 element_name:
654     IDENT {
655         ParseString& str = $1;
656         CSSParser* p = static_cast<CSSParser*>(parser);
657         Document* doc = p->document();
658         if (doc && doc->isHTMLDocument())
659             str.lower();
660         $$ = str;
661     }
662     | '*' {
663         static UChar star = '*';
664         $$.characters = &star;
665         $$.length = 1;
666     }
667   ;
668
669 specifier_list:
670     specifier {
671         $$ = $1;
672     }
673     | specifier_list specifier {
674         if (!$2)
675             $$ = 0;
676         else if ($1) {
677             $$ = $1;
678             CSSParser* p = static_cast<CSSParser*>(parser);
679             CSSSelector* end = $1;
680             while (end->m_tagHistory)
681                 end = end->m_tagHistory;
682             end->m_relation = CSSSelector::SubSelector;
683             end->m_tagHistory = p->sinkFloatingSelector($2);
684         }
685     }
686     | specifier_list error {
687         $$ = 0;
688     }
689 ;
690
691 specifier:
692     IDSEL {
693         CSSParser* p = static_cast<CSSParser*>(parser);
694         $$ = p->createFloatingSelector();
695         $$->m_match = CSSSelector::Id;
696         if (!p->strict)
697             $1.lower();
698         $$->m_attr = idAttr;
699         $$->m_value = $1;
700     }
701   | HEX {
702         if ($1.characters[0] >= '0' && $1.characters[0] <= '9') {
703             $$ = 0;
704         } else {
705             CSSParser* p = static_cast<CSSParser*>(parser);
706             $$ = p->createFloatingSelector();
707             $$->m_match = CSSSelector::Id;
708             if (!p->strict)
709                 $1.lower();
710             $$->m_attr = idAttr;
711             $$->m_value = $1;
712         }
713     }
714   | class
715   | attrib
716   | pseudo
717     ;
718
719 class:
720     '.' IDENT {
721         CSSParser* p = static_cast<CSSParser*>(parser);
722         $$ = p->createFloatingSelector();
723         $$->m_match = CSSSelector::Class;
724         if (!p->strict)
725             $2.lower();
726         $$->m_attr = classAttr;
727         $$->m_value = $2;
728     }
729   ;
730
731 attr_name:
732     IDENT maybe_space {
733         ParseString& str = $1;
734         CSSParser* p = static_cast<CSSParser*>(parser);
735         Document* doc = p->document();
736         if (doc && doc->isHTMLDocument())
737             str.lower();
738         $$ = str;
739     }
740     ;
741
742 attrib:
743     '[' maybe_space attr_name ']' {
744         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
745         $$->m_attr = QualifiedName(nullAtom, $3, nullAtom);
746         $$->m_match = CSSSelector::Set;
747     }
748     | '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' {
749         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
750         $$->m_attr = QualifiedName(nullAtom, $3, nullAtom);
751         $$->m_match = (CSSSelector::Match)$4;
752         $$->m_value = $6;
753     }
754     | '[' maybe_space namespace_selector attr_name ']' {
755         AtomicString namespacePrefix = $3;
756         CSSParser* p = static_cast<CSSParser*>(parser);
757         $$ = p->createFloatingSelector();
758         $$->m_attr = QualifiedName(namespacePrefix, $4,
759                                    static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
760         $$->m_match = CSSSelector::Set;
761     }
762     | '[' maybe_space namespace_selector attr_name match maybe_space ident_or_string maybe_space ']' {
763         AtomicString namespacePrefix = $3;
764         CSSParser* p = static_cast<CSSParser*>(parser);
765         $$ = p->createFloatingSelector();
766         $$->m_attr = QualifiedName(namespacePrefix, $4,
767                                    static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
768         $$->m_match = (CSSSelector::Match)$5;
769         $$->m_value = $7;
770     }
771   ;
772
773 match:
774     '=' {
775         $$ = CSSSelector::Exact;
776     }
777     | INCLUDES {
778         $$ = CSSSelector::List;
779     }
780     | DASHMATCH {
781         $$ = CSSSelector::Hyphen;
782     }
783     | BEGINSWITH {
784         $$ = CSSSelector::Begin;
785     }
786     | ENDSWITH {
787         $$ = CSSSelector::End;
788     }
789     | CONTAINS {
790         $$ = CSSSelector::Contain;
791     }
792     ;
793
794 ident_or_string:
795     IDENT
796   | STRING
797     ;
798
799 pseudo:
800     ':' IDENT {
801         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
802         $$->m_match = CSSSelector::PseudoClass;
803         $2.lower();
804         $$->m_value = $2;
805         CSSSelector::PseudoType type = $$->pseudoType();
806         if (type == CSSSelector::PseudoUnknown)
807             $$ = 0;
808         else if (type == CSSSelector::PseudoEmpty ||
809                  type == CSSSelector::PseudoFirstChild ||
810                  type == CSSSelector::PseudoFirstOfType ||
811                  type == CSSSelector::PseudoLastChild ||
812                  type == CSSSelector::PseudoLastOfType ||
813                  type == CSSSelector::PseudoOnlyChild ||
814                  type == CSSSelector::PseudoOnlyOfType) {
815             CSSParser* p = static_cast<CSSParser*>(parser);
816             Document* doc = p->document();
817             if (doc)
818                 doc->setUsesSiblingRules(true);
819         } else if (type == CSSSelector::PseudoFirstLine) {
820             CSSParser* p = static_cast<CSSParser*>(parser);
821             if (Document* doc = p->document())
822                 doc->setUsesFirstLineRules(true);
823         }
824     }
825     | ':' ':' IDENT {
826         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
827         $$->m_match = CSSSelector::PseudoElement;
828         $3.lower();
829         $$->m_value = $3;
830         CSSSelector::PseudoType type = $$->pseudoType();
831         if (type == CSSSelector::PseudoUnknown)
832             $$ = 0;
833         else if (type == CSSSelector::PseudoFirstLine) {
834             CSSParser* p = static_cast<CSSParser*>(parser);
835             if (Document* doc = p->document())
836                 doc->setUsesFirstLineRules(true);
837         }
838     }
839     // used by :nth-*(ax+b)
840     | ':' FUNCTION NTH ')' {
841         CSSParser *p = static_cast<CSSParser*>(parser);
842         $$ = p->createFloatingSelector();
843         $$->m_match = CSSSelector::PseudoClass;
844         $$->m_argument = $3;
845         $$->m_value = $2;
846         CSSSelector::PseudoType type = $$->pseudoType();
847         if (type == CSSSelector::PseudoUnknown)
848             $$ = 0;
849         else if (type == CSSSelector::PseudoNthChild ||
850                  type == CSSSelector::PseudoNthOfType ||
851                  type == CSSSelector::PseudoNthLastChild ||
852                  type == CSSSelector::PseudoNthLastOfType) {
853             if (p->document())
854                 p->document()->setUsesSiblingRules(true);
855         }
856     }
857     // used by :nth-*
858     | ':' FUNCTION INTEGER ')' {
859         CSSParser *p = static_cast<CSSParser*>(parser);
860         $$ = p->createFloatingSelector();
861         $$->m_match = CSSSelector::PseudoClass;
862         $$->m_argument = String::number($3);
863         $$->m_value = $2;
864         CSSSelector::PseudoType type = $$->pseudoType();
865         if (type == CSSSelector::PseudoUnknown)
866             $$ = 0;
867         else if (type == CSSSelector::PseudoNthChild ||
868                  type == CSSSelector::PseudoNthOfType ||
869                  type == CSSSelector::PseudoNthLastChild ||
870                  type == CSSSelector::PseudoNthLastOfType) {
871             if (p->document())
872                 p->document()->setUsesSiblingRules(true);
873         }
874     }
875     // used by :nth-*(odd/even) and :lang
876     | ':' FUNCTION IDENT ')' {
877         CSSParser *p = static_cast<CSSParser*>(parser);
878         $$ = p->createFloatingSelector();
879         $$->m_match = CSSSelector::PseudoClass;
880         $$->m_argument = $3;
881         $2.lower();
882         $$->m_value = $2;
883         CSSSelector::PseudoType type = $$->pseudoType();
884         if (type == CSSSelector::PseudoUnknown)
885             $$ = 0;
886         else if (type == CSSSelector::PseudoNthChild ||
887                  type == CSSSelector::PseudoNthOfType ||
888                  type == CSSSelector::PseudoNthLastChild ||
889                  type == CSSSelector::PseudoNthLastOfType) {
890             if (p->document())
891                 p->document()->setUsesSiblingRules(true);
892         }
893     }
894     // used by :not
895     | ':' NOTFUNCTION maybe_space simple_selector maybe_space ')' {
896         if (!$4)
897             $$ = 0;
898         else {
899             CSSParser* p = static_cast<CSSParser*>(parser);
900             $$ = p->createFloatingSelector();
901             $$->m_match = CSSSelector::PseudoClass;
902             $$->m_simpleSelector = p->sinkFloatingSelector($4);
903             $2.lower();
904             $$->m_value = $2;
905         }
906     }
907   ;
908
909 declaration_list:
910     declaration {
911         $$ = $1;
912     }
913     | decl_list declaration {
914         $$ = $1;
915         if ( $2 )
916             $$ = $2;
917     }
918     | decl_list {
919         $$ = $1;
920     }
921     | error invalid_block_list error {
922         $$ = false;
923     }
924     | error {
925         $$ = false;
926     }
927     | decl_list error {
928         $$ = $1;
929     }
930     ;
931
932 decl_list:
933     declaration ';' maybe_space {
934         $$ = $1;
935     }
936     | declaration invalid_block_list ';' maybe_space {
937         $$ = false;
938     }
939     | error ';' maybe_space {
940         $$ = false;
941     }
942     | error invalid_block_list error ';' maybe_space {
943         $$ = false;
944     }
945     | decl_list declaration ';' maybe_space {
946         $$ = $1;
947         if ($2)
948             $$ = $2;
949     }
950     | decl_list error ';' maybe_space {
951         $$ = $1;
952     }
953     | decl_list error invalid_block_list error ';' maybe_space {
954         $$ = $1;
955     }
956     ;
957
958 declaration:
959     property ':' maybe_space expr prio {
960         $$ = false;
961         CSSParser* p = static_cast<CSSParser*>(parser);
962         if ($1 && $4) {
963             p->valueList = p->sinkFloatingValueList($4);
964             int oldParsedProperties = p->numParsedProperties;
965             $$ = p->parseValue($1, $5);
966             if (!$$)
967                 p->rollbackLastProperties(p->numParsedProperties - oldParsedProperties);
968             delete p->valueList;
969             p->valueList = 0;
970         }
971     }
972     |
973     property error {
974         $$ = false;
975     }
976     |
977     property ':' maybe_space error expr prio {
978         /* The default movable type template has letter-spacing: .none;  Handle this by looking for
979         error tokens at the start of an expr, recover the expr and then treat as an error, cleaning
980         up and deleting the shifted expr.  */
981         $$ = false;
982     }
983     |
984     IMPORTANT_SYM maybe_space {
985         /* Handle this case: div { text-align: center; !important } Just reduce away the stray !important. */
986         $$ = false;
987     }
988     |
989     property ':' maybe_space {
990         /* div { font-family: } Just reduce away this property with no value. */
991         $$ = false;
992     }
993     |
994     property ':' maybe_space error {
995         /* if we come across rules with invalid values like this case: p { weight: *; }, just discard the rule */
996         $$ = false;
997     }
998   ;
999
1000 property:
1001     IDENT maybe_space {
1002         $$ = cssPropertyID($1);
1003     }
1004   ;
1005
1006 prio:
1007     IMPORTANT_SYM maybe_space { $$ = true; }
1008     | /* empty */ { $$ = false; }
1009   ;
1010
1011 expr:
1012     term {
1013         CSSParser* p = static_cast<CSSParser*>(parser);
1014         $$ = p->createFloatingValueList();
1015         $$->addValue(p->sinkFloatingValue($1));
1016     }
1017     | expr operator term {
1018         CSSParser* p = static_cast<CSSParser*>(parser);
1019         $$ = $1;
1020         if ($$) {
1021             if ($2) {
1022                 Value v;
1023                 v.id = 0;
1024                 v.unit = Value::Operator;
1025                 v.iValue = $2;
1026                 $$->addValue(v);
1027             }
1028             $$->addValue(p->sinkFloatingValue($3));
1029         }
1030     }
1031     | expr error {
1032         $$ = 0;
1033     }
1034   ;
1035
1036 operator:
1037     '/' maybe_space {
1038         $$ = '/';
1039     }
1040   | ',' maybe_space {
1041         $$ = ',';
1042     }
1043   | /* empty */ {
1044         $$ = 0;
1045   }
1046   ;
1047
1048 term:
1049   unary_term { $$ = $1; }
1050   | unary_operator unary_term { $$ = $2; $$.fValue *= $1; }
1051   | STRING maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_STRING; }
1052   | IDENT maybe_space {
1053       $$.id = cssValueKeywordID($1);
1054       $$.unit = CSSPrimitiveValue::CSS_IDENT;
1055       $$.string = $1;
1056   }
1057   /* We might need to actually parse the number from a dimension, but we can't just put something that uses $$.string into unary_term. */
1058   | DIMEN maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
1059   | unary_operator DIMEN maybe_space { $$.id = 0; $$.string = $2; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
1060   | URI maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_URI; }
1061   | UNICODERANGE maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_UNICODE_RANGE }
1062   | hexcolor { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; }
1063   | '#' maybe_space { $$.id = 0; $$.string = ParseString(); $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; } /* Handle error case: "color: #;" */
1064   /* FIXME: according to the specs a function can have a unary_operator in front. I know no case where this makes sense */
1065   | function {
1066       $$ = $1;
1067   }
1068   | '%' maybe_space {} /* Handle width: %; */
1069   ;
1070
1071 unary_term:
1072   INTEGER maybe_space { $$.id = 0; $$.isInt = true; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
1073   | FLOATTOKEN maybe_space { $$.id = 0; $$.isInt = false; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
1074   | PERCENTAGE maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PERCENTAGE; }
1075   | PXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PX; }
1076   | CMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CM; }
1077   | MMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MM; }
1078   | INS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_IN; }
1079   | PTS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PT; }
1080   | PCS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PC; }
1081   | DEGS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DEG; }
1082   | RADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_RAD; }
1083   | GRADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_GRAD; }
1084   | MSECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MS; }
1085   | SECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_S; }
1086   | HERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_HZ; }
1087   | KHERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_KHZ; }
1088   | EMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EMS; }
1089   | QEMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = Value::Q_EMS; }
1090   | EXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EXS; }
1091     ;
1092
1093
1094 function:
1095     FUNCTION maybe_space expr ')' maybe_space {
1096         CSSParser* p = static_cast<CSSParser*>(parser);
1097         Function* f = p->createFloatingFunction();
1098         f->name = $1;
1099         f->args = p->sinkFloatingValueList($3);
1100         $$.id = 0;
1101         $$.unit = Value::Function;
1102         $$.function = f;
1103     } |
1104     FUNCTION maybe_space error {
1105         CSSParser* p = static_cast<CSSParser*>(parser);
1106         Function* f = p->createFloatingFunction();
1107         f->name = $1;
1108         f->args = 0;
1109         $$.id = 0;
1110         $$.unit = Value::Function;
1111         $$.function = f;
1112   }
1113   ;
1114 /*
1115  * There is a constraint on the color that it must
1116  * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
1117  * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
1118  */
1119 hexcolor:
1120   HEX maybe_space { $$ = $1; }
1121   | IDSEL maybe_space { $$ = $1; }
1122   ;
1123
1124
1125 /* error handling rules */
1126
1127 invalid_at:
1128     '@' error invalid_block {
1129         $$ = 0;
1130     }
1131   | '@' error ';' {
1132         $$ = 0;
1133     }
1134     ;
1135
1136 invalid_import:
1137     import {
1138         $$ = 0;
1139     }
1140     ;
1141
1142 invalid_rule:
1143     error invalid_block {
1144         $$ = 0;
1145     }
1146 /*
1147   Seems like the two rules below are trying too much and violating
1148   http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html
1149
1150   | error ';' {
1151         $$ = 0;
1152     }
1153   | error '}' {
1154         $$ = 0;
1155     }
1156 */
1157     ;
1158
1159 invalid_block:
1160     '{' error invalid_block_list error '}'
1161   | '{' error '}'
1162     ;
1163
1164 invalid_block_list:
1165     invalid_block
1166   | invalid_block_list error invalid_block
1167 ;
1168
1169 %%