使用Boost.Spirit X3解析CSS [英] Parsing CSS with Boost.Spirit X3
问题描述
我正在尝试使用Boost.Spirit X3编写(部分)CSS解析器.
I'm attempting to write a (partial) CSS parser using Boost.Spirit X3.
我有(非常)基本的设置工作:
I have the (very) basic setup working:
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>
和匹配的符号表条目,并对一些更复杂的属性(例如颜色)使用一些自定义规则.
Here value
is just a simple string parser, and property
a symbol table of all the CSS property names to an enum listing all the properties. But now I wonder if I couldn't in some way encode all the possible key-value pairs, in a strongly typed manner? Concretely, I'd use symbols<enum_type>
with matching symbol table entries for each property that has a fixed number of possibilities, and some custom rule for more complex properties like colors.
问题是 declaration
规则必须具有特定的属性,在CSS中,声明块可以包含任意数量的元素,这些元素都具有自己的属性"类型.最后,我想得到一个结构,然后以以下形式传递给BOOST_FUSION_ADAPT_STRUCT:
The thing is that the declaration
rule has to have a certain attribute, and in CSS, the declaration block can contain any number of elements all with their own "attribute" type. In the end I'd like to end up with a struct I'd pass to BOOST_FUSION_ADAPT_STRUCT in the following form:
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{};
};
随后将正确默认使用哪个初始化未指定的成员.
Which would then properly default initialize any unspecified members.
我看到X3出现了一些我不知道如何解决的问题:
I see a few issues popping up for X3 that I don't know how to solve:
- 如上所述的强类型规则属性
- 融合适应的结构期望所有成员都被解析,这排除了我对我的简单方法实际起作用的想法.
我发现了一个 Boost.Spirit.Qi 2实现,但是由于X3如此不同,最终结果似乎不清楚,因此我似乎找不到任何帮助...
I have found what seems like a Boost.Spirit.Qi 2 implementation, but as X3 is so different and their end result seems unclear, I can't seem to find any help in that...
推荐答案
您似乎希望从结构定义中生成解析器代码.您可以,但是您可能应该使用代码生成器.
It looks like you wish to generate your parser code from struct definition. You can, however you should probably use a code generator.
这就是我知道您可以与Qi保持合理距离的原因:
Here's how I know you can get reasonably close with Qi:
#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)
更多信息/想法
可以更详细地了解这种方法:
More Info/Ideas
This approach is seen in more detail:
我也给了这个问题一个X3风格的答案:
I gave that question has an X3-style answer as well:
对于X3的更多启发,我衷心推荐:
For more X3 inspiration I heartily recommend:
x3::as_parser
x3::base_parser
/x3::parser<CRTP>
(e.g. Spirit-Qi: How can I write a nonterminal parser?,
让我烦恼的是,我们应该能够使用结构化绑定,这样我们就不再需要Phoenix了.
One pet peeve for me is that we should be able to use structured binding so we don't nee Phoenix anymore.
这篇关于使用Boost.Spirit X3解析CSS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!