【问题标题】:Simple expression parser example using Boost::Spirit?使用 Boost::Spirit 的简单表达式解析器示例?
【发布时间】:2011-01-21 21:43:15
【问题描述】:

是否有人知道在线资源,我可以在其中找到如何使用 Boost::Spirit 编写简单的表达式解析器?

我不一定需要评估表达式,但我需要解析它并能够返回一个布尔值来指示表达式是否可解析(例如括号不匹配等)。

我需要解析器能够识别函数名称(例如 foo 和 foobar),所以这也是一个有用的示例,可以帮助我学习编写 BNF 表示法。

表达式将是正规算术方程,即由以下符号组成:

  1. 开/关括号
  2. 算术运算符
  3. 识别函数名称,并检查其所需参数

【问题讨论】:

  • 您查看过 Spirits 文档和示例吗?
  • Spirit 的文档并不像我希望的那么简单。我设法通过它来学习,但更好的教程肯定会让学习变得更容易。
  • 感谢 Tronic。当我仔细阅读 Spirit 主页上的文档时,这就是我的观点。
  • 您最近是否浏览过 Spirit 的网站 (boost-spirit.com)?有很多可用的材料,与您的表达式解析器问题并不真正相关,但作为主要文档的附录非常有用。
  • 我和你的情况一样。这个问题有更新吗?

标签: c++ expression boost-spirit


【解决方案1】:

这是我放置的一些旧 Spirit 原型代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <exception>
#include <iterator>
#include <sstream>
#include <list>

#include <boost/spirit.hpp>
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace boost::spirit;
using namespace boost;

void g(unsigned int i)
{   
    cout << "row: " << i << endl;
}

struct u
{
    u(const char* c): s(c) {}
    void operator()(const char* first, const char* last) const
    {
        cout << s << ": " << string(first, last) << endl;   
    }
private:
    string s;
};


struct Exp
{
};

struct Range: public Exp
{
};

struct Index: public Exp
{
};

struct String: public Exp
{
};

struct Op
{
    virtual ~Op() = 0;
    virtual string name() = 0;
};

Op::~Op() {}

struct CountIf: public Op
{
    string name() { return "CountIf"; }
};

struct Sum: public Op
{
    string name() { return "Sum"; }
};

struct Statement
{
    virtual ~Statement() = 0;
    virtual void print() = 0;
};

Statement::~Statement() {}

struct Formula: public Statement
{
    Formula(const char* first, const char* last): s(first, last), op(new CountIf)
    {
        typedef rule<phrase_scanner_t> r_t;

        r_t r_index     = (+alpha_p)[u("col")] >> uint_p[&g];
        r_t r_range     = r_index >> ':' >> r_index;
        r_t r_string    = ch_p('\"') >> *alnum_p >> '\"';
        r_t r_exp       = r_range | r_index | r_string; // will invoke actions for index twice due to range
        r_t r_list      = !(r_exp[u("arg")] % ',');
        r_t r_op        = as_lower_d["countif"] | as_lower_d["sum"];
        r_t r_formula   = r_op >> '(' >> r_list >> ')';

        cout << s << ": matched: " << boolalpha << parse(s.c_str(), r_formula, space_p).full << endl; 
    }
    void print() { cout << "Formula: " << s << " / " << op->name() << endl; }
private:
    string s;
    shared_ptr<Op> op;
    list<shared_ptr<Exp> > exp_list;
};

struct Comment: public Statement
{
    Comment(const char* first, const char* last): comment(first, last) {}
    void print() {cout << "Comment: " << comment << endl; }
private:
    string comment;
};


struct MakeFormula
{
    MakeFormula(list<shared_ptr<Statement> >& list_): list(list_) {}
    void operator()(const char* first, const char* last) const
    {
        cout << "MakeFormula: " << string(first, last) << endl;
        list.push_back(shared_ptr<Statement>(new Formula(first, last)));
    }
private:
    list<shared_ptr<Statement> >& list;
};

struct MakeComment
{
    MakeComment(list<shared_ptr<Statement> >& list_): list(list_) {}
    void operator()(const char* first, const char* last) const
    {
        cout << "MakeComment: " << string(first, last) << endl;
        list.push_back(shared_ptr<Statement>(new Comment(first, last)));
    }
private:
    list<shared_ptr<Statement> >& list;
};


int main(int argc, char* argv[])
try
{
    //typedef vector<string> v_t;
    //v_t v(argv + 1, argv + argc);
    // copy(v.begin(), v.end(), ostream_iterator<v_t::value_type>(cout, "\n"));

    string s;
    getline(cin, s);

    //        =COUNTIF(J2:J36, "Abc")

    typedef list<shared_ptr<Statement> > list_t;
    list_t list;

    typedef rule<phrase_scanner_t> r_t;

    r_t r_index     = (+alpha_p)[u("col")] >> uint_p[&g];
    r_t r_range     = r_index >> ':' >> r_index;
    r_t r_string    = ch_p('\"') >> *alnum_p >> '\"';
    r_t r_exp       = r_range | r_index | r_string; // will invoke actions for index twice due to range
    r_t r_list      = !(r_exp[u("arg")] % ',');
    r_t r_op        = as_lower_d["countif"] | as_lower_d["sum"];
    r_t r_formula   = r_op >> '(' >> r_list >> ')';
    r_t r_statement = (ch_p('=')  >> r_formula   [MakeFormula(list)])
                    | (ch_p('\'') >> (*anychar_p)[MakeComment(list)])
                    ;

    cout << s << ": matched: " << boolalpha << parse(s.c_str(), r_statement, space_p).full << endl; 

    for (list_t::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        (*it)->print();
    }
}
catch(const exception& ex)
{
    cerr << "Error: " << ex.what() << endl;
}

尝试运行它并输入如下行:

=COUNTIF(J2:J36, "Abc")

【讨论】:

  • 很好的例子!好吧,但这真的简单吗?
  • 嗯,这不是完全微不足道的,但它仍然只是我在国内航班上编写的一页(可能是双面!)打印代码。 :)
【解决方案2】:

当前版本的 Spirit (V2.x) 包含一系列计算器示例,从非常简单到成熟的 mini-c 解释器。您应该看看那里,因为它们是编写您自己的表达式解析器的完美起点。

【讨论】:

    【解决方案3】:

    我也不确定这是否也算简单,但我使用了http://code.google.com/p/uri-grammar/source/browse/trunk/src/uri/grammar.hpp 提供的这个 uri-grammar。它可能不是微不足道的,但至少它解析了你可能已经理解的东西(URI)。阅读这些语法时,最好从下往上阅读,因为这是定义最通用标记的地方。

    【讨论】:

      猜你喜欢
      • 2012-01-17
      • 1970-01-01
      • 2014-09-13
      • 2012-02-28
      • 2011-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多