jjzjj

c++ - 来自 Boost.Spirit 语法的属性:来自 std:vector of boost::variant 的错误

coder 2024-02-20 原文

我有一个用于读取棋盘游戏位置描述的工作解析器(国际跳棋,official grammar):

#include <boost/spirit/home/x3.hpp>
#include <iostream>

namespace x3 = boost::spirit::x3;

auto const colon   = x3::lit(':');
auto const comma   = x3::lit(',');
auto const dash    = x3::lit('-');
auto const dot     = x3::lit('.');    
auto const king    = x3::char_('K');
auto const color   = x3::char_("BW");
auto const num_sq  = x3::int_;

auto const num_pc  = -king >> num_sq;               // Kxx means king on square xx, xx a man on that square
auto const num_rng = num_pc >> dash >> num_sq;      // xx-yy means range of squares xx through yy (inclusive)
auto const num_seq = (num_rng | num_pc) % comma;    // <--- attribute should be std::vector<boost::variant>
auto const ccn     = colon >> color >> -num_seq;
auto const num_not = x3::repeat(2)[ccn];            // need to specify both white and black pieces
auto const fen     = color >> num_not >> -dot;

Live On Coliru

现在我想从合成属性中提取值,所以我围绕 Boost.Fusion 等进行了样板舞蹈,

namespace ast {

struct num_pc  { boost::optional<char> k; int sq; };
struct num_rng { boost::optional<char> k; int first, last; };
using rng_or_pc = boost::variant<num_rng, num_pc>;
struct num_seq { std::vector<rng_or_pc> sqrs; };
struct ccn     { char c; boost::optional<num_seq> seq; };
struct num_not { std::vector<ccn> n; };
struct fen     { char c; num_not n; };

}   // namespace ast

BOOST_FUSION_ADAPT_STRUCT(ast::num_pc,  (boost::optional<char>, k), (int, sq))
BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, (boost::optional<char>, k), (int, first), (int, last))
BOOST_FUSION_ADAPT_STRUCT(ast::num_seq, (std::vector<ast::rng_or_pc>, sqrs))
BOOST_FUSION_ADAPT_STRUCT(ast::ccn,     (char, c), (boost::optional<ast::num_seq>, seq))
BOOST_FUSION_ADAPT_STRUCT(ast::num_not, (std::vector<ast::ccn>, n))
BOOST_FUSION_ADAPT_STRUCT(ast::fen,     (char, c), (ast::num_not, n))

x3::rule<class num_pc_class,  ast::num_pc > num_pc  = "num_pc";
x3::rule<class num_rng_class, ast::num_rng> num_rng = "num_rng";
x3::rule<class num_seq_class, ast::num_seq> num_seq = "num_seq";
x3::rule<class ccn_class,     ast::ccn    > ccn     = "ccn";
x3::rule<class num_not_class, ast::num_not> num_not = "num_not";
x3::rule<class fen_class,     ast::fen    > fen     = "fen";

auto const colon   = x3::lit(':');
auto const comma   = x3::lit(',');
auto const dash    = x3::lit('-');
auto const dot     = x3::lit('.');    
auto const king    = x3::char_('K');
auto const color   = x3::char_("BW");
auto const num_sq  = x3::int_;

auto const num_pc_def  = -king >> num_sq;
auto const num_rng_def = num_pc >> dash >> num_sq;
auto const num_seq_def = (num_rng | num_pc) % comma;
auto const ccn_def     = colon >> color >> -num_seq;
auto const num_not_def = x3::repeat(2)[ccn];
auto const fen_def     = color >> num_not >> -dot;

BOOST_SPIRIT_DEFINE(num_pc, num_rng, num_seq, ccn, num_not, fen)

Live On Coliru

但是,我收到一个错误提示

error: static_assert failed "Attribute does not have the expected size."

还有几页:

^ main.cpp:16:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'std::vector<boost::variant<ast::num_rng, ast::num_pc>, std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >' to 'ast::num_seq' for 1st argument struct num_seq { std::vector<rng_or_pc> sqrs; };

^ main.cpp:16:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'std::vector<boost::variant<ast::num_rng, ast::num_pc>, std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >' to 'const ast::num_seq' for 1st argument struct num_seq { std::vector<rng_or_pc> sqrs; };

问题:这个错误是从哪里来的,如何解决?显然我的 num_seq 的合成属性规则不等于 std::vector<boost::variant>> .我该如何纠正这个问题?

最佳答案

我花了一些时间试图理解语法。

I strongly suggest readable identifiers. It's very hard to understand what's going on, while I have the strong impression it's actually a really simple grammar

我建议使用如下所示的简化版本。

  • 因为您的语法不使用递归,所以实际上不需要规则和标记的解析器类型。
  • 还为解析器工件使用命名空间。
  • 考虑使用 skipper 进行封装,而不是让调用者决定 (x3::skip[])
  • 添加一些帮助程序以便能够打印 AST 以进行验证:

    template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
        os << "{"; for (auto& el : v) os << el << " "; return os << "}";
    }
    std::ostream& operator<<(std::ostream& os, num_pc const& p)  { if (p.k) os << p.k; return os << p.sq; }
    std::ostream& operator<<(std::ostream& os, num_rng const& r) { return os << r.pc << "-" << r.last; }
    std::ostream& operator<<(std::ostream& os, ccn const& o)     { return os << o.c << " " << o.seq;                      } 
    std::ostream& operator<<(std::ostream& os, num_not const& nn) { return os << nn.n; }
    
  • 我也会避免不必要地包装另一个 vector :

    using num_not = std::vector<ccn>;
    
  • 使用现代 ADAPT 宏(因为您根据定义使用 C++14):

    BOOST_FUSION_ADAPT_STRUCT(ast::num_pc,  k, sq)
    BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, pc, last)
    BOOST_FUSION_ADAPT_STRUCT(ast::ccn,     c, seq)
    BOOST_FUSION_ADAPT_STRUCT(ast::fen,     c, n)
    
  • -

现场演示

Live On Coliru

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <vector>

namespace ast {

    struct num_pc {
        boost::optional<char> k;
        int sq;
    };

    struct num_rng {
        num_pc pc;
        int last;
    };

    using rng_or_pc = boost::variant<num_rng, num_pc>;
    using num_seq = std::vector<rng_or_pc>;

    struct ccn {
        char c;
        boost::optional<num_seq> seq;
    };

    using num_not = std::vector<ccn>;

    struct fen {
        char c;
        num_not n;
    };

    template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
        os << "{"; for (auto& el : v) os << el << " "; return os << "}";
    }
    std::ostream& operator<<(std::ostream& os, num_pc const& p)  { if (p.k) os << p.k; return os << p.sq; }
    std::ostream& operator<<(std::ostream& os, num_rng const& r) { return os << r.pc << "-" << r.last; }
    std::ostream& operator<<(std::ostream& os, ccn const& o)     { return os << o.c << " " << o.seq;                      } 
}

BOOST_FUSION_ADAPT_STRUCT(ast::num_pc,  k, sq)
BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, pc, last)
BOOST_FUSION_ADAPT_STRUCT(ast::ccn,     c, seq)
BOOST_FUSION_ADAPT_STRUCT(ast::fen,     c, n)

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

    namespace grammar
    {
        using namespace x3;

        template<typename T>
            auto as = [](auto p) { return rule<struct _, T>{} = as_parser(p); };

        uint_type const number {};
        auto const color   = char_("BW");
        auto const num_pc  = as<ast::num_pc>  ( -char_('K') >> number          ); 
        auto const num_rng = as<ast::num_rng> ( num_pc >> '-' >> number        ); 
        auto const num_seq = as<ast::num_seq> ( (num_rng | num_pc) % ','       ); 
        auto const ccn     = as<ast::ccn>     ( ':' >> color >> -num_seq       ); 
        auto const num_not = as<ast::num_not> ( repeat(2)[ccn]                 ); 
        auto const fen     = as<ast::fen>     ( color >> num_not >> -lit('.')  ); 
    }

    using grammar::fen;
}

int main() {
    for (std::string const t : {
        "B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29",
        "B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12",
        "W:B1-20:W31-50",   // initial position
        "W:B:W",            // empty board
        "W:B1:W",           // only black pieces
        "W:B:W50"           // only white pieces
    }) {
        auto b = t.begin(), e = t.end();
        ast::fen data;
        bool ok = phrase_parse(b, e, FEN::fen, FEN::x3::space, data);

        std::cout << t << "\n";
        if (ok) {
            std::cout << "Parsed: " << boost::fusion::as_vector(data) << "\n";
        } else {
            std::cout << "Parse failed:\n";
            std::cout << "\t on input: " << t << "\n";
        }
        if (b != e)
            std::cout << "\t Remaining unparsed: '" << std::string(b, e) << '\n';
    }
}

打印:

B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29
Parsed: (B {W  {18 24 27 28  K10  K15 } B  {12 16 20  K22  K25  K29 } })
B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12
Parsed: (B {W  {18 19 21 23 24 26 29 30 31 32 } B  {1 2 3 4 6 7 9 10 11 12 } })
W:B1-20:W31-50
Parsed: (W {B  {1-20 } W  {31-50 } })
W:B:W
Parsed: (W {B -- W -- })
W:B1:W
Parsed: (W {B  {1 } W -- })
W:B:W50
Parsed: (W {B -- W  {50 } })

关于c++ - 来自 Boost.Spirit 语法的属性:来自 std:vector of boost::variant 的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34559607/

有关c++ - 来自 Boost.Spirit 语法的属性:来自 std:vector of boost::variant 的错误的更多相关文章

  1. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  5. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  6. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  7. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  8. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

    我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

  9. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

  10. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

随机推荐