/* * 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 namespace facebook::react { namespace detail { class CSSValueParser { public: explicit constexpr CSSValueParser(CSSSyntaxParser& parser) : parser_{parser} {} /* * Attempts to parse the characters starting at the current component value * into one of the given data types. Types are attempted in order, which the * caller must consider (e.g. a number token should be preferred to be a * before ). */ template constexpr std::variant consumeValue( CSSDelimiter delimeter, CSSCompoundDataType) { using ReturnT = std::variant; auto consumedValue = tryConsumeParser...>( delimeter); if (!std::holds_alternative(consumedValue)) { return consumedValue; } return parser_.consumeComponentValue( delimeter, [&](const CSSPreservedToken& token) { return tryConsumePreservedToken< ReturnT, CSSDataTypeParser...>(token); }, [&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) { return tryConsumeSimpleBlock< ReturnT, CSSDataTypeParser...>(block, blockParser); }, [&](const CSSFunctionBlock& func, CSSSyntaxParser& blockParser) { return tryConsumeFunctionBlock< ReturnT, CSSDataTypeParser...>(func, blockParser); }); } constexpr bool isFinished() const { return parser_.isFinished(); } constexpr void consumeWhitespace() { parser_.consumeWhitespace(); } private: template constexpr ReturnT tryConsumePreservedToken( const CSSPreservedToken& /*token*/) { return {}; } template < typename ReturnT, CSSValidDataTypeParser ParserT, CSSValidDataTypeParser... RestParserT> constexpr ReturnT tryConsumePreservedToken(const CSSPreservedToken& token) { if constexpr (CSSPreservedTokenSink) { if (auto ret = ParserT::consumePreservedToken(token)) { return *ret; } } return tryConsumePreservedToken(token); } template constexpr ReturnT tryConsumeSimpleBlock( const CSSSimpleBlock& /*token*/, CSSSyntaxParser& /*blockParser*/) { return {}; } template < typename ReturnT, CSSValidDataTypeParser ParserT, CSSValidDataTypeParser... RestParserT> constexpr ReturnT tryConsumeSimpleBlock( const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) { if constexpr (CSSSimpleBlockSink) { auto currentParser = blockParser; if (auto ret = ParserT::consumeSimpleBlock(block, blockParser)) { return *ret; } blockParser = currentParser; } return tryConsumeSimpleBlock(block, blockParser); } template constexpr ReturnT tryConsumeFunctionBlock( const CSSFunctionBlock& /*func*/, CSSSyntaxParser& /*blockParser*/) { return {}; } template < typename ReturnT, CSSValidDataTypeParser ParserT, CSSValidDataTypeParser... RestParserT> constexpr ReturnT tryConsumeFunctionBlock( const CSSFunctionBlock& func, CSSSyntaxParser& blockParser) { if constexpr (CSSFunctionBlockSink) { auto currentParser = blockParser; if (auto ret = ParserT::consumeFunctionBlock(func, blockParser)) { return *ret; } blockParser = currentParser; } 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 originalParser = parser_; if (parser_.consumeDelimiter(delimeter)) { if (auto ret = ParserT::consume(parser_)) { return *ret; } } parser_ = originalParser; } return tryConsumeParser(delimeter); } CSSSyntaxParser& parser_; }; } // namespace detail /** * Parse a single CSS property value. Returns a variant holding std::monostate * on syntax error. */ template constexpr auto parseCSSProperty(std::string_view css) -> CSSVariantWithTypes< CSSMergedDataTypes, std::monostate> { CSSSyntaxParser syntaxParser(css); detail::CSSValueParser parser(syntaxParser); parser.consumeWhitespace(); auto value = parser.consumeValue( CSSDelimiter::None, CSSMergedDataTypes{}); parser.consumeWhitespace(); if (parser.isFinished()) { return value; } return {}; }; /** * Attempts to parse the next CSS value of a given set of data types, at the * current location of the syntax parser, advancing the syntax parser */ template constexpr auto parseNextCSSValue( CSSSyntaxParser& syntaxParser, CSSDelimiter delimeter = CSSDelimiter::None) -> CSSVariantWithTypes< CSSMergedDataTypes, std::monostate> { detail::CSSValueParser valueParser(syntaxParser); return valueParser.consumeValue( delimeter, CSSMergedDataTypes{}); } /** * Attempts to parse the next CSS value of a given set of data types, at the * current location of the syntax parser, without advancing the syntax parser */ template constexpr auto peekNextCSSValue( CSSSyntaxParser& syntaxParser, CSSDelimiter delimeter = CSSDelimiter::None) -> CSSVariantWithTypes< CSSMergedDataTypes, std::monostate> { auto savedParser = syntaxParser; detail::CSSValueParser valueParser(syntaxParser); auto ret = valueParser.consumeValue( delimeter, CSSMergedDataTypes{}); syntaxParser = savedParser; return ret; } } // namespace facebook::react