【问题标题】:Rolling back changes in alternative parsers in qi spirit在 qi 精神中回滚替代解析器的变化
【发布时间】:2016-02-02 14:52:35
【问题描述】:

我在使用备用解析器时遇到了一些问题,如果第一个选项失败,我需要解析器回滚更改。我尝试使用 hold[] 但我收到错误“无法推断模板参数”

我正在尝试解析 AST,并将其存储在:

struct node;

BOOST_FUSION_DEFINE_STRUCT(
    , node,
    (std::string, element)
    (std::string, category)
    (std::vector<node>, children)
    )

'element'是解析的元素,'category'是分类,然后我有一个向量来存放他的孩子

所以,举例来说,我有几个类似的案例:

ABC=
    (qi::char_('a')[at_c<0>(qi::_val) = "a", at_c<1>(qi::_val) = "ABC"]
    >> B[push_back(at_c<2>(qi::_val), qi::_1)]
    >> qi::char_('c'))
    ;

ABD=
    (qi::char_('a')[at_c<0>(qi::_val) = "a", at_c<1>(qi::_val) = "ABD"]
    >> B[push_back(at_c<2>(qi::_val), qi::_1)]
    >> qi::char_('d')
    )
    ;

test = ABC | ABD;

B 是一些返回节点的规则。如果我运行它,我会在第二个选项中重复 B,因为它是在第一个中推入的。正如我所说,我试过了:

test = hold[ABC]| ABD;

但我得到了那个错误。我找到的解决方案是将规则 B 的结果存储在局部变量中,然后仅在规则末尾推送它(意味着规则匹配)。但这会导致非常复杂的语义动作,还有其他选择吗?

【问题讨论】:

标签: c++ parsing visual-c++ boost boost-spirit-qi


【解决方案1】:

我想说这里所有的语义动作都是无用的[1]。

所有这些都可以替换为放置良好的qi::attr,以简单地就地合成所需的属性,您可以再次使用 Spirit 的自动属性传播魔法。

根据我的经验,如果您开始对最简单的事情使用语义操作,那么最好不要使用 Spirit

先简化

所以,这是我对 AST node 类型的看法:

struct node {
    std::string element, category;
    std::vector<node> children;
};

BOOST_FUSION_ADAPT_STRUCT(node, element, category, children)

它在功能上与您的相同,但更像 c++,因此更友好。现在,你没有给出规则声明,所以“想象”它们如下:

qi::rule<It, node(), qi::space_type> ABC, ABD, test;
qi::rule<It, std::vector<node>(), qi::space_type> B;

// also for demo purposes:
B = '{' >> -(test % ',') >> '}';

现在,我对你的规则的建议,简化了:

ABC = qi::string("a") >> qi::attr("ABC") >> B >> 'c';
ABD = qi::string("a") >> qi::attr("ABD") >> B >> 'd';

test = ABC | ABD;

确实,您需要hold,因为/otherwise/ ABC/ABD 都将绑定到相同的内部合成属性node&amp;:

ABC = qi::hold[ qi::string("a") >> qi::attr("ABC") >> B >> 'c' ];
ABD = qi::hold[ qi::string("a") >> qi::attr("ABD") >> B >> 'd' ];

这就是你想要/需要的。

测试/演示程序

我为语法构建了一些测试用例,因为我“逆向设计”它并添加了调试代码:

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>

struct node {
    std::string element, category;
    std::vector<node> children;
};

BOOST_FUSION_ADAPT_STRUCT(node, element, category, children)

static std::ostream& operator<<(std::ostream& os, node const& n) {
    return os << boost::fusion::as_vector(n);
}

static std::ostream& operator<<(std::ostream& os, std::vector<node> const& v) {
    os << "{";
    for (size_t i = 0; v.size()>1 &&  i<v.size()-1; ++i)
        os << v.at(i) << ", ";

    if (!v.empty()) os << v.back();
    return os << "}";
}

namespace qi = boost::spirit::qi;

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

    qi::rule<It, node(), qi::space_type> ABC, ABD, test;
    qi::rule<It, std::vector<node>(), qi::space_type> B;

#if 1
    ABC = qi::hold[ qi::string("a") >> qi::attr("ABC") >> B >> 'c' ];
    ABD = qi::hold[ qi::string("a") >> qi::attr("ABD") >> B >> 'd' ];
#else
    ABC = qi::string("a") >> qi::attr("ABC") >> B >> 'c' ;
    ABD = qi::string("a") >> qi::attr("ABD") >> B >> 'd' ;
#endif

    test = ABC | ABD;
    B    = '{' >> -(test % ',') >> '}';

    for (std::string const s : {
            "a{a{}c}c",
            "a{a{}d}d",
            "a{a{}c}d",
            "a{a{}d,a{}c}c",
        })
    {
        std::cout << "\n-- Parsing '" << s << "'\n";
        It f = s.begin(), l = s.end();

        node parsed;
        bool ok = qi::phrase_parse(f, l, test, qi::space, parsed);

        if (ok)
            std::cout << "Parsed: " << parsed << "\n";
        else
            std::cout << "Parse failed\n";

        if (f!=l)
            std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
    }
}

输出(保留):

-- Parsing 'a{a{}c}c'
Parsed: (a ABC {(a ABC {})})

-- Parsing 'a{a{}d}d'
Parsed: (a ABD {(a ABD {})})

-- Parsing 'a{a{}c}d'
Parsed: (a ABD {(a ABC {})})

-- Parsing 'a{a{}d,a{}c}c'
Parsed: (a ABC {(a ABD {}), (a ABC {})})

[1]。另见Boost Spirit: "Semantic actions are evil"?

【讨论】:

  • 如果你想知道我是怎么做的,这里是录制的实时提要:livecoding.tv/video/showcase-qi-hold-and-semanticactionsareevil (experiment)
  • 但是如果我正在解析的元素的顺序有时与结构的顺序不匹配,我该如何使用自动属性传播。比如我可能有一个表达式:A &gt;&gt; operator &gt;&gt; B,比如“a+b”,所以节点的元素是运算符'+','a','b'是他的孩子。
  • 您可以只使用不同的顺序 ADAPT_STRUCT 或使用 ADAPT_STRUCT_NAMED
  • 我在 coliru 中尝试过,它可以工作,但是当我在 Visual Studio 2013 中使用 boost 版本 1.59 尝试相同的代码时,qi::hold 得到它无法推断模板参数的错误
  • @kaladin: 可能是 Visual Studio 2013 的 bug,你能试试 Visual Studio 2015
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-03
相关资源
最近更新 更多