【问题标题】:Simple parser and generator简单的解析器和生成器
【发布时间】:2012-07-10 19:37:23
【问题描述】:

我需要从 c++ 对象解析和生成一些文本。

语法是:

command #param #param #param

有一组命令,其中一些没有参数等。 参数主要是数字。

问题是:我应该使用 Boost Spirit 来完成这项任务吗?或者只是简单地将每一行评估函数标记为从字符串比较与命令调用,读取其他参数并从中创建 cpp 对象?

如果您建议使用 Spirit 或任何其他解决方案,如果您能提供一些与我的问题类似的示例,那就太好了。我已经阅读并尝试了 Boost Spirit 文档中的所有示例。

【问题讨论】:

标签: c++ parsing tokenize boost-spirit


【解决方案1】:

我对问题“Using boost::bind with boost::function: retrieve binded variable type”或多或少精确地实现了这个in a previous answer

使用 Boost Spirit 的完整工作示例程序(需要一个非常相似的语法)在这里:https://gist.github.com/1314900。您只想删除语法的 /execute 文字,因此请编辑 Line 41 from

    if (!phrase_parse(f,l, "/execute" > (

    if (!phrase_parse(f,l, (

示例脚本

WriteLine "bogus"
Write     "here comes the answer: "
Write     42
Write     31415e-4
Write     "that is the inverse of" 24 "and answers nothing"
Shutdown  "Bye" 9
Shutdown  "Test default value for retval"

现在执行后会产生以下输出:

WriteLine('bogus');
Write(string: 'here comes the answer: ');
Write(double: 42);
Write(double: 3.1415);
Write(string: 'that is the inverse of');
Write(double: 24);
Write(string: 'and answers nothing');
Shutdown(reason: 'Bye', retval: 9)
Shutdown(reason: 'Test default value for retval', retval: 0)

完整代码

出于存档目的:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <fstream>

namespace qi  = boost::spirit::qi;
namespace phx = boost::phoenix;

///////////////////////////////////
// 'domain classes' (scriptables)
struct Echo
{
    void WriteLine(const std::string& s) { std::cout << "WriteLine('"     << s << "');" << std::endl; }
    void WriteStr (const std::string& s) { std::cout << "Write(string: '" << s << "');" << std::endl; }
    void WriteInt (int i)                { std::cout << "Write(int: "     << i <<  ");" << std::endl; }
    void WriteDbl (double d)             { std::cout << "Write(double: "  << d <<  ");" << std::endl; }
    void NewLine  ()                     { std::cout << "NewLine();"                    << std::endl; }
} echoService;

struct Admin
{
    void Shutdown(const std::string& reason, int retval) 
    { 
        std::cout << "Shutdown(reason: '" << reason << "', retval: " << retval << ")" << std::endl;
        // exit(retval);
    }
} adminService;

void execute(const std::string& command)
{
    typedef std::string::const_iterator It;
    It f(command.begin()), l(command.end());

    using namespace qi;
    using phx::bind;
    using phx::ref;

    rule<It, std::string(), space_type> stringlit = lexeme[ '"' >> *~char_('"') >> '"' ];

    try
    {
        if (!phrase_parse(f,l, /*"/execute" >*/ (
                (lit("WriteLine")  
                    > stringlit  [ bind(&Echo::WriteLine, ref(echoService), _1) ])
              | (lit("Write") >> +(
                      double_    [ bind(&Echo::WriteDbl,  ref(echoService), _1) ] // the order matters
                    | int_       [ bind(&Echo::WriteInt,  ref(echoService), _1) ]
                    | stringlit  [ bind(&Echo::WriteStr,  ref(echoService), _1) ]
                ))
              | (lit("NewLine")  [ bind(&Echo::NewLine,   ref(echoService)) ])
              | (lit("Shutdown")  > (stringlit > (int_ | attr(0))) 
                                 [ bind(&Admin::Shutdown, ref(adminService), _1, _2) ])
            ), space))
        {
            if (f!=l) // allow whitespace only lines
                std::cerr << "** (error interpreting command: " << command << ")" << std::endl;
        }
    }
    catch (const expectation_failure<It>& e)
    {
        std::cerr << "** (unexpected input '" << std::string(e.first, std::min(e.first+10, e.last)) << "') " << std::endl;
    }

    if (f!=l)
        std::cerr << "** (warning: skipping unhandled input '" << std::string(f,l) << "')" << std::endl;
}

int main()
{
    std::ifstream ifs("input.txt");

    std::string command;
    while (std::getline(ifs/*std::cin*/, command))
        execute(command);
}

【讨论】:

    【解决方案2】:

    对于格式简单、易于测试的输入,标记化就足够了。
    标记化时,您可以从输入中读取一行并将其放入字符串流(iss)中。从 iss 中,您读取第一个单词并将其传递给命令工厂,该命令工厂为您创建正确的命令。然后可以将iss传递给新命令的readInParameters函数,这样每个命令就可以解析自己的参数,检查所有参数是否有效。

    未测试的代码示例:

    std::string line;
    std::getline(inputStream, line);
    std::istringstream iss(line);
    std::string strCmd;
    iss >> strCmd;
    try
    {
      std::unique_ptr<Cmd> newCmd = myCmdFactory(strCmd);
      newCmd->readParameters(iss);
      newCmd->execute();
      //...
    }
    catch (std::exception& e) 
    { 
      std::cout << "Issue with received command: " << e.what() << "\n"; 
    }
    

    【讨论】:

      猜你喜欢
      • 2012-06-28
      • 2012-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-25
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多