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