Skip to content

Commit 43854d5

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
CSSDataTypeParser consume() function (#48986)
Summary: This adds a new `consume()` function to data type parsers which passes a raw parser. This can be used for types which are compounds of other data types, where we may want to accept more than the first token. This will be used for shadow parsing, but also fixes a hypothetical future bug with ratios. E.g. `calc(foo) / calc(bar)` may be a valid ratio, not starting with a token. We instead just want to try to parse a number data type from the stream. The form of parsing a preserved token + rest is removed, with the assumption that anything parsing more than a single token should use compound parsing. Reviewed By: lenaic Differential Revision: D68735370
1 parent b273c57 commit 43854d5

File tree

4 files changed

+106
-82
lines changed

4 files changed

+106
-82
lines changed

packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h

+11-13
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,27 @@ concept CSSSimpleBlockSink =
4848
* concrete representation.
4949
*/
5050
template <typename T, typename ReturnT = std::any>
51-
concept CSSPreservedTokenSink =
52-
requires(const CSSPreservedToken& token, CSSSyntaxParser& parser) {
53-
{
54-
T::consumePreservedToken(token, parser)
55-
} -> std::convertible_to<ReturnT>;
56-
};
51+
concept CSSPreservedTokenSink = requires(const CSSPreservedToken& token) {
52+
{ T::consumePreservedToken(token) } -> std::convertible_to<ReturnT>;
53+
};
5754

5855
/**
59-
* Accepts a CSS preserved token and may parse it into a concrete
60-
* representation.
56+
* Accepts a raw syntax parser, to be able to parse compounded values
6157
*/
6258
template <typename T, typename ReturnT = std::any>
63-
concept CSSSimplePreservedTokenSink = requires(const CSSPreservedToken& token) {
64-
{ T::consumePreservedToken(token) } -> std::convertible_to<ReturnT>;
59+
concept CSSParserSink = requires(CSSSyntaxParser& parser) {
60+
{ T::consume(parser) } -> std::convertible_to<ReturnT>;
6561
};
6662

6763
/**
6864
* Represents a valid specialization of CSSDataTypeParser
6965
*/
7066
template <typename T, typename ReturnT = std::any>
71-
concept CSSValidDataTypeParser = CSSFunctionBlockSink<T, ReturnT> ||
72-
CSSSimpleBlockSink<T, ReturnT> || CSSPreservedTokenSink<T, ReturnT> ||
73-
CSSSimplePreservedTokenSink<T, ReturnT>;
67+
concept CSSValidDataTypeParser =
68+
((CSSFunctionBlockSink<T, ReturnT> || CSSSimpleBlockSink<T, ReturnT> ||
69+
CSSPreservedTokenSink<T, ReturnT>) &&
70+
!CSSParserSink<T, ReturnT>) ||
71+
CSSParserSink<T, ReturnT>;
7472

7573
/**
7674
* Concrete representation for a CSS data type

packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h

+10-7
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,26 @@ struct CSSRatio {
3939

4040
template <>
4141
struct CSSDataTypeParser<CSSRatio> {
42-
static constexpr auto consumePreservedToken(
43-
const CSSPreservedToken& token,
44-
CSSSyntaxParser& parser) -> std::optional<CSSRatio> {
42+
static constexpr auto consume(CSSSyntaxParser& parser)
43+
-> std::optional<CSSRatio> {
4544
// <ratio> = <number [0,∞]> [ / <number [0,∞]> ]?
4645
// https://www.w3.org/TR/css-values-4/#ratio
47-
if (token.numericValue() >= 0) {
48-
float numerator = token.numericValue();
46+
auto numerator = parseNextCSSValue<CSSNumber>(parser);
47+
if (!std::holds_alternative<CSSNumber>(numerator)) {
48+
return {};
49+
}
4950

51+
auto numeratorValue = std::get<CSSNumber>(numerator).value;
52+
if (numeratorValue >= 0) {
5053
auto denominator =
5154
peekNextCSSValue<CSSNumber>(parser, CSSDelimiter::Solidus);
5255
if (std::holds_alternative<CSSNumber>(denominator) &&
5356
std::get<CSSNumber>(denominator).value >= 0) {
5457
parseNextCSSValue<CSSNumber>(parser, CSSDelimiter::Solidus);
55-
return CSSRatio{numerator, std::get<CSSNumber>(denominator).value};
58+
return CSSRatio{numeratorValue, std::get<CSSNumber>(denominator).value};
5659
}
5760

58-
return CSSRatio{numerator, 1.0f};
61+
return CSSRatio{numeratorValue, 1.0f};
5962
}
6063

6164
return {};

packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h

+54-54
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,59 @@ class CSSSyntaxParser {
173173
}
174174
}
175175

176+
/**
177+
* Consume a delimiter, returning false if a required delimiter is not found.
178+
*/
179+
constexpr bool consumeDelimiter(CSSDelimiter delimiter) {
180+
if (delimiter == CSSDelimiter::None) {
181+
return true;
182+
}
183+
184+
bool hasWhiteSpace = peek().type() == CSSTokenType::WhiteSpace;
185+
consumeWhitespace();
186+
187+
switch (delimiter) {
188+
case CSSDelimiter::Comma:
189+
if (peek().type() == CSSTokenType::Comma) {
190+
consumeToken();
191+
consumeWhitespace();
192+
return true;
193+
}
194+
return false;
195+
case CSSDelimiter::Whitespace:
196+
return hasWhiteSpace;
197+
case CSSDelimiter::OptionalWhitespace:
198+
return true;
199+
case CSSDelimiter::CommaOrWhitespace:
200+
if (peek().type() == CSSTokenType::Comma) {
201+
consumeToken();
202+
consumeWhitespace();
203+
return true;
204+
}
205+
return hasWhiteSpace;
206+
case CSSDelimiter::Solidus:
207+
if (peek().type() == CSSTokenType::Delim &&
208+
peek().stringValue() == "/") {
209+
consumeToken();
210+
consumeWhitespace();
211+
return true;
212+
}
213+
return false;
214+
case CSSDelimiter::SolidusOrWhitespace:
215+
if (peek().type() == CSSTokenType::Delim &&
216+
peek().stringValue() == "/") {
217+
consumeToken();
218+
consumeWhitespace();
219+
return true;
220+
}
221+
return hasWhiteSpace;
222+
case CSSDelimiter::None:
223+
return true;
224+
}
225+
226+
return false;
227+
}
228+
176229
private:
177230
constexpr CSSSyntaxParser(CSSSyntaxParser& parser, CSSTokenType terminator)
178231
: tokenizer_{parser.tokenizer_},
@@ -207,7 +260,7 @@ struct CSSComponentValueVisitorDispatcher {
207260
CSSDelimiter delimiter,
208261
const VisitorsT&... visitors) {
209262
auto originalParser = parser;
210-
if (!consumeDelimiter(delimiter)) {
263+
if (!parser.consumeDelimiter(delimiter)) {
211264
parser = originalParser;
212265
return {};
213266
}
@@ -252,59 +305,6 @@ struct CSSComponentValueVisitorDispatcher {
252305
return ReturnT{};
253306
}
254307

255-
/**
256-
* Consume a delimiter, returning false if a required delimiter is not found.
257-
*/
258-
constexpr bool consumeDelimiter(CSSDelimiter delimiter) {
259-
if (delimiter == CSSDelimiter::None) {
260-
return true;
261-
}
262-
263-
bool hasWhiteSpace = parser.peek().type() == CSSTokenType::WhiteSpace;
264-
parser.consumeWhitespace();
265-
266-
switch (delimiter) {
267-
case CSSDelimiter::Comma:
268-
if (parser.peek().type() == CSSTokenType::Comma) {
269-
parser.consumeToken();
270-
parser.consumeWhitespace();
271-
return true;
272-
}
273-
return false;
274-
case CSSDelimiter::Whitespace:
275-
return hasWhiteSpace;
276-
case CSSDelimiter::OptionalWhitespace:
277-
return true;
278-
case CSSDelimiter::CommaOrWhitespace:
279-
if (parser.peek().type() == CSSTokenType::Comma) {
280-
parser.consumeToken();
281-
parser.consumeWhitespace();
282-
return true;
283-
}
284-
return hasWhiteSpace;
285-
case CSSDelimiter::Solidus:
286-
if (parser.peek().type() == CSSTokenType::Delim &&
287-
parser.peek().stringValue() == "/") {
288-
parser.consumeToken();
289-
parser.consumeWhitespace();
290-
return true;
291-
}
292-
return false;
293-
case CSSDelimiter::SolidusOrWhitespace:
294-
if (parser.peek().type() == CSSTokenType::Delim &&
295-
parser.peek().stringValue() == "/") {
296-
parser.consumeToken();
297-
parser.consumeWhitespace();
298-
return true;
299-
}
300-
return hasWhiteSpace;
301-
case CSSDelimiter::None:
302-
return true;
303-
}
304-
305-
return false;
306-
}
307-
308308
constexpr std::optional<ReturnT> visitFunction(
309309
const CSSComponentValueVisitor<ReturnT> auto& visitor,
310310
const CSSComponentValueVisitor<ReturnT> auto&... rest) {

packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h

+31-8
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ class CSSValueParser {
3535
CSSDelimiter delimeter = CSSDelimiter::None) {
3636
using ReturnT = std::variant<std::monostate, AllowedTypesT...>;
3737

38+
auto consumedValue =
39+
tryConsumeParser<ReturnT, CSSDataTypeParser<AllowedTypesT>...>(
40+
delimeter);
41+
42+
if (!std::holds_alternative<std::monostate>(consumedValue)) {
43+
return consumedValue;
44+
}
45+
3846
return parser_.consumeComponentValue<ReturnT>(
3947
delimeter,
4048
[&](const CSSPreservedToken& token) {
@@ -75,14 +83,6 @@ class CSSValueParser {
7583
CSSValidDataTypeParser... RestParserT>
7684
constexpr ReturnT tryConsumePreservedToken(const CSSPreservedToken& token) {
7785
if constexpr (CSSPreservedTokenSink<ParserT>) {
78-
auto currentParser = parser_;
79-
if (auto ret = ParserT::consumePreservedToken(token, parser_)) {
80-
return *ret;
81-
}
82-
parser_ = currentParser;
83-
}
84-
85-
if constexpr (CSSSimplePreservedTokenSink<ParserT>) {
8686
if (auto ret = ParserT::consumePreservedToken(token)) {
8787
return *ret;
8888
}
@@ -141,6 +141,29 @@ class CSSValueParser {
141141
return tryConsumeFunctionBlock<ReturnT, RestParserT...>(func, blockParser);
142142
}
143143

144+
template <typename ReturnT>
145+
constexpr ReturnT tryConsumeParser(CSSDelimiter /*delimeter*/) {
146+
return {};
147+
}
148+
149+
template <
150+
typename ReturnT,
151+
CSSValidDataTypeParser ParserT,
152+
CSSValidDataTypeParser... RestParserT>
153+
constexpr ReturnT tryConsumeParser(CSSDelimiter delimeter) {
154+
if constexpr (CSSParserSink<ParserT>) {
155+
auto currentParser = parser_;
156+
if (parser_.consumeDelimiter(delimeter)) {
157+
if (auto ret = ParserT::consume(parser_)) {
158+
return *ret;
159+
}
160+
}
161+
parser_ = currentParser;
162+
}
163+
164+
return tryConsumeParser<ReturnT, RestParserT...>(delimeter);
165+
}
166+
144167
CSSSyntaxParser& parser_;
145168
};
146169

0 commit comments

Comments
 (0)