c++ Boost::spirit::qi无法为字符串匹配器编译

7bsow1i6  于 2023-01-10  发布在  其他
关注(0)|答案(1)|浏览(156)

我正在尝试使用boost::spirit::qi编写一个解析器,它将按原样解析一对"之间的所有内容,并允许对"字符进行转义。也就是说,"ab\n\""应该返回ab\n\"。我尝试使用以下代码(godbolt link):

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

namespace qi = boost::spirit::qi;

int main() {
    std::string input{R"("ab\n\"")"};
    std::cout << "[" << input << "]\n";

    std::string output;

    using Skipper = qi::rule<std::string::const_iterator>;
    Skipper skip = qi::space;
    qi::rule<std::string::const_iterator, std::string(), Skipper> qstring;

    qstring %= qi::lit("\"") 
        > ( *( (qi::print - qi::lit('"') - qi::lit("\\")) | (qi::char_("\\") > qi::print) ) )
                                                            //   ^^^^^
        > qi::lit("\"");

    auto success = qi::phrase_parse(input.cbegin(), input.cend(), qstring, skip, output);

     if (!success) {
        std::cout << "Failed to parse";
        return 1;
    }
    
    std::cout << "output = [" << output << "]\n";

    return 0;
}

由于一些模板错误,这无法编译,

/opt/compiler-explorer/libs/boost_1_81_0/boost/spirit/home/support/container.hpp:130:12: error: 'char' is not a class, struct, or union type
  130 |     struct container_value
      |            ^~~~~~~~~~~~~~~
.....
/opt/compiler-explorer/libs/boost_1_81_0/boost/spirit/home/qi/detail/pass_container.hpp:320:66: error: no type named 'type' in 'struct boost::spirit::traits::container_value<char, void>'
  320 |             typedef typename traits::container_value<Attr>::type value_type;

如果我将带下划线的qi::char_("\\")改为qi::lit("\\"),我可以让代码编译,但这不会为\创建一个匹配的属性。我还发现,如果我创建一个新规则,只包含Kleene星星,我可以让代码编译,但有没有办法在单个表达式中使用正确的类型?

qi::rule<std::string::const_iterator, std::string(), Skipper> qstring;
qi::rule<std::string::const_iterator, std::string(), Skipper> qstringbody;

qstringbody %= ( *( (qi::print - qi::lit('"') - qi::lit("\\")) | (qi::char_("\\") > qi::print) ) );
qstring %= qi::lit("\"") 
    > qstringbody
    > qi::lit("\"");
but5z9lq

but5z9lq1#

qi::char_("")与qi::lit("")匹配,但这不会为其匹配的创建属性
这就是你所需要的。解析应该把输入的表示(语法)转换成你有意义的表示(语义)。当然,有一个反映转义的AST是***可能的,但是那样你就不会解析成字符串,而是类似于

struct char_or_escape {
      enum { hex_escape, octal_escape, C_style_char_esc, unicode_codepoint_escape, named_unicode_escape } type;
      std::variant<uint32_t, std::string> value;
};
using StringAST = std::vector<char_or_escape>;

假设您不想保留原始输入(否则,qi::raw[]就是您的朋友)。

应用它

这是我的简化

qi::rule<It, std::string(), Skipper> qstring //
    = '"' > *(qi::print - '"' - "\\" | "\\" > qi::print) > '"';

旁注:它似乎只需要可打印的。我将在下面删除这个假设。当然,您可以根据需要重新引入字符子集。

qstring = '"' > *(~qi::char_("\"\\") | '\\' > qi::char_) > '"';

对分支重新排序后,就不再需要排除'\\',同时还能更好地表达意图:

qstring = '"' > *('\\' > qi::char_ | ~qi::char_('"')) > '"';

现在,根据示例输入,我推测您可能需要一个C-style treatment of escapes

qi::symbols<char, char> c_esc;
c_esc.add("\\\\", '\\')                                                            //
    ("\\a", '\a')("\\b", '\b')("\\n", '\n')("\\f", '\f')("\\t", '\t')("\\r", '\r') //
    ("\\v", '\v')("\\0", '\0')("\\e", 0x1b)("\\'", '\'')("\\\"", '"')("\\?", 0x3f);

qstring = '"' > *(c_esc | '\\' >> qi::char_ | ~qi::char_('"')) > '"';

(Note这些字符中的一些是冗余的,因为它们已经被编码为辅助输入字符)。

演示

    • 第一个e第一个f第一个x
#include <boost/spirit/include/qi.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;

int main() {
    using It = std::string::const_iterator;

    using Skipper = qi::space_type;
    qi::rule<It, std::string(), Skipper> qstring;

    qi::symbols<char, char> c_esc;
    c_esc.add("\\\\", '\\')                                                            //
        ("\\a", '\a')("\\b", '\b')("\\n", '\n')("\\f", '\f')("\\t", '\t')("\\r", '\r') //
        ("\\v", '\v')("\\0", '\0')("\\e", 0x1b)("\\'", '\'')("\\\"", '"')("\\?", 0x3f);

    qstring = '"' > *(c_esc | '\\' >> qi::char_ | ~qi::char_('"')) > '"';

    for (std::string input :
         {
             R"("")",
             R"("ab\n\"")",
             R"("ab\r\n\'")",
         }) //
    {
        std::string output;
        bool success = phrase_parse(input.cbegin(), input.cend(), qstring, qi::space, output);

        if (!success)
            std::cout << quoted(input) << " -> FAILED\n";
        else
            std::cout << quoted(input) << " -> " << quoted(output) << "\n";
    }
}

印刷

"\"\"" -> ""
"\"ab\\n\\\"\"" -> "ab
\""
"\"ab\\r\\n\\'\"" -> "ab
'"

进一步阅读

有关更完整的转义处理,请参见此处:创建一个boost::spirit::x3解析器,用于带转义序列处理的带引号字符串(也是替代符号的方法)。
它包含了一系列更详细的示例(JSON风格的unicode转义等)。

相关问题