7
7
8
8
#pragma once
9
9
10
+ #include < concepts>
10
11
#include < optional>
11
- #include < variant>
12
12
#include < vector>
13
13
14
14
#include < react/renderer/css/CSSTokenizer.h>
15
15
16
16
namespace facebook ::react {
17
17
18
18
/* *
19
- * CSSSyntaxParser allows parsing streams of CSS text into "component values",
20
- * being either a preserved token, or a function.
19
+ * Describes context for a CSS function component value.
20
+ */
21
+ struct CSSFunctionBlock {
22
+ std::string_view name{};
23
+ };
24
+
25
+ /* *
26
+ * Describes a preserved token component value.
27
+ */
28
+ using CSSPreservedToken = CSSToken;
29
+
30
+ /* *
31
+ * Describes context for a CSS function component value.
32
+ */
33
+ struct CSSSimpleBlock {
34
+ CSSTokenType openBracketType{};
35
+ };
36
+
37
+ /* *
38
+ * A CSSFunctionVisitor is called to start parsing a function component value.
39
+ * At this point, the Parser is positioned at the start of the function
40
+ * component value list. It is expected that the visitor finishes before the end
41
+ * of the function block.
42
+ */
43
+ template <typename T, typename ReturnT>
44
+ concept CSSFunctionVisitor = requires (T visitor, CSSFunctionBlock func) {
45
+ { visitor (func) } -> std::convertible_to<ReturnT>;
46
+ };
47
+
48
+ /* *
49
+ * A CSSPreservedTokenVisitor is called after parsing a preserved token
50
+ * component value.
51
+ */
52
+ template <typename T, typename ReturnT>
53
+ concept CSSPreservedTokenVisitor =
54
+ requires (T visitor, CSSPreservedToken token) {
55
+ { visitor (token) } -> std::convertible_to<ReturnT>;
56
+ };
57
+
58
+ /* *
59
+ * A CSSSimpleBlockVisitor is called after parsing a simple block component
60
+ * value. It is expected that the visitor finishes before the end
61
+ * of the block.
62
+ */
63
+ template <typename T, typename ReturnT>
64
+ concept CSSSimpleBlockVisitor = requires (T visitor, CSSSimpleBlock block) {
65
+ { visitor (block) } -> std::convertible_to<ReturnT>;
66
+ };
67
+
68
+ /* *
69
+ * Any visitor for a component value.
70
+ */
71
+ template <typename T, typename ReturnT>
72
+ concept CSSComponentValueVisitor = CSSFunctionVisitor<T, ReturnT> ||
73
+ CSSPreservedTokenVisitor<T, ReturnT> || CSSSimpleBlockVisitor<T, ReturnT>;
74
+
75
+ /* *
76
+ * Represents a variadic set of CSSComponentValueVisitor with no more than one
77
+ * of a specific type of visitor.
78
+ */
79
+ template <typename ReturnT, typename ... VisitorsT>
80
+ concept CSSUniqueComponentValueVisitors =
81
+ (CSSComponentValueVisitor<VisitorsT, ReturnT> && ...) &&
82
+ ((CSSFunctionVisitor<VisitorsT, ReturnT> ? 1 : 0 ) + ... + 0 ) <= 1 &&
83
+ ((CSSPreservedTokenVisitor<VisitorsT, ReturnT> ? 1 : 0 ) + ... + 0 ) <= 1 &&
84
+ ((CSSSimpleBlockVisitor<VisitorsT, ReturnT> ? 1 : 0 ) + ... + 0 ) <= 1 ;
85
+
86
+ /* *
87
+ * Describes the delimeter to expect before the next component value.
88
+ */
89
+ enum class CSSComponentValueDelimiter {
90
+ Comma,
91
+ Whitespace,
92
+ None,
93
+ };
94
+
95
+ /* *
96
+ * CSSSyntaxParser allows parsing streams of CSS text into "component
97
+ * values".
21
98
*
22
99
* https://www.w3.org/TR/css-syntax-3/#component-value
23
100
*/
24
101
class CSSSyntaxParser {
25
- public:
26
- struct Function ;
27
-
28
- using PreservedToken = CSSToken;
29
- using ComponentValue = std::variant<std::monostate, PreservedToken, Function>;
30
-
31
- struct Function {
32
- std::string_view name{};
33
- std::vector<ComponentValue> args{};
34
- };
102
+ template <typename ReturnT, CSSComponentValueVisitor<ReturnT>... VisitorsT>
103
+ friend struct CSSComponentValueVisitorDispatcher ;
35
104
105
+ public:
36
106
/* *
37
107
* Construct the parser over the given string_view, which must stay alive for
38
108
* the duration of the CSSSyntaxParser.
39
109
*/
40
110
explicit constexpr CSSSyntaxParser (std::string_view css)
41
111
: tokenizer_{css}, currentToken_(tokenizer_.next()) {}
42
112
113
+ constexpr CSSSyntaxParser (const CSSSyntaxParser&) = default;
114
+ constexpr CSSSyntaxParser (CSSSyntaxParser&&) = default;
115
+
116
+ constexpr CSSSyntaxParser& operator =(const CSSSyntaxParser&) = default ;
117
+ constexpr CSSSyntaxParser& operator =(CSSSyntaxParser&&) = default ;
118
+
43
119
/* *
44
- * Directly consume the next component value
120
+ * Directly consume the next component value. The component value is provided
121
+ * to a passed in "visitor", typically a lambda which accepts the component
122
+ * value in a new scope. The visitor may read this component parameter into a
123
+ * higher-level data structure, and continue parsing within its scope using
124
+ * the same underlying CSSSyntaxParser.
45
125
*
46
126
* https://www.w3.org/TR/css-syntax-3/#consume-component-value
127
+ *
128
+ * @param <ReturnT> caller-specified return type of visitors. This type will
129
+ * be set to its default constructed state if consuming a component value with
130
+ * no matching visitors, or syntax error
131
+ * @param visitors A unique list of CSSComponentValueVisitor to be called on a
132
+ * match
133
+ * @param delimiter The expected delimeter to occur before the next component
134
+ * value
135
+ * @returns the visitor returned value, or a default constructed value if no
136
+ * visitor was matched, or a syntax error occurred.
137
+ */
138
+ template <typename ReturnT>
139
+ constexpr ReturnT consumeComponentValue (
140
+ CSSComponentValueDelimiter delimiter,
141
+ const CSSComponentValueVisitor<ReturnT> auto &... visitors)
142
+ requires(CSSUniqueComponentValueVisitors<ReturnT, decltype (visitors)...>);
143
+
144
+ template <typename ReturnT>
145
+ constexpr ReturnT consumeComponentValue (
146
+ const CSSComponentValueVisitor<ReturnT> auto &... visitors)
147
+ requires(CSSUniqueComponentValueVisitors<ReturnT, decltype (visitors)...>);
148
+
149
+ /* *
150
+ * The parser is considered finished when there are no more remaining tokens
151
+ * to be processed
47
152
*/
48
- inline ComponentValue consumeComponentValue () {
49
- if (peek ().type () == CSSTokenType::Function) {
50
- auto function = consumeFunction ();
51
- return function.has_value () ? ComponentValue{std::move (*function)}
52
- : ComponentValue{};
53
- } else {
54
- return consumeToken ();
153
+ constexpr bool isFinished () const {
154
+ return currentToken_.type () == CSSTokenType::EndOfFile;
155
+ }
156
+
157
+ /* *
158
+ * Consume any whitespace tokens.
159
+ */
160
+ constexpr void consumeWhitespace () {
161
+ if (currentToken_.type () == CSSTokenType::WhiteSpace) {
162
+ currentToken_ = tokenizer_.next ();
55
163
}
56
164
}
57
165
@@ -66,40 +174,149 @@ class CSSSyntaxParser {
66
174
return prevToken;
67
175
}
68
176
69
- inline std::optional<Function> consumeFunction () {
70
- // https://www.w3.org/TR/css-syntax-3/#consume-a-function
71
- Function function{. name = consumeToken (). stringValue () };
177
+ CSSTokenizer tokenizer_;
178
+ CSSToken currentToken_;
179
+ };
72
180
73
- while (true ) {
74
- auto nextValue = consumeComponentValue ();
75
- if (std::holds_alternative<std::monostate>(nextValue)) {
76
- return {};
77
- }
181
+ template <typename ReturnT, CSSComponentValueVisitor<ReturnT>... VisitorsT>
182
+ struct CSSComponentValueVisitorDispatcher {
183
+ CSSSyntaxParser& parser;
78
184
79
- if (auto token = std::get_if<CSSToken>(&nextValue)) {
80
- if (token->type () == CSSTokenType::CloseParen) {
81
- return function;
185
+ constexpr ReturnT consumeComponentValue (
186
+ CSSComponentValueDelimiter delimiter,
187
+ const VisitorsT&... visitors) {
188
+ switch (delimiter) {
189
+ case CSSComponentValueDelimiter::Comma:
190
+ parser.consumeWhitespace ();
191
+ if (parser.peek ().type () != CSSTokenType::Comma) {
192
+ return ReturnT{};
82
193
}
83
- if (token->type () == CSSTokenType::EndOfFile) {
84
- return {};
194
+ parser.consumeToken ();
195
+ parser.consumeWhitespace ();
196
+ break ;
197
+ case CSSComponentValueDelimiter::Whitespace:
198
+ parser.consumeWhitespace ();
199
+ break ;
200
+ case CSSComponentValueDelimiter::None:
201
+ break ;
202
+ }
203
+
204
+ switch (parser.peek ().type ()) {
205
+ case CSSTokenType::Function:
206
+ if (auto ret = visitFunction (visitors...)) {
207
+ return *ret;
85
208
}
86
- function.args .emplace_back (std::move (*token));
87
- continue ;
88
- }
209
+ break ;
210
+ case CSSTokenType::OpenParen:
211
+ if (auto ret =
212
+ visitSimpleBlock (CSSTokenType::CloseParen, visitors...)) {
213
+ return *ret;
214
+ }
215
+ break ;
216
+ case CSSTokenType::OpenSquare:
217
+ if (auto ret =
218
+ visitSimpleBlock (CSSTokenType::CloseSquare, visitors...)) {
219
+ return *ret;
220
+ }
221
+ break ;
222
+ case CSSTokenType::OpenCurly:
223
+ if (auto ret =
224
+ visitSimpleBlock (CSSTokenType::CloseCurly, visitors...)) {
225
+ return *ret;
226
+ }
227
+ break ;
228
+ default :
229
+ if (auto ret = visitPreservedToken (visitors...)) {
230
+ return *ret;
231
+ }
232
+ break ;
233
+ }
89
234
90
- if (auto func = std::get_if<Function>(&nextValue)) {
91
- function.args .emplace_back (std::move (*func));
92
- continue ;
93
- }
235
+ return ReturnT{};
236
+ }
94
237
238
+ constexpr ReturnT consumeNextCommaDelimitedValue (
239
+ const VisitorsT&... visitors) {
240
+ parser.consumeWhitespace ();
241
+ if (parser.consumeToken ().type () != CSSTokenType::Comma) {
95
242
return {};
96
243
}
244
+ parser.consumeWhitespace ();
245
+ return consumeComponentValue (std::forward<VisitorsT>(visitors)...);
246
+ }
97
247
98
- return function;
248
+ constexpr ReturnT consumeNextWhitespaceDelimitedValue (
249
+ const VisitorsT&... visitors) {
250
+ parser.consumeWhitespace ();
251
+ return consumeComponentValue (std::forward<VisitorsT>(visitors)...);
99
252
}
100
253
101
- CSSTokenizer tokenizer_;
102
- CSSToken currentToken_;
254
+ constexpr std::optional<ReturnT> visitFunction (const VisitorsT&... visitors) {
255
+ for (auto visitor : {visitors...}) {
256
+ if constexpr (CSSFunctionVisitor<decltype (visitor), ReturnT>) {
257
+ auto functionValue =
258
+ visitor ({.name = parser.consumeToken ().stringValue ()});
259
+ parser.consumeWhitespace ();
260
+ if (parser.peek ().type () == CSSTokenType::CloseParen) {
261
+ parser.consumeToken ();
262
+ return functionValue;
263
+ }
264
+
265
+ return {};
266
+ }
267
+ }
268
+
269
+ return {};
270
+ }
271
+
272
+ constexpr std::optional<ReturnT> visitSimpleBlock (
273
+ CSSTokenType endToken,
274
+ const VisitorsT&... visitors) {
275
+ for (auto visitor : {visitors...}) {
276
+ if constexpr (CSSSimpleBlockVisitor<decltype (visitor), ReturnT>) {
277
+ auto blockValue =
278
+ visitor ({.openBracketType = parser.consumeToken ().type ()});
279
+ parser.consumeWhitespace ();
280
+ if (parser.peek ().type () == endToken) {
281
+ parser.consumeToken ();
282
+ return blockValue;
283
+ }
284
+
285
+ return {};
286
+ }
287
+ }
288
+ return {};
289
+ }
290
+
291
+ constexpr std::optional<ReturnT> visitPreservedToken (
292
+ const VisitorsT&... visitors) {
293
+ for (auto visitor : {visitors...}) {
294
+ if constexpr (CSSPreservedTokenVisitor<decltype (visitor), ReturnT>) {
295
+ return visitor (parser.consumeToken ());
296
+ }
297
+ }
298
+ return {};
299
+ }
103
300
};
104
301
302
+ template <typename ReturnT>
303
+ constexpr ReturnT CSSSyntaxParser::consumeComponentValue (
304
+ CSSComponentValueDelimiter delimiter,
305
+ const CSSComponentValueVisitor<ReturnT> auto &... visitors)
306
+ requires(CSSUniqueComponentValueVisitors<ReturnT, decltype (visitors)...>)
307
+ {
308
+ return CSSComponentValueVisitorDispatcher<ReturnT, decltype (visitors)...>{
309
+ *this }
310
+ .consumeComponentValue (delimiter, visitors...);
311
+ }
312
+
313
+ template <typename ReturnT>
314
+ constexpr ReturnT CSSSyntaxParser::consumeComponentValue (
315
+ const CSSComponentValueVisitor<ReturnT> auto &... visitors)
316
+ requires(CSSUniqueComponentValueVisitors<ReturnT, decltype (visitors)...>)
317
+ {
318
+ return consumeComponentValue<ReturnT>(
319
+ CSSComponentValueDelimiter::None, visitors...);
320
+ }
321
+
105
322
} // namespace facebook::react
0 commit comments