尝试解析引用的字符串文字时,Boost :: Spirit无法编译

时间:2014-12-24 16:03:42

标签: c++ parsing boost boost-spirit

我尝试使用Boost :: Spirit解析带有转义序列的带引号的字符串。不幸的是,似乎在语法定义中包含引号会导致大量(非常无用的)编译时错误(正如人们对Boost所期望的那样)。省略引号可以让程序编译,但显然它不会像它应该的那样表现出来。这是代码(实际上是更大图片的一部分,但它证明了这个问题):

#include "boost/spirit/include/qi.hpp"
#include "boost/proto/deep_copy.hpp"
#include "boost/optional.hpp"

#include <string>

using boost::spirit::qi::char_;
using boost::spirit::qi::lexeme;
using boost::proto::deep_copy;


auto string_literal = deep_copy(
    lexeme[
            // char_('"')
            /* >> */ *((char_ - '"' - '\\') | (char_('\\') >> char_))
            // >> char_('"')
          ]);


template <class Iterator, class Grammar>
boost::optional<std::string> parse_string(Iterator first, Iterator last, Grammar&& gr)
{
    using boost::spirit::qi::space;
    using boost::spirit::qi::phrase_parse;

    std::string temp;
    bool success = phrase_parse(
        first,
        last,
        gr,
        space,
        temp
    );

    if (first == last && success)
        return temp;
    else return boost::none;
}


int main()
{
    std::string str;
    std::cout << "string_literal: ";

    getline(std::cin, str);

    auto presult = parse_string(str.begin(), str.end(), string_literal);
    if (presult) {
        std::cout << "parsed: " << *presult;
    } else
        std::cout << "failure\n";

    return 0;
}

取消注释string_literal定义的注释部分会导致错误。在其当前状态(带注释)中,代码编译。我已经尝试了一些事情,例如将引号移动到parse_string,以及使用不太具体的定义(上面的那个是我能提出的最不具体的,仍然有用,正确的语法是在OCaml language manual,但我想我可以单独验证转义序列),但没有任何效果。

My Boost版本是1.56.0,我的编译器是MinGW-w64 g ++ 4.9.1。任何帮助都是最受欢迎的。

1 个答案:

答案 0 :(得分:1)

我花了一点时间才看到它。

问题是 - 最终 - 事实是 [1]

(qi::char_('\\') >> qi::char_) | (qi::char_ - '"')

合成

boost::variant<
    boost::fusion::vector2<char, char>,
    char>

而不是,正如您可能期望charstd::vector<char>。 Spirit的属性兼容性规则接近 magic ,它们让你逃脱它(这非常漂亮),但它也隐藏了你的意识中的问题。

当你进一步复杂化规则时,只会抱怨它。

现在我可以看到两条可能的路径:要么返回 de-escaped 字符串值(不带引号),要将其更改为: [2] < /强>

    qi::lexeme [
            '"' >>
                *(('\\' >> qi::char_) | (qi::char_ - '"'))
            >> '"'
        ]

您想要使用引号捕获原始字符串,而您根本不关心公开的属性:

    qi::raw [
            '"' >>
                *(('\\' >> qi::char_) | (qi::char_ - '"'))
            >> '"'
        ]

后者使用从源 - 迭代器对(qi::raw[])到std::string的隐式属性转换(绑定属性)。

查看完整的内容:

<强> Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/proto/deep_copy.hpp>
#include <boost/optional.hpp>

#include <string>

namespace qi = boost::spirit::qi;

namespace {

    auto string_literal = boost::proto::deep_copy(
#if 1
        qi::lexeme [
                '"' >>
                    *(('\\' >> qi::char_) | (qi::char_ - '"'))
                >> '"'
            ]
#else
        qi::raw [
                '"' >>
                    *(('\\' >> qi::char_) | (qi::char_ - '"'))
                >> '"'
            ]
#endif
        );

}

template <class Iterator, class Grammar>
boost::optional<std::string> parse_string(Iterator first, Iterator last, Grammar&& gr)
{
    std::string temp;

    bool success = qi::phrase_parse(
        first,
        last,
        std::forward<Grammar>(gr),
        qi::space,
        temp
    );

    if (success && first == last)
        return temp;
    else return boost::none;
}


int main()
{
    std::string str;
    std::cout << "string_literal: ";

    getline(std::cin, str);

    auto presult = parse_string(str.begin(), str.end(), string_literal);
    if (presult) {
        std::cout << "parsed: '" << *presult << "'\n";
    } else
        std::cout << "failure\n";

    return 0;
}

通过重新排序分支稍微简化

[1]

[2] (请注意'\\'等同于qi::lit('\\')表达式模板操作数的隐式转换)