使用 Boost Spirit X3 解析多个 CSS 选择器
Parsing multiple CSS selectors with Boost Spirit X3
我正在尝试使用 Boost Spirit X3 解析多个 CSS 选择器。例如,如果我有以下适用于多个 ID(选择器)的 CSS 规则:
#ID1, #ID2, #ID3 { color:#000000; }
其中 #ID1, #ID2, #ID3
是选择器,color:#000000;
是声明块,相同的声明块适用于所有 3 个选择器。并假设 rule
、selector
和 declaration_block
的结构如下:
struct Rule {
Selector selector;
DeclationBlock declarationBlock;
}
struct Selector {
std::string target;
}
struct DeclarationBlock {
std::vector<Declaration> declarations;
}
而且我已经有了选择器和声明块的 Spirit 规则:
auto selector = x3::rule<struct SelectorId, css::Selector>{"selector"};
auto declaration_block = x3::rule<struct DeclarationBlockId, css::DeclarationBlock>{"declaration-block"};
单个选择器的解析规则很简单:
auto rule = x3::rule<struct RuleId, css::Rule>{"rule"} = selector >> declaration_block;
auto rules = x3::rule<struct RulesId, std::vector<css::Rule>>{"rules"} = *rule;
但是,我的问题是,如何为多个选择器解析同一个声明块?我正在尝试使用语义操作来基本上复制所有选择器的声明块,但我不知道这是否是最好的方法。
我不确定是否可以将声明块的解析器属性用于多个选择器。很可能不是。
我宁愿以反映实际代码的方式更改 AST(和解析器):
struct Rule {
std::vector<Selector> selectors; // why not?
DeclationBlock declarationBlock;
}
auto const selectors = selector % ",";
如果您希望 selector
和 declarationBlock
之间有一个 1:1 对应关系,那么,根据上面定义的 Rule
,应该可以在另一个这样的解析器:
struct SingleRule
{
Selector selector;
DeclationBlock declarationBlock;
};
auto unwrap = [](auto & ctx)
{
// iterate through all the selectors
// and add them to the vector together with linked
// declaration block
for (auto const & s: _attr(ctx).selectors)
_val(ctx).push_back(SingleRule{s, _attr(ctx).declarationBlock});
};
auto const unwrapped_rules
= x3::rule<struct UnwrappedRulesId, std::vector<css::SingleRule>>
= rule [unwrap];
虽然不确定这是个好主意。也许,从解析器中抛出最接近和最简单的 AST,然后再进行所有的简化和优化会更容易。
此外,已经有一个CSS-parser使用Boost.Spirit.X3编写。您可以尝试将其作为参考或灵感。
我正在尝试使用 Boost Spirit X3 解析多个 CSS 选择器。例如,如果我有以下适用于多个 ID(选择器)的 CSS 规则:
#ID1, #ID2, #ID3 { color:#000000; }
其中 #ID1, #ID2, #ID3
是选择器,color:#000000;
是声明块,相同的声明块适用于所有 3 个选择器。并假设 rule
、selector
和 declaration_block
的结构如下:
struct Rule {
Selector selector;
DeclationBlock declarationBlock;
}
struct Selector {
std::string target;
}
struct DeclarationBlock {
std::vector<Declaration> declarations;
}
而且我已经有了选择器和声明块的 Spirit 规则:
auto selector = x3::rule<struct SelectorId, css::Selector>{"selector"};
auto declaration_block = x3::rule<struct DeclarationBlockId, css::DeclarationBlock>{"declaration-block"};
单个选择器的解析规则很简单:
auto rule = x3::rule<struct RuleId, css::Rule>{"rule"} = selector >> declaration_block;
auto rules = x3::rule<struct RulesId, std::vector<css::Rule>>{"rules"} = *rule;
但是,我的问题是,如何为多个选择器解析同一个声明块?我正在尝试使用语义操作来基本上复制所有选择器的声明块,但我不知道这是否是最好的方法。
我不确定是否可以将声明块的解析器属性用于多个选择器。很可能不是。
我宁愿以反映实际代码的方式更改 AST(和解析器):
struct Rule {
std::vector<Selector> selectors; // why not?
DeclationBlock declarationBlock;
}
auto const selectors = selector % ",";
如果您希望 selector
和 declarationBlock
之间有一个 1:1 对应关系,那么,根据上面定义的 Rule
,应该可以在另一个这样的解析器:
struct SingleRule
{
Selector selector;
DeclationBlock declarationBlock;
};
auto unwrap = [](auto & ctx)
{
// iterate through all the selectors
// and add them to the vector together with linked
// declaration block
for (auto const & s: _attr(ctx).selectors)
_val(ctx).push_back(SingleRule{s, _attr(ctx).declarationBlock});
};
auto const unwrapped_rules
= x3::rule<struct UnwrappedRulesId, std::vector<css::SingleRule>>
= rule [unwrap];
虽然不确定这是个好主意。也许,从解析器中抛出最接近和最简单的 AST,然后再进行所有的简化和优化会更容易。
此外,已经有一个CSS-parser使用Boost.Spirit.X3编写。您可以尝试将其作为参考或灵感。