【问题标题】:Iterative update of abstract syntax tree with boost spirit具有boost精神的抽象语法树的迭代更新
【发布时间】:2015-02-03 21:32:22
【问题描述】:

我有一个可用的 boost Spirit 解析器,并且正在考虑是否可以使用 boost Spirit 对抽象语法树进行迭代更新?

我有一个类似的结构:

  struct ast;

  typedef boost::variant< boost::recursive_wrapper<ast> > node;

  struct ast
  {
    std::vector<int> value;
    std::vector<node> children;
  };

正在通过以下方式解析:

bool r = phrase_parse(begin, end, grammar, space, ast);

能不能用boost精神做抽象语法树的迭代更新?我还没有找到任何关于此的文档,但我在想解析器语义操作是否可以 push_back 对已经存在的 AST。有人试过吗?

这将允许像这样解析:

bool r = phrase_parse(begin, end, grammar, space, ast); //initial parsing

//the second parse will be called at a later state given some event/timer/io/something

bool r = phrase_parse(begin, end, grammar, space, ast); //additional parsing which will update the already existing AST

【问题讨论】:

  • 这可能吗? (是的)。有没有人试过这个? (是的)。我这样做了吗? (是的)。问题是否未明确说明? (是的)!我回答了吗?当然好。希望你喜欢

标签: c++ boost abstract-syntax-tree boost-spirit boost-spirit-qi


【解决方案1】:

您如何知道要合并哪些节点?还是您总是在根级别添加(“嫁接”)?在那种情况下,你为什么不解析另一个并将元素合并到现有的 ast 中?

ast& operator+=(ast&& other) {
    std::move(other.value.begin(),    other.value.end(),    back_inserter(value));
    std::move(other.children.begin(), other.children.end(), back_inserter(children));
    return *this;
}

演示时间

让我们为这个 AST 设计我能想到的最简单的语法:

start = '{' >> -(int_ % ',') >> ';' >> -(start % ',') >> '}';

请注意,我什至没有将 ; 设为可选。那好吧。样品。供读者练习。 ☡你知道的演习

我们实现了一个简单的函数ast parse(It f, It l),然后我们可以简单地合并asts:

int main() {
    ast merged;
    for(std::string const& input : {
                "{1 ,2 ,3 ;{4 ;{9 , 8 ;}},{5 ,6 ;}}",
                "{10,20,30;{40;{90, 80;}},{50,60;}}",
            })
    {
        merged += parse(input.begin(), input.end());
        std::cout << "merged + " << input << " --> " << merged << "\n";
    }
}

Live On Coliru

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

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct ast;

//typedef boost::make_recursive_variant<boost::recursive_wrapper<ast> >::type node;
typedef boost::variant<boost::recursive_wrapper<ast> > node;

struct ast {
    std::vector<int> value;
    std::vector<node> children;

    ast& operator+=(ast&& other) {
        std::move(other.value.begin(),    other.value.end(),    back_inserter(value));
        std::move(other.children.begin(), other.children.end(), back_inserter(children));
        return *this;
    }
};

BOOST_FUSION_ADAPT_STRUCT(ast,
    (std::vector<int>,value)
    (std::vector<node>,children)
)

template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, ast(), Skipper>
{
    grammar() : grammar::base_type(start) {
        using namespace qi;
        start = '{' >> -(int_ % ',') >> ';' >> -(start % ',') >> '}';
        BOOST_SPIRIT_DEBUG_NODES((start));
    }
  private:
    qi::rule<It, ast(), Skipper> start;
};

// for output:
static inline std::ostream& operator<<(std::ostream& os, ast const& v) {
    using namespace karma;
    rule<boost::spirit::ostream_iterator, ast()> r;
    r = '{' << -(int_ % ',') << ';' << -((r|eps) % ',')  << '}';
    return os << format(r, v);
}

template <typename It> ast parse(It f, It l) 
{
    ast parsed;

    static grammar<It> g;
    bool ok = qi::phrase_parse(f,l,g,qi::space,parsed);

    if (!ok || (f!=l)) {
        std::cout << "Parse failure\n";
        std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
        exit(255);
    }

    return parsed;
}

int main() {
    ast merged;
    for(std::string const& input : {
                "{1 ,2 ,3 ;{4 ;{9 , 8 ;}},{5 ,6 ;}}",
                "{10,20,30;{40;{90, 80;}},{50,60;}}",
            })
    {
        merged += parse(input.begin(), input.end());
        std::cout << "merged + " << input << " --> " << merged << "\n";
    }
}

当然,它会打印:

merged + {1 ,2 ,3 ;{4 ;{9 , 8 ;}},{5 ,6 ;}} --> {1,2,3;{4;{9,8;}},{5,6;}}
merged + {10,20,30;{40;{90, 80;}},{50,60;}} --> {1,2,3,10,20,30;{4;{9,8;}},{5,6;},{40;{90,80;}},{50,60;}}

更新

在这个简单的示例中,您可以将集合绑定到解析调用中的属性。如果没有移动元素所需的 operator+= 调用,同样的事情也会发生,因为规则被写入自动追加到绑定的容器属性。

CAVEAT:就地修改目标值的一个明显缺点是如果解析失败会发生什么。在该版本中,merged 值将是“未定义”(已从失败的解析中接收到部分信息)。

因此,如果您想“原子地”解析输入,第一种更明确的方法更适合。

所以下面是一个稍微短一点的写法:

Live On Coliru

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

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct ast;

//typedef boost::make_recursive_variant<boost::recursive_wrapper<ast> >::type node;
typedef boost::variant<boost::recursive_wrapper<ast> > node;

struct ast {
    std::vector<int> value;
    std::vector<node> children;
};

BOOST_FUSION_ADAPT_STRUCT(ast,
    (std::vector<int>,value)
    (std::vector<node>,children)
)

template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, ast(), Skipper>
{
    grammar() : grammar::base_type(start) {
        using namespace qi;
        start = '{' >> -(int_ % ',') >> ';' >> -(start % ',') >> '}';
        BOOST_SPIRIT_DEBUG_NODES((start));
    }
  private:
    qi::rule<It, ast(), Skipper> start;
};

// for output:
static inline std::ostream& operator<<(std::ostream& os, ast const& v) {
    using namespace karma;
    rule<boost::spirit::ostream_iterator, ast()> r;
    r = '{' << -(int_ % ',') << ';' << -((r|eps) % ',')  << '}';
    return os << format(r, v);
}

template <typename It> void parse(It f, It l, ast& into) 
{
    static grammar<It> g;
    bool ok = qi::phrase_parse(f,l,g,qi::space,into);

    if (!ok || (f!=l)) {
        std::cout << "Parse failure\n";
        std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
        exit(255);
    }
}

int main() {
    ast merged;
    for(std::string const& input : {
            "{1 ,2 ,3 ;{4 ;{9 , 8 ;}},{5 ,6 ;}}",
            "{10,20,30;{40;{90, 80;}},{50,60;}}",
            })
    {
        parse(input.begin(), input.end(), merged);
        std::cout << "merged + " << input << " --> " << merged << "\n";
    }
}

仍然打印

【讨论】:

  • 向大家致敬。这是一个非常好的和全面的答案。在接受答案之前我会做一些测试。
  • 如果您想为每个解析添加到 AST 中的根目录,这非常有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-25
  • 1970-01-01
  • 2015-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多