7#include "../utility/module.hpp"
8#include "../parser/module.hpp"
9#include "theme_mode.hpp"
10#include "theme_length.hpp"
11#include "style_sheet.hpp"
15namespace hi {
inline namespace v1 {
20 std::filesystem::path path;
25 return item.first < value;
27 if (it != _macros.end() and it->first == name) {
30 _macros.emplace(it, name,
std::move(declarations));
35 constexpr std::optional<std::vector<style_sheet_declaration>> get_macro(
std::string const& name)
const noexcept
38 return item.first < value;
40 if (it != _macros.end() and it->first == name) {
50 return item.first < value;
52 if (it != _lets.end() and it->first == name) {
55 _lets.emplace(it, name, value);
60 constexpr std::optional<style_sheet_value> get_let(
std::string const& name)
const noexcept
63 return item.first < value;
65 if (it != _lets.end() and it->first == name) {
75 return item.first < value;
77 if (it != _colors.end() and it->first == name) {
80 _colors.emplace(it, name,
color);
85 constexpr std::optional<color> get_color(
std::string const& name)
const noexcept
88 return item.first < value;
90 if (it != _colors.end() and it->first == name) {
108template<
typename It, std::sentinel_for<It> ItEnd>
109[[nodiscard]]
constexpr std::optional<hi::language_tag>
112 if (*it == token::id and *it ==
"lang") {
115 if (it == last or *it !=
'(') {
121 while (it != last and *it !=
')') {
123 language_tag_string +=
'*';
125 }
else if (*it ==
'-') {
126 language_tag_string +=
'-';
128 }
else if (*it == token::id) {
129 language_tag_string +=
static_cast<std::string>(*it);
132 throw parse_error(std::format(
133 "{} Unexpected token while parsing argument of ':lang()'.",
token_location(it, last, context.path)));
139 return language_tag{language_tag_string};
141 throw parse_error(std::format(
142 "{} Invalid language-tag '{}' while parsing argument of ':lang()'. {}",
144 language_tag_string, e.
what()));
148 if (it == last or *it !=
')') {
149 throw parse_error(std::format(
"{} Missing ')' at end of ':lang'.",
token_location(it, last, context.path)));
159template<
typename It, std::sentinel_for<It> ItEnd>
160[[nodiscard]]
constexpr std::optional<hi::text_phrasing_mask>
161parse_style_sheet_theme_state_phrasing(It& it, ItEnd last, style_sheet_parser_context& context)
163 if (*it == token::id and *it ==
"phrasing") {
166 if (it == last or *it !=
'(') {
167 throw parse_error(std::format(
"{} Missing '(' after ':phrasing'.",
token_location(it, last, context.path)));
171 if (it == last or *it != token::id) {
172 throw parse_error(std::format(
"{} Missing integer after ':phrasing('.",
token_location(it, last, context.path)));
175 hilet phrasing_mask = [&] {
177 return to_text_phrasing_mask(
static_cast<std::string>(*it));
179 throw parse_error(std::format(
180 "{} Could not convert argument '{}' of ':phrasing()' to integer. {}",
188 if (it == last or *it !=
')') {
189 throw parse_error(std::format(
"{} Missing ')' at end of ':phrasing'.",
token_location(it, last, context.path)));
193 return phrasing_mask;
199template<
typename It, std::sentinel_for<It> ItEnd>
200[[nodiscard]]
constexpr std::optional<std::pair<theme_state, theme_state_mask>>
201parse_style_sheet_theme_state(It& it, ItEnd last, style_sheet_parser_context& context)
203 if (*it == token::id and *it ==
"disabled") {
205 return std::pair{theme_state::disabled, theme_state_mask::mouse};
206 }
else if (*it == token::id and *it ==
"enabled") {
208 return std::pair{theme_state::enabled, theme_state_mask::mouse};
209 }
else if (*it == token::id and *it ==
"hover") {
211 return std::pair{theme_state::hover, theme_state_mask::mouse};
212 }
else if (*it == token::id and *it ==
"active") {
214 return std::pair{theme_state::active, theme_state_mask::mouse};
215 }
else if (*it == token::id and *it ==
"no-focus") {
217 return std::pair{theme_state::no_focus, theme_state_mask::focus};
218 }
else if (*it == token::id and *it ==
"focus") {
220 return std::pair{theme_state::focus, theme_state_mask::focus};
221 }
else if (*it == token::id and *it ==
"off") {
223 return std::pair{theme_state::off, theme_state_mask::value};
224 }
else if (*it == token::id and *it ==
"on") {
226 return std::pair{theme_state::on, theme_state_mask::value};
227 }
else if (*it == token::id and *it ==
"layer") {
230 if (it == last or *it !=
'(') {
231 throw parse_error(std::format(
"{} Missing '(' after ':layer'.",
token_location(it, last, context.path)));
235 if (it == last or *it != token::integer) {
236 throw parse_error(std::format(
"{} Missing integer after ':layer('.",
token_location(it, last, context.path)));
239 hilet layer_nr = [&] {
241 return static_cast<uint8_t
>(*it);
243 throw parse_error(std::format(
244 "{} Could not convert argument of ':layer()' to integer. {}",
251 hilet layer_state = [&] {
254 return theme_state::layer_0;
256 return theme_state::layer_1;
258 return theme_state::layer_2;
260 return theme_state::layer_3;
262 throw parse_error(std::format(
263 "{} Expect ':layer()' value of 0, 1, 2 or 3, got {}.",
token_location(it, last, context.path), layer_nr));
267 if (it == last or *it !=
')') {
268 throw parse_error(std::format(
"{} Missing ')' at end of ':layer'.",
token_location(it, last, context.path)));
272 return std::pair{layer_state, theme_state_mask::layers};
279template<
typename It, std::sentinel_for<It> ItEnd>
280[[nodiscard]]
constexpr std::optional<style_sheet_pattern>
281parse_style_sheet_pattern(It& it, ItEnd last, style_sheet_parser_context& context)
284 auto r = style_sheet_pattern{};
286 if (it != last and *it ==
'*') {
287 r.path.push_back(
"*");
289 }
else if (it != last and *it == token::id) {
296 auto is_child =
false;
297 while (it != last and *it !=
',' and *it !=
'{' and *it !=
':') {
302 }
else if (*it ==
'*') {
303 r.is_child.push_back(is_child);
304 r.path.push_back(
"*");
308 }
else if (*it == token::id) {
309 r.is_child.push_back(is_child);
315 throw parse_error(std::format(
316 "{} Expecting element, '*', '>', ',' or '{{' while parsing selector.",
token_location(it, last, context.path)));
323template<
typename It, std::sentinel_for<It> ItEnd>
324[[nodiscard]]
constexpr std::optional<style_sheet_selector>
325parse_style_sheet_selector(It& it, ItEnd last, style_sheet_parser_context& context)
328 auto r = style_sheet_selector{};
330 if (
auto pattern = parse_style_sheet_pattern(it, last, context)) {
331 r.push_back(*pattern);
336 while (it != last and *it ==
',') {
339 if (
auto pattern = parse_style_sheet_pattern(it, last, context)) {
340 r.push_back(*pattern);
342 throw parse_error(std::format(
"{} Missing pattern after ',' in selector.",
token_location(it, last, context.path)));
349template<
typename It, std::sentinel_for<It> ItEnd>
350[[nodiscard]]
constexpr std::optional<float>
351parse_style_sheet_color_component(It& it, ItEnd last, style_sheet_parser_context& context)
355 if (it.size() >= 2 and (it[0] == token::integer or it[0] == token::real) and it[1] ==
'%') {
358 }
else if (it != last and *it == token::real) {
359 r =
static_cast<float>(*it);
361 }
else if (it.size() >= 2 and it[0] ==
'-' and it[1] == token::real) {
362 r = -
static_cast<float>(it[1]);
364 }
else if (it != last and *it == token::integer) {
374template<
typename It, std::sentinel_for<It> ItEnd>
375[[nodiscard]]
constexpr std::optional<float>
376parse_style_sheet_alpha_component(It& it, ItEnd last, style_sheet_parser_context& context)
380 if (it.size() >= 2 and (it[0] == token::integer or it[0] == token::real) and it[1] ==
'%') {
381 r =
static_cast<float>(it[0]) * 0.01f;
383 }
else if (it != last and *it == token::real) {
384 r =
static_cast<float>(*it);
393template<
typename It, std::sentinel_for<It> ItEnd>
394[[nodiscard]]
constexpr std::optional<color> parse_style_sheet_color(It& it, ItEnd last, style_sheet_parser_context& context)
396 if (it != last and *it == token::color) {
398 auto r =
static_cast<color
>(*it);
403 throw parse_error(std::format(
404 "{} Invalid color literal '{}': {}",
410 }
else if (it != last and *it == token::id and *it ==
"rgb") {
416 if (it == last or *it !=
'(') {
418 std::format(
"{} Expect '(' after \"color-layers\" keyword.",
token_location(it, last, context.path)));
425 if (
auto component = parse_style_sheet_color_component(it, last, context)) {
428 throw parse_error(std::format(
"{} Expect a red-color-component after '('.",
token_location(it, last, context.path)));
431 if (it != last and *it ==
',') {
435 if (
auto component = parse_style_sheet_color_component(it, last, context)) {
438 throw parse_error(std::format(
439 "{} Expect a green-color-component after red-color-component.",
token_location(it, last, context.path)));
442 if (it != last and *it ==
',') {
446 if (
auto component = parse_style_sheet_color_component(it, last, context)) {
449 throw parse_error(std::format(
450 "{} Expect a blue-color-component after red-color-component.",
token_location(it, last, context.path)));
453 if (it != last and (*it ==
',' or *it ==
'/')) {
458 if (
auto component = parse_style_sheet_alpha_component(it, last, context)) {
462 if (it == last or *it !=
')') {
463 throw parse_error(std::format(
"{} Expect ')' after colors-components.",
token_location(it, last, context.path)));
468 }
else if (it != last and *it == token::id) {
473 if (
auto color = context.get_color(name)) {
478 std::format(
"{} Color name \"{}\" was not declared with @color.",
token_location(it, last, context.path), name));
486template<
typename It, std::sentinel_for<It> ItEnd>
487[[nodiscard]]
constexpr std::vector<color> parse_style_sheet_colors(It& it, ItEnd last, style_sheet_parser_context& context)
491 if (
auto color = parse_style_sheet_color(it, last, context)) {
497 if (it != last and *it ==
',') {
501 while (it != last and *it !=
';' and *it !=
'!') {
502 if (
auto color = parse_style_sheet_color(it, last, context)) {
505 throw parse_error(std::format(
"{} Expect a sequence of colors.",
token_location(it, last, context.path)));
508 if (it != last and *it ==
',') {
516template<
typename It, std::sentinel_for<It> ItEnd>
517[[nodiscard]]
constexpr std::optional<theme_length>
518parse_style_sheet_length(It& it, ItEnd last, style_sheet_parser_context& context)
520 if (it.size() >= 2 and (it[0] == token::integer or it[0] == token::real) and it[1] == token::id) {
521 hilet r = [&]() -> theme_length {
523 return dips{
static_cast<double>(it[0])};
524 }
else if (it[1] ==
"pt") {
525 return dips{
points{
static_cast<double>(it[0])}};
526 }
else if (it[1] ==
"mm") {
527 return dips{millimeters{
static_cast<double>(it[0])}};
528 }
else if (it[1] ==
"cm") {
529 return dips{centimeters{
static_cast<double>(it[0])}};
530 }
else if (it[1] ==
"dm") {
531 return dips{decimeters{
static_cast<double>(it[0])}};
532 }
else if (it[1] ==
"m") {
533 return dips{meters{
static_cast<double>(it[0])}};
534 }
else if (it[1] ==
"in") {
535 return dips{
inches{
static_cast<double>(it[0])}};
536 }
else if (it[1] ==
"px") {
537 return pixels(
static_cast<double>(it[0]));
538 }
else if (it[1] ==
"em") {
539 return em_quads(
static_cast<double>(it[0]));
541 throw parse_error(std::format(
542 "{} Expected either \"pt\", \"cm\", \"in\", \"em\" or \"px\" after number",
550 }
else if (it != last and (*it == token::integer or *it == token::real)) {
552 hilet r = theme_length{
dips{
static_cast<float>(*it)}};
561template<
typename It, std::sentinel_for<It> ItEnd>
563parse_style_sheet_lengths(It& it, ItEnd last, style_sheet_parser_context& context)
567 if (
auto length = parse_style_sheet_length(it, last, context)) {
573 if (it != last and *it ==
',') {
577 while (it != last and *it !=
';' and *it !=
'!') {
578 if (
auto length = parse_style_sheet_length(it, last, context)) {
579 r.push_back(*length);
581 throw parse_error(std::format(
"{} Expect a sequence of lengths.",
token_location(it, last, context.path)));
584 if (it != last and *it ==
',') {
592template<
typename It, std::sentinel_for<It> ItEnd>
593[[nodiscard]]
constexpr std::optional<style_sheet_value>
594parse_style_sheet_let_expansion(It& it, ItEnd last, style_sheet_parser_context& context)
596 if (it.size() < 2 or it[0] !=
'@' or it[1] != token::id) {
603 if (
auto value = context.get_let(name)) {
606 throw parse_error(std::format(
"{} Trying to expand undeclared @let {}.",
token_location(it, last, context.path), name));
610template<
typename It, std::sentinel_for<It> ItEnd>
611[[nodiscard]]
constexpr std::optional<style_sheet_value>
612parse_style_sheet_value(It& it, ItEnd last, style_sheet_parser_context& context)
614 if (
auto value = parse_style_sheet_let_expansion(it, last, context)) {
617 }
else if (
auto color = parse_style_sheet_color(it, last, context)) {
618 return style_sheet_value{*color};
620 }
else if (
auto length = parse_style_sheet_length(it, last, context)) {
621 return style_sheet_value{*length};
628template<
typename It, std::sentinel_for<It> ItEnd>
630parse_style_sheet_font_family_declaration(It& it, ItEnd last, style_sheet_parser_context& context)
632 auto family_id = font_family_id{};
633 while (it != last and *it !=
';' and *it !=
'!') {
638 }
else if (family_id) {
642 }
else if (*it == token::id and *it ==
"serif") {
663 std::format(
"{} Could not find any serif fallback font.",
token_location(it, last, context.path)));
667 }
else if (*it == token::id and *it ==
"sans-serif") {
686 std::format(
"{} Could not find any sans-serif fallback font.",
token_location(it, last, context.path)));
690 }
else if (*it == token::id and *it ==
"monospace") {
701 std::format(
"{} Could not find any monospace fallback font.",
token_location(it, last, context.path)));
705 }
else if (*it == token::id and *it ==
"cursive") {
720 std::format(
"{} Could not find any monospace fallback font.",
token_location(it, last, context.path)));
724 }
else if (*it == token::id and *it ==
"fantasy") {
740 std::format(
"{} Could not find any fantasy fallback font.",
token_location(it, last, context.path)));
744 }
else if (*it == token::dstr) {
749 throw parse_error(std::format(
750 "{} Expecting a font-family name or serif, sans-serif, monospace, cursive or fantasy.",
756 throw parse_error(std::format(
757 "{} Could not find any of the fonts in this font-family declaration.",
token_location(it, last, context.path)));
761 r.
emplace_back(style_sheet_declaration_name::font_family, family_id);
765template<
typename It, std::sentinel_for<It> ItEnd>
767parse_style_sheet_font_style_declaration(It& it, ItEnd last, style_sheet_parser_context& context)
771 if (it != last and *it == token::id and *it ==
"normal") {
773 }
else if (it != last and *it == token::id and *it ==
"italic") {
775 }
else if (it != last and *it == token::id and *it ==
"oblique") {
778 throw parse_error(std::format(
779 "{} Expecting normal, italic or oblique as value of a font-style declaration.",
787template<
typename It, std::sentinel_for<It> ItEnd>
789parse_style_sheet_font_weight_declaration(It& it, ItEnd last, style_sheet_parser_context& context)
793 if (it != last and *it == token::id and *it ==
"thin") {
794 r.
emplace_back(style_sheet_declaration_name::font_weight, font_weight::thin);
795 }
else if (it != last and *it == token::id and *it ==
"extra-light") {
796 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::extra_light);
797 }
else if (it != last and *it == token::id and *it ==
"light") {
798 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::light);
799 }
else if (it != last and *it == token::id and (*it ==
"regular" or *it ==
"normal")) {
800 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::regular);
801 }
else if (it != last and *it == token::id and *it ==
"medium") {
802 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::medium);
803 }
else if (it != last and *it == token::id and *it ==
"semi-bold") {
804 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::semi_bold);
805 }
else if (it != last and *it == token::id and *it ==
"bold") {
806 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::bold);
807 }
else if (it != last and *it == token::id and *it ==
"extra-bold") {
808 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::extra_bold);
809 }
else if (it != last and *it == token::id and *it ==
"black") {
810 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::black);
811 }
else if (it != last and *it == token::id and *it ==
"extra-black") {
812 r.emplace_back(style_sheet_declaration_name::font_weight, font_weight::extra_black);
814 }
else if (it != last and *it == token::integer) {
815 r.emplace_back(style_sheet_declaration_name::font_weight,
font_weight_from_int(
static_cast<int>(*it)));
818 throw parse_error(std::format(
819 "{} Expecting a integer or value of a font-weight declaration.",
token_location(it, last, context.path)));
826template<
typename It, std::sentinel_for<It> ItEnd>
828parse_style_sheet_margin_declarations(It& it, ItEnd last, style_sheet_parser_context& context)
832 if (
auto lengths = parse_style_sheet_lengths(it, last, context); not lengths.empty()) {
833 if (lengths.size() == 1) {
834 r.
emplace_back(style_sheet_declaration_name::margin_top, lengths[0]);
835 r.emplace_back(style_sheet_declaration_name::margin_right, lengths[0]);
836 r.emplace_back(style_sheet_declaration_name::margin_bottom, lengths[0]);
837 r.emplace_back(style_sheet_declaration_name::margin_left, lengths[0]);
838 }
else if (lengths.size() == 2) {
839 r.emplace_back(style_sheet_declaration_name::margin_top, lengths[0]);
840 r.emplace_back(style_sheet_declaration_name::margin_right, lengths[1]);
841 r.emplace_back(style_sheet_declaration_name::margin_bottom, lengths[0]);
842 r.emplace_back(style_sheet_declaration_name::margin_left, lengths[1]);
843 }
else if (lengths.size() == 3) {
844 r.emplace_back(style_sheet_declaration_name::margin_top, lengths[0]);
845 r.emplace_back(style_sheet_declaration_name::margin_right, lengths[1]);
846 r.emplace_back(style_sheet_declaration_name::margin_bottom, lengths[2]);
847 r.emplace_back(style_sheet_declaration_name::margin_left, lengths[1]);
848 }
else if (lengths.size() == 4) {
849 r.emplace_back(style_sheet_declaration_name::margin_top, lengths[0]);
850 r.emplace_back(style_sheet_declaration_name::margin_right, lengths[1]);
851 r.emplace_back(style_sheet_declaration_name::margin_bottom, lengths[2]);
852 r.emplace_back(style_sheet_declaration_name::margin_left, lengths[3]);
854 throw parse_error(std::format(
855 "{} Expect 1 to 4 length values when parsing \"margin\" declaration, got {}.",
860 throw parse_error(std::format(
861 "{} Expect 1 to 4 length values when parsing \"margin\" declaration.",
token_location(it, last, context.path)));
867template<
typename It, std::sentinel_for<It> ItEnd>
869parse_style_sheet_spacing_declarations(It& it, ItEnd last, style_sheet_parser_context& context)
873 if (
auto lengths = parse_style_sheet_lengths(it, last, context); not lengths.empty()) {
874 if (lengths.size() == 1) {
875 r.
emplace_back(style_sheet_declaration_name::spacing_vertical, lengths[0]);
876 r.emplace_back(style_sheet_declaration_name::spacing_horizontal, lengths[0]);
877 }
else if (lengths.size() == 2) {
878 r.emplace_back(style_sheet_declaration_name::spacing_vertical, lengths[0]);
879 r.emplace_back(style_sheet_declaration_name::spacing_horizontal, lengths[1]);
881 throw parse_error(std::format(
882 "{} Expect 1 or 2 length values when parsing \"spacing\" declaration, got {}.",
887 throw parse_error(std::format(
888 "{} Expect 1 or 2 length values when parsing \"spacing\" declaration.",
token_location(it, last, context.path)));
894template<
typename It, std::sentinel_for<It> ItEnd>
896parse_style_sheet_border_radius_declarations(It& it, ItEnd last, style_sheet_parser_context& context)
900 if (
auto lengths = parse_style_sheet_lengths(it, last, context); not lengths.empty()) {
901 if (lengths.size() == 1) {
902 r.
emplace_back(style_sheet_declaration_name::border_top_left_radius, lengths[0]);
903 r.emplace_back(style_sheet_declaration_name::border_top_right_radius, lengths[0]);
904 r.emplace_back(style_sheet_declaration_name::border_bottom_left_radius, lengths[0]);
905 r.emplace_back(style_sheet_declaration_name::border_bottom_right_radius, lengths[0]);
906 }
else if (lengths.size() == 2) {
907 r.emplace_back(style_sheet_declaration_name::border_top_left_radius, lengths[0]);
908 r.emplace_back(style_sheet_declaration_name::border_top_right_radius, lengths[1]);
909 r.emplace_back(style_sheet_declaration_name::border_bottom_left_radius, lengths[1]);
910 r.emplace_back(style_sheet_declaration_name::border_bottom_right_radius, lengths[0]);
911 }
else if (lengths.size() == 4) {
912 r.emplace_back(style_sheet_declaration_name::border_top_left_radius, lengths[0]);
913 r.emplace_back(style_sheet_declaration_name::border_top_right_radius, lengths[1]);
914 r.emplace_back(style_sheet_declaration_name::border_bottom_left_radius, lengths[2]);
915 r.emplace_back(style_sheet_declaration_name::border_bottom_right_radius, lengths[3]);
917 throw parse_error(std::format(
918 "{} Expect 1, 2 or 4 length values when parsing \"border-radius\" declaration, got {}.",
923 throw parse_error(std::format(
924 "{} Expect 1, 2 or 4 length values when parsing \"border-radius\" declaration.",
931template<
typename It, std::sentinel_for<It> ItEnd>
933parse_style_sheet_caret_color_declarations(It& it, ItEnd last, style_sheet_parser_context& context)
937 if (
auto colors = parse_style_sheet_colors(it, last, context); not colors.empty()) {
938 if (colors.size() == 1) {
939 r.
emplace_back(style_sheet_declaration_name::caret_color_primary, colors[0]);
940 r.emplace_back(style_sheet_declaration_name::caret_color_secondary, colors[0]);
941 }
else if (colors.size() == 2) {
942 r.emplace_back(style_sheet_declaration_name::caret_color_primary, colors[0]);
943 r.emplace_back(style_sheet_declaration_name::caret_color_secondary, colors[1]);
945 throw parse_error(std::format(
946 "{} Expect 1 or 2 color values when parsing \"caret-color\" declaration, got {}.",
951 throw parse_error(std::format(
952 "{} Expect 1 or 2 color values when parsing \"caret-color\" declaration.",
token_location(it, last, context.path)));
958template<
typename It, std::sentinel_for<It> ItEnd>
959[[nodiscard]]
constexpr std::optional<std::vector<style_sheet_declaration>>
960parse_style_sheet_macro_expansion(It& it, ItEnd last, style_sheet_parser_context& context)
962 if (it.size() < 2 or it[0] !=
'@' or it[1] != token::id) {
970 if (
auto declarations = context.get_macro(name)) {
973 throw parse_error(std::format(
"{} Trying to expand undeclared @macro {}.",
token_location(it, last, context.path), name));
976 if (it == last or *it !=
';') {
977 throw parse_error(std::format(
978 "{} Missing ';' after @macro {} expansion while parsing declaration.",
token_location(it, last, context.path), name));
984template<
typename It, std::sentinel_for<It> ItEnd>
985[[nodiscard]]
constexpr std::optional<std::vector<style_sheet_declaration>>
986parse_style_sheet_declaration(It& it, ItEnd last, style_sheet_parser_context& context)
991 if (it.size() < 2 or it[0] != token::id or it[1] !=
':') {
998 if (name ==
"margin") {
999 r = parse_style_sheet_margin_declarations(it, last, context);
1001 }
else if (name ==
"spacing") {
1002 r = parse_style_sheet_spacing_declarations(it, last, context);
1004 }
else if (name ==
"border-radius") {
1005 r = parse_style_sheet_border_radius_declarations(it, last, context);
1007 }
else if (name ==
"caret-color") {
1008 r = parse_style_sheet_caret_color_declarations(it, last, context);
1010 }
else if (name ==
"font-family") {
1011 r = parse_style_sheet_font_family_declaration(it, last, context);
1013 }
else if (name ==
"font-style") {
1014 r = parse_style_sheet_font_style_declaration(it, last, context);
1016 }
else if (name ==
"font-weight") {
1017 r = parse_style_sheet_font_weight_declaration(it, last, context);
1021 if (
auto id = style_sheet_declaration_name_metadata.at_if(name)) {
1024 throw parse_error(std::format(
"{} Invalid declaration name '{}'.",
token_location(it, last, context.path), name));
1029 if (
auto value = parse_style_sheet_value(it, last, context)) {
1033 throw parse_error(std::format(
1034 "{} Missing value after ':' while parsing {} declaration.",
token_location(it, last, context.path), name));
1038 hilet value_mask = style_sheet_declaration_name_value_mask_metadata[id];
1039 if (std::holds_alternative<hi::dips>(value) and not to_bool(value_mask & style_sheet_value_mask::dips)) {
1040 throw parse_error(std::format(
1041 "{} Incorrect value type 'length:pt' for declaration of '{}'",
token_location(it, last, context.path), name));
1042 }
else if (std::holds_alternative<hi::pixels>(value) and not to_bool(value_mask & style_sheet_value_mask::pixels)) {
1043 throw parse_error(std::format(
1044 "{} Incorrect value type 'length:px' for declaration of '{}'",
token_location(it, last, context.path), name));
1045 }
else if (std::holds_alternative<hi::em_quads>(value) and not to_bool(value_mask & style_sheet_value_mask::em_quads)) {
1046 throw parse_error(std::format(
1047 "{} Incorrect value type 'length:em' for declaration of '{}'",
token_location(it, last, context.path), name));
1048 }
else if (std::holds_alternative<hi::color>(value) and not to_bool(value_mask & style_sheet_value_mask::color)) {
1049 throw parse_error(std::format(
1050 "{} Incorrect value type 'color' for declaration of '{}'",
token_location(it, last, context.path), name));
1052 std::holds_alternative<hi::font_family_id>(value) and
1053 not to_bool(value_mask & style_sheet_value_mask::font_family_id)) {
1054 throw parse_error(std::format(
1055 "{} Incorrect value type 'font family id' for declaration of '{}'",
1059 std::holds_alternative<hi::font_weight>(value) and not to_bool(value_mask & style_sheet_value_mask::font_weight)) {
1060 throw parse_error(std::format(
1061 "{} Incorrect value type 'font weight' for declaration of '{}'",
token_location(it, last, context.path), name));
1063 std::holds_alternative<hi::font_style>(value) and not to_bool(value_mask & style_sheet_value_mask::font_style)) {
1064 throw parse_error(std::format(
1065 "{} Incorrect value type 'font style' for declaration of '{}'",
token_location(it, last, context.path), name));
1072 if (it.size() >= 2 and it[0] ==
'!' and it[1] == token::id and it[1] ==
"important") {
1079 if (it == last or *it !=
';') {
1080 throw parse_error(std::format(
1081 "{} Missing ';' after value while parsing {} declaration.",
token_location(it, last, context.path), name));
1088template<
typename It, std::sentinel_for<It> ItEnd>
1089[[nodiscard]]
constexpr std::optional<style_sheet_rule_set>
1090parse_style_sheet_rule_set(It& it, ItEnd last, style_sheet_parser_context& context)
1093 auto r = style_sheet_rule_set{};
1095 if (
auto selector = parse_style_sheet_selector(it, last, context)) {
1096 r.selector = *selector;
1098 return std::nullopt;
1101 while (it != last and *it ==
':') {
1104 if (
auto state_and_mask = parse_style_sheet_theme_state(it, last, context)) {
1105 r.state |= state_and_mask->first;
1106 r.state_mask |= state_and_mask->second;
1108 }
else if (
auto language_tag = parse_style_sheet_theme_state_lang(it, last, context)) {
1109 r.language = *language_tag;
1111 }
else if (
auto phrasing_mask = parse_style_sheet_theme_state_phrasing(it, last, context)) {
1112 r.phrasing_mask = *phrasing_mask;
1116 std::format(
"{} Expecting state-id after ':' in selector.",
token_location(it, last, context.path)));
1120 if (it != last and *it ==
'{') {
1123 throw parse_error(std::format(
"{} Missing '{{' while parsing rule-set.",
token_location(it, last, context.path)));
1126 while (it != last and *it !=
'}') {
1127 if (
auto macro_declaration = parse_style_sheet_macro_expansion(it, last, context)) {
1128 r.declarations.insert(r.declarations.end(), macro_declaration->begin(), macro_declaration->end());
1130 }
else if (
auto rule_declaration = parse_style_sheet_declaration(it, last, context)) {
1133 r.declarations.insert(r.declarations.end(), rule_declaration->begin(), rule_declaration->end());
1136 std::format(
"{} Missing declaration while parsing rule-set.",
token_location(it, last, context.path)));
1140 if (it != last and *it ==
'}') {
1143 throw parse_error(std::format(
"{} Missing '}}' while parsing rule-set.",
token_location(it, last, context.path)));
1149template<
typename It, std::sentinel_for<It> ItEnd>
1150[[nodiscard]]
constexpr bool parse_style_sheet_color_at_rule(It& it, ItEnd last, style_sheet_parser_context& context)
1152 if (it.size() >= 2 and it[0] ==
'@' and it[1] == token::id and it[1] ==
"color") {
1155 if (it == last and *it != token::id) {
1156 throw parse_error(std::format(
"{} Expect name while parsing @color.",
token_location(it, last, context.path)));
1163 throw parse_error(std::format(
1164 "{} Undefined color-name \"{}\" while parsing @color declaration.",
1169 if (it == last or *it !=
':') {
1170 throw parse_error(std::format(
1171 "{} Missing ':' after color-name of @color {} declaration.",
token_location(it, last, context.path), name));
1175 if (
auto color = parse_style_sheet_color(it, last, context)) {
1176 if (not context.set_color(name, *color)) {
1178 std::format(
"{} @color {} was already declared earlier.",
token_location(it, last, context.path), name));
1182 std::format(
"{} Missing color-value in @color {} declaration.",
token_location(it, last, context.path), name));
1185 if (it == last or *it !=
';') {
1187 std::format(
"{} Missing ';' after @color {} declaration.",
token_location(it, last, context.path), name));
1197template<
typename It, std::sentinel_for<It> ItEnd>
1198[[nodiscard]]
constexpr bool parse_style_sheet_let_at_rule(It& it, ItEnd last, style_sheet_parser_context& context)
1201 if (it.size() < 2 or it[0] !=
'@' or it[1] != token::id or it[1] !=
"let") {
1206 if (it == last or *it != token::id) {
1207 throw parse_error(std::format(
"{} Expect a name after @let.",
token_location(it, last, context.path)));
1212 if (it == last or *it !=
':') {
1213 throw parse_error(std::format(
"{} Expect ':' after @let {}.",
token_location(it, last, context.path), let_name));
1217 if (
auto value = parse_style_sheet_value(it, last, context)) {
1218 if (not context.set_let(let_name, *value)) {
1220 std::format(
"{} @let {} was already declared earlier.",
token_location(it, last, context.path), let_name));
1223 throw parse_error(std::format(
"{} Expect value after @let {} :.",
token_location(it, last, context.path), let_name));
1226 if (it == last or *it !=
';') {
1228 std::format(
"{} Expect ';' after @let {} declaration.",
token_location(it, last, context.path), let_name));
1234template<
typename It, std::sentinel_for<It> ItEnd>
1235[[nodiscard]]
constexpr bool parse_style_sheet_macro_at_rule(It& it, ItEnd last, style_sheet_parser_context& context)
1239 if (it.size() < 2 or it[0] !=
'@' or it[1] != token::id or it[1] !=
"macro") {
1245 if (it == last or *it != token::id) {
1246 throw parse_error(std::format(
"{} Expect a name after @macro.",
token_location(it, last, context.path)));
1251 if (it == last or *it !=
'{') {
1252 throw parse_error(std::format(
"{} Expect '{{' after a @macro {}.",
token_location(it, last, context.path), macro_name));
1257 while (it != last and *it !=
'}') {
1258 if (
auto macro_declaration = parse_style_sheet_macro_expansion(it, last, context)) {
1259 declarations.
insert(declarations.end(), macro_declaration->begin(), macro_declaration->end());
1261 }
else if (
auto rule_declaration = parse_style_sheet_declaration(it, last, context)) {
1264 declarations.insert(declarations.end(), rule_declaration->begin(), rule_declaration->end());
1267 throw parse_error(std::format(
1268 "{} Missing declaration while parsing @macro {}.",
token_location(it, last, context.path), macro_name));
1272 if (it == last or *it !=
'}') {
1274 std::format(
"{} Expect '}}' after a @macro {} declarations.",
token_location(it, last, context.path), macro_name));
1278 if (not context.set_macro(macro_name,
std::move(declarations))) {
1280 std::format(
"{} @macro {} was already declared earlier.",
token_location(it, last, context.path), macro_name));
1285template<
typename It, std::sentinel_for<It> ItEnd>
1286[[nodiscard]]
constexpr std::optional<std::string>
1287parse_style_sheet_name_at_rule(It& it, ItEnd last, style_sheet_parser_context& context)
1289 if (it.size() < 2 or it[0] !=
'@' or it[1] != token::id or it[1] !=
"name") {
1290 return std::nullopt;
1294 if (it == last or *it != token::dstr) {
1295 throw parse_error(std::format(
"{} Expect string after @name.",
token_location(it, last, context.path)));
1300 if (it == last or *it !=
';') {
1301 throw parse_error(std::format(
"{} Expect ';' after @name \"{}\".",
token_location(it, last, context.path), r));
1308template<
typename It, std::sentinel_for<It> ItEnd>
1309[[nodiscard]]
constexpr std::optional<theme_mode>
1310parse_style_sheet_mode_at_rule(It& it, ItEnd last, style_sheet_parser_context& context)
1312 if (it.size() < 2 or it[0] !=
'@' or it[1] != token::id or it[1] !=
"mode") {
1313 return std::nullopt;
1318 if (it != last and *it == token::id and *it ==
"light") {
1319 return theme_mode::light;
1320 }
else if (it != last and *it == token::id and *it ==
"dark") {
1321 return theme_mode::dark;
1323 throw parse_error(std::format(
"{} Expect light or dark after @mode.",
token_location(it, last, context.path)));
1328 if (it == last or *it !=
';') {
1329 throw parse_error(std::format(
"{} Expect ';' after @name \"{}\".",
token_location(it, last, context.path), r));
1336template<
typename It, std::sentinel_for<It> ItEnd>
1337[[nodiscard]]
constexpr std::optional<style_sheet> parse_style_sheet(It& it, ItEnd last, style_sheet_parser_context& context)
1340 auto r = style_sheet{};
1342 if (
auto name = parse_style_sheet_name_at_rule(it, last, context)) {
1346 std::format(
"{} Did not find required @name as first declaration.",
token_location(it, last, context.path)));
1349 if (
auto mode = parse_style_sheet_mode_at_rule(it, last, context)) {
1353 std::format(
"{} Did not find required @mode declaration after @name in the style sheet.",
token_location(it, last, context.path)));
1356 while (it != last) {
1357 if (parse_style_sheet_color_at_rule(it, last, context)) {
1359 }
else if (parse_style_sheet_let_at_rule(it, last, context)) {
1361 }
else if (parse_style_sheet_macro_at_rule(it, last, context)) {
1363 }
else if (
auto rule_set = parse_style_sheet_rule_set(it, last, context)) {
1364 r.rule_sets.push_back(*rule_set);
1366 throw parse_error(std::format(
"{} Found unexpected token.",
token_location(it, last, context.path)));
1375template<
typename It, std::sentinel_for<It> ItEnd>
1376[[nodiscard]]
constexpr style_sheet parse_style_sheet(It first, ItEnd last, std::filesystem::path
const& path)
1378 auto lexer_it = lexer<lexer_config::css_style()>.parse(first, last);
1379 auto lookahead_it = make_lookahead_iterator<4>(lexer_it, std::default_sentinel);
1381 auto context = detail::style_sheet_parser_context{};
1382 context.path = path;
1383 if (
auto stylesheet = detail::parse_style_sheet(lookahead_it, std::default_sentinel, context)) {
1384 stylesheet->colors = context.move_colors();
1388 std::format(
"{} Could not parse style sheet file.",
token_location(lookahead_it, std::default_sentinel, path)));
1392[[nodiscard]]
constexpr style_sheet parse_style_sheet(std::string_view str, std::filesystem::path
const& path)
1394 return parse_style_sheet(str.begin(), str.end(), path);
1397[[nodiscard]]
inline style_sheet parse_style_sheet(std::filesystem::path
const& path)
1399 return parse_style_sheet(as_string_view(file_view{path}), path);
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
float sRGB_gamma_to_linear(float u) noexcept
sRGB gamma to linear transfer function.
Definition sRGB.hpp:65
DOXYGEN BUG.
Definition algorithm.hpp:13
constexpr font_weight font_weight_from_int(numeric_integral auto rhs)
Convert a font weight value between 50 and 1000 to a font weight.
Definition font_weight.hpp:46
font_family_id find_font_family(std::string const &family_name) noexcept
Find font family id.
Definition font_book.hpp:196
geometry/margins.hpp
Definition cache.hpp:11
unit< em_length_tag, double > em_quads
Em-quad: A font's line-height.
Definition units.hpp:200
unit< px_length_tag, double > pixels
A physical pixel on a display.
Definition units.hpp:196
constexpr std::string token_location(It &it, ItEnd last, std::filesystem::path const &path) noexcept
Create a location string for error messages.
Definition token.hpp:165
unit< si_length_tag, double, std::ratio< 254, 960 '000 >::type > dips
Device Independent Pixels: 1/96 inch.
Definition units.hpp:192
unit< si_length_tag, double, std::ratio< 254, 10 '000 >::type > inches
Inch: 254 mm.
Definition units.hpp:185
unit< si_length_tag, double, std::ratio< 254, 720 '000 >::type > points
Points: 1/72 inch.
Definition units.hpp:181
@ italic
A font that is italic.
@ oblique
A font that is oblique.
@ normal
A font that is normal, non-italic.
This is a RGBA floating point color.
Definition color.hpp:44
static color * find(std::string const &name) noexcept
Find a color by name.
Definition color.hpp:284
Definition style_sheet.hpp:53
Definition style_sheet_parser.hpp:18
Exception thrown during parsing on an error.
Definition exception.hpp:50
T emplace_back(T... args)