2006-05-13 Rob Buis <buis@kde.org>
[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> FLOAT
202 %token <val> INTEGER
203
204 %token <string> URI
205 %token <string> FUNCTION
206
207 %token <string> UNICODERANGE
208
209 %type <relation> combinator
210
211 %type <rule> ruleset
212 %type <rule> media
213 %type <rule> import
214 %type <rule> page
215 %type <rule> font_face
216 %type <rule> invalid_rule
217 %type <rule> invalid_at
218 %type <rule> invalid_import
219 %type <rule> rule
220
221 %type <string> maybe_ns_prefix
222
223 %type <string> namespace_selector
224
225 %type <string> string_or_uri
226 %type <string> ident_or_string
227 %type <string> medium
228 %type <string> hexcolor
229
230 %type <mediaList> media_list
231 %type <mediaList> maybe_media_list
232
233 %type <ruleList> ruleset_list
234
235 %type <prop_id> property
236
237 %type <selector> specifier
238 %type <selector> specifier_list
239 %type <selector> simple_selector
240 %type <selector> selector
241 %type <selector> selector_list
242 %type <selector> class
243 %type <selector> attrib
244 %type <selector> pseudo
245
246 %type <ok> declaration_list
247 %type <ok> decl_list
248 %type <ok> declaration
249
250 %type <b> prio
251
252 %type <i> match
253 %type <i> unary_operator
254 %type <tok> operator
255
256 %type <valueList> expr
257 %type <value> term
258 %type <value> unary_term
259 %type <value> function
260
261 %type <string> element_name
262 %type <string> attr_name
263
264 %%
265
266 stylesheet:
267     maybe_charset maybe_sgml import_list namespace_list rule_list
268   | webkit_rule maybe_space
269   | webkit_decls maybe_space
270   | webkit_value maybe_space
271   ;
272
273 webkit_rule:
274     WEBKIT_RULE_SYM '{' maybe_space ruleset maybe_space '}' {
275         static_cast<CSSParser*>(parser)->rule = $4;
276     }
277 ;
278
279 webkit_decls:
280     WEBKIT_DECLS_SYM '{' maybe_space declaration_list '}' {
281         /* can be empty */
282     }
283 ;
284
285 webkit_value:
286     WEBKIT_VALUE_SYM '{' maybe_space expr '}' {
287         CSSParser *p = static_cast<CSSParser *>(parser);
288         if ($4) {
289             p->valueList = p->sinkFloatingValueList($4);
290             int oldParsedProperties = p->numParsedProperties;
291             if (!p->parseValue(p->id, p->important))
292                 p->rollbackLastProperties(p->numParsedProperties - oldParsedProperties);
293             delete p->valueList;
294             p->valueList = 0;
295         }
296     }
297 ;
298
299 maybe_space:
300     /* empty */ %prec UNIMPORTANT_TOK
301   | maybe_space WHITESPACE
302   ;
303
304 maybe_sgml:
305     /* empty */
306   | maybe_sgml SGML_CD
307   | maybe_sgml WHITESPACE
308   ;
309
310 maybe_charset:
311    /* empty */
312   | CHARSET_SYM maybe_space STRING maybe_space ';'
313   | CHARSET_SYM error invalid_block
314   | CHARSET_SYM error ';'
315  ;
316
317 import_list:
318  /* empty */
319  | import_list import maybe_sgml {
320      CSSParser* p = static_cast<CSSParser*>(parser);
321      if ($2 && p->styleElement && p->styleElement->isCSSStyleSheet())
322          p->styleElement->append($2);
323  }
324  ;
325
326 namespace_list:
327 /* empty */
328 | namespace_list namespace maybe_sgml
329 ;
330
331 rule_list:
332    /* empty */
333  | rule_list rule maybe_sgml {
334      CSSParser* p = static_cast<CSSParser*>(parser);
335      if ($2 && p->styleElement && p->styleElement->isCSSStyleSheet())
336          p->styleElement->append($2);
337  }
338  ;
339
340 rule:
341     ruleset
342   | media
343   | page
344   | font_face
345   | invalid_rule
346   | invalid_at
347   | invalid_import
348     ;
349
350 import:
351     IMPORT_SYM maybe_space string_or_uri maybe_space maybe_media_list ';' {
352         $$ = static_cast<CSSParser *>(parser)->createImportRule($3, $5);
353     }
354   | IMPORT_SYM error invalid_block {
355         $$ = 0;
356     }
357   | IMPORT_SYM error ';' {
358         $$ = 0;
359     }
360   ;
361
362 namespace:
363 NAMESPACE_SYM maybe_space maybe_ns_prefix string_or_uri maybe_space ';' {
364     CSSParser *p = static_cast<CSSParser *>(parser);
365     if (p->styleElement && p->styleElement->isCSSStyleSheet())
366         static_cast<CSSStyleSheet*>(p->styleElement)->addNamespace(p, atomicString($3), atomicString($4));
367 }
368 | NAMESPACE_SYM error invalid_block
369 | NAMESPACE_SYM error ';'
370 ;
371
372 maybe_ns_prefix:
373 /* empty */ { $$.characters = 0; }
374 | IDENT WHITESPACE { $$ = $1; }
375 ;
376
377 string_or_uri:
378 STRING
379 | URI
380 ;
381
382 maybe_media_list:
383      /* empty */ {
384         $$ = static_cast<CSSParser*>(parser)->createMediaList();
385      }
386      | media_list
387 ;
388
389
390 media_list:
391     medium {
392         $$ = static_cast<CSSParser*>(parser)->createMediaList();
393         $$->appendMedium(domString($1).lower());
394     }
395     | media_list ',' maybe_space medium {
396         $$ = $1;
397         if ($$)
398             $$->appendMedium(domString($4).lower());
399     }
400     | media_list error {
401         $$ = 0;
402     }
403     ;
404
405 media:
406     MEDIA_SYM maybe_space media_list '{' maybe_space ruleset_list '}' {
407         $$ = static_cast<CSSParser*>(parser)->createMediaRule($3, $6);
408     }
409     | MEDIA_SYM maybe_space '{' maybe_space ruleset_list '}' {
410         $$ = static_cast<CSSParser*>(parser)->createMediaRule(0, $5);
411     }
412     ;
413
414 ruleset_list:
415     /* empty */ { $$ = 0; }
416     | ruleset_list ruleset maybe_space {
417         $$ = $1;
418         if ($2) {
419             if (!$$)
420                 $$ = static_cast<CSSParser*>(parser)->createRuleList();
421             $$->append($2);
422         }
423     }
424     ;
425
426 medium:
427   IDENT maybe_space {
428       $$ = $1;
429   }
430   ;
431
432 /*
433 page:
434     PAGE_SYM maybe_space IDENT? pseudo_page? maybe_space
435     '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space
436   ;
437
438 pseudo_page
439   : ':' IDENT
440   ;
441
442 font_face
443   : FONT_FACE_SYM maybe_space
444     '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space
445   ;
446 */
447
448 page:
449     PAGE_SYM error invalid_block {
450       $$ = 0;
451     }
452   | PAGE_SYM error ';' {
453       $$ = 0;
454     }
455     ;
456
457 font_face:
458     FONT_FACE_SYM error invalid_block {
459       $$ = 0;
460     }
461   | FONT_FACE_SYM error ';' {
462       $$ = 0;
463     }
464 ;
465
466 combinator:
467     '+' maybe_space { $$ = CSSSelector::DirectAdjacent; }
468   | '~' maybe_space { $$ = CSSSelector::IndirectAdjacent; }
469   | '>' maybe_space { $$ = CSSSelector::Child; }
470   | /* empty */ { $$ = CSSSelector::Descendant; }
471   ;
472
473 unary_operator:
474     '-' { $$ = -1; }
475   | '+' { $$ = 1; }
476   ;
477
478 ruleset:
479     selector_list '{' maybe_space declaration_list '}' {
480         $$ = static_cast<CSSParser*>(parser)->createStyleRule($1);
481     }
482   ;
483
484 selector_list:
485     selector %prec UNIMPORTANT_TOK {
486         $$ = $1;
487     }
488     | selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK {
489         if ($1 && $4) {
490             CSSParser* p = static_cast<CSSParser*>(parser);
491             $$ = $1;
492             $$->append(p->sinkFloatingSelector($4));
493         } else {
494             $$ = 0;
495         }
496     }
497   | selector_list error {
498         $$ = 0;
499     }
500    ;
501
502 selector:
503     simple_selector {
504         $$ = $1;
505     }
506     | selector combinator simple_selector {
507         $$ = $3;
508         if (!$1) {
509             $$ = 0;
510         }
511         else if ($$) {
512             CSSParser* p = static_cast<CSSParser*>(parser);
513             CSSSelector* end = $$;
514             while (end->tagHistory)
515                 end = end->tagHistory;
516             end->m_relation = $2;
517             end->tagHistory = p->sinkFloatingSelector($1);
518             if ($2 == CSSSelector::Descendant || $2 == CSSSelector::Child) {
519                 if (WebCore::Document* doc = p->document())
520                     doc->setUsesDescendantRules(true);
521             } else if ($2 == CSSSelector::DirectAdjacent || $2 == CSSSelector::IndirectAdjacent) {
522                 if (WebCore::Document* doc = p->document())
523                     doc->setUsesSiblingRules(true);
524             }
525         }
526     }
527     | selector error {
528         $$ = 0;
529     }
530     ;
531
532 namespace_selector:
533     /* empty */ '|' { $$.characters = 0; $$.length = 0; }
534     | '*' '|' { static UChar star = '*'; $$.characters = &star; $$.length = 1; }
535     | IDENT '|' { $$ = $1; }
536 ;
537
538 simple_selector:
539     element_name maybe_space {
540         CSSParser *p = static_cast<CSSParser *>(parser);
541         $$ = p->createFloatingSelector();
542         $$->tag = QualifiedName(nullAtom, atomicString($1), p->defaultNamespace);
543     }
544     | element_name specifier_list maybe_space {
545         $$ = $2;
546         if ($$) {
547             CSSParser *p = static_cast<CSSParser *>(parser);
548             $$->tag = QualifiedName(nullAtom, atomicString($1), p->defaultNamespace);
549         }
550     }
551     | specifier_list maybe_space {
552         $$ = $1;
553         CSSParser *p = static_cast<CSSParser *>(parser);
554         if ($$ && p->defaultNamespace != starAtom)
555             $$->tag = QualifiedName(nullAtom, starAtom, p->defaultNamespace);
556     }
557     | namespace_selector element_name maybe_space {
558         AtomicString namespacePrefix = atomicString($1);
559         CSSParser *p = static_cast<CSSParser *>(parser);
560         $$ = p->createFloatingSelector();
561         if (p->styleElement && p->styleElement->isCSSStyleSheet())
562             $$->tag = QualifiedName(namespacePrefix,
563                                     atomicString($2),
564                                     static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
565         else // FIXME: Shouldn't this case be an error?
566             $$->tag = QualifiedName(nullAtom, atomicString($2), p->defaultNamespace);
567     }
568     | namespace_selector element_name specifier_list maybe_space {
569         $$ = $3;
570         if ($$) {
571             AtomicString namespacePrefix = atomicString($1);
572             CSSParser *p = static_cast<CSSParser *>(parser);
573             if (p->styleElement && p->styleElement->isCSSStyleSheet())
574                 $$->tag = QualifiedName(namespacePrefix,
575                                         atomicString($2),
576                                         static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
577             else // FIXME: Shouldn't this case be an error?
578                 $$->tag = QualifiedName(nullAtom, atomicString($2), p->defaultNamespace);
579         }
580     }
581     | namespace_selector specifier_list maybe_space {
582         $$ = $2;
583         if ($$) {
584             AtomicString namespacePrefix = atomicString($1);
585             CSSParser *p = static_cast<CSSParser *>(parser);
586             if (p->styleElement && p->styleElement->isCSSStyleSheet())
587                 $$->tag = QualifiedName(namespacePrefix,
588                                         starAtom,
589                                         static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
590         }
591     }
592   ;
593
594 element_name:
595     IDENT {
596         ParseString& str = $1;
597         CSSParser *p = static_cast<CSSParser *>(parser);
598         WebCore::Document *doc = p->document();
599         if (doc && doc->isHTMLDocument())
600             str.lower();
601         $$ = str;
602     }
603     | '*' {
604         static UChar star = '*';
605         $$.characters = &star;
606         $$.length = 1;
607     }
608   ;
609
610 specifier_list:
611     specifier {
612         $$ = $1;
613     }
614     | specifier_list specifier {
615         $$ = $1;
616         if ($$) {
617             CSSParser* p = static_cast<CSSParser*>(parser);
618             CSSSelector* end = $1;
619             while (end->tagHistory)
620                 end = end->tagHistory;
621             end->m_relation = CSSSelector::SubSelector;
622             end->tagHistory = p->sinkFloatingSelector($2);
623         }
624     }
625     | specifier_list error {
626         $$ = 0;
627     }
628 ;
629
630 specifier:
631     HASH {
632         CSSParser *p = static_cast<CSSParser *>(parser);
633         $$ = p->createFloatingSelector();
634         $$->match = CSSSelector::Id;
635         if (!p->strict)
636             $1.lower();
637         $$->attr = idAttr;
638         $$->value = atomicString($1);
639     }
640   | class
641   | attrib
642   | pseudo
643     ;
644
645 class:
646     '.' IDENT {
647         CSSParser *p = static_cast<CSSParser *>(parser);
648         $$ = p->createFloatingSelector();
649         $$->match = CSSSelector::Class;
650         if (!p->strict)
651             $2.lower();
652         $$->attr = classAttr;
653         $$->value = atomicString($2);
654     }
655   ;
656
657 attr_name:
658     IDENT maybe_space {
659         ParseString& str = $1;
660         CSSParser *p = static_cast<CSSParser *>(parser);
661         WebCore::Document *doc = p->document();
662         if (doc && doc->isHTMLDocument())
663             str.lower();
664         $$ = str;
665     }
666     ;
667
668 attrib:
669     '[' maybe_space attr_name ']' {
670         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
671         $$->attr = QualifiedName(nullAtom, atomicString($3), nullAtom);
672         $$->match = CSSSelector::Set;
673     }
674     | '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' {
675         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
676         $$->attr = QualifiedName(nullAtom, atomicString($3), nullAtom);
677         $$->match = (CSSSelector::Match)$4;
678         $$->value = atomicString($6);
679     }
680     | '[' maybe_space namespace_selector attr_name ']' {
681         AtomicString namespacePrefix = atomicString($3);
682         CSSParser *p = static_cast<CSSParser *>(parser);
683         $$ = p->createFloatingSelector();
684         $$->attr = QualifiedName(namespacePrefix,
685                                  atomicString($4),
686                                  static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
687         $$->match = CSSSelector::Set;
688     }
689     | '[' maybe_space namespace_selector attr_name match maybe_space ident_or_string maybe_space ']' {
690         AtomicString namespacePrefix = atomicString($3);
691         CSSParser *p = static_cast<CSSParser *>(parser);
692         $$ = p->createFloatingSelector();
693         $$->attr = QualifiedName(namespacePrefix,
694                                  atomicString($4),
695                                  static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
696         $$->match = (CSSSelector::Match)$5;
697         $$->value = atomicString($7);
698     }
699   ;
700
701 match:
702     '=' {
703         $$ = CSSSelector::Exact;
704     }
705     | INCLUDES {
706         $$ = CSSSelector::List;
707     }
708     | DASHMATCH {
709         $$ = CSSSelector::Hyphen;
710     }
711     | BEGINSWITH {
712         $$ = CSSSelector::Begin;
713     }
714     | ENDSWITH {
715         $$ = CSSSelector::End;
716     }
717     | CONTAINS {
718         $$ = CSSSelector::Contain;
719     }
720     ;
721
722 ident_or_string:
723     IDENT
724   | STRING
725     ;
726
727 pseudo:
728     ':' IDENT {
729         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
730         $$->match = CSSSelector::PseudoClass;
731         $2.lower();
732         $$->value = atomicString($2);
733         if ($$->value == "empty" || $$->value == "only-child" ||
734             $$->value == "first-child" || $$->value == "last-child") {
735             CSSParser *p = static_cast<CSSParser *>(parser);
736             WebCore::Document *doc = p->document();
737             if (doc)
738                 doc->setUsesSiblingRules(true);
739         }
740     }
741     | ':' ':' IDENT {
742         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
743         $$->match = CSSSelector::PseudoElement;
744         $3.lower();
745         $$->value = atomicString($3);
746     }
747     | ':' FUNCTION maybe_space simple_selector maybe_space ')' {
748         CSSParser* p = static_cast<CSSParser*>(parser);
749         $$ = p->createFloatingSelector();
750         $$->match = CSSSelector::PseudoClass;
751         $$->simpleSelector = p->sinkFloatingSelector($4);
752         $2.lower();
753         $$->value = atomicString($2);
754     }
755   ;
756
757 declaration_list:
758     declaration {
759         $$ = $1;
760     }
761     | decl_list declaration {
762         $$ = $1;
763         if ( $2 )
764             $$ = $2;
765     }
766     | decl_list {
767         $$ = $1;
768     }
769     | error invalid_block_list error {
770         $$ = false;
771     }
772     | error {
773         $$ = false;
774     }
775     | decl_list error {
776         $$ = $1;
777     }
778     ;
779
780 decl_list:
781     declaration ';' maybe_space {
782         $$ = $1;
783     }
784     | declaration invalid_block_list ';' maybe_space {
785         $$ = false;
786     }
787     | error ';' maybe_space {
788         $$ = false;
789     }
790     | error invalid_block_list error ';' maybe_space {
791         $$ = false;
792     }
793     | decl_list declaration ';' maybe_space {
794         $$ = $1;
795         if ( $2 )
796             $$ = $2;
797     }
798     | decl_list error ';' maybe_space {
799         $$ = $1;
800     }
801     | decl_list error invalid_block_list error ';' maybe_space {
802         $$ = $1;
803     }
804     ;
805
806 declaration:
807     property ':' maybe_space expr prio {
808         $$ = false;
809         CSSParser *p = static_cast<CSSParser *>(parser);
810         if ($1 && $4) {
811             p->valueList = p->sinkFloatingValueList($4);
812             int oldParsedProperties = p->numParsedProperties;
813             $$ = p->parseValue($1, $5);
814             if (!$$)
815                 p->rollbackLastProperties(p->numParsedProperties - oldParsedProperties);
816             delete p->valueList;
817             p->valueList = 0;
818         }
819     }
820     |
821     property error {
822         $$ = false;
823     }
824     |
825     property ':' maybe_space error expr prio {
826         /* The default movable type template has letter-spacing: .none;  Handle this by looking for
827         error tokens at the start of an expr, recover the expr and then treat as an error, cleaning
828         up and deleting the shifted expr.  */
829         $$ = false;
830     }
831     |
832     IMPORTANT_SYM maybe_space {
833         /* Handle this case: div { text-align: center; !important } Just reduce away the stray !important. */
834         $$ = false;
835     }
836     |
837     property ':' maybe_space {
838         /* div { font-family: } Just reduce away this property with no value. */
839         $$ = false;
840     }
841   ;
842
843 property:
844     IDENT maybe_space {
845         $1.lower();
846         DeprecatedString str = deprecatedString($1);
847         const char* s = str.ascii();
848         int l = str.length();
849         $$ = getPropertyID(s, l);
850 #if SVG_SUPPORT
851         if ($$ == 0)
852             $$ = SVG::getSVGCSSPropertyID(s, l);
853 #endif
854     }
855   ;
856
857 prio:
858     IMPORTANT_SYM maybe_space { $$ = true; }
859     | /* empty */ { $$ = false; }
860   ;
861
862 expr:
863     term {
864         CSSParser* p = static_cast<CSSParser*>(parser);
865         $$ = p->createFloatingValueList();
866         $$->addValue(p->sinkFloatingValue($1));
867     }
868     | expr operator term {
869         CSSParser* p = static_cast<CSSParser*>(parser);
870         $$ = $1;
871         if ($$) {
872             if ($2) {
873                 Value v;
874                 v.id = 0;
875                 v.unit = Value::Operator;
876                 v.iValue = $2;
877                 $$->addValue(v);
878             }
879             $$->addValue(p->sinkFloatingValue($3));
880         }
881     }
882     | expr error {
883         $$ = 0;
884     }
885   ;
886
887 operator:
888     '/' maybe_space {
889         $$ = '/';
890     }
891   | ',' maybe_space {
892         $$ = ',';
893     }
894   | /* empty */ {
895         $$ = 0;
896   }
897   ;
898
899 term:
900   unary_term { $$ = $1; }
901   | unary_operator unary_term { $$ = $2; $$.fValue *= $1; }
902   | STRING maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_STRING; }
903   | IDENT maybe_space {
904       DeprecatedString str = deprecatedString($1);
905       $$.id = getValueID(str.lower().latin1(), str.length());
906 #if SVG_SUPPORT
907       if ($$.id == 0)
908           $$.id = SVG::getSVGCSSValueID(str.lower().latin1(), str.length());
909 #endif
910       $$.unit = CSSPrimitiveValue::CSS_IDENT;
911       $$.string = $1;
912   }
913   /* We might need to actually parse the number from a dimension, but we can't just put something that uses $$.string into unary_term. */
914   | DIMEN maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
915   | unary_operator DIMEN maybe_space { $$.id = 0; $$.string = $2; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
916   | URI maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_URI; }
917   | UNICODERANGE maybe_space { $$.id = 0; $$.iValue = 0; $$.unit = CSSPrimitiveValue::CSS_UNKNOWN;/* ### */ }
918   | hexcolor { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; }
919   | '#' maybe_space { $$.id = 0; $$.string = ParseString(); $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; } /* Handle error case: "color: #;" */
920 /* ### according to the specs a function can have a unary_operator in front. I know no case where this makes sense */
921   | function {
922       $$ = $1;
923   }
924   ;
925
926 unary_term:
927   INTEGER maybe_space { $$.id = 0; $$.isInt = true; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
928   | FLOAT maybe_space { $$.id = 0; $$.isInt = false; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
929   | PERCENTAGE maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PERCENTAGE; }
930   | PXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PX; }
931   | CMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CM; }
932   | MMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MM; }
933   | INS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_IN; }
934   | PTS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PT; }
935   | PCS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PC; }
936   | DEGS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DEG; }
937   | RADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_RAD; }
938   | GRADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_GRAD; }
939   | MSECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MS; }
940   | SECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_S; }
941   | HERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_HZ; }
942   | KHERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_KHZ; }
943   | EMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EMS; }
944   | QEMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = Value::Q_EMS; }
945   | EXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EXS; }
946     ;
947
948
949 function:
950     FUNCTION maybe_space expr ')' maybe_space {
951         CSSParser* p = static_cast<CSSParser*>(parser);
952         Function* f = p->createFloatingFunction();
953         f->name = $1;
954         f->args = p->sinkFloatingValueList($3);
955         $$.id = 0;
956         $$.unit = Value::Function;
957         $$.function = f;
958     } |
959     FUNCTION maybe_space error {
960         CSSParser* p = static_cast<CSSParser*>(parser);
961         Function* f = p->createFloatingFunction();
962         f->name = $1;
963         f->args = 0;
964         $$.id = 0;
965         $$.unit = Value::Function;
966         $$.function = f;
967   }
968   ;
969 /*
970  * There is a constraint on the color that it must
971  * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
972  * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
973  */
974 hexcolor:
975   HASH maybe_space { $$ = $1; }
976   ;
977
978
979 /* error handling rules */
980
981 invalid_at:
982     '@' error invalid_block {
983         $$ = 0;
984     }
985   | '@' error ';' {
986         $$ = 0;
987     }
988     ;
989
990 invalid_import:
991     import {
992         $$ = 0;
993     }
994     ;
995
996 invalid_rule:
997     error invalid_block {
998         $$ = 0;
999     }
1000 /*
1001   Seems like the two rules below are trying too much and violating
1002   http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html
1003
1004   | error ';' {
1005         $$ = 0;
1006     }
1007   | error '}' {
1008         $$ = 0;
1009     }
1010 */
1011     ;
1012
1013 invalid_block:
1014     '{' error invalid_block_list error '}'
1015   | '{' error '}'
1016     ;
1017
1018 invalid_block_list:
1019     invalid_block
1020   | invalid_block_list error invalid_block
1021 ;
1022
1023 %%