Boost::解析字符串时的精神斗争

2023-12-05

我正在尝试用 Boost::Spirit 解析字符串,但我无法让它工作。从今天起我就没有使用 Boost::Spirit 的经验了。

该字符串由用“;”分隔的命令组成。命令是

“INC 一些整数”

“BOMB 第一个整数 第二个整数”

“MOVE 第一个整数 第二个整数 第三个整数”

“MSG 一些字符串”

"WAIT"

我设法做到了这一点:

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

using namespace boost::spirit;

int main() {    
    std::string testInput = "MOVE 1 2 43;BOMB 0 3;INC 6;MOVE 2 3 99;MOVE 1 2 6";

    typedef std::string::iterator iter;
    using boost::phoenix::ref;

    iter start = testInput.begin();

    std::vector<int> IncCommands;
    std::vector<std::pair<int, int>> BombCommands;
    std::vector<std::tuple<int, int, int>> MoveCommands;

    qi::rule<iter, std::vector<int>(), ascii::space_type> nextIncrease = ("INC " >> qi::int_);
    //qi::rule<iter, std::vector<std::pair<int, int>>(), ascii::space_type> nextBomb = ("BOMB " >> qi::int_ >> qi::int_);
    //qi::rule<iter, std::vector<int>(), ascii::space_type> nextMove = ("MOVE " >> qi::int_ >> qi::int_ >> qi::int_);

    //qi::rule<iter, std::string, ascii::space_type> nextAction = (nextMove | nextBomb | nextIncrease) % ';';

    bool match = qi::phrase_parse(
        start,
        testInput.end(),
        nextIncrease,
        ascii::space,
        IncCommands
    );

    return 0;
}

我现在遇到的问题:

  1. 我不知道如何提取超过 1 个整数

  2. 我不知道如何将所有内容合并到正确的语法中,以便将所有内容解析为多个向量。

  3. 我还没考虑过味精和等待。


我建议一如既往地从所需的 AST 开始。

Spirit 与静态多态性配合得很好,所以我会使用一个变体来表示命令:

namespace AST {
    namespace Cmd {
        struct Move { int x,y,z;  };
        struct Bomb { int x,y;    };
        struct Inc  { int amount; };
        struct Msg  { std::string text; };
        struct Wait {};
    }

    using Command = boost::variant<Cmd::Move, Cmd::Bomb, Cmd::Inc, Cmd::Msg, Cmd::Wait>;
    using Commands = std::vector<Command>;
}

现在,编写最直接的语法来匹配它:

template <typename It>
struct ScriptGrammar : qi::grammar<It, AST::Commands()>
{
    ScriptGrammar() : ScriptGrammar::base_type(start) {
        using namespace qi;
        start   = skip(space) [ script ];
        script  = command % ";";
        command = move|bomb|inc|msg|wait;

        move = "MOVE" >> int_ >> int_ >> int_;
        bomb = "BOMB" >> int_ >> int_;
        inc  = "INC"  >> int_;
        msg  = "MSG"  >> text;
        wait = "WAIT" >> qi::attr(AST::Cmd::Wait{});

        text  = +~char_(";");
        BOOST_SPIRIT_DEBUG_NODES((start)(script)(command)(move)(bomb)(inc)(msg)(wait)(text))
    }
  private:
    using Skipper = qi::space_type;
    qi::rule<It, AST::Commands(), Skipper>  script;
    qi::rule<It, AST::Command(), Skipper>   command;
    qi::rule<It, AST::Cmd::Move(), Skipper> move;
    qi::rule<It, AST::Cmd::Bomb(), Skipper> bomb;
    qi::rule<It, AST::Cmd::Inc(), Skipper>  inc;
    qi::rule<It, AST::Cmd::Msg(), Skipper>  msg;
    qi::rule<It, AST::Cmd::Wait(), Skipper> wait;
    // lexeme
    qi::rule<It, AST::Commands()>  start;
    qi::rule<It, std::string()>  text;
};

添加一些调试胶水(Fusion 适配和输出流),我们就有了一个工作示例:

Live On Coliru

#define BOOST_SPIRIT_DEBUG
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix/phoenix.hpp>

namespace AST {
    namespace Cmd {
        struct Move { int x,y,z;  };
        struct Bomb { int x,y;    };
        struct Inc  { int amount; };
        struct Msg  { std::string text; };
        struct Wait {};
    }

    using Command = boost::variant<Cmd::Move, Cmd::Bomb, Cmd::Inc, Cmd::Msg, Cmd::Wait>;
    using Commands = std::vector<Command>;
}

BOOST_FUSION_ADAPT_STRUCT(AST::Cmd::Move, x,y,z)
BOOST_FUSION_ADAPT_STRUCT(AST::Cmd::Bomb, x,y)
BOOST_FUSION_ADAPT_STRUCT(AST::Cmd::Inc, amount)
BOOST_FUSION_ADAPT_STRUCT(AST::Cmd::Msg, text)
BOOST_FUSION_ADAPT_STRUCT(AST::Cmd::Wait)

namespace AST { namespace Cmd { // For demo/debug
    std::ostream& operator<<(std::ostream& os, Move const& cmd) { return os << "MOVE " << boost::fusion::as_vector(cmd); } 
    std::ostream& operator<<(std::ostream& os, Bomb const& cmd) { return os << "BOMB " << boost::fusion::as_vector(cmd); } 
    std::ostream& operator<<(std::ostream& os, Inc const& cmd)  { return os << "INC " << boost::fusion::as_vector(cmd);  } 
    std::ostream& operator<<(std::ostream& os, Msg const& cmd)  { return os << "MSG " << boost::fusion::as_vector(cmd);  } 
    std::ostream& operator<<(std::ostream& os, Wait const& cmd) { return os << "WAIT " << boost::fusion::as_vector(cmd); } 
} }

namespace qi = boost::spirit::qi;

template <typename It>
struct ScriptGrammar : qi::grammar<It, AST::Commands()>
{
    ScriptGrammar() : ScriptGrammar::base_type(start) {
        using namespace qi;
        start   = skip(space) [ script ];
        script  = command % ";";
        command = move|bomb|inc|msg|wait;

        move = "MOVE" >> int_ >> int_ >> int_;
        bomb = "BOMB" >> int_ >> int_;
        inc  = "INC"  >> int_;
        msg  = "MSG"  >> text;
        wait = "WAIT" >> qi::attr(AST::Cmd::Wait{});

        text  = +~char_(";");
        BOOST_SPIRIT_DEBUG_NODES((start)(script)(command)(move)(bomb)(inc)(msg)(wait)(text))
    }
  private:
    using Skipper = qi::space_type;
    qi::rule<It, AST::Commands(), Skipper>  script;
    qi::rule<It, AST::Command(), Skipper>   command;
    qi::rule<It, AST::Cmd::Move(), Skipper> move;
    qi::rule<It, AST::Cmd::Bomb(), Skipper> bomb;
    qi::rule<It, AST::Cmd::Inc(), Skipper>  inc;
    qi::rule<It, AST::Cmd::Msg(), Skipper>  msg;
    qi::rule<It, AST::Cmd::Wait(), Skipper> wait;
    // lexeme
    qi::rule<It, AST::Commands()>  start;
    qi::rule<It, std::string()>  text;
};

int main() {    
    std::string const testInput = "MOVE 1 2 43;BOMB 0 3;INC 6;MOVE 2 3 99;MSG MOVE ZIG;WAIT;MSG FOR GREAT JUSTICE!;MOVE 1 2 6";

    typedef std::string::const_iterator iter;

    iter start = testInput.begin(), end = testInput.end();

    AST::Commands script;

    bool match = qi::parse(start, testInput.end(), ScriptGrammar<iter>(), script);

    if (match) {
        std::cout << "Parsed " << script.size() << " commands\n";
        std::copy(script.begin(), script.end(), std::ostream_iterator<AST::Command>(std::cout, ";"));
    } else {
        std::cout << "Parse failed\n";
    }

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

哪个打印:

Parsed 8 commands
MOVE (1 2 43);BOMB (0 3);INC (6);MOVE (2 3 99);MSG (MOVE ZIG);WAIT ();MSG (FOR GREAT JUSTICE!);MOVE (1 2 6);

以及可选的 BOOST_SPIRIT_DEBUG 输出:

<start>
  <try>MOVE 1 2 43;BOMB 0 3</try>
  <script>
    <try>MOVE 1 2 43;BOMB 0 3</try>
    <command>
      <try>MOVE 1 2 43;BOMB 0 3</try>
      <move>
        <try>MOVE 1 2 43;BOMB 0 3</try>
        <success>;BOMB 0 3;INC 6;MOVE</success>
        <attributes>[[1, 2, 43]]</attributes>
      </move>
      <success>;BOMB 0 3;INC 6;MOVE</success>
      <attributes>[[1, 2, 43]]</attributes>
    </command>
    <command>
      <try>BOMB 0 3;INC 6;MOVE </try>
      <move>
        <try>BOMB 0 3;INC 6;MOVE </try>
        <fail/>
      </move>
      <bomb>
        <try>BOMB 0 3;INC 6;MOVE </try>
        <success>;INC 6;MOVE 2 3 99;M</success>
        <attributes>[[0, 3]]</attributes>
      </bomb>
      <success>;INC 6;MOVE 2 3 99;M</success>
      <attributes>[[0, 3]]</attributes>
    </command>
    <command>
      <try>INC 6;MOVE 2 3 99;MS</try>
      <move>
        <try>INC 6;MOVE 2 3 99;MS</try>
        <fail/>
      </move>
      <bomb>
        <try>INC 6;MOVE 2 3 99;MS</try>
        <fail/>
      </bomb>
      <inc>
        <try>INC 6;MOVE 2 3 99;MS</try>
        <success>;MOVE 2 3 99;MSG MOV</success>
        <attributes>[[6]]</attributes>
      </inc>
      <success>;MOVE 2 3 99;MSG MOV</success>
      <attributes>[[6]]</attributes>
    </command>
    <command>
      <try>MOVE 2 3 99;MSG MOVE</try>
      <move>
        <try>MOVE 2 3 99;MSG MOVE</try>
        <success>;MSG MOVE ZIG;WAIT;M</success>
        <attributes>[[2, 3, 99]]</attributes>
      </move>
      <success>;MSG MOVE ZIG;WAIT;M</success>
      <attributes>[[2, 3, 99]]</attributes>
    </command>
    <command>
      <try>MSG MOVE ZIG;WAIT;MS</try>
      <move>
        <try>MSG MOVE ZIG;WAIT;MS</try>
        <fail/>
      </move>
      <bomb>
        <try>MSG MOVE ZIG;WAIT;MS</try>
        <fail/>
      </bomb>
      <inc>
        <try>MSG MOVE ZIG;WAIT;MS</try>
        <fail/>
      </inc>
      <msg>
        <try>MSG MOVE ZIG;WAIT;MS</try>
        <text>
          <try>MOVE ZIG;WAIT;MSG FO</try>
          <success>;WAIT;MSG FOR GREAT </success>
          <attributes>[[M, O, V, E,  , Z, I, G]]</attributes>
        </text>
        <success>;WAIT;MSG FOR GREAT </success>
        <attributes>[[[M, O, V, E,  , Z, I, G]]]</attributes>
      </msg>
      <success>;WAIT;MSG FOR GREAT </success>
      <attributes>[[[M, O, V, E,  , Z, I, G]]]</attributes>
    </command>
    <command>
      <try>WAIT;MSG FOR GREAT J</try>
      <move>
        <try>WAIT;MSG FOR GREAT J</try>
        <fail/>
      </move>
      <bomb>
        <try>WAIT;MSG FOR GREAT J</try>
        <fail/>
      </bomb>
      <inc>
        <try>WAIT;MSG FOR GREAT J</try>
        <fail/>
      </inc>
      <msg>
        <try>WAIT;MSG FOR GREAT J</try>
        <fail/>
      </msg>
      <wait>
        <try>WAIT;MSG FOR GREAT J</try>
        <success>;MSG FOR GREAT JUSTI</success>
        <attributes>[[]]</attributes>
      </wait>
      <success>;MSG FOR GREAT JUSTI</success>
      <attributes>[[]]</attributes>
    </command>
    <command>
      <try>MSG FOR GREAT JUSTIC</try>
      <move>
        <try>MSG FOR GREAT JUSTIC</try>
        <fail/>
      </move>
      <bomb>
        <try>MSG FOR GREAT JUSTIC</try>
        <fail/>
      </bomb>
      <inc>
        <try>MSG FOR GREAT JUSTIC</try>
        <fail/>
      </inc>
      <msg>
        <try>MSG FOR GREAT JUSTIC</try>
        <text>
          <try>FOR GREAT JUSTICE!;M</try>
          <success>;MOVE 1 2 6</success>
          <attributes>[[F, O, R,  , G, R, E, A, T,  , J, U, S, T, I, C, E, !]]</attributes>
        </text>
        <success>;MOVE 1 2 6</success>
        <attributes>[[[F, O, R,  , G, R, E, A, T,  , J, U, S, T, I, C, E, !]]]</attributes>
      </msg>
      <success>;MOVE 1 2 6</success>
      <attributes>[[[F, O, R,  , G, R, E, A, T,  , J, U, S, T, I, C, E, !]]]</attributes>
    </command>
    <command>
      <try>MOVE 1 2 6</try>
      <move>
        <try>MOVE 1 2 6</try>
        <success></success>
        <attributes>[[1, 2, 6]]</attributes>
      </move>
      <success></success>
      <attributes>[[1, 2, 6]]</attributes>
    </command>
    <success></success>
    <attributes>[[[1, 2, 43], [0, 3], [6], [2, 3, 99], [[M, O, V, E,  , Z, I, G]], [], [[F, O, R,  , G, R, E, A, T,  , J, U, S, T, I, C, E, !]], [1, 2, 6]]]</attributes>
  </script>
  <success></success>
  <attributes>[[[1, 2, 43], [0, 3], [6], [2, 3, 99], [[M, O, V, E,  , Z, I, G]], [], [[F, O, R,  , G, R, E, A, T,  , J, U, S, T, I, C, E, !]], [1, 2, 6]]]</attributes>
</start>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Boost::解析字符串时的精神斗争 的相关文章

随机推荐

  • AES 解密时,最终块未正确填充

    首先 我会告诉大家我的主要目标是什么 我将在客户端使用 AES 加密一些内容 然后使用 RSA 公钥加密重要的 AES 规范 并将 AES 加密数据和 RSA 加密 AES 规范发送到服务器 因此 在服务器上 我将使用 RSA 私钥解密 A
  • MySQL 用户定义的纬度经度语法函数

    我创建了一个 MySQL 函数来确定一组纬度和经度坐标是否在另一组纬度和经度坐标的特定范围内 但是 该函数给了我一个语法错误 因此我无法测试它是否正常工作 任何有助于找出导致错误的原因的帮助将不胜感激 该函数及其描述如下 它的工作原理是将起
  • RxAndroidBle 多种特性通知和读/写

    我在设置多个特征的通知时遇到问题 我已经查看了文档 许多示例仅涵盖了非常精细的情况 我的用例如下 1 扫描设备 2 用户选择要连接的设备 连接持续到应用程序关闭 3 订阅多种特性的通知 4 一次读 写单个特征 在某些情况下一次读 写多个特征
  • 我无法让程序正确读取输入文件中的值(二维数组)

    我的程序错误地读取了该值 当我尝试从此文件中获取值时 存在同样的问题 14 14 8 0 4 16 2 0 1 7 7 4 7 0 23 0 44 290 350 16 16 10 0 5 17 2 2 1 8 8 5 4 0 27 0 5
  • 解析 Amazon MWS Scratchpad 响应

    我正在尝试解析来自亚马逊的 xml 文件 但发现困难 我正在使用 simplexml load string xml simplexml load string 这里是我的 xml 但当我这样做时 echo xml gt GetMatchi
  • 将 CALayer 子层压平为一层

    在我的应用程序中 我有一个根层 以及许多作为 rootLayer 的子层的图像 我想将 rootLayer 的所有子层展平为一个没有任何子层的层 图像 我认为我应该通过在核心图形上下文中绘制所有子图层来做到这一点 但我不知道该怎么做 我希望
  • Linux 如何通过错误号获取错误描述

    在 Linux 中 特别是我有 Ubuntu 14 如果某个程序因错误而终止 我可以通过以下方式获取数字错误代码 多变的 failing app echo 然而数字本身并不能告诉我太多信息 如何获取错误名称和描述 有一个错误列表 man e
  • ”' aria-label='使用 Pandas Dataframe 的半正弦距离计算器“无法将系列转换为 ”'> 使用 Pandas Dataframe 的半正弦距离计算器“无法将系列转换为

    我尝试在 Panda Dataframe 上使用半正矢计算 from math import radians cos sin asin sqrt def haversine lon1 lat1 lon2 lat2 convert decim
  • 如何制作 jQuery 倒计时

    我想要一个 jQuery 倒计时 页面下载完成后开始计数 数到 0 后重定向到一个 url 我怎样才能做到这一点 我想我应该稍微分解一下并提供一些既可以倒计时又可以重定向的东西 毕竟 明天您可能想要倒计时并操作 DOM 因此 我提出了以下
  • BottomNavigationView 与导航图再次加载片段

    我在用BottomNavigationView with nav graph下面是我的代码 InMainActivity class navController Navigation findNavController this R id
  • 当 'type = "norm" 时如何计算 ggplot stat_ellipse() 的面积?

    类似这个问题 当 type norm 时 有什么方法可以计算这个椭圆的面积吗 默认为type t type norm 显示不同的椭圆 因为它假设多元正态分布而不是多元 t 分布 这是代码和情节 使用与其他帖子类似的代码 library gg
  • 页面加载后jquery加载div

    实际上我正在寻找的加载是首先加载页面 然后加载包含大量数据的 div 所以 我想首先加载主页 然后使用 jQuery 函数加载正文 div 内容 但有一些延迟 实现这个的简单方法是什么 div div navigation div div
  • 如何使用 Lombok 访问 getter 和 setter 方法? [复制]

    这个问题在这里已经有答案了 我已经在我的项目中添加了 Lombok maven 存储库 并成功使用了 Lombok 注释 我尝试从另一个类访问 setter 和 getter 方法 但无法访问这些方法 我正在使用 spring Boot 版
  • SQL Server - 有没有办法批量解决排序规则冲突

    我们遇到的情况是 开发和生产 SQL Server 之间的数据库 表和某些列的排序规则不同 这对开发造成了严重破坏 事情会在开发上工作 然后由于升级时的排序规则冲突而中断 数据和结构将从产品复制到开发 这又会因为冲突等而破坏开发上的查询 我
  • 执行Python程序

    我已经在网上搜索答案很长一段时间了 但这让我非常头疼 我使用的是 Ubuntu 12 04 我想从终端执行 Python 脚本而不使用完整路径 因此 我通过将以下内容添加到 bashrc 中 将 home kyril python scri
  • 嵌入式 Jetty 找不到带注释的 Servlet

    短的 我有一个提供 war 工件的项目 其中包含带有注释的 servlet 但没有 web xml 如果我尝试在 jetty 中使用 war 我总是只能得到 war 内容的目录列表 而不是 servlet 执行 任何想法 很长的故事 我的
  • SQL 按最近日期选择具有两个唯一列的行

    使用以下查询和结果 我正在查找 ChargeId 和 ChargeType 唯一的最新条目 select chargeId chargeType serviceMonth from invoice CHARGEID CHARGETYPE S
  • Hindley-Milner 的哪一部分是你不明白的?

    I swear曾经有一个T shirt出售带有不朽文字的 哪一部分 do you not理解 就我而言 答案是 全部 特别是 我经常在 Haskell 论文中看到这样的符号 但我不知道它的含义 我不知道它应该是数学的哪个分支 我当然认识希腊
  • 在Interceptor.intercept()内部,我如何知道Action是否已经被执行?

    我正在使用拦截器在基于 Struts 的应用程序中实现一些内容 但我对它们的生命周期如何工作感到困惑 根据 Struts 文档 拦截者 编写拦截器 and 大图景 它应该像这样工作 FirstInterceptor NextIntercep
  • Boost::解析字符串时的精神斗争

    我正在尝试用 Boost Spirit 解析字符串 但我无法让它工作 从今天起我就没有使用 Boost Spirit 的经验了 该字符串由用 分隔的命令组成 命令是 INC 一些整数 BOMB 第一个整数 第二个整数 MOVE 第一个整数