/* * Copyright (C) 2002-2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) * Copyright (C) 2008 Eric Seidel * Copyright (C) 2012 Intel Corporation. All rights reserved. * * 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 * */ %pure-parser %parse-param { CSSParser* parser } %lex-param { CSSParser* parser } %union { double number; CSSParserString string; CSSSelector::MarginBoxType marginBox; CSSParserValue value; } %{ static inline int cssyyerror(void*, const char*) { return 1; } #if YYDEBUG > 0 static inline bool isCSSTokenAString(int yytype) { switch (yytype) { case IDENT: case STRING: case NTH: case HEX: case IDSEL: case DIMEN: case INVALIDDIMEN: case URI: case FUNCTION: case ANYFUNCTION: case NOTFUNCTION: case CALCFUNCTION: case MINFUNCTION: case MAXFUNCTION: case UNICODERANGE: return true; default: return false; } } #endif static inline CSSParserValue makeIdentValue(CSSParserString string) { CSSParserValue v; v.id = cssValueKeywordID(string); v.unit = CSSPrimitiveValue::CSS_IDENT; v.string = string; return v; } %} #if ENABLE_CSS_GRID_LAYOUT #if ENABLE_PICTURE_SIZES %expect 34 #else %expect 30 #endif #else #if ENABLE_PICTURE_SIZES %expect 33 #else %expect 29 #endif #endif %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 %right IDENT %token NTH %nonassoc HEX %nonassoc IDSEL %nonassoc ':' %nonassoc '.' %nonassoc '[' %nonassoc '*' %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 #if ENABLE_PICTURE_SIZES %token WEBKIT_SIZESATTR_SYM #endif %token WEBKIT_SELECTOR_SYM %token WEBKIT_REGION_RULE_SYM %token WEBKIT_VIEWPORT_RULE_SYM %token TOPLEFTCORNER_SYM %token TOPLEFT_SYM %token TOPCENTER_SYM %token TOPRIGHT_SYM %token TOPRIGHTCORNER_SYM %token BOTTOMLEFTCORNER_SYM %token BOTTOMLEFT_SYM %token BOTTOMCENTER_SYM %token BOTTOMRIGHT_SYM %token BOTTOMRIGHTCORNER_SYM %token LEFTTOP_SYM %token LEFTMIDDLE_SYM %token LEFTBOTTOM_SYM %token RIGHTTOP_SYM %token RIGHTMIDDLE_SYM %token RIGHTBOTTOM_SYM %token ATKEYWORD %token IMPORTANT_SYM %token MEDIA_ONLY %token MEDIA_NOT %token MEDIA_AND %token REMS %token CHS %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 TURNS %token MSECS %token SECS %token HERTZ %token KHERTZ %token DIMEN %token INVALIDDIMEN %token PERCENTAGE %token FLOATTOKEN %token INTEGER %token VW %token VH %token VMIN %token VMAX %token DPPX %token DPI %token DPCM %token FR %token URI %token FUNCTION %token ANYFUNCTION %token NOTFUNCTION %token CALCFUNCTION %token MINFUNCTION %token MAXFUNCTION %token UNICODERANGE %union { CSSSelector::Relation relation; } %type combinator %union { StyleRuleBase* rule; } %type block_rule block_valid_rule font_face import keyframes media page region rule ruleset valid_rule %destructor { if ($$) $$->deref(); } block_rule block_valid_rule font_face import keyframes media page region rule ruleset valid_rule %union { Vector>* ruleList; } %type block_rule_list block_valid_rule_list %destructor { delete $$; } block_rule_list block_valid_rule_list %type ident_or_string maybe_ns_prefix namespace_selector string_or_uri %type margin_sym %union { MediaQuerySet* mediaList; } %type media_list maybe_media_list %destructor { if ($$) $$->deref(); } media_list maybe_media_list %union { MediaQuery* mediaQuery; } %type media_query %destructor { delete $$; } media_query %union { MediaQuery::Restrictor mediaQueryRestrictor; } %type maybe_media_restrictor %union { MediaQueryExp* mediaQueryExp; } %type media_query_exp %destructor { delete $$; } media_query_exp #if ENABLE_PICTURE_SIZES %union { MediaQueryExp* mediaCondition; } %type media_condition %destructor { delete $$; } media_condition %union { SourceSize* sourceSize; } %type source_size %destructor { delete $$; } source_size %union { SourceSizeList* sourceSizeList; } %type source_size_list %destructor { delete $$; } source_size_list %type source_size_length #endif %union { Vector>* mediaQueryExpList; } %type media_query_exp_list maybe_and_media_query_exp_list %destructor { delete $$; } media_query_exp_list maybe_and_media_query_exp_list %type keyframe_name %union { StyleKeyframe* keyframe; } %type keyframe_rule %destructor { if ($$) $$->deref(); } keyframe_rule %union { Vector>* keyframeRuleList; } %type keyframes_rule %destructor { delete $$; } keyframes_rule // These two parser values never need to be destroyed because they are never functions or value lists. %type key unary_term // These parser values need to be destroyed because they might be functions. %type calc_func_term calc_function function min_or_max_function term %destructor { destroy($$); } calc_func_term calc_function function min_or_max_function term %union { CSSPropertyID id; } %type property %union { CSSParserSelector* selector; } %type attrib class page_selector pseudo pseudo_page selector selector_with_trailing_whitespace simple_selector specifier specifier_list %destructor { delete $$; } attrib class page_selector pseudo pseudo_page selector selector_with_trailing_whitespace simple_selector specifier specifier_list %union { Vector>* selectorList; } %type selector_list simple_selector_list %destructor { delete $$; } selector_list simple_selector_list %union { bool boolean; } %type declaration declaration_list decl_list priority %union { CSSSelector::Match match; } %type match %union { int integer; } %type unary_operator maybe_unary_operator %union { char character; } %type operator calc_func_operator %union { CSSParserValueList* valueList; } %type calc_func_expr calc_func_expr_list calc_func_paren_expr expr key_list maybe_media_value valid_calc_func_expr valid_expr %destructor { delete $$; } calc_func_expr calc_func_expr_list calc_func_paren_expr expr key_list maybe_media_value valid_calc_func_expr valid_expr %type min_or_max %type element_name %union { CSSParser::Location location; } %type error_location #if ENABLE_CSS_GRID_LAYOUT %type ident_list %destructor { delete $$; } ident_list %type track_names_list %destructor { destroy($$); } track_names_list #endif #if ENABLE_CSS3_CONDITIONAL_RULES %token SUPPORTS_AND %token SUPPORTS_NOT %token SUPPORTS_OR %token SUPPORTS_SYM %token WEBKIT_SUPPORTS_CONDITION_SYM %type supports %destructor { if ($$) $$->deref(); } supports %type supports_condition supports_condition_in_parens supports_conjunction supports_declaration_condition supports_disjunction supports_error supports_negation #endif #if ENABLE_CSS_DEVICE_ADAPTATION %type viewport %destructor { if ($$) $$->deref(); } viewport #endif #if ENABLE_VIDEO_TRACK %token CUEFUNCTION #endif %% stylesheet: maybe_space maybe_charset maybe_sgml rule_list | webkit_rule maybe_space | webkit_decls maybe_space | webkit_value maybe_space | webkit_mediaquery maybe_space #if ENABLE_PICTURE_SIZES | webkit_source_size_list maybe_space #endif | webkit_selector maybe_space | webkit_keyframe_rule maybe_space #if ENABLE_CSS3_CONDITIONAL_RULES | webkit_supports_condition maybe_space #endif ; webkit_rule: WEBKIT_RULE_SYM '{' maybe_space valid_rule maybe_space '}' { parser->m_rule = adoptRef($4); } ; webkit_keyframe_rule: WEBKIT_KEYFRAME_RULE_SYM '{' maybe_space keyframe_rule maybe_space '}' { parser->m_keyframe = adoptRef($4); } ; webkit_decls: WEBKIT_DECLS_SYM '{' maybe_space_before_declaration declaration_list '}' ; webkit_value: WEBKIT_VALUE_SYM '{' maybe_space expr '}' { if ($4) { parser->m_valueList = std::unique_ptr($4); int oldParsedProperties = parser->m_parsedProperties.size(); if (!parser->parseValue(parser->m_id, parser->m_important)) parser->rollbackLastProperties(parser->m_parsedProperties.size() - oldParsedProperties); parser->m_valueList = nullptr; } } ; webkit_mediaquery: WEBKIT_MEDIAQUERY_SYM WHITESPACE maybe_space media_query '}' { parser->m_mediaQuery = std::unique_ptr($4); } ; webkit_selector: WEBKIT_SELECTOR_SYM '{' maybe_space selector_list '}' { if ($4) { if (parser->m_selectorListForParseSelector) parser->m_selectorListForParseSelector->adoptSelectorVector(*$4); parser->recycleSelectorVector(std::unique_ptr>>($4)); } } ; #if ENABLE_CSS3_CONDITIONAL_RULES webkit_supports_condition: WEBKIT_SUPPORTS_CONDITION_SYM '{' maybe_space supports_condition '}' { parser->m_supportsCondition = $4; } ; #endif /* for expressions that require at least one whitespace to be present, like the + and - operators in calc expressions */ space: WHITESPACE | space WHITESPACE ; 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 ; closing_parenthesis: ')' | %prec LOWEST_PREC TOKEN_EOF ; charset: CHARSET_SYM maybe_space STRING maybe_space ';' { if (parser->m_styleSheet) parser->m_styleSheet->parserSetEncodingFromCharsetRule($3); if (parser->isExtractingSourceData() && parser->m_currentRuleDataStack->isEmpty() && parser->m_ruleSourceDataResult) parser->addNewRuleToSourceTree(CSSRuleSourceData::createUnknown()); } | CHARSET_SYM error invalid_block | CHARSET_SYM error ';' ; // Ignore any @charset rule not at the beginning of the style sheet. ignored_charset: CHARSET_SYM maybe_space STRING maybe_space ';' | CHARSET_SYM maybe_space ';' ; rule_list: /* empty */ | rule_list rule maybe_sgml { if (RefPtr rule = adoptRef($2)) { if (parser->m_styleSheet) parser->m_styleSheet->parserAppendRule(rule.releaseNonNull()); } } ; valid_rule: ruleset | media | page | font_face | keyframes | namespace { $$ = nullptr; } | import | region #if ENABLE_CSS3_CONDITIONAL_RULES | supports #endif #if ENABLE_CSS_DEVICE_ADAPTATION | viewport #endif ; rule: valid_rule { $$ = $1; parser->m_hadSyntacticallyValidCSSRule = true; } | ignored_charset { $$ = nullptr; } | invalid_rule { $$ = nullptr; } | invalid_at { $$ = nullptr; } ; block_rule_list: /* empty */ { $$ = nullptr; } | block_rule_list block_rule maybe_sgml { $$ = $1; if (RefPtr rule = adoptRef($2)) { if (!$$) $$ = new Vector>; $$->append(rule.release()); } } ; block_valid_rule_list: /* empty */ { $$ = nullptr; } | block_valid_rule_list block_valid_rule maybe_sgml { $$ = $1; if (RefPtr rule = adoptRef($2)) { if (!$$) $$ = new Vector>; $$->append(rule.release()); } } ; block_valid_rule: ruleset | page | font_face | media | keyframes #if ENABLE_CSS3_CONDITIONAL_RULES | supports #endif #if ENABLE_CSS_DEVICE_ADAPTATION | viewport #endif ; block_rule: block_valid_rule | invalid_rule { $$ = nullptr; } | invalid_at { $$ = nullptr; } | namespace { $$ = nullptr; } | import | region ; at_import_header_end_maybe_space: maybe_space { parser->markRuleHeaderEnd(); parser->markRuleBodyStart(); } ; before_import_rule: /* empty */ { parser->markRuleHeaderStart(CSSRuleSourceData::IMPORT_RULE); } ; import: before_import_rule IMPORT_SYM at_import_header_end_maybe_space string_or_uri maybe_space maybe_media_list ';' { $$ = parser->createImportRule($4, adoptRef($6)).leakRef(); } | before_import_rule IMPORT_SYM at_import_header_end_maybe_space string_or_uri maybe_space maybe_media_list TOKEN_EOF { $$ = parser->createImportRule($4, adoptRef($6)).leakRef(); } | before_import_rule IMPORT_SYM at_import_header_end_maybe_space string_or_uri maybe_space maybe_media_list invalid_block { $$ = nullptr; parser->popRuleData(); if ($6) $6->deref(); } | before_import_rule IMPORT_SYM error ';' { $$ = nullptr; parser->popRuleData(); } | before_import_rule IMPORT_SYM error invalid_block { $$ = nullptr; parser->popRuleData(); } ; namespace: NAMESPACE_SYM maybe_space maybe_ns_prefix string_or_uri maybe_space ';' { parser->addNamespace($3, $4); } | NAMESPACE_SYM maybe_space maybe_ns_prefix string_or_uri maybe_space invalid_block | NAMESPACE_SYM error invalid_block | NAMESPACE_SYM error ';' ; maybe_ns_prefix: /* empty */ { $$.clear(); } | IDENT maybe_space; string_or_uri: STRING | URI ; maybe_media_value: /*empty*/ { $$ = nullptr; } | ':' maybe_space expr maybe_space { $$ = $3; } ; #if ENABLE_PICTURE_SIZES media_condition: maybe_space '(' maybe_space IDENT maybe_space maybe_media_value ')' maybe_space { std::unique_ptr mediaValue($6); $4.lower(); $$ = new MediaQueryExp($4, mediaValue.get()); } ; webkit_source_size_list: WEBKIT_SIZESATTR_SYM WHITESPACE source_size_list '}' { parser->m_sourceSizeList = std::unique_ptr($3); }; source_size_list: maybe_space source_size maybe_space { $$ = new SourceSizeList(); $$->append(std::unique_ptr($2)); } | maybe_space source_size maybe_space ',' maybe_space source_size_list maybe_space { $$ = $6; $$->append(std::unique_ptr($2)); }; source_size_length: unary_term { $$ = $1; } | calc_function { $$ = $1; }; source_size: media_condition source_size_length { $$ = new SourceSize(std::unique_ptr($1), $2); } | source_size_length { $$ = new SourceSize(std::make_unique(emptyString(), nullptr), $1); }; #endif media_query_exp: maybe_media_restrictor maybe_space '(' maybe_space IDENT maybe_space maybe_media_value ')' maybe_space { // If restrictor is specified, media query expression is invalid. // Create empty media query expression and continue parsing media query. std::unique_ptr mediaValue($7); if ($1 != MediaQuery::None) $$ = new MediaQueryExp(emptyString(), nullptr); else { $5.lower(); $$ = new MediaQueryExp($5, mediaValue.get()); } } ; media_query_exp_list: media_query_exp { $$ = new Vector>; $$->append(std::unique_ptr($1)); } | media_query_exp_list maybe_space MEDIA_AND maybe_space media_query_exp { $$ = $1; $$->append(std::unique_ptr($5)); } ; maybe_and_media_query_exp_list: /*empty*/ { $$ = new Vector>; } | 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 { $$ = new MediaQuery(MediaQuery::None, "all", std::unique_ptr>>($1)); } | maybe_media_restrictor maybe_space IDENT maybe_space maybe_and_media_query_exp_list { $3.lower(); $$ = new MediaQuery($1, $3, std::unique_ptr>>($5)); } ; maybe_media_list: /* empty */ { $$ = MediaQuerySet::create().leakRef(); } | media_list ; media_list: media_query { $$ = MediaQuerySet::create().leakRef(); $$->addMediaQuery(std::unique_ptr($1)); parser->updateLastMediaLine($$); } | media_list ',' maybe_space media_query { $$ = $1; std::unique_ptr mediaQuery($4); if ($$) { $$->addMediaQuery(WTF::move(mediaQuery)); parser->updateLastMediaLine($$); } } | media_list error { $$ = nullptr; if ($1) $1->deref(); } ; at_rule_body_start: /* empty */ { parser->markRuleBodyStart(); } ; before_media_rule: /* empty */ { parser->markRuleHeaderStart(CSSRuleSourceData::MEDIA_RULE); } ; at_rule_header_end_maybe_space: maybe_space { parser->markRuleHeaderEnd(); } ; media: before_media_rule MEDIA_SYM maybe_space media_list at_rule_header_end '{' at_rule_body_start maybe_space block_rule_list save_block { $$ = parser->createMediaRule(adoptRef($4), std::unique_ptr>>($9).get()).leakRef(); } | before_media_rule MEDIA_SYM at_rule_header_end_maybe_space '{' at_rule_body_start maybe_space block_rule_list save_block { $$ = parser->createEmptyMediaRule(std::unique_ptr>>($7).get()).leakRef(); } | before_media_rule MEDIA_SYM at_rule_header_end_maybe_space ';' { $$ = nullptr; parser->popRuleData(); } ; #if ENABLE_CSS3_CONDITIONAL_RULES supports: before_supports_rule SUPPORTS_SYM maybe_space supports_condition at_supports_rule_header_end '{' at_rule_body_start maybe_space block_rule_list save_block { $$ = parser->createSupportsRule($4, std::unique_ptr>>($9).get()).leakRef(); } | before_supports_rule SUPPORTS_SYM supports_error { $$ = nullptr; parser->popRuleData(); parser->popSupportsRuleData(); } ; supports_error: error ';' | error invalid_block ; before_supports_rule: /* empty */ { parser->markRuleHeaderStart(CSSRuleSourceData::SUPPORTS_RULE); parser->markSupportsRuleHeaderStart(); } ; at_supports_rule_header_end: /* empty */ { parser->markRuleHeaderEnd(); parser->markSupportsRuleHeaderEnd(); } ; supports_condition: supports_condition_in_parens | supports_negation | supports_conjunction | supports_disjunction ; supports_negation: SUPPORTS_NOT maybe_space supports_condition_in_parens { $$ = !$3; } ; supports_conjunction: supports_condition_in_parens SUPPORTS_AND maybe_space supports_condition_in_parens { $$ = $1 && $4; } | supports_conjunction SUPPORTS_AND maybe_space supports_condition_in_parens { $$ = $1 && $4; } ; supports_disjunction: supports_condition_in_parens SUPPORTS_OR maybe_space supports_condition_in_parens { $$ = $1 || $4; } | supports_disjunction SUPPORTS_OR maybe_space supports_condition_in_parens { $$ = $1 || $4; } ; supports_condition_in_parens: '(' maybe_space supports_condition ')' maybe_space { $$ = $3; } | supports_declaration_condition { $$ = $1; } | '(' error ')' { $$ = false; } ; supports_declaration_condition: '(' maybe_space property ':' maybe_space expr priority ')' maybe_space { $$ = false; CSSParser* p = static_cast(parser); std::unique_ptr propertyValue($6); if ($3 && propertyValue) { p->m_valueList = WTF::move(propertyValue); int oldParsedProperties = p->m_parsedProperties.size(); $$ = p->parseValue($3, $7); // We just need to know if the declaration is supported as it is written. Rollback any additions. if ($$) p->rollbackLastProperties(p->m_parsedProperties.size() - oldParsedProperties); p->m_valueList = nullptr; } p->markPropertyEnd($7, false); } ; #endif before_keyframes_rule: /* empty */ { parser->markRuleHeaderStart(CSSRuleSourceData::KEYFRAMES_RULE); } ; keyframes: before_keyframes_rule WEBKIT_KEYFRAMES_SYM maybe_space keyframe_name at_rule_header_end_maybe_space '{' at_rule_body_start maybe_space keyframes_rule closing_brace { $$ = parser->createKeyframesRule($4, std::unique_ptr>>($9)).leakRef(); } ; keyframe_name: IDENT | STRING ; keyframes_rule: /* empty */ { $$ = new Vector>; } | keyframes_rule keyframe_rule maybe_space { $$ = $1; if (RefPtr keyframe = adoptRef($2)) $$->append(keyframe.release()); } ; keyframe_rule: key_list maybe_space '{' maybe_space declaration_list closing_brace { $$ = parser->createKeyframe(*std::unique_ptr($1)).leakRef(); } ; key_list: key { $$ = new CSSParserValueList; $$->addValue($1); } | key_list maybe_space ',' maybe_space key { $$ = $1; ASSERT($5.unit != CSSParserValue::Function); // No need to call destroy. if ($$) $$->addValue($5); } ; key: maybe_unary_operator PERCENTAGE { $$.id = CSSValueInvalid; $$.isInt = false; $$.fValue = $1 * $2; $$.unit = CSSPrimitiveValue::CSS_NUMBER; } | IDENT { $$.id = CSSValueInvalid; $$.isInt = false; $$.unit = CSSPrimitiveValue::CSS_NUMBER; CSSParserString& str = $1; if (str.equalIgnoringCase("from")) $$.fValue = 0; else if (str.equalIgnoringCase("to")) $$.fValue = 100; else { $$.unit = 0; YYERROR; } } | error { $$.unit = 0; } ; before_page_rule: /* empty */ { parser->markRuleHeaderStart(CSSRuleSourceData::PAGE_RULE); } ; page: before_page_rule PAGE_SYM maybe_space page_selector at_rule_header_end_maybe_space '{' at_rule_body_start maybe_space_before_declaration declarations_and_margins closing_brace { if ($4) $$ = parser->createPageRule(std::unique_ptr($4)).leakRef(); else { // Clear properties in the invalid @page rule. parser->clearProperties(); // Also clear margin at-rules here once we fully implement margin at-rules parsing. $$ = nullptr; parser->popRuleData(); } } | before_page_rule PAGE_SYM error invalid_block { parser->popRuleData(); $$ = nullptr; } | before_page_rule PAGE_SYM error ';' { parser->popRuleData(); $$ = nullptr; } ; page_selector: IDENT { $$ = new CSSParserSelector(QualifiedName(nullAtom, $1, parser->m_defaultNamespace)); $$->setForPage(); } | IDENT pseudo_page { $$ = $2; if ($$) { $$->prependTagSelector(QualifiedName(nullAtom, $1, parser->m_defaultNamespace)); $$->setForPage(); } } | pseudo_page { $$ = $1; if ($$) $$->setForPage(); } | /* empty */ { $$ = new CSSParserSelector; $$->setForPage(); } ; declarations_and_margins: declaration_list | declarations_and_margins margin_box maybe_space declaration_list ; margin_box: margin_sym { parser->startDeclarationsForMarginBox(); } maybe_space '{' maybe_space declaration_list closing_brace { parser->createMarginAtRule($1); } ; margin_sym: TOPLEFTCORNER_SYM { $$ = CSSSelector::TopLeftCornerMarginBox; } | TOPLEFT_SYM { $$ = CSSSelector::TopLeftMarginBox; } | TOPCENTER_SYM { $$ = CSSSelector::TopCenterMarginBox; } | TOPRIGHT_SYM { $$ = CSSSelector::TopRightMarginBox; } | TOPRIGHTCORNER_SYM { $$ = CSSSelector::TopRightCornerMarginBox; } | BOTTOMLEFTCORNER_SYM { $$ = CSSSelector::BottomLeftCornerMarginBox; } | BOTTOMLEFT_SYM { $$ = CSSSelector::BottomLeftMarginBox; } | BOTTOMCENTER_SYM { $$ = CSSSelector::BottomCenterMarginBox; } | BOTTOMRIGHT_SYM { $$ = CSSSelector::BottomRightMarginBox; } | BOTTOMRIGHTCORNER_SYM { $$ = CSSSelector::BottomRightCornerMarginBox; } | LEFTTOP_SYM { $$ = CSSSelector::LeftTopMarginBox; } | LEFTMIDDLE_SYM { $$ = CSSSelector::LeftMiddleMarginBox; } | LEFTBOTTOM_SYM { $$ = CSSSelector::LeftBottomMarginBox; } | RIGHTTOP_SYM { $$ = CSSSelector::RightTopMarginBox; } | RIGHTMIDDLE_SYM { $$ = CSSSelector::RightMiddleMarginBox; } | RIGHTBOTTOM_SYM { $$ = CSSSelector::RightBottomMarginBox; } ; before_font_face_rule: /* empty */ { parser->markRuleHeaderStart(CSSRuleSourceData::FONT_FACE_RULE); } ; font_face: before_font_face_rule FONT_FACE_SYM at_rule_header_end_maybe_space '{' at_rule_body_start maybe_space_before_declaration declaration_list closing_brace { $$ = parser->createFontFaceRule().leakRef(); } | before_font_face_rule FONT_FACE_SYM error invalid_block { $$ = nullptr; parser->popRuleData(); } | before_font_face_rule FONT_FACE_SYM error ';' { $$ = nullptr; parser->popRuleData(); } ; #if ENABLE_CSS_DEVICE_ADAPTATION before_viewport_rule: /* empty */ { parser->markViewportRuleBodyStart(); parser->markRuleHeaderStart(CSSRuleSourceData::VIEWPORT_RULE); } ; viewport: before_viewport_rule WEBKIT_VIEWPORT_RULE_SYM at_rule_header_end_maybe_space '{' at_rule_body_start maybe_space_before_declaration declaration_list closing_brace { $$ = parser->createViewportRule().leakRef(); parser->markViewportRuleBodyEnd(); } | before_viewport_rule WEBKIT_VIEWPORT_RULE_SYM error invalid_block { $$ = nullptr; parser->popRuleData(); parser->markViewportRuleBodyEnd(); } | before_viewport_rule WEBKIT_VIEWPORT_RULE_SYM error ';' { $$ = nullptr; parser->popRuleData(); parser->markViewportRuleBodyEnd(); } ; #endif before_region_rule: /* empty */ { parser->markRuleHeaderStart(CSSRuleSourceData::REGION_RULE); } ; region: before_region_rule WEBKIT_REGION_RULE_SYM maybe_space selector_list at_rule_header_end '{' at_rule_body_start maybe_space block_valid_rule_list save_block { std::unique_ptr>> ruleList($9); if ($4) $$ = parser->createRegionRule(std::unique_ptr>>($4).get(), ruleList.get()).leakRef(); else { $$ = nullptr; parser->popRuleData(); } } ; combinator: '+' maybe_space { $$ = CSSSelector::DirectAdjacent; } | '~' maybe_space { $$ = CSSSelector::IndirectAdjacent; } | '>' maybe_space { $$ = CSSSelector::Child; } ; maybe_unary_operator: unary_operator | { $$ = 1; } ; unary_operator: '-' { $$ = -1; } | '+' { $$ = 1; } ; maybe_space_before_declaration: maybe_space { parser->markPropertyStart(); } ; before_selector_list: { parser->markRuleHeaderStart(CSSRuleSourceData::STYLE_RULE); parser->markSelectorStart(); } ; at_rule_header_end: { parser->markRuleHeaderEnd(); } ; at_selector_end: { parser->markSelectorEnd(); } ; ruleset: before_selector_list selector_list at_selector_end at_rule_header_end '{' at_rule_body_start maybe_space_before_declaration declaration_list closing_brace { $$ = parser->createStyleRule($2).leakRef(); parser->recycleSelectorVector(std::unique_ptr>>($2)); } ; before_selector_group_item: { parser->markSelectorStart(); } ; selector_list: selector %prec UNIMPORTANT_TOK { $$ = nullptr; if ($1) { $$ = parser->createSelectorVector().release(); $$->append(std::unique_ptr($1)); parser->updateLastSelectorLineAndPosition(); } } | selector_list at_selector_end ',' maybe_space before_selector_group_item selector %prec UNIMPORTANT_TOK { std::unique_ptr>> selectorList($1); std::unique_ptr selector($6); $$ = nullptr; if (selectorList && selector) { $$ = selectorList.release(); $$->append(WTF::move(selector)); parser->updateLastSelectorLineAndPosition(); } } | selector_list error { $$ = nullptr; delete $1; } ; selector_with_trailing_whitespace: selector WHITESPACE; selector: simple_selector | selector_with_trailing_whitespace | selector_with_trailing_whitespace simple_selector { std::unique_ptr left($1); std::unique_ptr right($2); $$ = nullptr; if (left && right) { right->appendTagHistory(CSSSelector::Descendant, WTF::move(left)); $$ = right.release(); } } | selector combinator simple_selector { std::unique_ptr left($1); std::unique_ptr right($3); $$ = nullptr; if (left && right) { right->appendTagHistory($2, WTF::move(left)); $$ = right.release(); } } | selector error { $$ = nullptr; delete $1; } ; namespace_selector: '|' { $$.clear(); } | '*' '|' { static LChar star = '*'; $$.init(&star, 1); } | IDENT '|' ; simple_selector: element_name { $$ = new CSSParserSelector(QualifiedName(nullAtom, $1, parser->m_defaultNamespace)); } | element_name specifier_list { $$ = $2; if ($$) parser->rewriteSpecifiersWithElementName(nullAtom, $1, *$$); } | specifier_list { $$ = $1; if ($$) parser->rewriteSpecifiersWithNamespaceIfNeeded(*$$); } | namespace_selector element_name { $$ = new CSSParserSelector(parser->determineNameInNamespace($1, $2)); } | namespace_selector element_name specifier_list { $$ = $3; if ($$) parser->rewriteSpecifiersWithElementName($1, $2, *$$); } | namespace_selector specifier_list { $$ = $2; if ($$) parser->rewriteSpecifiersWithElementName($1, starAtom, *$$); } ; simple_selector_list: simple_selector %prec UNIMPORTANT_TOK { $$ = nullptr; if ($1) { $$ = parser->createSelectorVector().release(); $$->append(std::unique_ptr($1)); } } | simple_selector_list maybe_space ',' maybe_space simple_selector %prec UNIMPORTANT_TOK { std::unique_ptr>> list($1); std::unique_ptr selector($5); $$ = nullptr; if (list && selector) { $$ = list.release(); $$->append(WTF::move(selector)); } } | simple_selector_list error { $$ = nullptr; delete $1; } ; element_name: IDENT { if (parser->m_context.isHTMLDocument) $1.lower(); $$ = $1; } | '*' { static LChar star = '*'; $$.init(&star, 1); } ; specifier_list: specifier | specifier_list specifier { std::unique_ptr list($1); std::unique_ptr specifier($2); $$ = nullptr; if (list && specifier) $$ = parser->rewriteSpecifiers(WTF::move(list), WTF::move(specifier)).release(); } | specifier_list error { $$ = nullptr; delete $1; } ; specifier: IDSEL { $$ = new CSSParserSelector; $$->setMatch(CSSSelector::Id); if (parser->m_context.mode == CSSQuirksMode) $1.lower(); $$->setValue($1); } | HEX { if ($1[0] >= '0' && $1[0] <= '9') $$ = nullptr; else { $$ = new CSSParserSelector; $$->setMatch(CSSSelector::Id); if (parser->m_context.mode == CSSQuirksMode) $1.lower(); $$->setValue($1); } } | class | attrib | pseudo ; class: '.' IDENT { $$ = new CSSParserSelector; $$->setMatch(CSSSelector::Class); if (parser->m_context.mode == CSSQuirksMode) $2.lower(); $$->setValue($2); } ; attrib: '[' maybe_space IDENT maybe_space ']' { $$ = new CSSParserSelector; $$->setAttribute(QualifiedName(nullAtom, $3, nullAtom), parser->m_context.isHTMLDocument); $$->setMatch(CSSSelector::Set); } | '[' maybe_space IDENT maybe_space match maybe_space ident_or_string maybe_space ']' { $$ = new CSSParserSelector; $$->setAttribute(QualifiedName(nullAtom, $3, nullAtom), parser->m_context.isHTMLDocument); $$->setMatch($5); $$->setValue($7); } | '[' maybe_space namespace_selector IDENT maybe_space ']' { $$ = new CSSParserSelector; $$->setAttribute(parser->determineNameInNamespace($3, $4), parser->m_context.isHTMLDocument); $$->setMatch(CSSSelector::Set); } | '[' maybe_space namespace_selector IDENT maybe_space match maybe_space ident_or_string maybe_space ']' { $$ = new CSSParserSelector; $$->setAttribute(parser->determineNameInNamespace($3, $4), parser->m_context.isHTMLDocument); $$->setMatch($6); $$->setValue($8); } ; match: '=' { $$ = CSSSelector::Exact; } | INCLUDES { $$ = CSSSelector::List; } | DASHMATCH { $$ = CSSSelector::Hyphen; } | BEGINSWITH { $$ = CSSSelector::Begin; } | ENDSWITH { $$ = CSSSelector::End; } | CONTAINS { $$ = CSSSelector::Contain; } ; ident_or_string: IDENT | STRING ; pseudo_page: ':' IDENT { $$ = CSSParserSelector::parsePagePseudoSelector($2); } pseudo: ':' IDENT { $$ = CSSParserSelector::parsePseudoClassAndCompatibilityElementSelector($2); } | ':' ':' IDENT { $$ = CSSParserSelector::parsePseudoElementSelector($3); } #if ENABLE_VIDEO_TRACK // used by ::cue(:past/:future) | ':' ':' CUEFUNCTION maybe_space simple_selector_list maybe_space ')' { $$ = CSSParserSelector::parsePseudoElementCueFunctionSelector($3, $5); } #endif // use by :-webkit-any. // FIXME: should we support generic selectors here or just simple_selectors? // Use simple_selector_list for now to match -moz-any. // See http://lists.w3.org/Archives/Public/www-style/2010Sep/0566.html for some // related discussion with respect to :not. | ':' ANYFUNCTION maybe_space simple_selector_list maybe_space ')' { $$ = nullptr; if ($4) { auto selector = std::make_unique(); selector->setMatch(CSSSelector::PseudoClass); selector->adoptSelectorVector(*std::unique_ptr>>($4)); selector->setPseudoClassValue($2); if (selector->pseudoClassType() == CSSSelector::PseudoClassAny) $$ = selector.release(); } } // used by :nth-*(ax+b) | ':' FUNCTION maybe_space NTH maybe_space ')' { $$ = nullptr; auto selector = std::make_unique(); selector->setMatch(CSSSelector::PseudoClass); selector->setArgument($4); selector->setPseudoClassValue($2); if (selector->pseudoClassType() != CSSSelector::PseudoClassUnknown) $$ = selector.release(); } // used by :nth-* | ':' FUNCTION maybe_space maybe_unary_operator INTEGER maybe_space ')' { $$ = nullptr; auto selector = std::make_unique(); selector->setMatch(CSSSelector::PseudoClass); selector->setArgument(AtomicString::number($4 * $5)); selector->setPseudoClassValue($2); if (selector->pseudoClassType() != CSSSelector::PseudoClassUnknown) $$ = selector.release(); } // used by :nth-*(odd/even) and :lang | ':' FUNCTION maybe_space IDENT maybe_space ')' { auto selector = std::make_unique(); selector->setMatch(CSSSelector::PseudoClass); selector->setArgument($4); selector->setPseudoClassValue($2); CSSSelector::PseudoClassType type = selector->pseudoClassType(); if (type == CSSSelector::PseudoClassUnknown) selector = nullptr; else if (type == CSSSelector::PseudoClassNthChild || type == CSSSelector::PseudoClassNthOfType || type == CSSSelector::PseudoClassNthLastChild || type == CSSSelector::PseudoClassNthLastOfType) { if (!isValidNthToken($4)) selector = nullptr; } $$ = selector.release(); } // used by :not | ':' NOTFUNCTION maybe_space simple_selector maybe_space ')' { std::unique_ptr selector($4); $$ = nullptr; if (selector && selector->isSimple()) { $$ = new CSSParserSelector; $$->setMatch(CSSSelector::PseudoClass); Vector> selectorVector; selectorVector.append(WTF::move(selector)); $$->adoptSelectorVector(selectorVector); $$->setPseudoClassValue($2); } } ; declaration_list: /* empty */ { $$ = false; } | declaration | decl_list declaration { $$ = $1 || $2; } | decl_list | decl_list_recovery { $$ = false; } | decl_list decl_list_recovery ; decl_list: declaration ';' maybe_space { parser->markPropertyStart(); $$ = $1; } | decl_list_recovery ';' maybe_space { parser->markPropertyStart(); $$ = false; } | decl_list declaration ';' maybe_space { parser->markPropertyStart(); $$ = $1; if ($2) $$ = $2; } | decl_list decl_list_recovery ';' maybe_space { parser->markPropertyStart(); $$ = $1; } ; decl_list_recovery: error error_location error_recovery { parser->syntaxError($2, CSSParser::PropertyDeclarationError); } ; declaration: property ':' maybe_space expr priority { $$ = false; bool isPropertyParsed = false; std::unique_ptr propertyValue($4); if ($1 && propertyValue) { parser->m_valueList = WTF::move(propertyValue); int oldParsedProperties = parser->m_parsedProperties.size(); $$ = parser->parseValue($1, $5); if (!$$) parser->rollbackLastProperties(parser->m_parsedProperties.size() - oldParsedProperties); else isPropertyParsed = true; parser->m_valueList = nullptr; } parser->markPropertyEnd($5, isPropertyParsed); } | property declaration_recovery { $$ = false; } | property ':' maybe_space expr priority declaration_recovery { // When we encounter something like p {color: red !important fail;} we should drop the declaration. parser->markPropertyEnd(false, false); delete $4; $$ = false; } | IMPORTANT_SYM maybe_space declaration_recovery { // Handle this case -- div { text-align: center; !important } -- by just reducing away the stray !important. $$ = false; } | property ':' maybe_space declaration_recovery { // If we come across rules with invalid values like this case: p { weight: *; }, just discard the rule. parser->markPropertyEnd(false, false); $$ = false; } ; declaration_recovery: error error_location error_recovery { parser->syntaxError($2); } ; property: IDENT maybe_space { $$ = cssPropertyID($1); } ; priority: IMPORTANT_SYM maybe_space { $$ = true; } | /* empty */ { $$ = false; } ; #if ENABLE_CSS_GRID_LAYOUT ident_list: IDENT maybe_space { $$ = new CSSParserValueList; $$->addValue(makeIdentValue($1)); } | ident_list IDENT maybe_space { $$ = $1; $$->addValue(makeIdentValue($2)); } ; track_names_list: '(' maybe_space closing_parenthesis { $$.setFromValueList(std::make_unique()); } | '(' maybe_space ident_list closing_parenthesis { $$.setFromValueList(std::unique_ptr($3)); } | '(' maybe_space expr_recovery closing_parenthesis { $$.id = CSSValueInvalid; $$.unit = 0; YYERROR; } ; #endif expr: valid_expr | valid_expr expr_recovery { $$ = nullptr; delete $1; } ; valid_expr: term { $$ = new CSSParserValueList; $$->addValue($1); } | valid_expr operator term { $$ = $1; if (!$$) destroy($3); else { if ($2) { CSSParserValue v; v.id = CSSValueInvalid; v.unit = CSSParserValue::Operator; v.iValue = $2; $$->addValue(v); } $$->addValue($3); } } ; expr_recovery: error error_location error_recovery ; operator: '/' maybe_space { $$ = '/'; } | ',' maybe_space { $$ = ','; } | /* empty */ { $$ = 0; } ; term: unary_term maybe_space | unary_operator unary_term maybe_space { $$ = $2; $$.fValue *= $1; } | STRING maybe_space { $$.id = CSSValueInvalid; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_STRING; } | IDENT maybe_space { $$ = makeIdentValue($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 = CSSValueInvalid; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_DIMENSION; } | unary_operator DIMEN maybe_space { $$.id = CSSValueInvalid; $$.string = $2; $$.unit = CSSPrimitiveValue::CSS_DIMENSION; } | URI maybe_space { $$.id = CSSValueInvalid; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_URI; } | UNICODERANGE maybe_space { $$.id = CSSValueInvalid; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_UNICODE_RANGE; } | HEX maybe_space { $$.id = CSSValueInvalid; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_PARSER_HEXCOLOR; } | '#' maybe_space { $$.id = CSSValueInvalid; $$.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 maybe_space | calc_function maybe_space | min_or_max_function maybe_space | '%' maybe_space { /* Handle width: %; */ $$.id = CSSValueInvalid; $$.unit = 0; } #if ENABLE_CSS_GRID_LAYOUT | track_names_list maybe_space #endif ; unary_term: INTEGER { $$.id = CSSValueInvalid; $$.isInt = true; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; } | FLOATTOKEN { $$.id = CSSValueInvalid; $$.isInt = false; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; } | PERCENTAGE { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PERCENTAGE; } | PXS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PX; } | CMS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CM; } | MMS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MM; } | INS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_IN; } | PTS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PT; } | PCS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PC; } | DEGS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DEG; } | RADS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_RAD; } | GRADS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_GRAD; } | TURNS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_TURN; } | MSECS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MS; } | SECS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_S; } | HERTZ { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_HZ; } | KHERTZ { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_KHZ; } | EMS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EMS; } | QEMS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSParserValue::Q_EMS; } | EXS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EXS; } | REMS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_REMS; if (parser->m_styleSheet) parser->m_styleSheet->parserSetUsesRemUnits(true); } | CHS { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CHS; } | VW { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_VW; } | VH { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_VH; } | VMIN { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_VMIN; } | VMAX { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_VMAX; } | DPPX { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DPPX; } | DPI { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DPI; } | DPCM { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DPCM; } | FR { $$.id = CSSValueInvalid; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_FR; } ; function: FUNCTION maybe_space expr closing_parenthesis { CSSParserFunction* f = new CSSParserFunction; f->name = $1; f->args = std::unique_ptr($3); $$.id = CSSValueInvalid; $$.unit = CSSParserValue::Function; $$.function = f; } | FUNCTION maybe_space closing_parenthesis { CSSParserFunction* f = new CSSParserFunction; f->name = $1; f->args = std::unique_ptr(new CSSParserValueList); $$.id = CSSValueInvalid; $$.unit = CSSParserValue::Function; $$.function = f; } | FUNCTION maybe_space expr_recovery closing_parenthesis { CSSParserFunction* f = new CSSParserFunction; f->name = $1; f->args = nullptr; $$.id = CSSValueInvalid; $$.unit = CSSParserValue::Function; $$.function = f; } ; calc_func_term: unary_term | unary_operator unary_term { $$ = $2; $$.fValue *= $1; } ; /* * The grammar requires spaces around binary ‘+’ and ‘-’ operators. * The '*' and '/' operators do not require spaces. * http://www.w3.org/TR/css3-values/#calc-syntax */ calc_func_operator: space '+' space { $$ = '+'; } | space '-' space { $$ = '-'; } | calc_maybe_space '*' maybe_space { $$ = '*'; } | calc_maybe_space '/' maybe_space { $$ = '/'; } ; calc_maybe_space: /* empty */ | WHITESPACE ; calc_func_paren_expr: '(' maybe_space calc_func_expr calc_maybe_space closing_parenthesis { $$ = nullptr; if ($3) { $$ = $3; CSSParserValue v; v.id = CSSValueInvalid; v.unit = CSSParserValue::Operator; v.iValue = '('; $$->insertValueAt(0, v); v.iValue = ')'; $$->addValue(v); } } ; calc_func_expr: valid_calc_func_expr | valid_calc_func_expr expr_recovery { $$ = nullptr; delete $1; } ; valid_calc_func_expr: calc_func_term { $$ = new CSSParserValueList; $$->addValue($1); } | calc_func_expr calc_func_operator calc_func_term { std::unique_ptr expression($1); $$ = nullptr; if (expression && $2) { $$ = expression.release(); CSSParserValue v; v.id = CSSValueInvalid; v.unit = CSSParserValue::Operator; v.iValue = $2; $$->addValue(v); $$->addValue($3); } else { destroy($3); } } | calc_func_expr calc_func_operator calc_func_paren_expr { std::unique_ptr left($1); std::unique_ptr right($3); $$ = nullptr; if (left && $2 && right) { CSSParserValue v; v.id = CSSValueInvalid; v.unit = CSSParserValue::Operator; v.iValue = $2; left->addValue(v); left->extend(*right); $$ = left.release(); } } | calc_func_paren_expr ; calc_func_expr_list: calc_func_expr calc_maybe_space | calc_func_expr_list ',' maybe_space calc_func_expr calc_maybe_space { std::unique_ptr list($1); std::unique_ptr expression($4); $$ = nullptr; if (list && expression) { $$ = list.release(); CSSParserValue v; v.id = CSSValueInvalid; v.unit = CSSParserValue::Operator; v.iValue = ','; $$->addValue(v); $$->extend(*expression); } } ; calc_function: CALCFUNCTION maybe_space calc_func_expr calc_maybe_space closing_parenthesis { CSSParserFunction* f = new CSSParserFunction; f->name = $1; f->args = std::unique_ptr($3); $$.id = CSSValueInvalid; $$.unit = CSSParserValue::Function; $$.function = f; } | CALCFUNCTION maybe_space expr_recovery closing_parenthesis { $$.id = CSSValueInvalid; $$.unit = 0; YYERROR; } ; min_or_max: MINFUNCTION | MAXFUNCTION ; min_or_max_function: min_or_max maybe_space calc_func_expr_list closing_parenthesis { CSSParserFunction* f = new CSSParserFunction; f->name = $1; f->args = std::unique_ptr($3); $$.id = CSSValueInvalid; $$.unit = CSSParserValue::Function; $$.function = f; } | min_or_max maybe_space expr_recovery closing_parenthesis { $$.id = CSSValueInvalid; $$.unit = 0; YYERROR; } ; /* error handling rules */ save_block: closing_brace | error closing_brace ; invalid_at: ATKEYWORD error invalid_block | ATKEYWORD error ';' ; invalid_rule: error invalid_block ; invalid_block: '{' error_recovery closing_brace { parser->invalidBlockHit(); } ; invalid_square_brackets_block: '[' error_recovery ']' | '[' error_recovery TOKEN_EOF ; invalid_parentheses_block: opening_parenthesis error_recovery closing_parenthesis; opening_parenthesis: '(' | FUNCTION | CALCFUNCTION | MINFUNCTION | MAXFUNCTION | ANYFUNCTION | NOTFUNCTION #if ENABLE_VIDEO_TRACK | CUEFUNCTION #endif ; error_location: { $$ = parser->currentLocation(); } ; error_recovery: /* empty */ | error_recovery error | error_recovery invalid_block | error_recovery invalid_square_brackets_block | error_recovery invalid_parentheses_block ; %%