Boost spirit x3, вставляя пустые строки во время прямого анализа - PullRequest
0 голосов
/ 22 января 2019

Я пытаюсь проанализировать некоторые текстовые файлы с помощью анализатора Boost X3, и я обнаружил некоторые различия при использовании лямбда-функции или при использовании правил "прямого" синтаксического анализа.

Следующий пример кода:

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

namespace x3 = boost::spirit::x3;

namespace parsers
{
    namespace lambda
    {
        using namespace boost::spirit::x3;

        std::vector<std::string> files;        
        auto f = [&](const auto& ctx) { files.push_back(_attr(ctx)); };
        auto const line = "[FILE_TO_BE_SEARCHED]" >> eol
                       >> eol
                       >> (*~char_("\r\n"))[f];

        auto const ignore = *~char_("\r\n");

        auto const start = skip(blank)[(line | ignore) % eol];
    }

    namespace direct
    {
        using namespace boost::spirit::x3;

        auto const line = "[FILE_TO_BE_SEARCHED]" >> eol
                       >> eol
                       >> *~char_("\r\n");

        auto const ignore = omit[*~char_("\r\n")];

        auto const start = skip(blank)[(line | ignore) % eol];
    }
}

std::string file(
"   -- HEADER\n\
    -- Version: 0.1.0\n\
    -- Author: J. A.\n\
    -- Copyright:\n\
\n\
    -----------------\n\
    -- Comments\n\
    -----------------\n\
\n\
    [FILE_TO_BE_SEARCHED]\n\
\n\
        File1.txt\n\
\n\
    [FILE_TO_BE_SEARCHED]\n\
\n\
        File2.txt\n\
\n\
    [FILE_TO_BE_SEARCHED]\n\
\n\
        File3.txt\n\
\n\
    -- Comments...\n\
\n\
    [END]\n\
\n\
    -- MD5: 0x1AF3\n"
);

const std::vector<std::string> parse_lambda()
{
    x3::parse(file.begin(), file.end(), parsers::lambda::start);

    return std::move(parsers::lambda::files);
}

const std::vector<std::string> parse_direct()
{
   std::vector<std::string> files;
   x3::parse(file.begin(), file.end(), parsers::direct::start, files);

   return std::move(files);
}

void print(const std::vector<std::string> files)
{
    int i = 0;
    std::cout << "Files found: " << files.size() << '\n';
    for (const auto& file : files)
       std::cout << ++i << " - '" << file << "'\n";       
}

int main()
{
    std::cout << "Lambda parser:" << '\n';
    const auto files_lambda = parse_lambda();
    print(files_lambda);

    std::cout << "\nDirect parser:" << '\n';
    const auto files_direct = parse_direct();
    print(files_direct);

    return 0;
}

Он выводит следующие строки:

Lambda parser:
Files found: 3
1 - 'File1.txt'
2 - 'File2.txt'
3 - 'File3.txt'

Direct parser:
Files found: 21
1 - ''
2 - ''
3 - ''
4 - ''
5 - ''
6 - ''
7 - ''
8 - ''
9 - ''
10 - 'File1.txt'
11 - ''
12 - 'File2.txt'
13 - ''
14 - 'File3.txt'
15 - ''
16 - ''
17 - ''
18 - ''
19 - ''
20 - ''
21 - ''

Вот ссылка Coliru на этот же исходный код: https://coliru.stacked -crooked.com / a / bad4dd5002eb3fec или используя следующую командную строку Coliru: cat /Archive2/ba/d4dd5002eb3fec/main.cpp

Я хотел бы задать следующие вопросы:

1) Как выполнить прямой анализ (нелямбда-функция) для получения того же вывода, что и лямбда-парсер?

2) Почему директива omit дает сбой и выдает пустые строки при доступе к правилу игнорирования?

3) Последнее, но не менееважно, является ли X3 значительным улучшением по сравнению с Qi (я имею в виду, что скорость компиляции и вероятный синтаксис c ++ 14)?Это стабильно и доказательство производства кода?

Большое спасибо, Пабло.

1 Ответ

0 голосов
/ 22 января 2019

Я бы просто использовал seek[], чтобы очень просто объяснить распространение:

namespace direct {
    using namespace boost::spirit::x3;

    auto const line  = "[FILE_TO_BE_SEARCHED]" >> eol >> eol >> +~char_("\r\n") >> eol;
    auto const start = *skip(blank) [ seek[line] ];
}

Это работает без лишних слов.

Обратите внимание на различные модернизации / упрощения здесь: Live On Coliru

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

namespace x3 = boost::spirit::x3;

namespace parsers {
    namespace lambda {
        using namespace boost::spirit::x3;

        std::vector<std::string> files;
        auto f = [&](const auto &ctx) { files.push_back(_attr(ctx)); };

        auto const line   = "[FILE_TO_BE_SEARCHED]" >> eol >> eol >> (*~char_("\r\n"))[f];
        auto const ignore = *~char_("\r\n");
        auto const start  = skip(blank)[(line | ignore) % eol];
    }

    namespace direct {
        using namespace boost::spirit::x3;

        auto const line   = "[FILE_TO_BE_SEARCHED]" >> eol >> eol >> +~char_("\r\n") >> eol;
        auto const start  = *skip(blank) [ seek[line] ];
    }
}

std::string const file = R"( -- HEADER
    -- Version: 0.1.0
    -- Author: J. A.
    -- Copyright:

    -----------------
    -- Comments
    -----------------

    [FILE_TO_BE_SEARCHED]

        File1.txt

    [FILE_TO_BE_SEARCHED]

        File2.txt

    [FILE_TO_BE_SEARCHED]

        File3.txt

    -- Comments...

    [END]

    -- MD5: 0x1AF3
)";

std::vector<std::string> parse_lambda() { // const is a pessimization here
    x3::parse(file.begin(), file.end(), parsers::lambda::start);
    return std::move(parsers::lambda::files);
}

std::vector<std::string> parse_direct() { // const is a pessimization here
    std::vector<std::string> files;
    x3::parse(file.begin(), file.end(), parsers::direct::start, files);

    return files; // std::move is a pessimization here
}

void print(std::vector<std::string> const& files) {
    int i = 0;
    std::cout << "Files found: " << files.size() << "\n";
    for (auto&& file : files)
        std::cout << ++i << " - " << std::quoted(file) << "\n";
}

int main() {
    std::cout << "Lambda parser:\n";
    print(parse_lambda());

    std::cout << "\nDirect parser:\n";
    print(parse_direct());
}

печать

Lambda parser:
Files found: 3
1 - "File1.txt"
2 - "File2.txt"
3 - "File3.txt"

Direct parser:
Files found: 3
1 - "File1.txt"
2 - "File2.txt"
3 - "File3.txt"

На остальные вопросы:

...