CYD-UI
A C++ library for building native graphic user interfaces
Loading...
Searching...
No Matches
parser.cppm
Go to the documentation of this file.
1
5
6export module cydui.styling.tss.parser;
7
8import std;
9export import reflect;
10import reflect.serialize;
11export import fabric.grammar;
12import fabric.grammar.operators;
13
14export import cydui.dimensions;
15export import cydui.styling.selectors;
16export import cydui.styling.rules;
17export import cydui.graphics.types;
18
19#define TERMINAL(C) using C##_t = fabric::terminal<#C[0]>; static C##_t C
20#define TERMINAL_DIGIT(C) using d##C##_t = fabric::terminal<#C[0]>; static d##C##_t d##C
21#define TERMINAL_W_NAME(C, NAME) using NAME##_t = fabric::terminal<C>; static NAME##_t NAME
22
23#define RULE_EXPR__(...) using rule = decltype(__VA_ARGS__);}
24#define RULE(NAME) struct NAME: public fabric::node_t { \
25 static constexpr const char* name = #NAME; \
26 static constexpr bool transient = false; \
27 using sptr = std::shared_ptr<NAME>; \
28 NAME(): fabric::node_t(refl::type_id<NAME>) {} \
29 RULE_EXPR__
30
31#define TRULE(NAME) struct NAME: public fabric::node_t { \
32 static constexpr const char* name = #NAME; \
33 static constexpr bool transient = true; \
34 using sptr = std::shared_ptr<NAME>; \
35 NAME(): fabric::node_t(refl::type_id<NAME>) {} \
36 RULE_EXPR__
37
38#define RULE_ACTION__(...) static void action(const sptr& $node) __VA_ARGS__ }
39#define ARULE_DATA_TYPE__(...) __VA_ARGS__ data{}; RULE_ACTION__
40#define RULE_EXPR_W_ACTION__(...) using rule = decltype(__VA_ARGS__); ARULE_DATA_TYPE__
41#define ARULE(NAME) struct NAME: public fabric::node_t { \
42 static constexpr const char* name = #NAME; \
43 static constexpr bool transient = false; \
44 using sptr = std::shared_ptr<NAME>; \
45 NAME(): fabric::node_t(refl::type_id<NAME>) {} \
46 RULE_EXPR_W_ACTION__
47
48#define GRAMMAR(NAME) \
49struct NAME; \
50namespace grammar_metadata { \
51 struct NAME { \
52 static constexpr const char* name = #NAME; \
53 }; \
54} \
55struct NAME: grammar_metadata::NAME
56
57#define START(RULE) using start = RULE
58
59
60
61namespace syntax {
62 export GRAMMAR(tss) {
73
74 TERMINAL(a);
75 TERMINAL(A);
76 TERMINAL(b);
77 TERMINAL(B);
78 TERMINAL(c);
79 TERMINAL(C);
80 TERMINAL(d);
81 TERMINAL(D);
82 TERMINAL(e);
83 TERMINAL(E);
84 TERMINAL(f);
85 TERMINAL(F);
86 TERMINAL(g);
87 TERMINAL(G);
88 TERMINAL(h);
89 TERMINAL(H);
90 TERMINAL(i);
91 TERMINAL(I);
92 TERMINAL(j);
93 TERMINAL(J);
94 TERMINAL(k);
95 TERMINAL(K);
96 TERMINAL(l);
97 TERMINAL(L);
98 TERMINAL(m);
99 TERMINAL(M);
100 TERMINAL(n);
101 TERMINAL(N);
102 TERMINAL(o);
103 TERMINAL(O);
104 TERMINAL(p);
105 TERMINAL(P);
106 TERMINAL(q);
107 TERMINAL(Q);
108 TERMINAL(r);
109 TERMINAL(R);
110 TERMINAL(s);
111 TERMINAL(S);
112 TERMINAL(t);
113 TERMINAL(T);
114 TERMINAL(u);
115 TERMINAL(U);
116 TERMINAL(v);
117 TERMINAL(V);
118 TERMINAL(w);
119 TERMINAL(W);
120 TERMINAL(x);
121 TERMINAL(X);
122 TERMINAL(y);
123 TERMINAL(Y);
124 TERMINAL(z);
125 TERMINAL(Z);
126
127 TERMINAL_W_NAME(':', colon);
128 TERMINAL_W_NAME(';', semicolon);
129 TERMINAL_W_NAME('+', plus);
130 TERMINAL_W_NAME('-', minus);
131 TERMINAL_W_NAME('_', underscore);
132 TERMINAL_W_NAME('#', shebang);
133 TERMINAL_W_NAME('.', dot);
134 TERMINAL_W_NAME(',', comma);
135 TERMINAL_W_NAME('*', asterisc);
136 TERMINAL_W_NAME(' ', whitespace);
137 TERMINAL_W_NAME('\n', newline);
138 TERMINAL_W_NAME('\r', return_);
139 TERMINAL_W_NAME('\'', quote);
140 TERMINAL_W_NAME('"', double_quote);
141
142 TERMINAL_W_NAME('[', left_brackets);
143 TERMINAL_W_NAME(']', right_brackets);
144 TERMINAL_W_NAME('{', left_braces);
145 TERMINAL_W_NAME('}', right_braces);
146 TERMINAL_W_NAME('(', left_parens);
147 TERMINAL_W_NAME(')', right_parens);
148 TERMINAL_W_NAME('<', left_angle_brackets);
149 TERMINAL_W_NAME('>', right_angle_brackets);
150 TERMINAL_W_NAME('/', forward_slash);
151
152 RULE(skip_wn)(*(whitespace | newline | return_));
153
154 RULE(comma_separator)(*(whitespace | newline | return_), comma, *(whitespace | newline | return_));
155 RULE(colon_asigner)(*(whitespace | newline | return_), colon, *(whitespace | newline | return_));
156 RULE(braces_begin)(*(whitespace | newline | return_), left_braces, *(whitespace | newline | return_));
157 RULE(braces_end)(*(whitespace | newline | return_), right_braces, *(whitespace | newline | return_));
158
159 RULE(end_of_declaration)(*(whitespace | newline | return_), semicolon, *(whitespace | newline | return_));
160
161 TRULE(digit_excluding_zero) (d1 | d2 | d3 | d4 | d5 | d6 | d7 | d8 | d9);
162
163 TRULE(digit) (d0 | digit_excluding_zero{});
164
165 TRULE(hex_digit_chars)(
166 a | b | c | d | e | f |
167 A | B | C | D | E | F);
168 TRULE(hex_digit)(hex_digit_chars{} | digit{});
169
170 RULE(number) (~(plus | minus), ((*digit{}, dot, +digit{}) | +digit{}));
171
172 TRULE(alpha) (a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z );
173 TRULE(ALPHA) (A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z );
174
175 TRULE(alphanum) (alpha{} | ALPHA{} | digit{});
176
177 TRULE(tss_identifier) ((alpha{} | ALPHA{} | minus | underscore), *(alphanum{} | minus | underscore));
178
179 TRULE(tss_single_quoted_str_char)(
180 alphanum{} | colon | semicolon | plus | minus | underscore | shebang | dot | comma | asterisc | whitespace
181 | newline | return_ | double_quote | left_brackets | right_brackets | left_braces
182 | right_braces | left_parens | right_parens | left_angle_brackets | right_angle_brackets
183 | forward_slash
184 );
185 TRULE(tss_single_quoted_str) (!quote, *tss_single_quoted_str_char{}, !quote);
186
187 TRULE(tss_double_quoted_str_char)(
188 alphanum{} | colon | semicolon | plus | minus | underscore | shebang | dot | comma | asterisc | whitespace
189 | newline | return_ | quote | left_brackets | right_brackets | left_braces
190 | right_braces | left_parens | right_parens | left_angle_brackets | right_angle_brackets
191 | forward_slash
192 );
193 TRULE(tss_double_quoted_str) (!double_quote, *tss_double_quoted_str_char{}, !double_quote);
194
195 // SELECTORS
196 RULE(tss_class_selector) (dot, tss_identifier{});
197
198 RULE(tss_id_selector) (shebang, tss_identifier{});
199
200 RULE(tss_pseudo_state_selector) (colon, tss_identifier{});
201
202 RULE(tss_element_selector) (tss_identifier{}, *(forward_slash, tss_identifier{}));
203
204 struct selector_data {
205 cydui::StyleRuleSelector selector{};
206 };
207 ARULE(tss_descendent_selector_item) (tss_element_selector{}, *(tss_class_selector{} | tss_id_selector{} | tss_pseudo_state_selector{}))
208 (selector_data)({
209 auto& selector = $node->data.selector;
210
211 if (not $node->children.empty()) {
212 if ($node->children[0]->is_type<tss_element_selector>()) {
213 const auto *es = $node->children[0]->as<tss_element_selector>();
214 selector.component = es->children[0]->text;
215
216 std::size_t pos = selector.component.find("/");
217 while (pos != std::string::npos) {
218 selector.component.replace(pos, 1, "::");
219 pos = selector.component.find("/", pos + 2);
220 }
221 }
222
223 for (std::size_t i = 1; i < $node->children.size(); ++i) {
224 auto& child = $node->children[i];
225 if (child->is_type<tss_id_selector>()) {
226 const auto *is = child->as<tss_id_selector>();
227 selector.tags.insert(is->children[0]->text.substr(1));
228 } else if (child->is_type<tss_pseudo_state_selector>()) {
229 const auto *ps = child->as<tss_pseudo_state_selector>();
230 selector.pseudo_states.insert(ps->children[0]->text.substr(1));
231 }
232 }
233 }
234
235 // refl::serializer<formats::json_fmt>::to_stream(std::cout, selector);
236 // std::cout << std::endl;
237 });
238
239 RULE(tss_children_selector_item) (!right_angle_brackets, !skip_wn{}, tss_descendent_selector_item{});
240
241 struct combined_selector_data {
243 std::string key_element{};
244 };
245 ARULE(tss_combined_selector) (tss_descendent_selector_item{}, !skip_wn{}, *((tss_children_selector_item{} | tss_descendent_selector_item{}), !skip_wn{}))
246 (combined_selector_data)({
247 auto& selector = $node->data.selector;
248 auto& key = $node->data.key_element;
249
250 selector.selectors.emplace_back( //
252 $node->children[0]->as<tss_descendent_selector_item>()->data.selector
253 );
254
255 if ($node->children.size() > 1) {
256 for (std::size_t i = 1; i < $node->children.size(); ++i) {
257 const auto& child = $node->children[i];
258 if (child->is_type<tss_children_selector_item>()) {
259 const auto *cs = child->as<tss_children_selector_item>()
260 ->children[0]->as<tss_descendent_selector_item>();
261 selector.selectors.emplace_back( //
263 cs->data.selector
264 );
265 } else if (child->is_type<tss_descendent_selector_item>()) {
266 const auto *ss = child->as<tss_descendent_selector_item>();
267 selector.selectors.emplace_back( //
269 ss->data.selector
270 );
271 }
272 }
273 }
274
275 key = selector.selectors.back().second.component;
276 });
277
278 TRULE(tss_grouping_selector) (*(tss_combined_selector{}, !comma_separator{}), tss_combined_selector{}, !skip_wn{});
279
280 struct selector_list_data {
281 std::vector<cydui::StyleRuleCombinedSelector> selectors{};
282 std::vector<std::string> selector_keys{};
283 };
284 ARULE(tss_selector) (asterisc | tss_grouping_selector{})
285 (selector_list_data)({
286 for (const auto& child: $node->children) {
287 if (child->is_type<tss_combined_selector>()) {
288 const auto* cs = child->as<tss_combined_selector>();
289 $node->data.selectors.emplace_back(cs->data.selector);
290 $node->data.selector_keys.emplace_back(cs->data.key_element);
291 }
292 }
293 });
294
295 // DECLARATIONS
296 struct tss_decl_expression_list;
297
298 RULE(tss_decl_function_call) (tss_identifier{}, left_parens, *(fabric::recurse<tss_decl_expression_list>{}, !comma_separator{}), ~fabric::recurse<tss_decl_expression_list>{}, right_parens);
299
300 ARULE(tss_decl_color_literal)
301 (shebang, hex_digit{}, hex_digit{}, hex_digit{}, hex_digit{}, hex_digit{}, hex_digit{},
302 ~(hex_digit{}, hex_digit{}))
303 (color::Color) ({
304 $node->data = color::Color::from_str($node->children[0]->text);
305 });
306
307 struct number_literal_data {
308 double value;
309 std::string unit;
310 };
311 ARULE(tss_decl_number_literal) (number{} , ~tss_identifier{})
312 (number_literal_data)({
313 const std::string& num = $node->children[0]->children[0]->text;
314 char* end;
315 $node->data.value = std::strtod(num.c_str(), &end);
316
317 if ($node->children.size() > 1) {
318 $node->data.unit = $node->children[1]->text;
319 }
320 });
321
322 ARULE(tss_decl_string_literal) (tss_single_quoted_str{} | tss_double_quoted_str{})
323 (std::string)({
324 $node->data = $node->children[0]->text;
325 });
326
327 ARULE(tss_decl_expression)(tss_decl_color_literal{} | tss_decl_function_call{} | tss_identifier{} | tss_decl_number_literal{} | tss_decl_string_literal{})
328 (refl::any)({
329 const auto& child = $node->children[0];
330 refl::any& value = $node->data;
331
332 if (child->is_type<tss_decl_color_literal>()) {
333 value = refl::any::make<color::Color>(child->as<tss_decl_color_literal>()->data);
334 } else if (child->is_type<tss_decl_function_call>()) {
335 } else if (child->is_type<tss_decl_string_literal>()) {
336 value = refl::any::make<std::string>(child->as<tss_decl_string_literal>()->data);
337 } else if (child->is_type<tss_decl_number_literal>()) {
338 auto d = child->as<tss_decl_number_literal>()->data;
339 if (d.unit.empty()) {
340 value = refl::any::make<double>(d.value);
341 } else if (d.unit == "px") {
342 value = refl::any::make<cydui::dimensions::screen_measure>(d.value);
343 }
344 }
345 });
346
347 ARULE(tss_decl_expression_list)(*(tss_decl_expression{}, !whitespace, !skip_wn{}), tss_decl_expression{}, !skip_wn{})
348 (refl::any)({
349 refl::any& value = $node->data;
350
351 if ($node->children.size() == 1) {
352 value = $node->children[0]->as<tss_decl_expression>()->data;
353 } else {
354 std::vector<refl::any> values{};
355 for (const auto& child: $node->children) {
356 values.push_back(child->as<tss_decl_expression>()->data);
357 }
358 const auto& vs = values;
359 value = refl::any::make<std::vector<refl::any>>(values);
360 }
361 });
362
363 RULE(tss_declaration_name) (tss_identifier{});
364
365 struct declaration_data {
366 std::string name;
367 refl::any value;
368 };
369 ARULE(tss_declaration)(tss_declaration_name{}, !colon_asigner{}, tss_decl_expression_list{}, !end_of_declaration{})
370 (declaration_data)({
371 auto& [name, value] = $node->data;
372 name = $node->children[0]->children[0]->text;
373 value = $node->children[1]->as<tss_decl_expression_list>()->data;
374 });
375
376 struct declaration_list_data {
377 refl::archive archive{};
378 };
379 ARULE(tss_declaration_list)(*(tss_declaration{}))
380 (declaration_list_data)({
381 refl::archive& archive = $node->data.archive;
382 for (const auto& decl: $node->children) {
383 const auto&[name, value] = decl->as<tss_declaration>()->data;
384 archive[name] = value;
385 }
386 });
387
388 // RULES
389 ARULE(tss_rule)(!skip_wn{}, tss_selector{}, !braces_begin{}, tss_declaration_list{}, !braces_end{})
391 $node->data = std::make_shared<cydui::StyleRule>();
392
393 if ($node->children[0]->is_type<tss_selector>()) {
394 const auto* sel = $node->children[0]->as<tss_selector>();
395 $node->data->selectors_ = sel->data.selectors;
396 $node->data->relevant_components_ = sel->data.selector_keys;
397 }
398 if ($node->children[1]->is_type<tss_declaration_list>()) {
399 const auto* sel = $node->children[1]->as<tss_declaration_list>();
400 $node->data->properties_ = sel->data.archive;
401 }
402 });
403
404 struct stylesheet_data {
405 std::vector<cydui::StyleRule::sptr> rules{};
406 };
407 ARULE(tss_stylesheet)(*tss_rule())
408 (stylesheet_data)({
409 for (const auto& child: $node->children) {
410 $node->data.rules.push_back(child->as<tss_rule>()->data);
411 }
412 });
413
414 START(tss_stylesheet);
415 };
416}
417
418export namespace lang::tss {
419 using parse_result = fabric::parse_result<syntax::tss>;
420
421 template <typename Input>
422 parse_result parse(const Input& input_) {
423 auto [ok, ast, log] = fabric::parse<syntax::tss>(input_);
424
425 return {ok, ast, log};
426 }
427}
#define TERMINAL(C)
Definition parser.cppm:19
#define TERMINAL_W_NAME(C, NAME)
Definition parser.cppm:21
#define GRAMMAR(NAME)
Definition parser.cppm:48
#define START(RULE)
Definition parser.cppm:57
#define RULE(NAME)
Definition parser.cppm:24
#define TERMINAL_DIGIT(C)
Definition parser.cppm:20
#define ARULE(NAME)
Definition parser.cppm:41
#define TRULE(NAME)
Definition parser.cppm:31
static Color from_str(const std::string &data)
Definition color.cppm:38
std::shared_ptr< StyleRule > sptr
Definition rule.cppm:16