%{ /* * This file is part of the KDE libraries * Copyright (C) 2002-2003 Lars Knoll (knoll@kde.org) * Copyright (c) 2003 Apple Computer * Copyright (C) 2003 Dirk Mueller (mueller@kde.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "cssparser.h" #include #include //#define CSS_DEBUG using namespace DOM; // // The following file defines the function // const struct props *findProp(const char *word, int len) // // with 'props->id' a CSS property in the range from CSS_PROP_MIN to // (and including) CSS_PROP_TOTAL-1 // turn off inlining to void warning with newer gcc #undef __inline #define __inline #include "cssproperties.c" #include "cssvalues.c" #undef __inline int DOM::getPropertyID(const char *tagStr, int len) { const struct props *propsPtr = findProp(tagStr, len); if (!propsPtr) return 0; return propsPtr->id; } static inline int getValueID(const char *tagStr, int len) { const struct css_value *val = findValue(tagStr, len); if (!val) return 0; return val->id; } #define YYDEBUG 0 #undef YYMAXDEPTH #define YYPARSE_PARAM parser %} %pure_parser %union { CSSRuleImpl *rule; CSSSelector *selector; QPtrList *selectorList; bool ok; MediaListImpl *mediaList; CSSMediaRuleImpl *mediaRule; CSSRuleListImpl *ruleList; ParseString string; float val; int prop_id; unsigned int attribute; unsigned int element; unsigned int ns; CSSSelector::Relation relation; CSSSelector::Match match; bool b; char tok; Value value; ValueList *valueList; } %{ static inline int cssyyerror(const char *x ) { #ifdef CSS_DEBUG tqDebug( "%s", x ); #else Q_UNUSED( x ); #endif return 1; } static int cssyylex( YYSTYPE *yylval ) { return CSSParser::current()->lex( yylval ); } #define null 1 %} %destructor { delete $$; $$ = 0; } expr; %destructor { delete $$; $$ = 0; } maybe_media_list media_list; %destructor { delete $$; $$ = 0; } selector_list; %destructor { delete $$; $$ = 0; } ruleset_list; %destructor { delete $$; $$ = 0; } specifier specifier_list simple_selector selector class attrib pseudo; %no-lines %verbose %left UNIMPORTANT_TOK %token S SGML_CD %token INCLUDES %token DASHMATCH %token BEGINSWITH %token ENDSWITH %token CONTAINS %token STRING %right IDENT %token NTH %nonassoc HASH %nonassoc ':' %nonassoc '.' %nonassoc '[' %nonassoc '*' %nonassoc error %left '|' %left IMPORT_SYM %token PAGE_SYM %token MEDIA_SYM %token FONT_FACE_SYM %token CHARSET_SYM %token NAMESPACE_SYM %token TDEHTML_RULE_SYM %token TDEHTML_DECLS_SYM %token TDEHTML_VALUE_SYM %token IMPORTANT_SYM %token QEMS %token EMS %token EXS %token PXS %token CMS %token MMS %token INS %token PTS %token PCS %token DEGS %token RADS %token GRADS %token MSECS %token SECS %token HERZ %token KHERZ %token DIMEN %token PERCENTAGE %token FLOAT %token INTEGER %token URI %token FUNCTION %token NOTFUNCTION %token UNICODERANGE %type combinator %type ruleset %type media %type import %type page %type font_face %type invalid_rule %type invalid_at %type rule %type namespace_selector %type string_or_uri %type ident_or_string %type medium %type hexcolor %type maybe_ns_prefix %type media_list %type maybe_media_list %type ruleset_list %type property %type specifier %type specifier_list %type simple_selector %type selector %type selector_list %type class %type attrib %type pseudo %type declaration_block %type declaration_list %type declaration %type prio %type match %type unary_operator %type operator %type expr %type term %type unary_term %type function %type element_name %type attrib_id %% stylesheet: maybe_charset maybe_sgml import_list namespace_list rule_list | tdehtml_rule maybe_space | tdehtml_decls maybe_space | tdehtml_value maybe_space ; tdehtml_rule: TDEHTML_RULE_SYM '{' maybe_space ruleset maybe_space '}' { CSSParser *p = static_cast(parser); p->rule = $4; } ; tdehtml_decls: TDEHTML_DECLS_SYM declaration_block { /* can be empty */ } ; tdehtml_value: TDEHTML_VALUE_SYM '{' maybe_space expr '}' { CSSParser *p = static_cast(parser); if ( $4 ) { p->valueList = $4; #ifdef CSS_DEBUG kdDebug( 6080 ) << " got property for " << p->id << (p->important?" important":"")<< endl; bool ok = #endif p->parseValue( p->id, p->important ); #ifdef CSS_DEBUG if ( !ok ) kdDebug( 6080 ) << " couldn't parse value!" << endl; #endif } #ifdef CSS_DEBUG else kdDebug( 6080 ) << " no value found!" << endl; #endif delete p->valueList; p->valueList = 0; } ; maybe_space: /* empty */ %prec UNIMPORTANT_TOK | maybe_space S ; maybe_sgml: /* empty */ | maybe_sgml SGML_CD | maybe_sgml S ; maybe_charset: /* empty */ | CHARSET_SYM maybe_space STRING maybe_space ';' { #ifdef CSS_DEBUG kdDebug( 6080 ) << "charset rule: " << qString($3) << endl; #endif } | CHARSET_SYM error invalid_block | CHARSET_SYM error ';' ; import_list: /* empty */ | import_list import maybe_sgml { CSSParser *p = static_cast(parser); if ( $2 && p->styleElement && p->styleElement->isCSSStyleSheet() ) { p->styleElement->append( $2 ); } else { delete $2; } } ; import: IMPORT_SYM maybe_space string_or_uri maybe_space maybe_media_list ';' { #ifdef CSS_DEBUG kdDebug( 6080 ) << "@import: " << qString($3) << endl; #endif CSSParser *p = static_cast(parser); if ( $5 && p->styleElement && p->styleElement->isCSSStyleSheet() ) $$ = new CSSImportRuleImpl( p->styleElement, domString($3), $5 ); else $$ = 0; } | IMPORT_SYM error invalid_block { $$ = 0; } | IMPORT_SYM error ';' { $$ = 0; } ; namespace_list: /* empty */ %prec UNIMPORTANT_TOK | namespace_list namespace maybe_sgml ; namespace: NAMESPACE_SYM maybe_space maybe_ns_prefix string_or_uri maybe_space ';' { #ifdef CSS_DEBUG kdDebug( 6080 ) << "@namespace: " << qString($4) << endl; #endif CSSParser *p = static_cast(parser); if (p->styleElement && p->styleElement->isCSSStyleSheet()) static_cast(p->styleElement)->addNamespace(p, domString($3), domString($4)); } | NAMESPACE_SYM error invalid_block | NAMESPACE_SYM error ';' ; maybe_ns_prefix: /* empty */ { $$.string = 0; } | IDENT S { $$ = $1; } ; rule_list: /* empty */ | rule_list rule maybe_sgml { CSSParser *p = static_cast(parser); if ( $2 && p->styleElement && p->styleElement->isCSSStyleSheet() ) { p->styleElement->append( $2 ); } else { delete $2; } } ; rule: ruleset | media | page | font_face | invalid_rule | invalid_at | import error { delete $1; $$ = 0; } ; string_or_uri: STRING | URI ; maybe_media_list: /* empty */ { $$ = new MediaListImpl(); } | media_list ; media_list: medium { $$ = new MediaListImpl(); $$->appendMedium( domString($1).lower() ); } | media_list ',' maybe_space medium { $$ = $1; if ($$) $$->appendMedium( domString($4).lower() ); } | media_list error { delete $1; $$ = 0; } ; media: MEDIA_SYM maybe_space media_list '{' maybe_space ruleset_list '}' { CSSParser *p = static_cast(parser); if ( $3 && $6 && p->styleElement && p->styleElement->isCSSStyleSheet() ) { $$ = new CSSMediaRuleImpl( static_cast(p->styleElement), $3, $6 ); } else { $$ = 0; delete $3; delete $6; } } ; ruleset_list: /* empty */ { $$ = 0; } | ruleset_list ruleset maybe_space { $$ = $1; if ( $2 ) { if ( !$$ ) $$ = new CSSRuleListImpl(); $$->append( $2 ); } } ; medium: IDENT maybe_space { $$ = $1; } ; /* page: PAGE_SYM maybe_space IDENT? pseudo_page? maybe_space '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space ; pseudo_page : ':' IDENT ; font_face : FONT_FACE_SYM maybe_space '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space ; */ page: PAGE_SYM error invalid_block { $$ = 0; } | PAGE_SYM error ';' { $$ = 0; } ; font_face: FONT_FACE_SYM error invalid_block { $$ = 0; } | FONT_FACE_SYM error ';' { $$ = 0; } ; combinator: '+' maybe_space { $$ = CSSSelector::DirectAdjacent; } | '~' maybe_space { $$ = CSSSelector::IndirectAdjacent; } | '>' maybe_space { $$ = CSSSelector::Child; } | /* empty */ { $$ = CSSSelector::Descendant; } ; unary_operator: '-' { $$ = -1; } | '+' { $$ = 1; } ; ruleset: selector_list declaration_block { #ifdef CSS_DEBUG kdDebug( 6080 ) << "got ruleset" << endl << " selector:" << endl; #endif CSSParser *p = static_cast(parser); if ( $1 && $2 && p->numParsedProperties ) { CSSStyleRuleImpl *rule = new CSSStyleRuleImpl( p->styleElement ); CSSStyleDeclarationImpl *decl = p->createStyleDeclaration( rule ); rule->setSelector( $1 ); rule->setDeclaration(decl); $$ = rule; } else { $$ = 0; delete $1; p->clearProperties(); } } ; selector_list: selector %prec UNIMPORTANT_TOK { if ( $1 ) { $$ = new QPtrList; $$->setAutoDelete( true ); #ifdef CSS_DEBUG kdDebug( 6080 ) << " got simple selector:" << endl; $1->print(); #endif $$->append( $1 ); tdehtml::CSSStyleSelector::precomputeAttributeDependencies(static_cast(parser)->document(), $1); } else { $$ = 0; } } | selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK { if ( $1 && $4 ) { $$ = $1; $$->append( $4 ); tdehtml::CSSStyleSelector::precomputeAttributeDependencies(static_cast(parser)->document(), $4); #ifdef CSS_DEBUG kdDebug( 6080 ) << " got simple selector:" << endl; $4->print(); #endif } else { delete $1; delete $4; $$ = 0; } } | selector_list error { delete $1; $$ = 0; } ; selector: simple_selector { $$ = $1; } | selector combinator simple_selector { if ( !$1 || !$3 ) { delete $1; delete $3; $$ = 0; } else { $$ = $3; CSSSelector *end = $3; while( end->tagHistory ) end = end->tagHistory; end->relation = $2; end->tagHistory = $1; } } | selector error { delete $1; $$ = 0; } ; namespace_selector: /* empty */ '|' { $$.string = 0; $$.length = 0; } | '*' '|' { static unsigned short star = '*'; $$.string = ☆ $$.length = 1; } | IDENT '|' { $$ = $1; } ; simple_selector: element_name maybe_space { $$ = new CSSSelector(); $$->tag = $1; } | element_name specifier_list maybe_space { $$ = $2; if ( $$ ) $$->tag = $1; } | specifier_list maybe_space { $$ = $1; if ( $$ ) $$->tag = makeId(static_cast(parser)->defaultNamespace(), anyLocalName); } | namespace_selector element_name maybe_space { $$ = new CSSSelector(); $$->tag = $2; CSSParser *p = static_cast(parser); if (p->styleElement && p->styleElement->isCSSStyleSheet()) static_cast(p->styleElement)->determineNamespace($$->tag, domString($1)); } | namespace_selector element_name specifier_list maybe_space { $$ = $3; if ($$) { $$->tag = $2; CSSParser *p = static_cast(parser); if (p->styleElement && p->styleElement->isCSSStyleSheet()) static_cast(p->styleElement)->determineNamespace($$->tag, domString($1)); } } | namespace_selector specifier_list maybe_space { $$ = $2; if ($$) { $$->tag = makeId(anyNamespace, anyLocalName); CSSParser *p = static_cast(parser); if (p->styleElement && p->styleElement->isCSSStyleSheet()) static_cast(p->styleElement)->determineNamespace($$->tag, domString($1)); } } ; element_name: IDENT { CSSParser *p = static_cast(parser); DOM::DocumentImpl *doc = p->document(); QString tag = qString($1); if ( doc ) { if (doc->isHTMLDocument()) tag = tag.lower(); const DOMString dtag(tag); $$ = makeId(p->defaultNamespace(), doc->getId(NodeImpl::ElementId, dtag.implementation(), false, true)); } else { $$ = makeId(p->defaultNamespace(), tdehtml::getTagID(tag.lower().ascii(), tag.length())); // this case should never happen - only when loading // the default stylesheet - which must not contain unknown tags // assert($$ != 0); } } | '*' { $$ = makeId(static_cast(parser)->defaultNamespace(), anyLocalName); } ; specifier_list: specifier { $$ = $1; $$->nonCSSHint = static_cast(parser)->nonCSSHint; } | specifier_list specifier { $$ = $1; if ( $$ ) { CSSSelector *end = $1; while( end->tagHistory ) end = end->tagHistory; end->relation = CSSSelector::SubSelector; end->tagHistory = $2; } } | specifier_list error { delete $1; $$ = 0; } ; specifier: HASH { $$ = new CSSSelector(); $$->match = CSSSelector::Id; $$->attr = ATTR_ID; $$->value = domString($1); } | class | attrib | pseudo ; class: '.' IDENT { $$ = new CSSSelector(); $$->match = CSSSelector::Class; $$->attr = ATTR_CLASS; $$->value = domString($2); } ; attrib_id: IDENT maybe_space { CSSParser *p = static_cast(parser); DOM::DocumentImpl *doc = p->document(); QString attr = qString($1); if ( doc ) { if (doc->isHTMLDocument()) attr = attr.lower(); const DOMString dattr(attr); #ifdef APPLE_CHANGES $$ = doc->attrId(0, dattr.implementation(), false); #else $$ = doc->getId(NodeImpl::AttributeId, dattr.implementation(), false, true); #endif } else { $$ = tdehtml::getAttrID(attr.lower().ascii(), attr.length()); // this case should never happen - only when loading // the default stylesheet - which must not contain unknown attributes assert($$ != 0); } } ; attrib: '[' maybe_space attrib_id ']' { $$ = new CSSSelector(); $$->attr = $3; $$->match = CSSSelector::Set; } | '[' maybe_space attrib_id match maybe_space ident_or_string maybe_space ']' { $$ = new CSSSelector(); $$->attr = $3; $$->match = $4; $$->value = domString($6); } | '[' maybe_space namespace_selector attrib_id ']' { $$ = new CSSSelector(); $$->attr = $4; $$->match = CSSSelector::Set; CSSParser *p = static_cast(parser); if (p->styleElement && p->styleElement->isCSSStyleSheet()) static_cast(p->styleElement)->determineNamespace($$->attr, domString($3)); } | '[' maybe_space namespace_selector attrib_id match maybe_space ident_or_string maybe_space ']' { $$ = new CSSSelector(); $$->attr = $4; $$->match = (CSSSelector::Match)$5; $$->value = domString($7); CSSParser *p = static_cast(parser); if (p->styleElement && p->styleElement->isCSSStyleSheet()) static_cast(p->styleElement)->determineNamespace($$->attr, domString($3)); } ; match: '=' { $$ = CSSSelector::Exact; } | INCLUDES { $$ = CSSSelector::List; } | DASHMATCH { $$ = CSSSelector::Hyphen; } | BEGINSWITH { $$ = CSSSelector::Begin; } | ENDSWITH { $$ = CSSSelector::End; } | CONTAINS { $$ = CSSSelector::Contain; } ; ident_or_string: IDENT | STRING ; pseudo: ':' IDENT { $$ = new CSSSelector(); $$->match = CSSSelector::PseudoClass; $$->value = domString($2); } | ':' ':' IDENT { $$ = new CSSSelector(); $$->match = CSSSelector::PseudoElement; $$->value = domString($3); } // used by :nth-* | ':' FUNCTION NTH ')' { $$ = new CSSSelector(); $$->match = CSSSelector::PseudoClass; $$->string_arg = domString($3); $$->value = domString($2); } // used by :nth-* | ':' FUNCTION INTEGER ')' { $$ = new CSSSelector(); $$->match = CSSSelector::PseudoClass; $$->string_arg = QString::number($3); $$->value = domString($2); } // used by :nth-* and :lang | ':' FUNCTION IDENT ')' { $$ = new CSSSelector(); $$->match = CSSSelector::PseudoClass; $$->string_arg = domString($3); $$->value = domString($2); } // used by :contains | ':' FUNCTION STRING ')' { $$ = new CSSSelector(); $$->match = CSSSelector::PseudoClass; $$->string_arg = domString($3); $$->value = domString($2); } // used only by :not | ':' NOTFUNCTION maybe_space simple_selector ')' { $$ = new CSSSelector(); $$->match = CSSSelector::PseudoClass; $$->simpleSelector = $4; $$->value = domString($2); } ; declaration_block: '{' maybe_space declaration '}' { $$ = $3; } | '{' maybe_space error '}' { $$ = false; } | '{' maybe_space declaration_list '}' { $$ = $3; } | '{' maybe_space declaration_list declaration '}' { $$ = $3; if ( $4 ) $$ = $4; } | '{' maybe_space declaration_list error '}' { $$ = $3; } ; declaration_list: declaration ';' maybe_space { $$ = $1; } | error ';' maybe_space { $$ = false; } | declaration_list declaration ';' maybe_space { $$ = $1; if ( $2 ) $$ = $2; } | declaration_list error ';' maybe_space { $$ = $1; } ; declaration: property ':' maybe_space expr prio { $$ = false; CSSParser *p = static_cast(parser); if ( $1 && $4 ) { p->valueList = $4; #ifdef CSS_DEBUG kdDebug( 6080 ) << " got property: " << $1 << ($5?" important":"")<< endl; #endif bool ok = p->parseValue( $1, $5 ); if ( ok ) $$ = ok; #ifdef CSS_DEBUG else kdDebug( 6080 ) << " couldn't parse value!" << endl; #endif } else { delete $4; } delete p->valueList; p->valueList = 0; } | error invalid_block { $$ = false; } ; property: IDENT maybe_space { QString str = qString($1); $$ = getPropertyID( str.lower().latin1(), str.length() ); } ; prio: IMPORTANT_SYM maybe_space { $$ = true; } | /* empty */ { $$ = false; } ; expr: term { $$ = new ValueList; $$->addValue( $1 ); } | expr operator term { $$ = $1; if ( $$ ) { if ( $2 ) { Value v; v.id = 0; v.unit = Value::Operator; v.iValue = $2; $$->addValue( v ); } $$->addValue( $3 ); } } ; operator: '/' maybe_space { $$ = '/'; } | ',' maybe_space { $$ = ','; } | /* empty */ { $$ = 0; } ; term: unary_term { $$ = $1; } | unary_operator unary_term { $$ = $2; $$.fValue *= $1; } /* DIMEN is an unary_term, but since we store the string we must not modify fValue */ | DIMEN maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_DIMENSION; } | STRING maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_STRING; } | IDENT maybe_space { QString str = qString( $1 ); $$.id = getValueID( str.lower().latin1(), str.length() ); $$.unit = CSSPrimitiveValue::CSS_IDENT; $$.string = $1; } | URI maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_URI; } | UNICODERANGE maybe_space { $$.id = 0; $$.iValue = 0; $$.unit = CSSPrimitiveValue::CSS_UNKNOWN;/* ### */ } | hexcolor { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; } /* ### according to the specs a function can have a unary_operator in front. I know no case where this makes sense */ | function { $$ = $1; } ; unary_term: INTEGER maybe_space { $$.id = 0; $$.isInt = true; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; } | FLOAT maybe_space { $$.id = 0; $$.isInt = false; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; } | PERCENTAGE maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PERCENTAGE; } | PXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PX; } | CMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CM; } | MMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MM; } | INS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_IN; } | PTS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PT; } | PCS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PC; } | DEGS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DEG; } | RADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_RAD; } | GRADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_GRAD; } | MSECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MS; } | SECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_S; } | HERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_HZ; } | KHERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_KHZ; } | EMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EMS; } | QEMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = Value::Q_EMS; } | EXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EXS; } ; function: FUNCTION maybe_space expr ')' maybe_space { Function *f = new Function; f->name = $1; f->args = $3; $$.id = 0; $$.unit = Value::Function; $$.function = f; } | FUNCTION maybe_space error { Function *f = new Function; f->name = $1; f->args = 0; $$.id = 0; $$.unit = Value::Function; $$.function = f; } ; /* * There is a constraint on the color that it must * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) * after the "#"; e.g., "#000" is OK, but "#abcd" is not. */ hexcolor: HASH maybe_space { $$ = $1; } ; /* error handling rules */ invalid_at: '@' error invalid_block { $$ = 0; #ifdef CSS_DEBUG kdDebug( 6080 ) << "skipped invalid @-rule" << endl; #endif } | '@' error ';' { $$ = 0; #ifdef CSS_DEBUG kdDebug( 6080 ) << "skipped invalid @-rule" << endl; #endif } ; invalid_rule: error invalid_block { $$ = 0; #ifdef CSS_DEBUG kdDebug( 6080 ) << "skipped invalid rule" << endl; #endif } /* Seems like the two rules below are trying too much and violating http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html | error ';' { $$ = 0; #ifdef CSS_DEBUG kdDebug( 6080 ) << "skipped invalid rule" << endl; #endif } | error '}' { $$ = 0; #ifdef CSS_DEBUG kdDebug( 6080 ) << "skipped invalid rule" << endl; #endif } */ ; invalid_block: '{' error invalid_block_list error '}' | '{' error '}' ; invalid_block_list: invalid_block | invalid_block_list error invalid_block ; %%