diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h b/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h index 111b208bc5d783..5b6a0788e566a1 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h @@ -48,29 +48,27 @@ concept CSSSimpleBlockSink = * concrete representation. */ template -concept CSSPreservedTokenSink = - requires(const CSSPreservedToken& token, CSSSyntaxParser& parser) { - { - T::consumePreservedToken(token, parser) - } -> std::convertible_to; - }; +concept CSSPreservedTokenSink = requires(const CSSPreservedToken& token) { + { T::consumePreservedToken(token) } -> std::convertible_to; +}; /** - * Accepts a CSS preserved token and may parse it into a concrete - * representation. + * Accepts a raw syntax parser, to be able to parse compounded values */ template -concept CSSSimplePreservedTokenSink = requires(const CSSPreservedToken& token) { - { T::consumePreservedToken(token) } -> std::convertible_to; +concept CSSParserSink = requires(CSSSyntaxParser& parser) { + { T::consume(parser) } -> std::convertible_to; }; /** * Represents a valid specialization of CSSDataTypeParser */ template -concept CSSValidDataTypeParser = CSSFunctionBlockSink || - CSSSimpleBlockSink || CSSPreservedTokenSink || - CSSSimplePreservedTokenSink; +concept CSSValidDataTypeParser = + ((CSSFunctionBlockSink || CSSSimpleBlockSink || + CSSPreservedTokenSink) && + !CSSParserSink) || + CSSParserSink; /** * Concrete representation for a CSS data type, or keywords diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h b/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h index a2d14978863a48..99844b8590cc18 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h @@ -37,23 +37,26 @@ struct CSSRatio { template <> struct CSSDataTypeParser { - static constexpr auto consumePreservedToken( - const CSSPreservedToken& token, - CSSSyntaxParser& parser) -> std::optional { + static constexpr auto consume(CSSSyntaxParser& parser) + -> std::optional { // = [ / ]? // https://www.w3.org/TR/css-values-4/#ratio - if (token.numericValue() >= 0) { - float numerator = token.numericValue(); + auto numerator = parseNextCSSValue(parser); + if (!std::holds_alternative(numerator)) { + return {}; + } + auto numeratorValue = std::get(numerator).value; + if (numeratorValue >= 0) { auto denominator = peekNextCSSValue(parser, CSSDelimiter::Solidus); if (std::holds_alternative(denominator) && std::get(denominator).value >= 0) { parseNextCSSValue(parser, CSSDelimiter::Solidus); - return CSSRatio{numerator, std::get(denominator).value}; + return CSSRatio{numeratorValue, std::get(denominator).value}; } - return CSSRatio{numerator, 1.0f}; + return CSSRatio{numeratorValue, 1.0f}; } return {}; diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h index 9fa449361e7ae1..dfb8f6ea31218c 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h @@ -173,6 +173,59 @@ class CSSSyntaxParser { } } + /** + * Consume a delimiter, returning false if a required delimiter is not found. + */ + constexpr bool consumeDelimiter(CSSDelimiter delimiter) { + if (delimiter == CSSDelimiter::None) { + return true; + } + + bool hasWhiteSpace = peek().type() == CSSTokenType::WhiteSpace; + consumeWhitespace(); + + switch (delimiter) { + case CSSDelimiter::Comma: + if (peek().type() == CSSTokenType::Comma) { + consumeToken(); + consumeWhitespace(); + return true; + } + return false; + case CSSDelimiter::Whitespace: + return hasWhiteSpace; + case CSSDelimiter::OptionalWhitespace: + return true; + case CSSDelimiter::CommaOrWhitespace: + if (peek().type() == CSSTokenType::Comma) { + consumeToken(); + consumeWhitespace(); + return true; + } + return hasWhiteSpace; + case CSSDelimiter::Solidus: + if (peek().type() == CSSTokenType::Delim && + peek().stringValue() == "/") { + consumeToken(); + consumeWhitespace(); + return true; + } + return false; + case CSSDelimiter::SolidusOrWhitespace: + if (peek().type() == CSSTokenType::Delim && + peek().stringValue() == "/") { + consumeToken(); + consumeWhitespace(); + return true; + } + return hasWhiteSpace; + case CSSDelimiter::None: + return true; + } + + return false; + } + private: constexpr CSSSyntaxParser(CSSSyntaxParser& parser, CSSTokenType terminator) : tokenizer_{parser.tokenizer_}, @@ -207,7 +260,7 @@ struct CSSComponentValueVisitorDispatcher { CSSDelimiter delimiter, const VisitorsT&... visitors) { auto originalParser = parser; - if (!consumeDelimiter(delimiter)) { + if (!parser.consumeDelimiter(delimiter)) { parser = originalParser; return {}; } @@ -252,59 +305,6 @@ struct CSSComponentValueVisitorDispatcher { return ReturnT{}; } - /** - * Consume a delimiter, returning false if a required delimiter is not found. - */ - constexpr bool consumeDelimiter(CSSDelimiter delimiter) { - if (delimiter == CSSDelimiter::None) { - return true; - } - - bool hasWhiteSpace = parser.peek().type() == CSSTokenType::WhiteSpace; - parser.consumeWhitespace(); - - switch (delimiter) { - case CSSDelimiter::Comma: - if (parser.peek().type() == CSSTokenType::Comma) { - parser.consumeToken(); - parser.consumeWhitespace(); - return true; - } - return false; - case CSSDelimiter::Whitespace: - return hasWhiteSpace; - case CSSDelimiter::OptionalWhitespace: - return true; - case CSSDelimiter::CommaOrWhitespace: - if (parser.peek().type() == CSSTokenType::Comma) { - parser.consumeToken(); - parser.consumeWhitespace(); - return true; - } - return hasWhiteSpace; - case CSSDelimiter::Solidus: - if (parser.peek().type() == CSSTokenType::Delim && - parser.peek().stringValue() == "/") { - parser.consumeToken(); - parser.consumeWhitespace(); - return true; - } - return false; - case CSSDelimiter::SolidusOrWhitespace: - if (parser.peek().type() == CSSTokenType::Delim && - parser.peek().stringValue() == "/") { - parser.consumeToken(); - parser.consumeWhitespace(); - return true; - } - return hasWhiteSpace; - case CSSDelimiter::None: - return true; - } - - return false; - } - constexpr std::optional visitFunction( const CSSComponentValueVisitor auto& visitor, const CSSComponentValueVisitor auto&... rest) { diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h index 7ce6935e7c690d..f17f427bc6c0af 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h @@ -35,6 +35,14 @@ class CSSValueParser { CSSDelimiter delimeter = CSSDelimiter::None) { using ReturnT = std::variant; + auto consumedValue = + tryConsumeParser...>( + delimeter); + + if (!std::holds_alternative(consumedValue)) { + return consumedValue; + } + return parser_.consumeComponentValue( delimeter, [&](const CSSPreservedToken& token) { @@ -75,14 +83,6 @@ class CSSValueParser { CSSValidDataTypeParser... RestParserT> constexpr ReturnT tryConsumePreservedToken(const CSSPreservedToken& token) { if constexpr (CSSPreservedTokenSink) { - auto currentParser = parser_; - if (auto ret = ParserT::consumePreservedToken(token, parser_)) { - return *ret; - } - parser_ = currentParser; - } - - if constexpr (CSSSimplePreservedTokenSink) { if (auto ret = ParserT::consumePreservedToken(token)) { return *ret; } @@ -141,6 +141,29 @@ class CSSValueParser { return tryConsumeFunctionBlock(func, blockParser); } + template + constexpr ReturnT tryConsumeParser(CSSDelimiter /*delimeter*/) { + return {}; + } + + template < + typename ReturnT, + CSSValidDataTypeParser ParserT, + CSSValidDataTypeParser... RestParserT> + constexpr ReturnT tryConsumeParser(CSSDelimiter delimeter) { + if constexpr (CSSParserSink) { + auto currentParser = parser_; + if (parser_.consumeDelimiter(delimeter)) { + if (auto ret = ParserT::consume(parser_)) { + return *ret; + } + } + parser_ = currentParser; + } + + return tryConsumeParser(delimeter); + } + CSSSyntaxParser& parser_; };