Неверный вывод std :: cout при использовании комбинированного правила немедленного = строка | float | int с использованием qi :: double_ an qi :: uint_ - PullRequest
1 голос
/ 24 марта 2020

я пытаюсь получить непосредственное правило для строк, int и float, чтобы я мог проанализировать следующие тесты

 //strings
 "\"hello\"",
 "   \"  hello \"  ",
 "  \"  hello \"\"stranger\"\" \"  ",
 //ints
 "1",
 "23",
 "456",
 //floats
 "3.3",
 "34.35"

попробовать онлайн: http://coliru.stacked-crooked.com/a/26fbd691876d9a8f

использование

qi::rule<std::string::const_iterator, std::string()> 
  double_quoted_string = '"' >> *("\"\"" >> qi::attr('"') | ~qi::char_('"')) >> '"';

qi::rule<std::string::const_iterator, std::string()> 
  number = (+qi::ascii::digit >> *(qi::char_('.') >> +qi::ascii::digit));

qi::rule<std::string::const_iterator, std::string()>
  immediate = double_quoted_string | number;

дает мне правильный результат - но мне нужно использовать двойной синтаксический анализ, потому что я хочу поддерживать эпоненциальную нотацию, NaN et c.

, но используя

qi::rule<std::string::const_iterator, std::string()>
  immediate = double_quoted_string | qi::uint_ | qi::double_;

печатает для целочисленных значений

"1" OK: ''
----
"23" OK: ''
----
"456" OK: '�'

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

протестировано в Coliru, последняя версия Win7x64 VS2017, LLVM clang-cl

иногда Colliru дает слишком много предупреждений, и компиляция останавливается

есть идеи, что здесь происходит?

часто означают предупреждения в духе - остановитесь здесь, что-то серьезно сломанное?

ОБНОВЛЕНИЕ : это также происходит, если я использую только double_, перед тем как я проверил его, и поведение изменилось с / без uint_ попытка парсера: https://wandbox.org/permlink/UqgItWkfC2I8tkNF

Ответы [ 2 ]

1 голос
/ 24 марта 2020

Используйте qi::raw в синтаксических анализаторах целых и двойных чисел с плавающей запятой, чтобы числа были преобразованы в лексическое значение: qi::raw[qi::uint_] и qi::raw[qi::double_].

Но важен также порядок разбора. Если uint_ синтаксический анализатор раньше double_, как здесь:

immediate = double_quoted_string | qi::raw[qi::uint_] | qi::raw[qi::double_];
BOOST_SPIRIT_DEBUG_NODES((immediate)); // for debug output

, тогда синтаксический анализатор uint_ будет частично использовать двойное число с плавающей запятой, а затем весь синтаксический анализ завершится неудачей:

<immediate>
  <try>34.35</try>
  <success>.35</success> //<----- this is what is left after uint_ parsed
  <attributes>[[3, 4]]</attributes> // <---- what uint_ parser successfully parsed
</immediate>
"34.35" Failed
Remaining unparsed: "34.35"

После смены порядка uint_ на double_:

immediate = double_quoted_string | qi::raw[qi::double_] | qi::raw[qi::uint_];

Результат:

"\"hello\"" OK: 'hello'
----
"   \"  hello \"  " OK: '  hello '
----
"  \"  hello \"\"stranger\"\" \"  " OK: '  hello "stranger" '
----
"1" OK: '1'
----
"64" OK: '64'
----
"456" OK: '456'
----
"3.3" OK: '3.3'
----
"34.35" OK: '34.35'
----
0 голосов
/ 25 марта 2020

Свободное определение «разбора» будет означать преобразование текстового представления в «другое» (часто более native ) представление.

В действительности не имеет смысла «разбирать» "число в std :: string. То, что вы видите, это автоматическое c распространение атрибутов, которое очень старается понять его (вставляя проанализированный номер в строку как символ).

Это не то, что вы хотели. Вместо этого вы хотите проанализировать целочисленное значение или двойное значение. Для этого Вы можете просто объявить тип атрибута варианта:

using V = boost::variant<std::string, double, unsigned int>;
qi::rule<std::string::const_iterator, V()>
    immediate = double_quoted_string | qi::double_ | qi::uint_;

Вот и все. Демонстрация в реальном времени, добавление проверки типов по результату:

Live On Coliru

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

namespace qi = boost::spirit::qi;
using namespace std::string_literals;

int main() {
    for (auto&& [str, type] : std::vector {
        std::pair("\"hello\""s,                typeid(std::string).name()),
        {"   \"  hello \"  "s,                 typeid(std::string).name()},
        {"  \"  hello \"\"stranger\"\" \"  "s, typeid(std::string).name()},
        {"1"s,                                 typeid(unsigned int).name()},
        {"23"s,                                typeid(unsigned int).name()},
        {"456"s,                               typeid(unsigned int).name()},
        {"3.3"s,                               typeid(double).name()},
        {"34.35"s,                             typeid(double).name()},
    }) {
        auto iter = str.cbegin(), end = str.cend();

        qi::rule<std::string::const_iterator, std::string()> double_quoted_string
            = '"' >> *("\"\"" >> qi::attr('"') | ~qi::char_('"')) >> '"';

        using V = boost::variant<std::string, double, unsigned int>;
        qi::rule<std::string::const_iterator, V()> immediate
            = double_quoted_string | qi::double_ | qi::uint_;

        std::cout << std::quoted(str) << " ";

        V res;
        bool r = qi::phrase_parse(iter, end, immediate, qi::blank, res);
        bool typecheck = (type == res.type().name());

        if (r) {
            std::cout << "OK: " << res << " typecheck " << (typecheck?"MATCH":"MISMATCH") << "\n";
        } else {
            std::cout << "Failed\n";
        }
        if (iter != end) {
            std::cout << "Remaining unparsed: " << std::quoted(std::string(iter, end)) << "\n";
        }
        std::cout << "----\n";
    }
}

Печать

"\"hello\"" OK: hello typecheck MATCH
----
"   \"  hello \"  " OK:   hello  typecheck MATCH
----
"  \"  hello \"\"stranger\"\" \"  " OK:   hello "stranger"  typecheck MATCH
----
"1" OK: 1 typecheck MISMATCH
----
"23" OK: 23 typecheck MISMATCH
----
"456" OK: 456 typecheck MISMATCH
----
"3.3" OK: 3.3 typecheck MATCH
----
"34.35" OK: 34.35 typecheck MATCH
----

Обратите внимание на изменение порядка uint_ после double_. Если вы сначала анализируете целые числа, он будет анализировать целую часть двойного числа до десятичного разделителя, а затем не сможет проанализировать остальные. Чтобы быть более точным, вы можете использовать строгий реальный синтаксический анализатор, так что только число, которое имеет дробь, будет проанализировано как двойные числа. Это действительно ограничивает диапазон для целых чисел, потому что unsigned int имеет гораздо меньший диапазон, чем double.

См. Parse int или double с использованием Boost Spirit (longest_d)

Live On Coliru

    qi::rule<std::string::const_iterator, V()> immediate
        = double_quoted_string
        | qi::real_parser<double, qi::strict_real_policies<double> >{}
        | qi::uint_;

Печать

"\"hello\"" OK: hello typecheck MATCH
----
"   \"  hello \"  " OK:   hello  typecheck MATCH
----
"  \"  hello \"\"stranger\"\" \"  " OK:   hello "stranger"  typecheck MATCH
----
"1" OK: 1 typecheck MATCH
----
"23" OK: 23 typecheck MATCH
----
"456" OK: 456 typecheck MATCH
----
"3.3" OK: 3.3 typecheck MATCH
----
"34.35" OK: 34.35 typecheck MATCH
----
...