Boost::Spirit 表达式解析器

2024-01-04

我的 boost::spirit 解析器还有另一个问题。

template<typename Iterator>
struct expression: qi::grammar<Iterator, ast::expression(), ascii::space_type> {
    expression() :
        expression::base_type(expr) {
        number %= lexeme[double_];
        varname %= lexeme[alpha >> *(alnum | '_')];

        binop = (expr >> '+' >> expr)[_val = construct<ast::binary_op<ast::add>>(_1,_2)]
              | (expr >> '-' >> expr)[_val = construct<ast::binary_op<ast::sub>>(_1,_2)]
              | (expr >> '*' >> expr)[_val = construct<ast::binary_op<ast::mul>>(_1,_2)]
              | (expr >> '/' >> expr)[_val = construct<ast::binary_op<ast::div>>(_1,_2)] ;

        expr %= number | varname | binop;
    }

    qi::rule<Iterator, ast::expression(), ascii::space_type> expr;
    qi::rule<Iterator, ast::expression(), ascii::space_type> binop;
    qi::rule<Iterator, std::string(), ascii::space_type> varname;
    qi::rule<Iterator, double(), ascii::space_type> number;
};

这是我的解析器。它解析了"3.1415" and "var"很好,但是当我尝试解析时"1+2"它告诉我parse failed。然后我尝试改变binop rule to

    binop = expr >>
           (('+' >> expr)[_val = construct<ast::binary_op<ast::add>>(_1, _2)]
          | ('-' >> expr)[_val = construct<ast::binary_op<ast::sub>>(_1, _2)]
          | ('*' >> expr)[_val = construct<ast::binary_op<ast::mul>>(_1, _2)]
          | ('/' >> expr)[_val = construct<ast::binary_op<ast::div>>(_1, _2)]);

但现在它当然无法构建 AST,因为_1 and _2设置不同。我只见过类似的东西_r1提到过,但作为新手,我不太明白如何boost::phoenix and boost::spirit相互影响。

怎么解决这个问题呢?


我并不完全清楚你想要实现什么目标。最重要的是,您不担心运算符结合性吗?我将仅显示基于使用右递归的简单答案 - 这导致左结合运算符正在解析中。

直接回答你的问题visible问题是要兼顾fusion::vector2<char, ast::expression>- 这真的没什么好玩的,尤其是在菲尼克斯拉姆达语义动作。 (我将在下面展示它是什么样子)。

同时我认为你应该阅读 Spirit 文档

  • here http://boost-spirit.com/old_docs/v1_6/doc/faq.html#left_recursion in the oldSpirit docs(消除左递归);尽管语法不再适用,但 Spirit 仍然生成 LL 递归下降解析器,因此左递归背后的概念仍然适用。下面的代码显示了这应用于灵气
  • here http://www.boost.org/doc/libs/1_48_0/libs/spirit/example/qi/calc_utree_naive.cpp: Qi 示例包含三个calculator示例,这些示例应该会提示您为什么运算符结合性很重要,以及如何表达捕获二元运算符结合性的语法。显然,它也展示了如何支持带括号的表达式覆盖默认的评估顺序。

Code:

我有三个版本的代码可以工作,解析输入如下:

std::string input("1/2+3-4*5");

into an ast::expression分组如下(使用 BOOST_SPIRIT_DEBUG):

<expr>
  ....
  <success></success>
  <attributes>[[1, [2, [3, [4, 5]]]]]</attributes>
</expr>

代码的链接在这里:

  • 步骤_#1_reduce_semantic_actions.cpp https://gist.github.com/267acc43ac7ee276e889#file_step_1_reduce_semantic_actions.cpp
  • 步骤_#2_drop_rule.cpp https://gist.github.com/267acc43ac7ee276e889#file_step_2_drop_rule.cpp
  • 步骤_#0_vector2.cpp https://gist.github.com/267acc43ac7ee276e889#file_step_0_vector2.cpp

Step 1: 减少语义动作 https://gist.github.com/267acc43ac7ee276e889#file_step_1_reduce_semantic_actions.cpp

First thing, I'd get rid of the alternative parse expressions per operator; this leads to excessive backtracking1. Also, as you've found out, it makes the grammar hard to maintain. So, here is a simpler variation that uses a function for the semantic action:

1check that using BOOST_SPIRIT_DEBUG!

static ast::expression make_binop(char discriminant, 
     const ast::expression& left, const ast::expression& right)
{
    switch(discriminant)
    {
        case '+': return ast::binary_op<ast::add>(left, right);
        case '-': return ast::binary_op<ast::sub>(left, right);
        case '/': return ast::binary_op<ast::div>(left, right);
        case '*': return ast::binary_op<ast::mul>(left, right);
    }
    throw std::runtime_error("unreachable in make_binop");
}

// rules:
number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];

simple = varname | number;
binop = (simple >> char_("-+*/") >> expr) 
    [ _val = phx::bind(make_binop, qi::_2, qi::_1, qi::_3) ]; 

expr = binop | simple;

Step 2: 删除多余的规则,使用_val https://gist.github.com/267acc43ac7ee276e889#file_step_2_drop_rule.cpp

正如您所看到的,这有可能降低复杂性。现在只是一小步,删除 binop 中间体(它已经变得相当多余):

number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];

simple = varname | number;
expr = simple [ _val = _1 ] 
    > *(char_("-+*/") > expr) 
            [ _val = phx::bind(make_binop, qi::_1, _val, qi::_2) ]
    > eoi;

如你看到的,

  • expr规则,即_val惰性占位符用作累积 binops 的伪局部变量。跨越规则,你必须使用qi::locals<ast::expression>对于这样的方法。 (这是你关于_r1).
  • 现在有明确的期望点,使语法更加健壮
  • the expr规则不再需要是自动规则(expr =代替expr %=)

Step 0: 直接摔跤融合类型 https://gist.github.com/267acc43ac7ee276e889#file_step_0_vector2.cpp

最后,为了有趣和血腥,让我向您展示如何处理您建议的代码,以及 _1、_2 等的移动绑定:

static ast::expression make_binop(
        const ast::expression& left, 
        const boost::fusion::vector2<char, ast::expression>& op_right)
{
    switch(boost::fusion::get<0>(op_right))
    {
        case '+': return ast::binary_op<ast::add>(left, boost::fusion::get<1>(op_right));
        case '-': return ast::binary_op<ast::sub>(left, boost::fusion::get<1>(op_right));
        case '/': return ast::binary_op<ast::div>(left, boost::fusion::get<1>(op_right));
        case '*': return ast::binary_op<ast::mul>(left, boost::fusion::get<1>(op_right));
    }
    throw std::runtime_error("unreachable in make_op");
}

// rules:
expression::base_type(expr) {
number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];

simple = varname | number;
binop %= (simple >> (char_("-+*/") > expr)) 
    [ _val = phx::bind(make_binop, qi::_1, qi::_2) ]; // note _2!!!

expr %= binop | simple;

正如你所看到的,编写make_binop就这样发挥作用!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Boost::Spirit 表达式解析器 的相关文章

随机推荐

  • java.net.ConnectException:连接被拒绝 TCP

    客观的 我想将java PC 项目中输入的文本发送到显示此文本的android应用程序 PC已连接到wifi Android 手机创建的热点 PC 客户端java项目代码 public class EcsDemo public static
  • 如何禁用 filebeat 的 close_inactive 设置?

    我的 filebeat 来自的容器docker elastic co beats filebeat 6 1 2 收割机被 close inactive 关闭 我不希望它们被关闭 文档来自close inactive from here ht
  • 数组和切片数据类型

    我发现自己很困惑array and slice数据类型 从 Go 文档来看 数组的描述如下 数组在 Go 和 C 中的工作方式存在重大差异 在 Go 中 数组是值 将一个数组分配给另一个数组会复制所有元素 特别是 如果将数组传递给函数 它将
  • 如何在后台运行脚本(linux openwrt)?

    我有这个脚本 bin sh while true do urlfile ls root wget wget download link txt head n 1 dir cat root wget wget dir txt if urlfi
  • 将 stdout 置于二进制模式的可移植方法

    我正在编写用作 cgi 的 C 程序 用于生成和输出 gif 图像 它们在带有以下形式标签的 HTML 页面中使用 img src cgi bin gifprogram cgi param val etc 其中查询字符串参数描述由 cgi
  • 在 VBScript 中对多维数组进行排序

    如何根据孔大小参数对多维数组进行 排序 例如 一个简单的例子是 从文本文件加载 gt Liv1 HoleSize 0 22 Liv1 HoleX 0 250 Liv1 HoleY 0 55 gt Liv1 HoleSize 1 14 Liv
  • ansible“原始”、“shell”和“命令”之间有什么区别?

    有什么区别raw shell and command在ansible剧本中 以及什么时候使用哪个 command https docs ansible com ansible latest modules command module ht
  • 如何使用perl修改crontab? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何通过 perl 脚本在 crontab 中附加 cron 任务 我想到了以下几点 usr bin perl use strict us
  • 使用 += 运算符使用 [] 创建新的 std::map 条目是否安全?

    假设我有一个std map
  • WPF 如何更改页面中的属性?

    当我单击 PAGE2 上的按钮时 我想更改 PAGE1 中控件的属性 Visible PAGE2 是一个弹出窗口 其中包含给用户的消息 我希望当他单击下面的按钮时 PAGE2 中的控件可见的属性 同时仍显示在后台 会发生变化 对我来说 问题
  • 如何在稳定基线3中获得action_propability()

    我刚刚开始自学稳定基线 3 的强化学习 我的长期目标是训练代理玩特定的回合制棋盘游戏 不过 目前我对新事物感到非常不知所措 我已经实现了一个健身房环境 我可以用它来手动玩游戏或让它选择随机动作 目前 我一直在尝试让一个模型根据观察结果向我提
  • Play 框架将 HTTP 重定向到 https

    我使用 play 框架版本 2 2 3 并使用以下命令运行 activator 以使用 https 访问站点 activator run Dhttp port disabled Dhttps port 9043 我在浏览器中输入了 URL
  • Prolog 和逻辑难题

    我似乎对 Prolog 中事实的统一有疑问 但无法确认 一切看起来都像这样should工作 并且考虑到 Prolog 的相对稀有性 我查找了使用 Prolog 解决逻辑难题的示例 但没有实际效果 This is额外的学分作业 所以我不确定它
  • Android 4.4 SDK 更新后 android-support-v7-appcombat jar 不匹配和应用程序崩溃

    过去几个月我已经在我的应用程序中成功使用 android support v7 appcompat 库来支持旧设备中的操作栏 最近我从 SDK Manager 下载了 Android 4 4 kitkat 更新以及系统映像和 SDK 平台
  • 理解自然连接的困难[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何确保在数据加载之前我不会访问数据?

    我是编程新手 我的代码收到错误 致命错误 在展开可选值时意外发现 nil 2017 10 27 16 06 16 755817 0200 Inspireme1 0 836 85307 致命错误 在展开可选值时意外发现 nil LLDB 新错
  • 卷曲 ssl 证书

    我总是在curl中关闭CURLOPT SSL VERIFYPEER 但我真的想在我的php centos curl检查其证书的地方进行升级 我有 CENTOS 4 7 i686 PHP 5 2 16 我从 stripe com 得到了一个不
  • 如何在 Apache CXF 中引发 403 错误 - Java

    我基于拦截器使用以下代码 当 check 返回 true 时 我想抛出 403 错误 Override public void handleMessage Message arg0 throws Fault HttpServletReque
  • 将 bg 和 fg 与给定 PID 一起使用

    我正在使用Ubuntu 如果给我一个作业的 PID 我怎样才能将暂停的作业转到后台 前台和运行状态 I know bg fg但他们要求job id not PID 另外 我暂停工作 kill STOP
  • Boost::Spirit 表达式解析器

    我的 boost spirit 解析器还有另一个问题 template