微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Boost Spirit x3 解析器的属性类型为 std::vector<boost::variant<ch​​ar, char>> 而不是 std::string

如何解决Boost Spirit x3 解析器的属性类型为 std::vector<boost::variant<ch​​ar, char>> 而不是 std::string

背景

我正在使用 Boost Spirit X3 从 Markdown 文件生成 HTML,如下所示:

// simplified code structure
auto str = x3::lexeme[+(x3::char_ - x3::eol)];

auto h1_action = [&](auto& ctx) { /* generate output */ };
auto h1 = ("# " > str)[h1_action];

auto p_action = [&](auto& ctx) { /* generate output */ };
auto p = (+(str > x3::eol))[p_action];

auto markdown = (h1 | p) % *x3::eol;

x3::phrase_parse(begin,end,markdown,x3::blank);

问题 1:符号表

我使用符号表来识别转义字符:

x3::symbols<char> esc;
esc.add
    ("\\(",'(')
    ("\\)",')')
    /* ... */;

auto dummy_action = [&](auto& ctx) {
    auto value = x3::attr(ctx);
};

auto esc_str = (x3::lexeme[+(esc | (x3::char_ - x3::eol))])[dummy_action];
上面的 lambda auto value 中的

dummy_actionstd::vector<boost::variant<char,char>> 类型,当根据 Compound Attribute Rules 它应该是 std::vector<char> 并且因此仅仅是 { {1}}。

string

这些规则规定,如果替代解析器的两个参数(在上面的解析器 a: A,b: B --> (a | b): variant<A,B> a: A,b: A --> (a | b): A 中,将是 esc_stresc)是相同的类型((x3::char_ - x3::eol)在这种情况下),结果是不是变体。但确实如此,我想了解原因。

问题 2:字符解析器

我想解析括号中的字符串,后跟 char,其中字符串本身也可以包含任意括号,例如x3::eol

(path/to/img(2).png)\n

同样的问题:我希望 auto paran = x3::char_(')') >> !x3::eol; auto ch = x3::char_ - (x3::eol | ')'); auto src = (x3::lexeme[x3::lit('(') >> +(paran | ch) >> ')'])[dummy_action]; 属性src 类型,因为替代解析器 string 的两个参数 paranch具有相同的属性,但(paran | ch)属性src

结论

显然,我可以简单地将结果转换为字符串,但是有没有人有其他方法可以解析上述示例并直接接收字符串作为结果或解释为什么结果是 {{1 }}。 (为什么你想要一个包含不止一次完全相同类型的变体?)

在此先感谢您的帮助!

编辑

Markdown 参考:我使用的是 this markdown reference,它没有指定底层语法,所以另外,我使用 Visual Studio Code 的内置 Markdown 预览功能来分析边缘情况。我知道我帖子中的所有解析器都不正确或至少不完整(例如,无法识别空标题)。

语义操作:我知道分离关注点是更好的方法,即首先生成 AST/DOM,然后从中生成输出。我只是无法这样做,因为我只想解析非常有限的降价子集,所以我选择了语义操作方法

解决方法

我同意属性综合可以......令人惊讶。

根本原因似乎是坚持对原始解析器表达式使用语义操作。通常的方法是这样的

WHERE userid = ' . Array['userid'] . '

然后,如果必须的话,将 SA 附加到更高级别规则中的 auto str = x3::rule<struct str_,std::string>{"str"} = x3::lexeme[+(esc | (x3::char_ - x3::eol))]; (您基本上拥有,在 str 中)。

第二个问题

似乎和第一个差不多。

同样的问题:我希望 src 的属性是字符串类型,因为替代解析器的两个参数 paran 和 ch (paran | ch)

如果你希望 src 的属性是一个特定的类型,你可能应该这样声明:

p_action

观察

  1. 我质疑语法是否正确。特别是,您当前指定如何将括号嵌入到超链接源规范中的方式与我所知道的降价引擎不匹配

    包括StackOverflow's,as you can see。最后一个括号不需要在 EOL 处。

    如果您有要实施的特定 Markdown 规范的参考,我很乐意评估更多。

  2. 此外,我不相信在语义操作中完成所有繁重工作的方法(参见 Boost Spirit: "Semantic actions are evil"?)是一种有用的方法。

    总的来说,我建议将解析和输出生成的关注点分开。这将使它更容易

    • 实现/测试正确性
    • 维护解析器
    • 改变从解析文档生成输出的方式

完整演示

这是一个将事物联系在一起的演示,同时仍然保持您当前的方法,强调语义操作。

希望所显示的改进和想法有所帮助。特别是在您学习或维护语法时,有条件地启用规则调试可能是一个很大的生产力加速器。

Live On Compiler Explorer

auto eol = x3::eol | x3::eoi;
auto paran
    //= x3::rule<struct last_paren_,std::string> {"paran"} // aids in debug output
    = char_(')') >> !eol;
auto src
    = x3::rule<struct src_,std::string>{"src"}
    = lexeme['(' >> *(~char_("\r\n \t\b\f)") | paran) >> ')'];

打印调试输出:

#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <fstream>
#include <iostream>
#include <boost/core/demangle.hpp>

namespace x3 = boost::spirit::x3;
namespace Parser {
    using x3::char_;
    using x3::lit;
    using x3::lexeme;
    // simplified code structure
#if 0
    auto str = lexeme[+(char_ - x3::eol)];
#else
    auto esc = [] {
        x3::symbols<char> esc;
        esc.add
            ("\\(",'(')("\\)",')')
            ("\\[",'[')("\\]",']')
        /* ... */;
        return esc;
    }();

    auto eol = x3::eol | x3::eoi;
    auto paran
        //= x3::rule<struct last_paren_,std::string> {"paran"} // aids in debug output
        = char_(')') >> !eol;
    auto src
        = x3::rule<struct src_,std::string>{"src"}
        = lexeme['(' >> *(~char_("\r\n \t\b\f)") | paran) >> ')'];

    auto hyperlink 
        = x3::rule<struct hyperlink_,std::string>{"hyperlink"}
        = '[' >> *(esc | ~char_("\r\n]")) >> ']' >> src;

    auto str
        = x3::rule<struct str_,std::string>{"str"}
        = lexeme[
            +( esc
            | &lit('[') >> hyperlink  // the &lit supresses verbose debug
            | (char_ - x3::eol)
            )];
#endif

    auto h1_action = [](auto &) { /* generate output */ };
    auto h1
        = x3::rule<struct h1_,std::string> {"h1"}
        = ("# " > str)[h1_action]
        ;

    auto p_action = [](auto &) { /* generate output */ };
    auto p
        = x3::rule<struct p_,std::string> {"p"}
        = (+(str > eol))[p_action];

    auto content
        = x3::rule<struct lines_,std::string> {"content"}
        = (h1 | p) % +x3::eol;

    auto markdown = x3::skip(x3::blank)[*x3::eol >> content];
} // namespace Parser

int main() {
#if 0
    std::ifstream ifs("input.txt");
    std::string const s(std::istreambuf_iterator<char>(ifs),{});
#else
    std::string const s = R"(
# Frist

This [introduction](https://en.wikipedia.org/wiki/Wikipedia:Introduction_(historical))
serves no purpose. Other than to show some [[hyper\]links](path/to/img(2).png)
)";
#endif

    parse(begin(s),end(s),Parser::markdown);
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。