2016-11-03 5 views
3

Я пытался определить синтаксический анализатор, где правила не полностью заранее определены, то есть они содержат переменную часть. Это не было проблемой для Spirit Qi, но я не смог реализовать его из-за статической природы X3. Я пробовал с директивой, что, к сожалению, недокументировано, но пока не повезло. Единственные примеры, которые я нашел до сих пор, находятся внутри выражения лямбда.Правило Parser, зависящее от параметра

Я построил простой пример, чтобы продемонстрировать проблему: синтаксический анализ целых чисел, где разделитель задан как параметр.

#include <boost/spirit/home/x3.hpp> 
#include <iostream> 

namespace x3 = boost::spirit::x3; 

namespace parsing { 
    x3::rule<struct parser> parser {"parser"}; 

    //struct separator {}; 
    char separator(','); 

    //auto parser_def = x3::int_ % x3::lit(x3::get<separator>(/* context */)); // candidate function template not viable: requires single argument 'context' 
    auto parser_def = x3::int_ % x3::lit(separator); 

    BOOST_SPIRIT_DEFINE(parser) 
} 

void parse(const std::string &data, const char separator) { 
    using namespace std; 

    //auto parser = x3::with<parsing::separator>(ref(separator)) [parsing::parser] >> x3::eoi; 
    auto parser = parsing::parser >> x3::eoi; 

    if (x3::parse(data.begin(), data.end(), parser)) 
     cout << "Parse succeeded\n"; 
    else 
     cout << "Parse failed\n"; 
} 

int main() { 
    parse("1 2 3", ' '); 
    parse("1,2,3", ','); 
    parse("1;2;3", ';'); 
} 

Я закомментирована части, где я пытался использовать с директивой.

Возможно ли это с помощью X3? Кто-нибудь делал это раньше?

+0

Просто мысли вслух здесь ... Вы можете создать свой собственный 'функцию parse', которая перенаправляет вызов функции синтаксического анализа, соответствующей' '%. В этом случае вы, вероятно, можете получить сепаратор из 'context' (который является аргументом, который должен быть передан функции разбора) с помощью' x3 :: get'? – Arunmu

ответ

1

Осмотрев еще несколько постов X3, я был просвещен этим ответом sehe: https://stackoverflow.com/a/38303379/7110782

Зная о х3 :: _ проход привел меня к этому решению:

#include <boost/spirit/home/x3.hpp> 
#include <iostream> 

namespace x3 = boost::spirit::x3; 

namespace parsing { 
    x3::rule<struct parser> parser {"parser"}; 

    struct separator {}; 

    // only the separator which is currently in the context is allowed (passes) 
    auto isSeparator = [](auto& ctx){ x3::_pass(ctx) = x3::_attr(ctx) == x3::get<separator>(ctx); }; 

    // at first match any char and then check whether it is the separator 
    auto parser_def = x3::int_ % x3::char_[isSeparator]; 

    BOOST_SPIRIT_DEFINE(parser) 
} 

void parse(const std::string &data, const char separator) { 
    using namespace std; 

    auto parser = x3::with<parsing::separator>(ref(separator)) [parsing::parser] >> x3::eoi; 

    if (x3::parse(data.begin(), data.end(), parser)) 
     cout << "Parse succeeded\n"; 
    else 
     cout << "Parse failed\n"; 
} 

int main() { 
    // succeed 
    parse("1 2 3", ' '); 
    parse("1,2,3", ','); 
    parse("1;2;3", ';'); 

    // fail 
    parse("1,2,3", ' '); 
    parse("1;2;3", ','); 
} 

Остается проверить в дальнейшим шагом является возможность установки нескольких параметров (возможно, через каскадный x3 :: с <>).

Edit:

Да, установка нескольких параметров через каскадный x3 :: с <> похоже на работу. Например:

auto parser = x3::with<parsing::separator>(ref(separator))[x3::with<parsing::separator2>(ref(separator2))[parsing::parser]] >> x3::eoi;