Spirit.X3 中的递归规则

2023-12-09

我想使用 Boost.Spirit x3 解析递归语法,但由于模板实例化深度问题而失败。

语法如下:

value: int | float | char | tuple
int: "int: " int_
float: "float: " real_ 
char: "char: " char_
tuple: "tuple: [" value* "]"

这是一个包含的示例:

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <variant>

struct value: std::variant<int,float,std::vector<value>>
{ 
    using std::variant<int,float,std::vector<value>>::variant;

    value& operator=(float) { return *this; } 
    value& operator=(int) { return *this; } 
    value& operator=(std::vector<value>) { return *this; } 
};

using namespace boost::fusion;
namespace x3 = boost::spirit::x3;

using x3::skip;
using x3::int_;
using x3::real_parser;
using x3::char_;

x3::rule<class value_, value> const value_ = "value";
x3::rule<class o_tuple_, std::vector<value>> o_tuple_ = "tuple";

using float_p = real_parser<float, x3::strict_real_policies<float>>;


const auto o_tuple__def = "tuple: " >> skip(boost::spirit::x3::space)["[" >> value_ % "," >> "]"];
BOOST_SPIRIT_DEFINE(o_tuple_)

const auto value__def
    = ("float: " >> float_p())
    | ("int: " >> int_)
    | o_tuple_
    ;

BOOST_SPIRIT_DEFINE(value_)

int main()
{
  std::string str;
  value val;

  using boost::spirit::x3::parse;
  auto first = str.cbegin(), last = str.cend();
  bool r = parse(first, last, value_, val);
}

如果该行有效| o_tuple_被注释(例如没有递归)。


这是X3中递归的常见问题。事情还没有解决。

我想我明白这个问题是因为x3::skip改变上下文对象。事实上,删除它会使事情编译,并成功解析一些简单的测试用例:

"float: 3.14",
"int: 3.14",
"tuple: [float: 3.14,int: 3]",

但是,显然,如果没有船长,以下内容将无法解析:

// the following _should_ have compiled with the original skip() configuration:
"tuple: [ float: 3.14,\tint: 3 ]",

现在,我敢说您可以通过在顶层应用船长来解决这个问题(这意味着实例化“循环”中涉及的所有规则的上下文都是相同的)。如果这样做,您将立即开始接受输入中更灵活的空格:

// the following would not have parsed with the original skip() configuration:
"float:3.14",
"int:3.14",
"tuple:[float: 3.14,int: 3]",
"tuple:[float:3.14,int:3]",
"tuple: [ float:3.14,\tint:3 ]",

即使编译成功,这些都不会用原始方法进行解析。

需要什么

这是我对代码所做的一些调整。

  1. 删除了无效的赋值运算符value::operator=(我不知道你为什么拥有它们)

  2. 添加代码以打印任何调试转储value:

    friend std::ostream& operator<<(std::ostream& os, base_type const& v) {
        struct {
            std::ostream& operator()(float const& f) const { return _os << "float:" << f; }
            std::ostream& operator()(int const& i)   const { return _os << "int:" << i; }
            std::ostream& operator()(std::vector<value> const& v) const { 
                _os << "tuple: [";
                for (auto& el : v) _os << el << ",";
                return _os << ']';
            }
            std::ostream& _os;
        } vis { os };
    
        return std::visit(vis, v);
    }
    
  3. 删除船长并从中分离出关键字:插补:

    namespace x3 = boost::spirit::x3;
    
    x3::rule<struct value_class, value> const value_ = "value";
    x3::rule<struct o_tuple_class, std::vector<value> > o_tuple_ = "tuple";
    
    x3::real_parser<float, x3::strict_real_policies<float> > float_;
    
    const auto o_tuple__def = "tuple" >> x3::lit(':') >> ("[" >> value_ % "," >> "]");
    
    const auto value__def
        = "float" >> (':' >> float_)
        | "int" >> (':' >> x3::int_)
        | o_tuple_
        ;
    
    BOOST_SPIRIT_DEFINE(value_, o_tuple_)
    
  4. 现在crucial步骤:在顶层添加船长:

    const auto entry_point = x3::skip(x3::space) [ value_ ];
    
  5. 创建优秀的测试驱动程序main():

    int main()
    {
        for (std::string const str : {
                "",
                "float: 3.14",
                "int: 3.14",
                "tuple: [float: 3.14,int: 3]",
                // the following _should_ have compiled with the original skip() configuration:
                "tuple: [ float: 3.14,\tint: 3 ]",
                // the following would not have parsed with the original skip() configuration:
                "float:3.14",
                "int:3.14",
                "tuple:[float: 3.14,int: 3]",
                "tuple:[float:3.14,int:3]",
                "tuple: [ float:3.14,\tint:3 ]",
                // one final show case for good measure
                R"(
                tuple: [
                   int  : 4,
                   float: 7e9,
                   tuple: [float: -inf],
    
    
                   int: 42
                ])"
        }) {
            std::cout << "============ '" << str << "'\n";
    
            //using boost::spirit::x3::parse;
            auto first = str.begin(), last = str.end();
            value val;
    
            if (parse(first, last, parser::entry_point, val))
                std::cout << "Parsed '" << val << "'\n";
            else
                std::cout << "Parse failed\n";
    
            if (first != last)
                std::cout << "Remaining input: '" << std::string(first, last) << "'\n";
        }
    }
    

现场演示

See it Live On Coliru

//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <variant>

struct value: std::variant<int,float,std::vector<value>>
{ 
    using base_type = std::variant<int,float,std::vector<value>>;
    using base_type::variant;

    friend std::ostream& operator<<(std::ostream& os, base_type const& v) {
        struct {
            std::ostream& operator()(float const& f) const { return _os << "float:" << f; }
            std::ostream& operator()(int const& i)   const { return _os << "int:" << i; }
            std::ostream& operator()(std::vector<value> const& v) const { 
                _os << "tuple: [";
                for (auto& el : v) _os << el << ",";
                return _os << ']';
            }
            std::ostream& _os;
        } vis { os };

        return std::visit(vis, v);
    }
};

namespace parser {
    namespace x3 = boost::spirit::x3;

    x3::rule<struct value_class, value> const value_ = "value";
    x3::rule<struct o_tuple_class, std::vector<value> > o_tuple_ = "tuple";

    x3::real_parser<float, x3::strict_real_policies<float> > float_;

    const auto o_tuple__def = "tuple" >> x3::lit(':') >> ("[" >> value_ % "," >> "]");

    const auto value__def
        = "float" >> (':' >> float_)
        | "int" >> (':' >> x3::int_)
        | o_tuple_
        ;

    BOOST_SPIRIT_DEFINE(value_, o_tuple_)

    const auto entry_point = x3::skip(x3::space) [ value_ ];
}

int main()
{
    for (std::string const str : {
            "",
            "float: 3.14",
            "int: 3.14",
            "tuple: [float: 3.14,int: 3]",
            // the following _should_ have compiled with the original skip() configuration:
            "tuple: [ float: 3.14,\tint: 3 ]",
            // the following would not have parsed with the original skip() configuration:
            "float:3.14",
            "int:3.14",
            "tuple:[float: 3.14,int: 3]",
            "tuple:[float:3.14,int:3]",
            "tuple: [ float:3.14,\tint:3 ]",
            // one final show case for good measure
            R"(
            tuple: [
               int  : 4,
               float: 7e9,
               tuple: [float: -inf],


               int: 42
            ])"
    }) {
        std::cout << "============ '" << str << "'\n";

        //using boost::spirit::x3::parse;
        auto first = str.begin(), last = str.end();
        value val;

        if (parse(first, last, parser::entry_point, val))
            std::cout << "Parsed '" << val << "'\n";
        else
            std::cout << "Parse failed\n";

        if (first != last)
            std::cout << "Remaining input: '" << std::string(first, last) << "'\n";
    }
}

Prints

============ ''
Parse failed
============ 'float: 3.14'
Parsed 'float:3.14'
============ 'int: 3.14'
Parsed 'int:3'
Remaining input: '.14'
============ 'tuple: [float: 3.14,int: 3]'
Parsed 'tuple: [float:3.14,int:3,]'
============ 'tuple: [ float: 3.14, int: 3 ]'
Parsed 'tuple: [float:3.14,int:3,]'
============ 'float:3.14'
Parsed 'float:3.14'
============ 'int:3.14'
Parsed 'int:3'
Remaining input: '.14'
============ 'tuple:[float: 3.14,int: 3]'
Parsed 'tuple: [float:3.14,int:3,]'
============ 'tuple:[float:3.14,int:3]'
Parsed 'tuple: [float:3.14,int:3,]'
============ 'tuple: [ float:3.14,  int:3 ]'
Parsed 'tuple: [float:3.14,int:3,]'
============ '
            tuple: [
               int  : 4,
               float: 7e9,
               tuple: [float: -inf],


               int: 42
            ]'
Parsed 'tuple: [int:4,float:7e+09,tuple: [float:-inf,],int:42,]'

¹ 其他指令也是如此,例如x3::with<>。问题是上下文变得extended在每个实例化级别上,而不是“修改”以恢复原始上下文类型,并结束实例化周期。

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

Spirit.X3 中的递归规则 的相关文章

随机推荐

  • 如何让 ASP.Net Web API 和 OData 将字符串值绑定为键?

    我正在浏览来自 asp net 的简短 Web Api OData 教程 http www asp net web api overview odata support in aspnet web api getting started w
  • 如何对 Reflex Dynamic 内的值进行分支?

    在最简单的情况下 假设我有一个Dynamic t Bool 当值为 true 时 我希望存在一个空 div 当值为 false 时 我不希望存在任何 dom 元素 更一般地说 如果我有一个Dynamic t Either MyA MyB 并
  • Maven war项目中要打包的不同文件

    我有一个 war 打包 Maven 项目 其中有一个名为 myapp properties 的 Java 属性文件 在此属性文件中 我存储了一些参数 例如数据库连接参数 这些参数将由 web 应用程序使用 例如 无论是在持续集成环境还是在我
  • 表示两个或多个因素之间的直接或反向关系的最佳 Guava (Google) 集合 API 是什么?

    BiMap 确实有逆方法 但我不确定它是否是解决该问题的正确集合 有人可以建议替代方法或收集 方法吗 举个例子会很有帮助 提前致谢 普拉卡什 您能否展示一个简单的代码示例来说明如何使用这种数据结构 键 值应该是唯一的吗 在这种情况下 BiM
  • 更改 Sublime Text 3 侧边栏宽度

    我想知道 ST3 是否可以更改侧边栏宽度 这是基于项目 基于主题还是设置中的某些内容 我没有在默认设置中看到 所以这就是我在这里提出问题的原因 谢谢 要调整侧边栏的大小 您所需要做的就是单击并拖动 遗憾的是 无法在用户首选项 项目或当前主题
  • 在总共 42 个 gem 中找不到“minitest”(~> 5.1)(Gem::MissingSpecError)

    我是 React Native 的新手 目前正在尝试制作一个简单的待办事项列表 不幸的是 我无法继续 因为我遇到了这个错误 我不知道如何修复 发生了什么 我的本机反应直到今天都工作正常 因为由于 RangeError 整数 42949672
  • 从 Python 调用时 C DLL 破解

    我有一个使用 C Python API 嵌入 Python 解释器的 DLL 如果调用一次 DLL 工作正常 但如果调用 DLL 两次 代码就会崩溃 并且我的程序会捕获内存错误 调用 DLL 的 C 代码很简单 并且对 DLL 函数 调用
  • Protractor 无法使用 Firefox 启动测试

    我无法在 Firefox 版本 56 0 1 上使用量角器开始测试 我的量角器版本是5 1 2 exports config allScriptsTimeout 11000 specs e2e e2e spec ts capabilitie
  • Java - 执行 .SH 文件

    我将如何执行 SH 文件 这是本地主机 没有远程连接或任何东西 我见过很多Runtime exec当我搜索时还有其他事情 但这些似乎不起作用 这是 Java 6 此外 如果重要的话 SH 所做的就是移动两个文件夹 Thanks 你可以使用流
  • c 中指针的运算符优先级

    下面的情况如何分析优先级 for i 0 i lt 20 i array p i i printf d n arr 以下代码与上面有何不同 for int i 0 i lt 20 i arr i i printf d n arr arr p
  • 使用部分字符串匹配创建指示变量

    我正在尝试为我的数据中的不同种族 民族创建指标变量 在我的数据 mydata 中 我有一个名为 Race 的变量 该变量包含一个人在调查问卷上标记为种族的每个框的输出 所以它看起来像这样 ID Race 6 American Indian
  • 如何更改 Skobbler 中路线的颜色

    你好 我想改变 skobbler 中路线的颜色 例如 我在 android 中做一个类似位智的应用程序 当路线交通繁忙时 我想将路线的颜色更改为红色 也许有人可以帮助我 提前致谢 目前 可以通过编辑样式 JSON 文件来设置路线 主路线和备
  • 如何在 iOS 上启用/禁用设备明智的隐藏式字幕设置?

    当设备上打开辅助功能选项时 我们无法通过设置来关闭隐藏式字幕closedCaptionEnabled选项为AVPlayer像我们通常那样的例子 有没有办法绕过这样的选项 甚至测试是否可以在必要时锁定 CC 按钮 您可以遍历每个 AVPlay
  • 从 Socket 读取字节数组

    我已经阅读了太阳指南和其他类似的问题 我想从 Socket 读取一些未知数量的字节到数组中 我有两个选择 我可以使用read byte 在循环中并将字节添加到字节数组中 或者我可以使用 DataInputStreamreadFully by
  • 有条件写入 xlsx

    Folks 我目前正在使用一个巨大的 Excel 工作表 python 3 7 1 和 pandas 0 23 4 我的任务是根据条件匹配写入单元格 像这样的东西 val li email protected if val in Cell
  • 如何使用 Java 向 iPhone 发送推送通知消息?

    我想使用 Java 向特定 iPhone 设备发送推送通知消息 我不知道该怎么做 我已经用谷歌搜索过这个 他们建议使用 PayLoad 类 但不从任何 jar 文件中获取此类 请问有人可以指导我使用 Java 将推送通知消息发送到 iPho
  • 如何使用 MonoDevelop 调试 MonoDevelop 加载项?

    主题说明了一切 我在 monodevelop 网站或通过 google 找不到任何信息 甚至添加System Diagnostics Debugger Break 并与运行mono debug MonoDevelop exe似乎没有做任何事
  • 十进制转换为基数 2-16(二进制到十六进制)

    嘿 我正在编写一个程序 将十进制数转换为从二进制到十六进制的任何基本单位 2 3 4 15 16 这就是我到目前为止所得到的 运行 2 15 之间的任何数字都会导致无限循环 我想知道您是否对我的项目的其余部分有任何建议 运行时 这将要求您提
  • Excel JS 加载项适用于 Excel,但不适用于 Excel Online

    我使用 WoodGrove Expense Trends 示例构建了一个 Excel JS 任务窗格加载项 并验证了它可以在我的计算机上的 Excel 2016 中正确运行 但是 当我转到 Excel Online 并尝试添加加载项 通过从
  • Spirit.X3 中的递归规则

    我想使用 Boost Spirit x3 解析递归语法 但由于模板实例化深度问题而失败 语法如下 value int float char tuple int int int float float real char char char