Да, x3 значительно упрощает составление правил.
В основном потому, что выражения синтаксического анализатора не имеют тенденции сохранять ссылки на временные объекты при назначении переменных, как это было в эпоху Qi¹.
Ограничение: объявление парсеров с внешней связью намного сложнее ... в X3, требуя танца с макросами, которые вы показываете BOOST_SPIRIT_{DECLARE,DEFINE}
.
Ваш образец
Это не сработает, потому что макрос предназначен для использования в области пространства имен. Хорошая новость в том, что вам это может не понадобиться, потому что нет необходимости объявлять правило отдельно от определения, если вы не имеете дело с рекурсивно требуемыми правилами.
В стороне x3::rule<char>
, скорее всего, ошибка. char
- это тип тега в этом объявлении, и это не лучший тип тега. Если вместо этого вам нужен тип атрибута, это должен быть второй аргумент шаблона.
auto SomeFunction(std::string &str)
{
return x3::rule<struct _tag, std::string> {"dynamic"}
= '[' >> x3::lit(str) >> ']';
}
На самом деле, я очень часто делаю небольшие фабрики на своем объекте объявлений:
template <typename Attr>
auto compose = [](auto p1, auto p2) {
return rule<struct _, Attr> {"compose"}
= nocase [
lexeme [ "property:" << as_parser(p1) ]
>> '='
lexeme [ "value:" << as_parser(p2) ]
];
};
Это немного надумано, но должно дать вам идеи. Используйте это как compose<int>("number", x3::int_)
или compose<std::string>("name", +x3::graph)
Некоторые вдохновляющие примеры
Описание оператора списка (%) в Boost.Spirit с изображением специального as<>[]
объекта:
namespace {
template <typename T>
struct as_type {
template <typename Expr>
auto operator[](Expr&& expr) const {
return x3::rule<struct _, T>{"as"} = x3::as_parser(std::forward<Expr>(expr));
}
};
template <typename T> static const as_type<T> as = {};
}
Избегайте выдачи expectation_failure при сбое парсера ожидания, что динамически составляет поиск символов:
x3::symbols<char> const keyword = []{
x3::symbols<char> kw;
kw += "for","begin","end","function","while","break","switch";
return kw;
}();
Динамическое переключение таблиц символов в x3, которое является очень полный пример со многими фабриками парсеров:
// (case insensitive) keyword handling
static auto kw = [](auto p) { return x3::lexeme[p >> !(x3::graph - x3::char_("/=,()"))]; };
static auto ikw = [](auto p) { return x3::no_case [kw(p)]; };
static auto qualifier = [](auto p) { return x3::lexeme['/' >> ikw(p)]; };
И даже показывает, как переопределить as_spirit_parser
для ваших собственных типов:
// Options and CiOptions
namespace util {
template <typename Tag>
auto as_spirit_parser(Options<Tag> const& o, bool to_lower = false) {
x3::symbols<typename Options<Tag>::type> p;
int n = 0;
for (std::string el : o._options) {
if (to_lower) boost::to_lower(el);
p.add(el, n++);
}
return kw(p);
}
template <typename Tag>
auto as_spirit_parser(IcOptions<Tag> const& o) {
return x3::no_case [ as_spirit_parser(o, true) ];
}
}
И довольно элегантный способ написать помощников по членному распространению с автоматически сгенерированными семантическими действиями:
auto set = [](auto member, auto p) {
auto propagate = [member](auto& ctx) {
traits::move_to(_attr(ctx), _val(ctx).*(member));
};
return as_parser(p)[propagate];
};
using T = ast::ShowSymbolsCommand;;
return qualifier("all") >> set(&T::all, attr(true))
| qualifier("full") >> set(&T::full, attr(true))
| qualifier("out") >> set(&T::out, '=' >> Filespec)
| qualifier("type") >> set(&T::types, '=' >> SymbolTypes)
| set(&T::wildcard, Wildcard);
Я настоятельно рекомендую вам использовать эти примеры по отдельности, чтобы понять, насколько мощна компоновка X3. Я бы подумал о воссоздании чего-то вроде qi::lazy
в X3 только тогда, когда вы / действительно / потребуете этого.
¹ или вообще что-нибудь на основе Proto, например, Phoenix
person
sehe
schedule
08.04.2018