Разбор CSS с Boost.Spirit X3 - PullRequest
       38

Разбор CSS с Boost.Spirit X3

0 голосов
/ 08 ноября 2018

Я пытаюсь написать (частичный) парсер CSS с помощью Boost.Spirit X3.

У меня (очень) базовая настройка работает:

const auto declaration_block_def = '{' >> +declaration >> '}';
const auto declaration_def = property >> ':' >> value >> ';';
const auto property_def = +(char_ - ":");
const auto value_def = +(char_ - ";");

Здесь value - это простой анализатор строк, а property таблица символов всех имен свойств CSS для перечисления, в котором перечислены все свойства. Но теперь мне интересно, не смог бы я каким-то образом закодировать все возможные пары ключ-значение строго типизированным способом? Конкретно, я бы использовал symbols<enum_type> с соответствующими записями таблицы символов для каждого свойства, у которого есть фиксированное число возможностей, и какое-то пользовательское правило для более сложных свойств, таких как цвета.

Дело в том, что правило declaration должно иметь определенный атрибут, а в CSS блок объявления может содержать любое количество элементов со своим собственным типом «атрибута». В конце я хотел бы получить структуру, которую я передал бы BOOST_FUSION_ADAPT_STRUCT в следующем виде:

enum class align_content : std::uint8_t;
enum class align_items : std::uint8_t;
enum class align_self : std::uint8_t;

struct declaration_block
{
  css::align_content align_content{};
  css::align_items align_items{};
  css::align_self align_self{};
};

Который затем будет правильно инициализировать по умолчанию любые неопределенные элементы.

Я вижу несколько проблем с X3, которые я не знаю, как решить:

  1. Строго типизированный атрибут правила, как указано выше
  2. Адаптированная для fusion структура ожидает, что все члены будут проанализированы, что исключает мое представление о том, что мой простой подход действительно работает.

Я нашел то, что похоже на реализацию Boost.Spirit.Qi 2 , но, поскольку X3 настолько отличается, и их конечный результат кажется неясным, я не могу найти никакой помощи в этом .. .

1 Ответ

0 голосов
/ 08 ноября 2018

Похоже, вы хотите сгенерировать код вашего парсера из определения структуры. Вы можете, однако, вам следует использовать генератор кода.

Вот как я знаю, что вы можете быть достаточно близки с Ци:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_auto.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <iostream>
#include <iomanip>
#include <type_traits>

namespace css {
    enum class align_content : std::uint8_t;
    enum class align_items   : std::uint8_t;
    enum class align_self    : std::uint8_t;
}

namespace qi = boost::spirit::qi;

template <typename T> static constexpr char const* name_of = nullptr;

template <> constexpr char const* name_of<css::align_content> = "content";
template <> constexpr char const* name_of<css::align_items> = "items";
template <> constexpr char const* name_of<css::align_self> = "self";

namespace {
    template <typename T> struct align_parser {
        static auto call() {
            return qi::copy(qi::lexeme[name_of<T>] >> ":" >> qi::int_ >> ';');
        };

        using type = decltype(call());
    };
}

namespace css {
    // grrr: https://stackoverflow.com/a/36568565/85371
    template<class T, bool = std::is_enum<T>::value> struct safe_underlying_type : std::underlying_type<T> {};
    template<class T> struct safe_underlying_type<T, false /* is_enum */> {};

    template <typename T, typename Underlying = typename safe_underlying_type<T>::type > std::ostream& operator<<(std::ostream& os, T v) {
        using Int = std::common_type_t<int, Underlying>;
        return os << name_of<T> << " -> " << static_cast<Int>(v);
    }
}

namespace boost::spirit::traits {
    template <> struct create_parser<css::align_content> : align_parser<css::align_content> {};
    template <> struct create_parser<css::align_items> : align_parser<css::align_items> {};
    template <> struct create_parser<css::align_self> : align_parser<css::align_self> {};
}

struct declaration_block {
    css::align_content align_content{};
    css::align_items   align_items{};
    css::align_self    align_self{};
};

BOOST_FUSION_ADAPT_STRUCT(declaration_block, align_content, align_items, align_self)

int main() {
    for (std::string const input : {
            "", 
            "self:42;",
            "content:7;items:99;self:42;",
            "content : 7 ; items : 99; self : 42; ",
            " self : 42; items : 99; content : 7 ; ",
        }) 
    {
        std::cout << " ==== Test: " << std::quoted(input) << "\n";
        auto f = input.begin(), l = input.end();

        declaration_block db;
        bool ok = qi::phrase_parse(f, l, (qi::auto_ ^ qi::auto_ ^ qi::auto_) | qi::eoi, qi::space, db);

        if (ok) {
            using boost::fusion::operator<<;
            std::cout << "Parsed: " << db << "\n";
        }
        else
            std::cout << "Failed\n";

        if (f != l)
            std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
    }
}

Печать

 ==== Test: ""
Parsed: (content -> 0 items -> 0 self -> 0)
 ==== Test: "self:42;"
Parsed: (content -> 0 items -> 0 self -> 42)
 ==== Test: "content:7;items:99;self:42;"
Parsed: (content -> 7 items -> 99 self -> 42)
 ==== Test: "content : 7 ; items : 99; self : 42; "
Parsed: (content -> 7 items -> 99 self -> 42)
 ==== Test: " self : 42; items : 99; content : 7 ; "
Parsed: (content -> 7 items -> 99 self -> 42)

Подробнее / Идеи

Этот подход рассматривается более подробно:

Я дал этому вопросу ответ также в стиле X3:

Для большего вдохновения X3 я от всей души рекомендую:

Для меня одна любимая мозоль - это то, что мы должны иметь возможность использовать структурированное связывание, чтобы мы больше не были Фениксом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...