Разностный парсер может быть создан двоичным -
(минус) оператор:
rule = qi::char_ - qi::lit("}}")
или даже сложные различия:
rule = qi::char_ - qi::lit("}}") - qi::lit("]]")
Но как я мог сгенерировать весь результат анализатора различий во время разбора?
Я предполагаю, что это может быть какая-то форма, как показано ниже:
phoenix::function<difference_parser_impl> difference_parser;
rule = qi::lazy(difference_parser(qi::char_, {"}}", "]]"}));
Здесь {..., ..., ...}
part на самом деле будет контейнером stl, но это не главное; Я могу справиться с этой частью.
Я нашел шаблон qi::difference<Left, Right>
— но я не мог понять, как его использовать.
Мне кажется, вы не столько ищете динамическое выражение «различие», сколько динамическое выражение «альтернативная переменная (a | b | c …)»:
expr - a - b - c
эквивалентно expr - (a|b|c)
Вы можете легко добиться разницы, используя либо:
expr - orCombine(alternatives)
или же
!orCombine(alternatives) >> expr
Теперь у этого есть много грубых краев, которые я объясню сначала. К счастью, есть более простой способ, используя qi::symbols
, который я продемонстрирую сразу после этого.
Если вы хотите, вы можете «генерировать» альтернативные выражения синтаксического анализатора по требованию, с небольшим количеством волшебства. Я показал, как это сделать в этом ответе:
Но
это удобно использовать вариадики, чтобы избежать промежуточного хранения (обратите внимание на deepcopy_
для защиты от неопределенного поведения):
template<typename ...Expr>
void parse_one_of(Expr& ...expressions)
{
auto parser = boost::fusion::fold(
boost::tie(expressions...),
qi::eps(false),
deepcopy_(arg2 | arg1)
);
Видя, как вам нужно действительно динамическая композиция альтернативного парсера, я не понимаю, как это можно адаптировать к вашим потребностям без взрыва сложности и возможности для тонкой ошибки (поверь мне, я уже пробовал).
Итак, вместо этого я рекомендую попробовать & Истинный подход, который «злоупотребляет» существующим «динамическим» парсером:
qi::symbols
Эта идея безнадежно заимствует у знаменитого «Nabialek Trick». Он использует qi :: символы, и, следовательно, имеет отличные характеристики производительности во время выполнения2.
Без дальнейших церемоний, это пример того, как вы можете использовать его, начиная с вектора строковых литералов:
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, std::string(), Skipper>
{
parser() : parser::base_type(start)
{
static const std::vector<std::string> not_accepted { "}}", "]]" };
using namespace qi;
exclude = exclusions(not_accepted);
start = *(char_ - exclude);
BOOST_SPIRIT_DEBUG_NODE(start);
}
private:
qi::rule<It, std::string(), Skipper> start;
typedef qi::symbols<char, qi::unused_type> Exclude;
Exclude exclude;
template<typename Elements>
Exclude exclusions(Elements const& elements) {
Exclude result;
for(auto& el : elements)
result.add(el);
return result;
}
};
Полный рабочий образец этого здесь: http://coliru.stacked-crooked.com/view?id=ddbb2549674bfed90e3c8df33b048574-7616891f9fd25da6391c2728423de797 и это печатает
parse success
data: 123
trailing unparsed: ']] 4'
Для дальнейшего использования:
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, std::string(), Skipper>
{
parser() : parser::base_type(start)
{
static const std::vector<std::string> not_accepted { "}}", "]]" };
using namespace qi;
exclude = exclusions(not_accepted);
start = *(char_ - exclude);
BOOST_SPIRIT_DEBUG_NODE(start);
}
private:
qi::rule<It, std::string(), Skipper> start;
typedef qi::symbols<char, qi::unused_type> Exclude;
Exclude exclude;
template<typename Elements>
Exclude exclusions(Elements const& elements) {
Exclude result;
for(auto& el : elements)
result.add(el);
return result;
}
};
int main()
{
const std::string input = "1 2 3]] 4";
typedef std::string::const_iterator It;
It f(begin(input)), l(end(input));
parser<It> p;
std::string data;
bool ok = qi::phrase_parse(f,l,p,qi::space,data);
if (ok)
{
std::cout << "parse success\n";
std::cout << "data: " << data << "\n";
}
else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
}
1 Я полагаю, что эта проблема скоро будет устранена в новой версии Spirit (в настоящее время она называется «Spirit X3» для экспериментальной версии).
2 Оно использует Пытается искать спички
Других решений пока нет …