diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h b/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h index 9ffb1e753ecce5..92179583c567f8 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h @@ -14,8 +14,8 @@ #include #include -#include #include +#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h deleted file mode 100644 index 6e016aa9c94c1c..00000000000000 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace facebook::react { - -namespace detail { -class CSSParser { - public: - explicit constexpr CSSParser(std::string_view css) - : tokenizer_{css}, currentToken_(tokenizer_.next()) {} - - template - constexpr CSSValueVariant consumeComponentValue() { - using CSSValueT = CSSValueVariant; - switch (peek().type()) { - case CSSTokenType::Ident: - if (auto keywordValue = - consumeIdentToken()) { - return *keywordValue; - } - break; - case CSSTokenType::Dimension: - if (auto dimensionValue = - consumeDimensionToken()) { - return *dimensionValue; - } - break; - case CSSTokenType::Percentage: - if (auto percentageValue = - consumePercentageToken()) { - return *percentageValue; - } - break; - case CSSTokenType::Number: - if (auto numberValue = - consumeNumberToken()) { - return *numberValue; - } - break; - default: - break; - } - - consumeToken(); - return {}; - } - - constexpr void consumeWhitespace() { - while (peek().type() == CSSTokenType::WhiteSpace) { - consumeToken(); - } - } - - constexpr bool hasMoreTokens() const { - return peek().type() != CSSTokenType::EndOfFile; - } - - private: - constexpr const CSSToken& peek() const { - return currentToken_; - } - - constexpr CSSToken consumeToken() { - auto prevToken = currentToken_; - currentToken_ = tokenizer_.next(); - return prevToken; - } - - template - constexpr std::optional consumeIdentToken() { - if constexpr (!std::is_same_v) { - if (auto keyword = parseCSSKeyword( - peek().stringValue())) { - consumeToken(); - return CSSValueT::keyword(*keyword); - } - } - if constexpr (traits::containsType()) { - if (auto keyword = - parseCSSKeyword(peek().stringValue())) { - consumeToken(); - return CSSValueT::cssWideKeyword(*keyword); - } - } - return {}; - } - - template - constexpr std::optional consumeDimensionToken() { - if constexpr (traits::containsType()) { - if (auto unit = parseCSSLengthUnit(peek().unit())) { - return CSSValueT::length(consumeToken().numericValue(), *unit); - } - } - if constexpr (traits::containsType()) { - if (auto unit = parseCSSAngleUnit(peek().unit())) { - return CSSValueT::angle( - canonicalize(consumeToken().numericValue(), *unit)); - } - } - return {}; - } - - template - constexpr std::optional consumePercentageToken() { - if constexpr (traits::containsType()) { - return CSSValueT::percentage(consumeToken().numericValue()); - } - return {}; - } - - template - constexpr std::optional consumeNumberToken() { - // = [ / ]? - // https://www.w3.org/TR/css-values-4/#ratio - if constexpr (traits::containsType()) { - if (isValidRatioPart(peek().numericValue())) { - float numerator = consumeToken().numericValue(); - float denominator = 1.0; - - consumeWhitespace(); - if (peek().type() != CSSTokenType::Ident && - peek().stringValue() == "/") { - consumeToken(); - consumeWhitespace(); - - if (peek().type() == CSSTokenType::Number && - isValidRatioPart(peek().numericValue())) { - denominator = consumeToken().numericValue(); - } else { - return CSSValueT{}; - } - } - - return CSSValueT::ratio(numerator, denominator); - } - } - - if constexpr (traits::containsType()) { - return CSSValueT::number(consumeToken().numericValue()); - } - - // For zero lengths the unit identifier is optional (i.e. can be - // syntactically represented as the 0). However, if a 0 could - // be parsed as either a or a in a property (such as - // line-height), it must parse as a . - // https://www.w3.org/TR/css-values-4/#lengths - if constexpr (traits::containsType()) { - if (peek().numericValue() == 0) { - return CSSValueT::length( - consumeToken().numericValue(), CSSLengthUnit::Px); - } - } - - return {}; - } - - static constexpr bool isValidRatioPart(float value) { - // If either number in the is 0 or infinite, it represents a - // degenerate ratio (and, generally, won’t do anything). - // https://www.w3.org/TR/css-values-4/#ratios - return value > 0.0f && value != +std::numeric_limits::infinity() && - value != -std::numeric_limits::infinity(); - } - - CSSTokenizer tokenizer_; - CSSToken currentToken_; -}; - -} // namespace detail - -/* - * Parse a single CSS component value as a keyword constrained to those - * allowable by KeywordRepresentationT. Returns a default-constructed - * CSSValueVariant (KeywordT::Unset) on syntax error. - * - * https://www.w3.org/TR/css-syntax-3/#parse-component-value - */ -template -constexpr void parseCSSComponentValue( - std::string_view css, - CSSValueVariant& value) { - detail::CSSParser parser(css); - - parser.consumeWhitespace(); - auto componentValue = parser.consumeComponentValue(); - parser.consumeWhitespace(); - - if (parser.hasMoreTokens()) { - value = {}; - } else { - value = componentValue; - } -}; - -template -CSSValueVariant parseCSSComponentValue(std::string_view css) { - CSSValueVariant value; - parseCSSComponentValue(css, value); - return value; -}; - -template -constexpr CSSDeclaredValue parseCSSProp(std::string_view css) { - // For now we only allow parsing props composed of a single component value. - CSSDeclaredValue value; - parseCSSComponentValue(css, value); - return value; -} - -} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h new file mode 100644 index 00000000000000..4e48c12637e34a --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace facebook::react { + +/** + * CSSSyntaxParser allows parsing streams of CSS text into "component values", + * being either a preserved token, or a function. + * + * https://www.w3.org/TR/css-syntax-3/#component-value + */ +class CSSSyntaxParser { + public: + struct Function; + + using PreservedToken = CSSToken; + using ComponentValue = std::variant; + + struct Function { + std::string_view name{}; + std::vector args{}; + }; + + /** + * Construct the parser over the given string_view, which must stay alive for + * the duration of the CSSSyntaxParser. + */ + explicit constexpr CSSSyntaxParser(std::string_view css) + : tokenizer_{css}, currentToken_(tokenizer_.next()) {} + + /** + * Directly consume the next component value + * + * https://www.w3.org/TR/css-syntax-3/#consume-component-value + */ + inline ComponentValue consumeComponentValue() { + if (peek().type() == CSSTokenType::Function) { + auto function = consumeFunction(); + return function.has_value() ? ComponentValue{std::move(*function)} + : ComponentValue{}; + } else { + return consumeToken(); + } + } + + private: + constexpr const CSSToken& peek() const { + return currentToken_; + } + + constexpr CSSToken consumeToken() { + auto prevToken = currentToken_; + currentToken_ = tokenizer_.next(); + return prevToken; + } + + inline std::optional consumeFunction() { + // https://www.w3.org/TR/css-syntax-3/#consume-a-function + Function function{.name = consumeToken().stringValue()}; + + while (true) { + auto nextValue = consumeComponentValue(); + if (std::holds_alternative(nextValue)) { + return {}; + } + + if (auto token = std::get_if(&nextValue)) { + if (token->type() == CSSTokenType::CloseParen) { + return function; + } + if (token->type() == CSSTokenType::EndOfFile) { + return {}; + } + function.args.emplace_back(std::move(*token)); + continue; + } + + if (auto func = std::get_if(&nextValue)) { + function.args.emplace_back(std::move(*func)); + continue; + } + + return {}; + } + + return function; + } + + CSSTokenizer tokenizer_; + CSSToken currentToken_; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h new file mode 100644 index 00000000000000..cb7e825540f017 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace facebook::react { + +namespace detail { + +class CSSValueParser { + public: + explicit inline CSSValueParser(std::string_view css) + : parser_{css}, currentComponentValue_{parser_.consumeComponentValue()} {} + + /* + * Attempts to parse the characters starting at the current component value + * into one of the given data types. + */ + template + inline CSSValueVariant consumeValue() { + using CSSValueT = CSSValueVariant; + + if (holdsToken()) { + switch (peekToken().type()) { + case CSSTokenType::Ident: + if (auto keywordValue = + consumeIdentToken()) { + return *keywordValue; + } + break; + case CSSTokenType::Dimension: + if (auto dimensionValue = + consumeDimensionToken()) { + return *dimensionValue; + } + break; + case CSSTokenType::Percentage: + if (auto percentageValue = + consumePercentageToken()) { + return *percentageValue; + } + break; + case CSSTokenType::Number: + if (auto numberValue = + consumeNumberToken()) { + return *numberValue; + } + break; + default: + break; + } + } + + if (holdsFunction()) { + // Function component values not yet supported + } + + consumeComponentValue(); + return {}; + } + + inline void consumeWhitespace() { + while (holdsToken() && peekToken().type() == CSSTokenType::WhiteSpace) { + consumeComponentValue(); + } + } + + constexpr bool isFinished() const { + return holdsToken() && peekToken().type() == CSSTokenType::EndOfFile; + } + + private: + constexpr const CSSSyntaxParser::ComponentValue& peek() const { + return currentComponentValue_; + } + + constexpr bool holdsToken() const { + return std::holds_alternative(peek()); + } + + constexpr bool holdsFunction() const { + return std::holds_alternative(peek()); + } + + constexpr const CSSSyntaxParser::PreservedToken& peekToken() const { + return std::get(peek()); + } + + constexpr const CSSSyntaxParser::Function& peekFunction() const { + return std::get(peek()); + } + + inline CSSSyntaxParser::ComponentValue consumeComponentValue() { + auto prevComponentValue = std::move(currentComponentValue_); + currentComponentValue_ = parser_.consumeComponentValue(); + return prevComponentValue; + } + + inline CSSSyntaxParser::PreservedToken consumeToken() { + return std::get(consumeComponentValue()); + } + + inline CSSSyntaxParser::Function consumeFunction() { + return std::get(consumeComponentValue()); + } + + template + inline std::optional consumeIdentToken() { + if constexpr (!std::is_same_v) { + if (auto keyword = parseCSSKeyword( + peekToken().stringValue())) { + consumeComponentValue(); + return CSSValueT::keyword(*keyword); + } + } + if constexpr (traits::containsType()) { + if (auto keyword = + parseCSSKeyword(peekToken().stringValue())) { + consumeComponentValue(); + return CSSValueT::cssWideKeyword(*keyword); + } + } + return {}; + } + + template + inline std::optional consumeDimensionToken() { + if constexpr (traits::containsType()) { + if (auto unit = parseCSSLengthUnit(peekToken().unit())) { + return CSSValueT::length(consumeToken().numericValue(), *unit); + } + } + if constexpr (traits::containsType()) { + if (auto unit = parseCSSAngleUnit(peekToken().unit())) { + return CSSValueT::angle( + canonicalize(consumeToken().numericValue(), *unit)); + } + } + return {}; + } + + template + constexpr std::optional consumePercentageToken() { + if constexpr (traits::containsType()) { + return CSSValueT::percentage(consumeToken().numericValue()); + } + return {}; + } + + template + constexpr std::optional consumeNumberToken() { + // = [ / ]? + // https://www.w3.org/TR/css-values-4/#ratio + if constexpr (traits::containsType()) { + if (isValidRatioPart(peekToken().numericValue())) { + float numerator = consumeToken().numericValue(); + float denominator = 1.0; + + consumeWhitespace(); + + if (holdsToken() && peekToken().type() == CSSTokenType::Delim && + peekToken().stringValue() == "/") { + consumeToken(); + consumeWhitespace(); + + // TODO: for now, we don't support denominator being a function + // component value. CSS math functions allow substituion though, where + // this usage is valid. + if (holdsToken() && peekToken().type() == CSSTokenType::Number && + isValidRatioPart(peekToken().numericValue())) { + denominator = consumeToken().numericValue(); + } else { + return {}; + } + } + + return CSSValueT::ratio(numerator, denominator); + } + } + + if constexpr (traits::containsType()) { + return CSSValueT::number(consumeToken().numericValue()); + } + + // For zero lengths the unit identifier is optional (i.e. can be + // syntactically represented as the 0). However, if a 0 + // could be parsed as either a or a in a + // property (such as line-height), it must parse as a . + // https://www.w3.org/TR/css-values-4/#lengths + if constexpr (traits::containsType()) { + if (peekToken().numericValue() == 0) { + return CSSValueT::length( + consumeToken().numericValue(), CSSLengthUnit::Px); + } + } + + return {}; + } + + constexpr bool isValidRatioPart(float value) { + // If either number in the is 0 or infinite, it represents a + // degenerate ratio (and, generally, won’t do anything). + // https://www.w3.org/TR/css-values-4/#ratios + return value > 0.0f && value != +std::numeric_limits::infinity() && + value != -std::numeric_limits::infinity(); + } + + CSSSyntaxParser parser_; + CSSSyntaxParser::ComponentValue currentComponentValue_; +}; + +template +inline void parseCSSValue( + std::string_view css, + CSSValueVariant& value) { + detail::CSSValueParser parser(css); + + parser.consumeWhitespace(); + auto componentValue = parser.consumeValue(); + parser.consumeWhitespace(); + + if (parser.isFinished()) { + value = std::move(componentValue); + } else { + value = {}; + } +}; + +} // namespace detail + +/** + * Parse a single CSS value. Returns a default-constructed + * CSSValueVariant (CSSKeyword::Unset) on syntax error. + */ +template +CSSValueVariant parseCSSValue(std::string_view css) { + CSSValueVariant value; + detail::parseCSSValue(css, value); + return value; +}; + +/** + * Parses a CSS property into its declared value type. + */ +template +constexpr CSSDeclaredValue parseCSSProp(std::string_view css) { + // For now we only allow parsing props composed of a single component value. + CSSDeclaredValue value; + detail::parseCSSValue(css, value); + return value; +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp index 60201259e5ccc7..393d48b1618c0f 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSSyntaxParserTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSSyntaxParserTest.cpp new file mode 100644 index 00000000000000..414e110d05ef17 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSSyntaxParserTest.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +namespace facebook::react { + +TEST(CSSSyntaxParser, simple) { + CSSSyntaxParser parser{"1px solid black"}; + + auto value = parser.consumeComponentValue(); + EXPECT_TRUE(std::holds_alternative(value)); + EXPECT_EQ( + std::get(value).type(), + CSSTokenType::Dimension); + EXPECT_EQ( + std::get(value).numericValue(), 1.0f); + EXPECT_EQ(std::get(value).unit(), "px"); + + value = parser.consumeComponentValue(); + EXPECT_TRUE(std::holds_alternative(value)); + EXPECT_EQ( + std::get(value).type(), + CSSTokenType::WhiteSpace); + + value = parser.consumeComponentValue(); + EXPECT_TRUE(std::holds_alternative(value)); + EXPECT_EQ( + std::get(value).type(), + CSSTokenType::Ident); + EXPECT_EQ( + std::get(value).stringValue(), "solid"); + + value = parser.consumeComponentValue(); + EXPECT_TRUE(std::holds_alternative(value)); + EXPECT_EQ( + std::get(value).type(), + CSSTokenType::WhiteSpace); + + value = parser.consumeComponentValue(); + EXPECT_TRUE(std::holds_alternative(value)); + EXPECT_EQ( + std::get(value).type(), + CSSTokenType::Ident); + EXPECT_EQ( + std::get(value).stringValue(), "black"); +} + +TEST(CSSSyntaxParser, single_function_no_args) { + CSSSyntaxParser parser{"foo()"}; + + auto value = parser.consumeComponentValue(); + EXPECT_TRUE(std::holds_alternative(value)); + + auto function = std::get(value); + EXPECT_EQ(function.name, "foo"); + EXPECT_TRUE(function.args.empty()); +} + +TEST(CSSSyntaxParser, single_function_with_args) { + CSSSyntaxParser parser{"foo(a b, c)"}; + + auto value = parser.consumeComponentValue(); + EXPECT_TRUE(std::holds_alternative(value)); + + auto function = std::get(value); + EXPECT_EQ(function.name, "foo"); + EXPECT_EQ(function.args.size(), 6); + + auto arg0 = std::get(function.args.at(0)); + EXPECT_EQ(arg0.type(), CSSTokenType::Ident); + EXPECT_EQ(arg0.stringValue(), "a"); + + auto arg1 = std::get(function.args.at(1)); + EXPECT_EQ(arg1.type(), CSSTokenType::WhiteSpace); + + auto arg2 = std::get(function.args.at(2)); + EXPECT_EQ(arg2.type(), CSSTokenType::Ident); + EXPECT_EQ(arg2.stringValue(), "b"); + + auto arg3 = std::get(function.args.at(3)); + EXPECT_EQ(arg3.type(), CSSTokenType::Comma); + + auto arg4 = std::get(function.args.at(4)); + EXPECT_EQ(arg4.type(), CSSTokenType::WhiteSpace); + + auto arg5 = std::get(function.args.at(5)); + EXPECT_EQ(arg5.type(), CSSTokenType::Ident); + EXPECT_EQ(arg5.stringValue(), "c"); +} + +TEST(CSSSyntaxParser, complex) { + CSSSyntaxParser parser{"foo(a bar())baz() 12px"}; + + auto function1 = + std::get(parser.consumeComponentValue()); + EXPECT_EQ(function1.name, "foo"); + EXPECT_EQ(function1.args.size(), 3); + + auto arg0 = std::get(function1.args.at(0)); + EXPECT_EQ(arg0.type(), CSSTokenType::Ident); + EXPECT_EQ(arg0.stringValue(), "a"); + + auto arg1 = std::get(function1.args.at(1)); + EXPECT_EQ(arg1.type(), CSSTokenType::WhiteSpace); + + auto arg2 = std::get(function1.args.at(2)); + EXPECT_EQ(arg2.name, "bar"); + EXPECT_TRUE(arg2.args.empty()); + + auto function2 = + std::get(parser.consumeComponentValue()); + EXPECT_EQ(function2.name, "baz"); + EXPECT_TRUE(function2.args.empty()); + + auto ws = + std::get(parser.consumeComponentValue()); + EXPECT_EQ(ws.type(), CSSTokenType::WhiteSpace); + + auto dim = + std::get(parser.consumeComponentValue()); + EXPECT_EQ(dim.type(), CSSTokenType::Dimension); + EXPECT_EQ(dim.numericValue(), 12.0f); + EXPECT_EQ(dim.unit(), "px"); +} + +TEST(CSSSyntaxParser, unterminated_functions) { + EXPECT_TRUE(std::holds_alternative( + CSSSyntaxParser{"foo("}.consumeComponentValue())); + + EXPECT_TRUE(std::holds_alternative( + CSSSyntaxParser{"foo(a bar()baz()"}.consumeComponentValue())); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSParserTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp similarity index 65% rename from packages/react-native/ReactCommon/react/renderer/css/tests/CSSParserTest.cpp rename to packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp index 16aac07d1e5b82..c64d5539a34ba1 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSParserTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp @@ -6,296 +6,276 @@ */ #include -#include +#include namespace facebook::react { -TEST(CSSParser, keyword_values) { - auto emptyValue = parseCSSComponentValue(""); +TEST(CSSValueParser, keyword_values) { + auto emptyValue = parseCSSValue(""); EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto autoValue = parseCSSComponentValue("auto"); + auto autoValue = parseCSSValue("auto"); EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); - auto autoCapsValue = - parseCSSComponentValue("AuTO"); + auto autoCapsValue = parseCSSValue("AuTO"); EXPECT_EQ(autoCapsValue.type(), CSSValueType::Keyword); EXPECT_EQ(autoCapsValue.getKeyword(), CSSKeyword::Auto); - auto autoDisallowedValue = parseCSSComponentValue("auto"); + auto autoDisallowedValue = parseCSSValue("auto"); EXPECT_EQ(autoDisallowedValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(autoDisallowedValue.getCSSWideKeyword(), CSSWideKeyword::Unset); auto whitespaceValue = - parseCSSComponentValue(" flex-start "); + parseCSSValue(" flex-start "); EXPECT_EQ(whitespaceValue.type(), CSSValueType::Keyword); EXPECT_EQ(whitespaceValue.getKeyword(), CSSKeyword::FlexStart); - auto badIdentValue = - parseCSSComponentValue("bad"); + auto badIdentValue = parseCSSValue("bad"); EXPECT_EQ(badIdentValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(badIdentValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto pxValue = parseCSSComponentValue("20px"); + auto pxValue = parseCSSValue("20px"); EXPECT_EQ(pxValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(pxValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto multiValue = parseCSSComponentValue("auto flex-start"); + auto multiValue = parseCSSValue("auto flex-start"); EXPECT_EQ(multiValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(multiValue.getCSSWideKeyword(), CSSWideKeyword::Unset); } -TEST(CSSParser, length_values) { - auto emptyValue = parseCSSComponentValue(""); +TEST(CSSValueParser, length_values) { + auto emptyValue = parseCSSValue(""); EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto autoValue = - parseCSSComponentValue("auto"); + auto autoValue = parseCSSValue("auto"); EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); - auto pxValue = parseCSSComponentValue("20px"); + auto pxValue = parseCSSValue("20px"); EXPECT_EQ(pxValue.type(), CSSValueType::Length); EXPECT_EQ(pxValue.getLength().value, 20.0f); EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px); - auto cmValue = parseCSSComponentValue("453cm"); + auto cmValue = parseCSSValue("453cm"); EXPECT_EQ(cmValue.type(), CSSValueType::Length); EXPECT_EQ(cmValue.getLength().value, 453.0f); EXPECT_EQ(cmValue.getLength().unit, CSSLengthUnit::Cm); - auto unitlessZeroValue = - parseCSSComponentValue("0"); + auto unitlessZeroValue = parseCSSValue("0"); EXPECT_EQ(unitlessZeroValue.type(), CSSValueType::Length); EXPECT_EQ(unitlessZeroValue.getLength().value, 0.0f); EXPECT_EQ(unitlessZeroValue.getLength().unit, CSSLengthUnit::Px); - auto unitlessNonzeroValue = - parseCSSComponentValue("123"); + auto unitlessNonzeroValue = parseCSSValue("123"); EXPECT_EQ(unitlessNonzeroValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(unitlessNonzeroValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto pctValue = parseCSSComponentValue("-40%"); + auto pctValue = parseCSSValue("-40%"); EXPECT_EQ(pctValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(pctValue.getCSSWideKeyword(), CSSWideKeyword::Unset); } -TEST(CSSParser, length_percentage_values) { - auto emptyValue = - parseCSSComponentValue(""); +TEST(CSSValueParser, length_percentage_values) { + auto emptyValue = parseCSSValue(""); EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto autoValue = parseCSSComponentValue< - CSSWideKeyword, - CSSKeyword, - CSSLength, - CSSPercentage>("auto"); + auto autoValue = + parseCSSValue( + "auto"); EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); auto pxValue = - parseCSSComponentValue("20px"); + parseCSSValue("20px"); EXPECT_EQ(pxValue.type(), CSSValueType::Length); EXPECT_EQ(pxValue.getLength().value, 20.0f); EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px); auto pctValue = - parseCSSComponentValue("-40%"); + parseCSSValue("-40%"); EXPECT_EQ(pctValue.type(), CSSValueType::Percentage); EXPECT_EQ(pctValue.getPercentage().value, -40.0f); } -TEST(CSSParser, number_values) { - auto emptyValue = parseCSSComponentValue(""); +TEST(CSSValueParser, number_values) { + auto emptyValue = parseCSSValue(""); EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto inheritValue = - parseCSSComponentValue("inherit"); + auto inheritValue = parseCSSValue("inherit"); EXPECT_EQ(inheritValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(inheritValue.getCSSWideKeyword(), CSSWideKeyword::Inherit); - auto pxValue = - parseCSSComponentValue("20px"); + auto pxValue = parseCSSValue("20px"); EXPECT_EQ(pxValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(pxValue.getCSSWideKeyword(), CSSWideKeyword::Unset); auto numberValue = - parseCSSComponentValue("123.456"); + parseCSSValue("123.456"); EXPECT_EQ(numberValue.type(), CSSValueType::Number); EXPECT_EQ(numberValue.getNumber().value, 123.456f); auto unitlessZeroValue = - parseCSSComponentValue("0"); + parseCSSValue("0"); EXPECT_EQ(unitlessZeroValue.type(), CSSValueType::Number); EXPECT_EQ(unitlessZeroValue.getNumber().value, 0.0f); } -TEST(CSSParser, ratio_values) { - auto emptyValue = parseCSSComponentValue(""); +TEST(CSSValueParser, ratio_values) { + auto emptyValue = parseCSSValue(""); EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto validRatio = parseCSSComponentValue("16/9"); + auto validRatio = parseCSSValue("16/9"); EXPECT_EQ(validRatio.type(), CSSValueType::Ratio); EXPECT_EQ(validRatio.getRatio().numerator, 16.0f); EXPECT_EQ(validRatio.getRatio().denominator, 9.0f); auto validRatioWithWhitespace = - parseCSSComponentValue("16 / 9"); + parseCSSValue("16 / 9"); EXPECT_EQ(validRatioWithWhitespace.type(), CSSValueType::Ratio); EXPECT_EQ(validRatioWithWhitespace.getRatio().numerator, 16.0f); EXPECT_EQ(validRatioWithWhitespace.getRatio().denominator, 9.0f); - auto singleNumberRatio = - parseCSSComponentValue("16"); + auto singleNumberRatio = parseCSSValue("16"); EXPECT_EQ(singleNumberRatio.type(), CSSValueType::Ratio); EXPECT_EQ(singleNumberRatio.getRatio().numerator, 16.0f); EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); - auto fractionalNumber = - parseCSSComponentValue("16.5"); + auto fractionalNumber = parseCSSValue("16.5"); EXPECT_EQ(fractionalNumber.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalNumber.getRatio().numerator, 16.5f); EXPECT_EQ(fractionalNumber.getRatio().denominator, 1.0f); - auto negativeNumber = parseCSSComponentValue("-16"); + auto negativeNumber = parseCSSValue("-16"); EXPECT_EQ(negativeNumber.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(negativeNumber.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto missingDenominator = - parseCSSComponentValue("16/"); + auto missingDenominator = parseCSSValue("16/"); EXPECT_EQ(missingDenominator.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(missingDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto negativeNumerator = - parseCSSComponentValue("-16/9"); + auto negativeNumerator = parseCSSValue("-16/9"); EXPECT_EQ(negativeNumerator.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(negativeNumerator.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto negativeDenominator = - parseCSSComponentValue("16/-9"); + auto negativeDenominator = parseCSSValue("16/-9"); EXPECT_EQ(negativeDenominator.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(negativeDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto fractionalNumerator = - parseCSSComponentValue("16.5/9"); + auto fractionalNumerator = parseCSSValue("16.5/9"); EXPECT_EQ(fractionalNumerator.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalNumerator.getRatio().numerator, 16.5f); EXPECT_EQ(fractionalNumerator.getRatio().denominator, 9.0f); auto fractionalDenominator = - parseCSSComponentValue("16/9.5"); + parseCSSValue("16/9.5"); EXPECT_EQ(fractionalDenominator.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalDenominator.getRatio().numerator, 16.0f); EXPECT_EQ(fractionalDenominator.getRatio().denominator, 9.5f); - auto degenerateRatio = parseCSSComponentValue("0"); + auto degenerateRatio = parseCSSValue("0"); EXPECT_EQ(degenerateRatio.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(degenerateRatio.getCSSWideKeyword(), CSSWideKeyword::Unset); } -TEST(CSSParser, number_ratio_values) { - auto emptyValue = - parseCSSComponentValue(""); +TEST(CSSValueParser, number_ratio_values) { + auto emptyValue = parseCSSValue(""); EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto validRatio = - parseCSSComponentValue("16/9"); + auto validRatio = parseCSSValue("16/9"); EXPECT_EQ(validRatio.type(), CSSValueType::Ratio); EXPECT_EQ(validRatio.getRatio().numerator, 16.0f); EXPECT_EQ(validRatio.getRatio().denominator, 9.0f); auto validRatioWithWhitespace = - parseCSSComponentValue("16 / 9"); + parseCSSValue("16 / 9"); EXPECT_EQ(validRatioWithWhitespace.type(), CSSValueType::Ratio); EXPECT_EQ(validRatioWithWhitespace.getRatio().numerator, 16.0f); EXPECT_EQ(validRatioWithWhitespace.getRatio().denominator, 9.0f); auto singleNumberRatio = - parseCSSComponentValue("16"); + parseCSSValue("16"); EXPECT_EQ(singleNumberRatio.type(), CSSValueType::Ratio); EXPECT_EQ(singleNumberRatio.getRatio().numerator, 16.0f); EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); auto fractionalNumber = - parseCSSComponentValue("16.5"); + parseCSSValue("16.5"); EXPECT_EQ(fractionalNumber.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalNumber.getRatio().numerator, 16.5f); EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); auto negativeNumber = - parseCSSComponentValue("-16"); + parseCSSValue("-16"); EXPECT_EQ(negativeNumber.type(), CSSValueType::Number); EXPECT_EQ(negativeNumber.getNumber().value, -16.0f); auto missingDenominator = - parseCSSComponentValue("16/"); + parseCSSValue("16/"); EXPECT_EQ(missingDenominator.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(missingDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); auto negativeNumerator = - parseCSSComponentValue("-16/9"); + parseCSSValue("-16/9"); EXPECT_EQ(negativeNumerator.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(negativeNumerator.getCSSWideKeyword(), CSSWideKeyword::Unset); auto negativeDenominator = - parseCSSComponentValue("16/-9"); + parseCSSValue("16/-9"); EXPECT_EQ(negativeDenominator.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(negativeDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); auto fractionalNumerator = - parseCSSComponentValue("16.5/9"); + parseCSSValue("16.5/9"); EXPECT_EQ(fractionalNumerator.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalNumerator.getRatio().numerator, 16.5f); EXPECT_EQ(fractionalNumerator.getRatio().denominator, 9.0f); auto fractionalDenominator = - parseCSSComponentValue("16/9.5"); + parseCSSValue("16/9.5"); EXPECT_EQ(fractionalDenominator.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalDenominator.getRatio().numerator, 16.0f); EXPECT_EQ(fractionalDenominator.getRatio().denominator, 9.5f); auto degenerateRatio = - parseCSSComponentValue("0"); + parseCSSValue("0"); EXPECT_EQ(degenerateRatio.type(), CSSValueType::Number); EXPECT_EQ(degenerateRatio.getNumber().value, 0.0f); } -TEST(CSSParser, angle_values) { - auto emptyValue = parseCSSComponentValue(""); +TEST(CSSValueParser, angle_values) { + auto emptyValue = parseCSSValue(""); EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto degreeValue = parseCSSComponentValue("10deg"); + auto degreeValue = parseCSSValue("10deg"); EXPECT_EQ(degreeValue.type(), CSSValueType::Angle); EXPECT_EQ(degreeValue.getAngle().degrees, 10.0f); - auto radianValue = parseCSSComponentValue("10rad"); + auto radianValue = parseCSSValue("10rad"); EXPECT_EQ(radianValue.type(), CSSValueType::Angle); EXPECT_NEAR(radianValue.getAngle().degrees, 572.958f, 0.001f); - auto negativeRadianValue = - parseCSSComponentValue("-10rad"); + auto negativeRadianValue = parseCSSValue("-10rad"); EXPECT_EQ(negativeRadianValue.type(), CSSValueType::Angle); EXPECT_NEAR(negativeRadianValue.getAngle().degrees, -572.958f, 0.001f); - auto gradianValue = - parseCSSComponentValue("10grad"); + auto gradianValue = parseCSSValue("10grad"); EXPECT_EQ(gradianValue.type(), CSSValueType::Angle); ASSERT_NEAR(gradianValue.getAngle().degrees, 9.0f, 0.001f); - auto turnValue = parseCSSComponentValue(".25turn"); + auto turnValue = parseCSSValue(".25turn"); EXPECT_EQ(turnValue.type(), CSSValueType::Angle); EXPECT_EQ(turnValue.getAngle().degrees, 90.0f); } -TEST(CSSParser, parse_prop) { +TEST(CSSValueParser, parse_prop) { auto emptyValue = parseCSSProp(""); EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); @@ -327,17 +307,4 @@ TEST(CSSParser, parse_prop) { EXPECT_EQ(keywordlessValue.getLength().unit, CSSLengthUnit::Px); } -TEST(CSSParser, parse_keyword_prop_constexpr) { - constexpr auto rowValue = parseCSSProp("row"); - EXPECT_EQ(rowValue.type(), CSSValueType::Keyword); - EXPECT_EQ(rowValue.getKeyword(), CSSKeyword::Row); -} - -TEST(CSSParser, parse_length_prop_constexpr) { - constexpr auto pxValue = parseCSSProp("2px"); - EXPECT_EQ(pxValue.type(), CSSValueType::Length); - EXPECT_EQ(pxValue.getLength().value, 2.0f); - EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px); -} - } // namespace facebook::react