/* * 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 /*unused*/) { using ReturnT = std::variant; auto consumedValue = tryConsumeParser...>(delimeter); if (!std::holds_alternative(consumedValue)) { return consumedValue; } return parser_.consumeComponentValue( delimeter, [&](const CSSPreservedToken &token) { return tryConsumePreservedToken...>(token); }, [&](const CSSSimpleBlock &block, CSSSyntaxParser &blockParser) { return tryConsumeSimpleBlock...>(block, blockParser); }, [&](const CSSFunctionBlock &func, CSSSyntaxParser &blockParser) { return tryConsumeFunctionBlock...>(func, blockParser); }); } constexpr bool isFinished() const { return parser_.isFinished(); } constexpr void consumeWhitespace() { parser_.consumeWhitespace(); } private: template constexpr ReturnT tryConsumePreservedToken(const CSSPreservedToken & /*token*/) { return {}; } template 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 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 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 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, 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, 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, std::monostate> { auto savedParser = syntaxParser; detail::CSSValueParser valueParser(syntaxParser); auto ret = valueParser.consumeValue(delimeter, CSSMergedDataTypes{}); syntaxParser = savedParser; return ret; } } // namespace facebook::react