Spirit X3, как не выполнить синтаксический анализ при вводе не-ascii?

Таким образом, цель состоит в том, чтобы не допускать символов от 80h до FFh во входной строке. У меня сложилось впечатление, что

using ascii::char_;

позаботится об этом. Но, как вы можете видеть в примере кода, он с радостью напечатает Parsing Successed.

В следующем сообщении списка рассылки Spirit Джоэл предложил разрешить синтаксический анализ этих символов, отличных от ascii, не выполняться. Но я не уверен, продолжал ли он это делать. [Spirit-general] кодировка ascii утверждает неверный ввод.. .

Вот мой пример кода:

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

namespace client::parser
{
    namespace x3 = boost::spirit::x3;
    namespace ascii = boost::spirit::x3::ascii;

    using ascii::char_;
    using ascii::space;
    using x3::lexeme;
    using x3::skip;

    const auto quoted_string = lexeme[char_('"') >> *(char_ - '"') >> char_('"')];
    const auto entry_point = skip(space) [ quoted_string ];
}

int main()
{
    for(std::string const input : { "\"naughty \x80" "bla bla bla\"" }) {
        std::string output;
        if (parse(input.begin(), input.end(), client::parser::entry_point, output)) {
            std::cout << "Parsing succeeded\n";
            std::cout << "input:  " << input << "\n";
            std::cout << "output: " << output << "\n";
        } else {
            std::cout << "Parsing failed\n";
        }
    }
}

Как я могу изменить пример, чтобы Spirit терпел неудачу при этом неверном вводе?

Кроме того, но очень связанный, я хотел бы знать, как мне использовать анализатор символов, который определяет кодировку char_set. Вы знаете char_(charset) из Документы X3: ветка разработки синтаксических анализаторов символов.

Документации не хватает настолько сильно, чтобы описать базовый функционал. Почему люди высшего уровня не могут заставить авторов библиотек приходить с документацией хотя бы на уровне cppreference.com?


person Zeyneb    schedule 06.07.2019    source источник
comment
Это поможет вам понять, как char_(charset) вариант работает.   -  person llonesmiz    schedule 07.07.2019


Ответы (2)


Ничего плохого в документах здесь нет. Это просто баг библиотеки.

Где код для any_char говорит:

template <typename Char, typename Context>
bool test(Char ch_, Context const&) const
{
    return ((sizeof(Char) <= sizeof(char_type)) || encoding::ischar(ch_));
}

Это должно было сказать

template <typename Char, typename Context>
bool test(Char ch_, Context const&) const
{
    return ((sizeof(Char) <= sizeof(char_type)) && encoding::ischar(ch_));
}

Это заставляет вашу программу вести себя так, как ожидается и требуется. Это поведение также соответствует поведению Ци:

Жить на Coliru

#include <boost/spirit/include/qi.hpp>

int main() {
    namespace qi = boost::spirit::qi;

    char const* input = "\x80";
    assert(!qi::parse(input, input+1, qi::ascii::char_));
}

Сообщил об ошибке здесь: https://github.com/boostorg/spirit/issues/520

person sehe    schedule 07.07.2019

Вы можете добиться этого, используя парсер print:

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

namespace client::parser
{
    namespace x3 = boost::spirit::x3;
    namespace ascii = boost::spirit::x3::ascii;

    using ascii::char_;
    using ascii::print;
    using ascii::space;
    using x3::lexeme;
    using x3::skip;

    const auto quoted_string = lexeme[char_('"') >> *(print - '"') >> char_('"')];
    const auto entry_point = skip(space) [ quoted_string ];
}

int main()
{
    for(std::string const input : { "\"naughty \x80\"", "\"bla bla bla\"" }) {
        std::string output;
        std::cout << "input:  " << input << "\n";
        if (parse(input.begin(), input.end(), client::parser::entry_point, output)) {
            std::cout << "output: " << output << "\n";
            std::cout << "Parsing succeeded\n";
        } else {
            std::cout << "Parsing failed\n";
        }
    }
}

Выход:

input:  "naughty �"
Parsing failed
input:  "bla bla bla"
output: "bla bla bla"
Parsing succeeded

https://wandbox.org/permlink/HSoB8uqMC3WME5yI


Удивительно, но почему-то проверка на char_ делается только тогда, когда sizeof(iterator char type) > sizeof(char):

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <string>
#include <boost/core/demangle.hpp>
#include <typeinfo>

namespace x3 = boost::spirit::x3;

template <typename Char>
void test(Char const* str)
{
    std::basic_string<Char> s = str;
    std::cout << boost::core::demangle(typeid(Char).name()) << ":\t";
    Char c;
    auto it = s.begin();
    if (x3::parse(it, s.end(), x3::ascii::char_, c) && it == s.end())
        std::cout << "OK: " << int(c) << "\n";
    else
        std::cout << "Failed\n";
}

int main()
{
    test("\x80");
    test(L"\x80");
    test(u8"\x80");
    test(u"\x80");
    test(U"\x80");
}

Выход:

char:   OK: -128
wchar_t:    Failed
char8_t:    OK: 128
char16_t:   Failed
char32_t:   Failed

https://wandbox.org/permlink/j9PQeRVnGZQeELFA

person Nikita Kniazev    schedule 07.07.2019
comment
Это ошибка. Я подал его. Использование print на самом деле ничего не решает. Тестовый код должен читать как минимум (print | space) - '"', чтобы соответствовать варианту использования. (Nitpick, вы изменили тестовый пример, разделив их. Сначала я думал так же, но единственная причина, по которой строковый литерал был разделен, заключалась в том, что "\x80b анализируется как недопустимый escape-символ в C++....) - person sehe; 07.07.2019
comment
Спасибо обоим! Строка ввода была задумана таким образом, потому что я последовал совету отсюда: stackoverflow.com/a/45612966/10803387 - person Zeyneb; 07.07.2019