%{

/*
 *  Copyright (C) 2002-2003 Lars Knoll (knoll@kde.org)
 *  Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
 *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
 *  Copyright (C) 2008 Eric Seidel <eric@webkit.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
 *
 */

#include "config.h"

#include "CSSMediaRule.h"
#include "CSSParser.h"
#include "CSSPropertyNames.h"
#include "CSSRuleList.h"
#include "CSSSelector.h"
#include "CSSStyleSheet.h"
#include "Document.h"
#include "HTMLNames.h"
#include "MediaList.h"
#include "WebKitCSSKeyframeRule.h"
#include "WebKitCSSKeyframesRule.h"
#include <stdlib.h>
#include <string.h>

using namespace WebCore;
using namespace HTMLNames;

#define YYENABLE_NLS 0
#define YYLTYPE_IS_TRIVIAL 1
#define YYMAXDEPTH 10000
#define YYDEBUG 0

// FIXME: Replace with %parse-param { CSSParser* parser } once we can depend on bison 2.x
#define YYPARSE_PARAM parser
#define YYLEX_PARAM parser

%}

%pure_parser

%union {
    bool boolean;
    char character;
    int integer;
    double number;
    CSSParserString string;

    CSSRule* rule;
    CSSRuleList* ruleList;
    CSSSelector* selector;
    Vector<CSSSelector*>* selectorList;
    CSSSelector::Relation relation;
    MediaList* mediaList;
    MediaQuery* mediaQuery;
    MediaQuery::Restrictor mediaQueryRestrictor;
    MediaQueryExp* mediaQueryExp;
    CSSParserValue value;
    CSSParserValueList* valueList;
    Vector<MediaQueryExp*>* mediaQueryExpList;
    WebKitCSSKeyframeRule* keyframeRule;
    WebKitCSSKeyframesRule* keyframesRule;
    float val;
}

%{

static inline int cssyyerror(const char*)
{
    return 1;
}

static int cssyylex(YYSTYPE* yylval, void* parser)
{
    return static_cast<CSSParser*>(parser)->lex(yylval);
}

%}

%expect 49

%nonassoc LOWEST_PREC

%left UNIMPORTANT_TOK

%token WHITESPACE SGML_CD
%token TOKEN_EOF 0

%token INCLUDES
%token DASHMATCH
%token BEGINSWITH
%token ENDSWITH
%token CONTAINS

%token <string> STRING
%right <string> IDENT
%token <string> NTH

%nonassoc <string> HEX
%nonassoc <string> IDSEL
%nonassoc ':'
%nonassoc '.'
%nonassoc '['
%nonassoc <string> '*'
%nonassoc error
%left '|'

%token IMPORT_SYM
%token PAGE_SYM
%token MEDIA_SYM
%token FONT_FACE_SYM
%token CHARSET_SYM
%token NAMESPACE_SYM
%token WEBKIT_RULE_SYM
%token WEBKIT_DECLS_SYM
%token WEBKIT_KEYFRAME_RULE_SYM
%token WEBKIT_KEYFRAMES_SYM
%token WEBKIT_VALUE_SYM
%token WEBKIT_MEDIAQUERY_SYM
%token WEBKIT_SELECTOR_SYM
%token WEBKIT_VARIABLES_SYM
%token WEBKIT_DEFINE_SYM
%token VARIABLES_FOR
%token WEBKIT_VARIABLES_DECLS_SYM
%token ATKEYWORD

%token IMPORTANT_SYM
%token MEDIA_ONLY
%token MEDIA_NOT
%token MEDIA_AND

%token <number> QEMS
%token <number> EMS
%token <number> EXS
%token <number> PXS
%token <number> CMS
%token <number> MMS
%token <number> INS
%token <number> PTS
%token <number> PCS
%token <number> DEGS
%token <number> RADS
%token <number> GRADS
%token <number> TURNS
%token <number> MSECS
%token <number> SECS
%token <number> HERZ
%token <number> KHERZ
%token <string> DIMEN
%token <number> PERCENTAGE
%token <number> FLOATTOKEN
%token <number> INTEGER

%token <string> URI
%token <string> FUNCTION
%token <string> NOTFUNCTION

%token <string> UNICODERANGE

%token <string> VARCALL

%type <relation> combinator

%type <rule> charset
%type <rule> ruleset
%type <rule> valid_rule_or_import
%type <rule> media
%type <rule> import
%type <rule> page
%type <rule> font_face
%type <rule> keyframes
%type <rule> invalid_rule
%type <rule> save_block
%type <rule> invalid_at
%type <rule> invalid_at_list
%type <rule> invalid_import
%type <rule> invalid_media
%type <rule> rule
%type <rule> valid_rule
%type <ruleList> block_rule_list 
%type <rule> block_rule
%type <rule> block_valid_rule
%type <rule> variables_rule
%type <mediaList> variables_media_list

%type <string> maybe_ns_prefix

%type <string> namespace_selector

%type <string> string_or_uri
%type <string> ident_or_string
%type <string> medium
%type <string> hexcolor

%type <string> media_feature
%type <mediaList> media_list
%type <mediaList> maybe_media_list
%type <mediaQuery> media_query
%type <mediaQueryRestrictor> maybe_media_restrictor
%type <valueList> maybe_media_value
%type <mediaQueryExp> media_query_exp
%type <mediaQueryExpList> media_query_exp_list
%type <mediaQueryExpList> maybe_and_media_query_exp_list

%type <string> keyframe_name
%type <keyframeRule> keyframe_rule
%type <keyframesRule> keyframes_rule
%type <valueList> key_list
%type <value> key

%type <integer> property

%type <selector> specifier
%type <selector> specifier_list
%type <selector> simple_selector
%type <selector> selector
%type <selectorList> selector_list
%type <selector> selector_with_trailing_whitespace
%type <selector> class
%type <selector> attrib
%type <selector> pseudo

%type <boolean> declaration_list
%type <boolean> decl_list
%type <boolean> declaration

%type <boolean> prio

%type <integer> match
%type <integer> unary_operator
%type <character> operator

%type <valueList> expr
%type <value> term
%type <value> unary_term
%type <value> function

%type <string> element_name
%type <string> attr_name

%type <string> variable_name
%type <boolean> variables_declaration_list
%type <boolean> variables_decl_list
%type <boolean> variables_declaration
%type <value> variable_reference

%%

stylesheet:
    maybe_charset maybe_sgml import_list variables_list namespace_list rule_list
  | webkit_rule maybe_space
  | webkit_decls maybe_space
  | webkit_value maybe_space
  | webkit_mediaquery maybe_space
  | webkit_selector maybe_space
  | webkit_variables_decls maybe_space
  | webkit_keyframe_rule maybe_space
  ;

valid_rule_or_import:
    valid_rule
  | import
  ;

webkit_rule:
    WEBKIT_RULE_SYM '{' maybe_space valid_rule_or_import maybe_space '}' {
        static_cast<CSSParser*>(parser)->m_rule = $4;
    }
;

webkit_keyframe_rule:
    WEBKIT_KEYFRAME_RULE_SYM '{' maybe_space keyframe_rule maybe_space '}' {
        static_cast<CSSParser*>(parser)->m_keyframe = $4;
    }
;

webkit_decls:
    WEBKIT_DECLS_SYM '{' maybe_space declaration_list '}' {
        /* can be empty */
    }
;

webkit_variables_decls:
    WEBKIT_VARIABLES_DECLS_SYM '{' maybe_space variables_declaration_list '}' {
        /* can be empty */
    }
;

webkit_value:
    WEBKIT_VALUE_SYM '{' maybe_space expr '}' {
        CSSParser* p = static_cast<CSSParser*>(parser);
        if ($4) {
            p->m_valueList = p->sinkFloatingValueList($4);
            int oldParsedProperties = p->m_numParsedProperties;
            if (!p->parseValue(p->m_id, p->m_important))
                p->rollbackLastProperties(p->m_numParsedProperties - oldParsedProperties);
            delete p->m_valueList;
            p->m_valueList = 0;
        }
    }
;

webkit_mediaquery:
     WEBKIT_MEDIAQUERY_SYM WHITESPACE maybe_space media_query '}' {
         CSSParser* p = static_cast<CSSParser*>(parser);
         p->m_mediaQuery = p->sinkFloatingMediaQuery($4);
     }
;

webkit_selector:
    WEBKIT_SELECTOR_SYM '{' maybe_space selector_list '}' {
        if ($4) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            if (p->m_selectorListForParseSelector)
                p->m_selectorListForParseSelector->adoptSelectorVector(*$4);
        }
    }
;

maybe_space:
    /* empty */ %prec UNIMPORTANT_TOK
  | maybe_space WHITESPACE
  ;

maybe_sgml:
    /* empty */
  | maybe_sgml SGML_CD
  | maybe_sgml WHITESPACE
  ;

maybe_charset:
   /* empty */
  | charset {
  }
  ;

closing_brace:
    '}'
  | %prec LOWEST_PREC TOKEN_EOF
  ;

charset:
  CHARSET_SYM maybe_space STRING maybe_space ';' {
     CSSParser* p = static_cast<CSSParser*>(parser);
     $$ = static_cast<CSSParser*>(parser)->createCharsetRule($3);
     if ($$ && p->m_styleSheet)
         p->m_styleSheet->append($$);
  }
  | CHARSET_SYM error invalid_block {
  }
  | CHARSET_SYM error ';' {
  }
;

import_list:
 /* empty */
 | import_list import maybe_sgml {
     CSSParser* p = static_cast<CSSParser*>(parser);
     if ($2 && p->m_styleSheet)
         p->m_styleSheet->append($2);
 }
 | invalid_at_list {
 }
 ;

variables_list:
/* empty */
| variables_list variables_rule maybe_sgml {
    CSSParser* p = static_cast<CSSParser*>(parser);
     if ($2 && p->m_styleSheet)
         p->m_styleSheet->append($2);
}
;

namespace_list:
/* empty */
| namespace_list namespace maybe_sgml
;

rule_list:
   /* empty */
 | rule_list rule maybe_sgml {
     CSSParser* p = static_cast<CSSParser*>(parser);
     if ($2 && p->m_styleSheet)
         p->m_styleSheet->append($2);
 }
 ;

valid_rule:
    ruleset
  | media
  | page
  | font_face
  | keyframes
  ;

rule:
    valid_rule
  | invalid_rule
  | invalid_at
  | invalid_import
  ;

block_rule_list: 
    /* empty */ { $$ = 0; }
  | block_rule_list block_rule maybe_sgml {
      $$ = $1;
      if ($2) {
          if (!$$)
              $$ = static_cast<CSSParser*>(parser)->createRuleList();
          $$->append($2);
      }
  }
  ;

block_valid_rule:
    ruleset
  | page
  | font_face
  | keyframes
  ;

block_rule:
    block_valid_rule
  | invalid_rule
  | invalid_at
  | invalid_import
  | invalid_media
  ;


import:
    IMPORT_SYM maybe_space string_or_uri maybe_space maybe_media_list ';' {
        $$ = static_cast<CSSParser*>(parser)->createImportRule($3, $5);
    }
  | IMPORT_SYM maybe_space string_or_uri maybe_space maybe_media_list invalid_block {
        $$ = 0;
    }
  | IMPORT_SYM error ';' {
        $$ = 0;
    }
  | IMPORT_SYM error invalid_block {
        $$ = 0;
    }
  ;

variables_rule:
    WEBKIT_VARIABLES_SYM maybe_space maybe_media_list '{' maybe_space variables_declaration_list '}' {
        $$ = static_cast<CSSParser*>(parser)->createVariablesRule($3, true);
    }
    |
    WEBKIT_DEFINE_SYM maybe_space variables_media_list '{' maybe_space variables_declaration_list '}' {
        $$ = static_cast<CSSParser*>(parser)->createVariablesRule($3, false);
    }
    ;

variables_media_list:
    /* empty */ {
        $$ = static_cast<CSSParser*>(parser)->createMediaList();
    }
    |
    VARIABLES_FOR WHITESPACE media_list {
        $$ = $3;
    }
    ;

variables_declaration_list:
    variables_declaration {
        $$ = $1;
    }
    | variables_decl_list variables_declaration {
        $$ = $1;
        if ($2)
            $$ = $2;
    }
    | variables_decl_list {
        $$ = $1;
    }
    | error invalid_block_list error {
        $$ = false;
    }
    | error {
        $$ = false;
    }
    | variables_decl_list error {
        $$ = $1;
    }
    ;

variables_decl_list:
    variables_declaration ';' maybe_space {
        $$ = $1;
    }
    | variables_declaration invalid_block_list ';' maybe_space {
        $$ = false;
    }
    | error ';' maybe_space {
        $$ = false;
    }
    | error invalid_block_list error ';' maybe_space {
        $$ = false;
    }
    | variables_decl_list variables_declaration ';' maybe_space {
        $$ = $1;
        if ($2)
            $$ = $2;
    }
    | variables_decl_list error ';' maybe_space {
        $$ = $1;
    }
    | variables_decl_list error invalid_block_list error ';' maybe_space {
        $$ = $1;
    }
    ;

variables_declaration:
    variable_name ':' maybe_space expr {
        $$ = static_cast<CSSParser*>(parser)->addVariable($1, $4);
    }
    |
    variable_name maybe_space '{' maybe_space declaration_list '}' maybe_space {
        $$ = static_cast<CSSParser*>(parser)->addVariableDeclarationBlock($1);
    }
    |
    variable_name error {
        $$ = false;
    }
    |
    variable_name ':' maybe_space error expr {
        $$ = false;
    }
    |
    variable_name ':' maybe_space {
        /* @variables { varname: } Just reduce away this variable with no value. */
        $$ = false;
    }
    |
    variable_name ':' maybe_space error {
        /* if we come across rules with invalid values like this case: @variables { varname: *; }, just discard the property/value pair */
        $$ = false;
    }
    ;

variable_name:
    IDENT maybe_space {
        $$ = $1;
    }
    ;

namespace:
NAMESPACE_SYM maybe_space maybe_ns_prefix string_or_uri maybe_space ';' {
    CSSParser* p = static_cast<CSSParser*>(parser);
    if (p->m_styleSheet)
        p->m_styleSheet->addNamespace(p, $3, $4);
}
| NAMESPACE_SYM error invalid_block
| NAMESPACE_SYM error ';'
;

maybe_ns_prefix:
/* empty */ { $$.characters = 0; }
| IDENT WHITESPACE { $$ = $1; }
;

string_or_uri:
STRING
| URI
;

media_feature:
    IDENT maybe_space {
        $$ = $1;
    }
    ;

maybe_media_value:
    /*empty*/ {
        $$ = 0;
    }
    | ':' maybe_space expr maybe_space {
        $$ = $3;
    }
    ;

media_query_exp:
    '(' maybe_space media_feature maybe_space maybe_media_value ')' maybe_space {
        $3.lower();
        $$ = static_cast<CSSParser*>(parser)->createFloatingMediaQueryExp($3, $5);
    }
    ;

media_query_exp_list:
    media_query_exp {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingMediaQueryExpList();
        $$->append(p->sinkFloatingMediaQueryExp($1));
    }
    | media_query_exp_list maybe_space MEDIA_AND maybe_space media_query_exp {
        $$ = $1;
        $$->append(static_cast<CSSParser*>(parser)->sinkFloatingMediaQueryExp($5));
    }
    ;

maybe_and_media_query_exp_list:
    /*empty*/ {
        $$ = static_cast<CSSParser*>(parser)->createFloatingMediaQueryExpList();
    }
    | MEDIA_AND maybe_space media_query_exp_list {
        $$ = $3;
    }
    ;

maybe_media_restrictor:
    /*empty*/ {
        $$ = MediaQuery::None;
    }
    | MEDIA_ONLY {
        $$ = MediaQuery::Only;
    }
    | MEDIA_NOT {
        $$ = MediaQuery::Not;
    }
    ;

media_query:
    media_query_exp_list {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingMediaQuery(p->sinkFloatingMediaQueryExpList($1));
    }
    |
    maybe_media_restrictor maybe_space medium maybe_and_media_query_exp_list {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $3.lower();
        $$ = p->createFloatingMediaQuery($1, $3, p->sinkFloatingMediaQueryExpList($4));
    }
    ;

maybe_media_list:
     /* empty */ {
        $$ = static_cast<CSSParser*>(parser)->createMediaList();
     }
     | media_list
     ;

media_list:
    media_query {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createMediaList();
        $$->appendMediaQuery(p->sinkFloatingMediaQuery($1));
    }
    | media_list ',' maybe_space media_query {
        $$ = $1;
        if ($$)
            $$->appendMediaQuery(static_cast<CSSParser*>(parser)->sinkFloatingMediaQuery($4));
    }
    | media_list error {
        $$ = 0;
    }
    ;

media:
    MEDIA_SYM maybe_space media_list '{' maybe_space block_rule_list save_block {
        $$ = static_cast<CSSParser*>(parser)->createMediaRule($3, $6);
    }
    | MEDIA_SYM maybe_space '{' maybe_space block_rule_list save_block {
        $$ = static_cast<CSSParser*>(parser)->createMediaRule(0, $5);
    }
    ;

medium:
  IDENT maybe_space {
      $$ = $1;
  }
  ;

keyframes:
    WEBKIT_KEYFRAMES_SYM maybe_space keyframe_name maybe_space '{' maybe_space keyframes_rule '}' {
        $$ = $7;
        $7->setNameInternal($3);
    }
    ;
  
keyframe_name:
    IDENT
    | STRING
    ;

keyframes_rule:
    /* empty */ { $$ = static_cast<CSSParser*>(parser)->createKeyframesRule(); }
    | keyframes_rule keyframe_rule maybe_space {
        $$ = $1;
        if ($2)
            $$->append($2);
    }
    ;

keyframe_rule:
    key_list maybe_space '{' maybe_space declaration_list '}' {
        $$ = static_cast<CSSParser*>(parser)->createKeyframeRule($1);
    }
    ;

key_list:
    key {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingValueList();
        $$->addValue(p->sinkFloatingValue($1));
    }
    | key_list maybe_space ',' maybe_space key {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = $1;
        if ($$)
            $$->addValue(p->sinkFloatingValue($5));
    }
    ;

key:
    PERCENTAGE { $$.id = 0; $$.isInt = false; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
    | IDENT {
        $$.id = 0; $$.isInt = false; $$.unit = CSSPrimitiveValue::CSS_NUMBER;
        CSSParserString& str = $1;
        if (equalIgnoringCase(static_cast<const String&>(str), "from"))
            $$.fValue = 0;
        else if (equalIgnoringCase(static_cast<const String&>(str), "to"))
            $$.fValue = 100;
        else
            YYERROR;
    }
    ;

/*
page:
    PAGE_SYM maybe_space IDENT? pseudo_page? maybe_space
    '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space
  ;

pseudo_page
  : ':' IDENT
  ;
*/

page:
    PAGE_SYM error invalid_block {
      $$ = 0;
    }
  | PAGE_SYM error ';' {
      $$ = 0;
    }
    ;

font_face:
    FONT_FACE_SYM maybe_space
    '{' maybe_space declaration_list '}'  maybe_space {
        $$ = static_cast<CSSParser*>(parser)->createFontFaceRule();
    }
    | 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; }
  ;

unary_operator:
    '-' { $$ = -1; }
  | '+' { $$ = 1; }
  ;

ruleset:
    selector_list '{' maybe_space declaration_list closing_brace {
        $$ = static_cast<CSSParser*>(parser)->createStyleRule($1);
    }
  ;

selector_list:
    selector %prec UNIMPORTANT_TOK {
        if ($1) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            $$ = p->reusableSelectorVector();
            deleteAllValues(*$$);
            $$->shrink(0);
            $$->append(p->sinkFloatingSelector($1));
        }
    }
    | selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK {
        if ($1 && $4) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            $$ = $1;
            $$->append(p->sinkFloatingSelector($4));
        } else
            $$ = 0;
    }
  | selector_list error {
        $$ = 0;
    }
   ;

selector_with_trailing_whitespace:
    selector WHITESPACE {
        $$ = $1;
    }
    ;

selector:
    simple_selector {
        $$ = $1;
    }
    | selector_with_trailing_whitespace
    {
        $$ = $1;
    }
    | selector_with_trailing_whitespace simple_selector
    {
        $$ = $2;
        if (!$1)
            $$ = 0;
        else if ($$) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            CSSSelector* end = $$;
            while (end->tagHistory())
                end = end->tagHistory();
            end->m_relation = CSSSelector::Descendant;
            end->setTagHistory(p->sinkFloatingSelector($1));
            if (Document* doc = p->document())
                doc->setUsesDescendantRules(true);
        }
    }
    | selector combinator simple_selector {
        $$ = $3;
        if (!$1)
            $$ = 0;
        else if ($$) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            CSSSelector* end = $$;
            while (end->tagHistory())
                end = end->tagHistory();
            end->m_relation = $2;
            end->setTagHistory(p->sinkFloatingSelector($1));
            if ($2 == CSSSelector::Child) {
                if (Document* doc = p->document())
                    doc->setUsesDescendantRules(true);
            } else if ($2 == CSSSelector::DirectAdjacent || $2 == CSSSelector::IndirectAdjacent) {
                if (Document* doc = p->document())
                    doc->setUsesSiblingRules(true);
            }
        }
    }
    | selector error {
        $$ = 0;
    }
    ;

namespace_selector:
    /* empty */ '|' { $$.characters = 0; $$.length = 0; }
    | '*' '|' { static UChar star = '*'; $$.characters = &star; $$.length = 1; }
    | IDENT '|' { $$ = $1; }
;
    
simple_selector:
    element_name {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        $$->m_tag = QualifiedName(nullAtom, $1, p->m_defaultNamespace);
    }
    | element_name specifier_list {
        $$ = $2;
        if ($$) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            $$->m_tag = QualifiedName(nullAtom, $1, p->m_defaultNamespace);
        }
    }
    | specifier_list {
        $$ = $1;
        CSSParser* p = static_cast<CSSParser*>(parser);
        if ($$ && p->m_defaultNamespace != starAtom)
            $$->m_tag = QualifiedName(nullAtom, starAtom, p->m_defaultNamespace);
    }
    | namespace_selector element_name {
        AtomicString namespacePrefix = $1;
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        if (p->m_styleSheet)
            $$->m_tag = QualifiedName(namespacePrefix, $2,
                                      p->m_styleSheet->determineNamespace(namespacePrefix));
        else // FIXME: Shouldn't this case be an error?
            $$->m_tag = QualifiedName(nullAtom, $2, p->m_defaultNamespace);
    }
    | namespace_selector element_name specifier_list {
        $$ = $3;
        if ($$) {
            AtomicString namespacePrefix = $1;
            CSSParser* p = static_cast<CSSParser*>(parser);
            if (p->m_styleSheet)
                $$->m_tag = QualifiedName(namespacePrefix, $2,
                                          p->m_styleSheet->determineNamespace(namespacePrefix));
            else // FIXME: Shouldn't this case be an error?
                $$->m_tag = QualifiedName(nullAtom, $2, p->m_defaultNamespace);
        }
    }
    | namespace_selector specifier_list {
        $$ = $2;
        if ($$) {
            AtomicString namespacePrefix = $1;
            CSSParser* p = static_cast<CSSParser*>(parser);
            if (p->m_styleSheet)
                $$->m_tag = QualifiedName(namespacePrefix, starAtom,
                                          p->m_styleSheet->determineNamespace(namespacePrefix));
        }
    }
  ;

element_name:
    IDENT {
        CSSParserString& str = $1;
        CSSParser* p = static_cast<CSSParser*>(parser);
        Document* doc = p->document();
        if (doc && doc->isHTMLDocument())
            str.lower();
        $$ = str;
    }
    | '*' {
        static UChar star = '*';
        $$.characters = &star;
        $$.length = 1;
    }
  ;

specifier_list:
    specifier {
        $$ = $1;
    }
    | specifier_list specifier {
        if (!$2)
            $$ = 0;
        else if ($1) {
            $$ = $1;
            CSSParser* p = static_cast<CSSParser*>(parser);
            CSSSelector* end = $1;
            while (end->tagHistory())
                end = end->tagHistory();
            end->m_relation = CSSSelector::SubSelector;
            end->setTagHistory(p->sinkFloatingSelector($2));
        }
    }
    | specifier_list error {
        $$ = 0;
    }
;

specifier:
    IDSEL {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        $$->m_match = CSSSelector::Id;
        if (!p->m_strict)
            $1.lower();
        $$->m_value = $1;
    }
  | HEX {
        if ($1.characters[0] >= '0' && $1.characters[0] <= '9') {
            $$ = 0;
        } else {
            CSSParser* p = static_cast<CSSParser*>(parser);
            $$ = p->createFloatingSelector();
            $$->m_match = CSSSelector::Id;
            if (!p->m_strict)
                $1.lower();
            $$->m_value = $1;
        }
    }
  | class
  | attrib
  | pseudo
    ;

class:
    '.' IDENT {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        $$->m_match = CSSSelector::Class;
        if (!p->m_strict)
            $2.lower();
        $$->m_value = $2;
    }
  ;

attr_name:
    IDENT maybe_space {
        CSSParserString& str = $1;
        CSSParser* p = static_cast<CSSParser*>(parser);
        Document* doc = p->document();
        if (doc && doc->isHTMLDocument())
            str.lower();
        $$ = str;
    }
    ;

attrib:
    '[' maybe_space attr_name ']' {
        $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
        $$->setAttribute(QualifiedName(nullAtom, $3, nullAtom));
        $$->m_match = CSSSelector::Set;
    }
    | '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' {
        $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
        $$->setAttribute(QualifiedName(nullAtom, $3, nullAtom));
        $$->m_match = (CSSSelector::Match)$4;
        $$->m_value = $6;
    }
    | '[' maybe_space namespace_selector attr_name ']' {
        AtomicString namespacePrefix = $3;
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        $$->setAttribute(QualifiedName(namespacePrefix, $4,
                                   p->m_styleSheet->determineNamespace(namespacePrefix)));
        $$->m_match = CSSSelector::Set;
    }
    | '[' maybe_space namespace_selector attr_name match maybe_space ident_or_string maybe_space ']' {
        AtomicString namespacePrefix = $3;
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        $$->setAttribute(QualifiedName(namespacePrefix, $4,
                                   p->m_styleSheet->determineNamespace(namespacePrefix)));
        $$->m_match = (CSSSelector::Match)$5;
        $$->m_value = $7;
    }
  ;

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 {
        $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
        $$->m_match = CSSSelector::PseudoClass;
        $2.lower();
        $$->m_value = $2;
        CSSSelector::PseudoType type = $$->pseudoType();
        if (type == CSSSelector::PseudoUnknown)
            $$ = 0;
        else if (type == CSSSelector::PseudoEmpty ||
                 type == CSSSelector::PseudoFirstChild ||
                 type == CSSSelector::PseudoFirstOfType ||
                 type == CSSSelector::PseudoLastChild ||
                 type == CSSSelector::PseudoLastOfType ||
                 type == CSSSelector::PseudoOnlyChild ||
                 type == CSSSelector::PseudoOnlyOfType) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            Document* doc = p->document();
            if (doc)
                doc->setUsesSiblingRules(true);
        } else if (type == CSSSelector::PseudoFirstLine) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            if (Document* doc = p->document())
                doc->setUsesFirstLineRules(true);
        } else if (type == CSSSelector::PseudoBefore ||
                   type == CSSSelector::PseudoAfter) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            if (Document* doc = p->document())
                doc->setUsesBeforeAfterRules(true);
        }
    }
    | ':' ':' IDENT {
        $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
        $$->m_match = CSSSelector::PseudoElement;
        $3.lower();
        $$->m_value = $3;
        CSSSelector::PseudoType type = $$->pseudoType();
        if (type == CSSSelector::PseudoUnknown)
            $$ = 0;
        else if (type == CSSSelector::PseudoFirstLine) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            if (Document* doc = p->document())
                doc->setUsesFirstLineRules(true);
        } else if (type == CSSSelector::PseudoBefore ||
                   type == CSSSelector::PseudoAfter) {
            CSSParser* p = static_cast<CSSParser*>(parser);
            if (Document* doc = p->document())
                doc->setUsesBeforeAfterRules(true);
        }
    }
    // used by :nth-*(ax+b)
    | ':' FUNCTION NTH ')' {
        CSSParser *p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        $$->m_match = CSSSelector::PseudoClass;
        $$->setArgument($3);
        $$->m_value = $2;
        CSSSelector::PseudoType type = $$->pseudoType();
        if (type == CSSSelector::PseudoUnknown)
            $$ = 0;
        else if (type == CSSSelector::PseudoNthChild ||
                 type == CSSSelector::PseudoNthOfType ||
                 type == CSSSelector::PseudoNthLastChild ||
                 type == CSSSelector::PseudoNthLastOfType) {
            if (p->document())
                p->document()->setUsesSiblingRules(true);
        }
    }
    // used by :nth-*
    | ':' FUNCTION INTEGER ')' {
        CSSParser *p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        $$->m_match = CSSSelector::PseudoClass;
        $$->setArgument(String::number($3));
        $$->m_value = $2;
        CSSSelector::PseudoType type = $$->pseudoType();
        if (type == CSSSelector::PseudoUnknown)
            $$ = 0;
        else if (type == CSSSelector::PseudoNthChild ||
                 type == CSSSelector::PseudoNthOfType ||
                 type == CSSSelector::PseudoNthLastChild ||
                 type == CSSSelector::PseudoNthLastOfType) {
            if (p->document())
                p->document()->setUsesSiblingRules(true);
        }
    }
    // used by :nth-*(odd/even) and :lang
    | ':' FUNCTION IDENT ')' {
        CSSParser *p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingSelector();
        $$->m_match = CSSSelector::PseudoClass;
        $$->setArgument($3);
        $2.lower();
        $$->m_value = $2;
        CSSSelector::PseudoType type = $$->pseudoType();
        if (type == CSSSelector::PseudoUnknown)
            $$ = 0;
        else if (type == CSSSelector::PseudoNthChild ||
                 type == CSSSelector::PseudoNthOfType ||
                 type == CSSSelector::PseudoNthLastChild ||
                 type == CSSSelector::PseudoNthLastOfType) {
            if (p->document())
                p->document()->setUsesSiblingRules(true);
        }
    }
    // used by :not
    | ':' NOTFUNCTION maybe_space simple_selector maybe_space ')' {
        if (!$4)
            $$ = 0;
        else {
            CSSParser* p = static_cast<CSSParser*>(parser);
            $$ = p->createFloatingSelector();
            $$->m_match = CSSSelector::PseudoClass;
            $$->setSimpleSelector(p->sinkFloatingSelector($4));
            $2.lower();
            $$->m_value = $2;
        }
    }
  ;

declaration_list:
    declaration {
        $$ = $1;
    }
    | decl_list declaration {
        $$ = $1;
        if ( $2 )
            $$ = $2;
    }
    | decl_list {
        $$ = $1;
    }
    | error invalid_block_list error {
        $$ = false;
    }
    | error {
        $$ = false;
    }
    | decl_list error {
        $$ = $1;
    }
    | decl_list invalid_block_list {
        $$ = $1;
    }
    ;

decl_list:
    declaration ';' maybe_space {
        $$ = $1;
    }
    | declaration invalid_block_list ';' maybe_space {
        $$ = false;
    }
    | error ';' maybe_space {
        $$ = false;
    }
    | error invalid_block_list error ';' maybe_space {
        $$ = false;
    }
    | decl_list declaration ';' maybe_space {
        $$ = $1;
        if ($2)
            $$ = $2;
    }
    | decl_list error ';' maybe_space {
        $$ = $1;
    }
    | decl_list error invalid_block_list error ';' maybe_space {
        $$ = $1;
    }
    ;

declaration:
    property ':' maybe_space expr prio {
        $$ = false;
        CSSParser* p = static_cast<CSSParser*>(parser);
        if ($1 && $4) {
            p->m_valueList = p->sinkFloatingValueList($4);
            int oldParsedProperties = p->m_numParsedProperties;
            $$ = p->parseValue($1, $5);
            if (!$$)
                p->rollbackLastProperties(p->m_numParsedProperties - oldParsedProperties);
            delete p->m_valueList;
            p->m_valueList = 0;
        }
    }
    |
    variable_reference maybe_space {
        CSSParser* p = static_cast<CSSParser*>(parser);
        p->m_valueList = new CSSParserValueList;
        p->m_valueList->addValue(p->sinkFloatingValue($1));
        int oldParsedProperties = p->m_numParsedProperties;
        $$ = p->parseValue(CSSPropertyWebkitVariableDeclarationBlock, false);
        if (!$$)
            p->rollbackLastProperties(p->m_numParsedProperties - oldParsedProperties);
        delete p->m_valueList;
        p->m_valueList = 0;
    }
    |
    property error {
        $$ = false;
    }
    |
    property ':' maybe_space error expr prio {
        /* The default movable type template has letter-spacing: .none;  Handle this by looking for
        error tokens at the start of an expr, recover the expr and then treat as an error, cleaning
        up and deleting the shifted expr.  */
        $$ = false;
    }
    |
    property ':' maybe_space expr prio error {
        /* When we encounter something like p {color: red !important fail;} we should drop the declaration */
        $$ = false;
    }
    |
    IMPORTANT_SYM maybe_space {
        /* Handle this case: div { text-align: center; !important } Just reduce away the stray !important. */
        $$ = false;
    }
    |
    property ':' maybe_space {
        /* div { font-family: } Just reduce away this property with no value. */
        $$ = false;
    }
    |
    property ':' maybe_space error {
        /* if we come across rules with invalid values like this case: p { weight: *; }, just discard the rule */
        $$ = false;
    }
    |
    property invalid_block {
        /* if we come across: div { color{;color:maroon} }, ignore everything within curly brackets */
        $$ = false;
    }
  ;

property:
    IDENT maybe_space {
        $$ = cssPropertyID($1);
    }
  ;

prio:
    IMPORTANT_SYM maybe_space { $$ = true; }
    | /* empty */ { $$ = false; }
  ;

expr:
    term {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = p->createFloatingValueList();
        $$->addValue(p->sinkFloatingValue($1));
    }
    | expr operator term {
        CSSParser* p = static_cast<CSSParser*>(parser);
        $$ = $1;
        if ($$) {
            if ($2) {
                CSSParserValue v;
                v.id = 0;
                v.unit = CSSParserValue::Operator;
                v.iValue = $2;
                $$->addValue(v);
            }
            $$->addValue(p->sinkFloatingValue($3));
        }
    }
    | expr error {
        $$ = 0;
    }
  ;

operator:
    '/' maybe_space {
        $$ = '/';
    }
  | ',' maybe_space {
        $$ = ',';
    }
  | /* empty */ {
        $$ = 0;
  }
  ;

term:
  unary_term { $$ = $1; }
  | unary_operator unary_term { $$ = $2; $$.fValue *= $1; }
  | STRING maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_STRING; }
  | IDENT maybe_space {
      $$.id = cssValueKeywordID($1);
      $$.unit = CSSPrimitiveValue::CSS_IDENT;
      $$.string = $1;
  }
  /* We might need to actually parse the number from a dimension, but we can't just put something that uses $$.string into unary_term. */
  | DIMEN maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_DIMENSION; }
  | unary_operator DIMEN maybe_space { $$.id = 0; $$.string = $2; $$.unit = CSSPrimitiveValue::CSS_DIMENSION; }
  | URI maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_URI; }
  | UNICODERANGE maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_UNICODE_RANGE; }
  | hexcolor { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_PARSER_HEXCOLOR; }
  | '#' maybe_space { $$.id = 0; $$.string = CSSParserString(); $$.unit = CSSPrimitiveValue::CSS_PARSER_HEXCOLOR; } /* Handle error case: "color: #;" */
  /* FIXME: according to the specs a function can have a unary_operator in front. I know no case where this makes sense */
  | function {
      $$ = $1;
  }
  | variable_reference maybe_space {
      $$ = $1;
  }
  | '%' maybe_space { $$.id = 0; $$.fValue = 0; $$.unit = CSSPrimitiveValue::CSS_PERCENTAGE; } /* Handle width: %; ANDROID: Fix an uninitialized Value object causing the device to crash */
  ;

unary_term:
  INTEGER maybe_space { $$.id = 0; $$.isInt = true; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
  | FLOATTOKEN 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; }
  | TURNS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_TURN; }
  | 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 = CSSParserValue::Q_EMS; }
  | EXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EXS; }
    ;

variable_reference:
  VARCALL {
      $$.id = 0;
      $$.string = $1;
      $$.unit = CSSPrimitiveValue::CSS_PARSER_VARIABLE_FUNCTION_SYNTAX;
  }
  ;

function:
    FUNCTION maybe_space expr ')' maybe_space {
        CSSParser* p = static_cast<CSSParser*>(parser);
        CSSParserFunction* f = p->createFloatingFunction();
        f->name = $1;
        f->args = p->sinkFloatingValueList($3);
        $$.id = 0;
        $$.unit = CSSParserValue::Function;
        $$.function = f;
    } |
    FUNCTION maybe_space error {
        CSSParser* p = static_cast<CSSParser*>(parser);
        CSSParserFunction* f = p->createFloatingFunction();
        f->name = $1;
        f->args = 0;
        $$.id = 0;
        $$.unit = CSSParserValue::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:
  HEX maybe_space { $$ = $1; }
  | IDSEL maybe_space { $$ = $1; }
  ;


/* error handling rules */

save_block:
    closing_brace {
        $$ = 0;
    }
  | error closing_brace {
        $$ = 0;
    }
    ;

invalid_at:
    ATKEYWORD error invalid_block {
        $$ = 0;
    }
  | ATKEYWORD error ';' {
        $$ = 0;
    }
    ;

invalid_at_list:
    invalid_at maybe_sgml
  | invalid_at_list invalid_at maybe_sgml
  ;

invalid_import:
    import {
        $$ = 0;
    }
    ;

invalid_media:
    media {
        $$ = 0;
    }
    ;

invalid_rule:
    error invalid_block {
        $$ = 0;
    }

/*
  Seems like the two rules below are trying too much and violating
  http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html

  | error ';' {
        $$ = 0;
    }
  | error '}' {
        $$ = 0;
    }
*/
    ;

invalid_block:
    '{' error invalid_block_list error closing_brace
  | '{' error closing_brace
    ;

invalid_block_list:
    invalid_block
  | invalid_block_list error invalid_block
;

%%