我正在尝试将多个 boost::spirit 解析规则组合成更大的复合规则,并具有以下代码:
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/adt/adapt_adt.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include <fstream>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct PdCanvas {
int topX;
int topY;
int wX;
int wY;
std::string name;
int openOnLoad;
};
struct PdArrayData {
std::vector<float> data;
};
BOOST_FUSION_ADAPT_STRUCT(
PdCanvas,
(int, topX)
(int, topY)
(int, wX)
(int, wY)
(std::string, name)
(int, openOnLoad));
BOOST_FUSION_ADAPT_STRUCT(
PdArrayData,
(std::vector<float>, data));
using PdRecord = boost::variant<
PdArrayData,
PdCanvas
>;
template <typename Iterator> struct PdCanvasGrammar : qi::grammar<Iterator, PdCanvas()> {
PdCanvasGrammar() : PdCanvasGrammar::base_type(start) {
using namespace qi;
start = skip(space)[canvasRule >> eoi];
name = +(('\\' >> space) |graph);
canvasRule = "#N canvas" >> int_ >> int_ >> int_ >> int_ >> name >> int_ >> ';';
BOOST_SPIRIT_DEBUG_NODES((start)(canvasRule)(name))
}
private:
qi::rule<Iterator, PdCanvas()> start;
qi::rule<Iterator, PdCanvas(), qi::space_type> canvasRule;
qi::rule<Iterator, std::string()> name;
};
template <typename Iterator>
struct PdRecordGrammar : qi::grammar<Iterator, PdRecord(), ascii::space_type> {
PdRecordGrammar () : PdRecordGrammar::base_type(recordRule) {
using namespace qi;
arrayDataRule = (lit("#A") >> +(qi::float_) >> ";");
canvasRule = PdCanvasGrammar<Iterator>();
recordRule = (canvasRule | (arrayDataRule) );
BOOST_SPIRIT_DEBUG_NODES((arrayDataRule)(canvasRule)(recordRule))
}
qi::rule<Iterator, PdArrayData(), ascii::space_type> arrayDataRule;
qi::rule<Iterator, PdCanvas()> canvasRule;
qi::rule<Iterator, PdRecord(), ascii::space_type> recordRule;
};
int main(int argc, char** argv)
{
if(argc != 2)
{
std::cout << "Usage: " <<argv[0] << " <PatchFile>" << std::endl;
exit(1);
}
std::ifstream inputFile(argv[1]);
std::string inputString(std::istreambuf_iterator<char>(inputFile), {});
PdRecord root;
PdRecordGrammar<std::string::iterator> parser;
std::cout << "Loaded file:\n " << inputString << std::endl;
PdCanvas canvObj;
PdCanvasGrammar<std::string::iterator> canvParse;
bool canvSuccess = qi::phrase_parse(inputString.begin(), inputString.end(), canvParse, boost::spirit::ascii::space, canvObj);
std::cout << "Canvas success: " << canvSuccess << std::endl;
bool success = qi::phrase_parse(inputString.begin(), inputString.end(), parser, boost::spirit::ascii::space, root);
std::cout << "Success: " << success << std::endl;
return 0;
}
然后我测试了以下字符串的代码,应该解析:
#N canvas 0 0 400 300 moo 1;
运行代码给出以下输出:
Loaded file:
#N canvas 0 0 400 300 moo 1;
<start>
<try>#N canvas 0 0 400 30</try>
<canvasRule>
<try>#N canvas 0 0 400 30</try>
<name>
<try>moo 1;\n</try>
<success> 1;\n</success>
<attributes>[[m, o, o]]</attributes>
</name>
<success>\n</success>
<attributes>[[0, 0, 400, 300, [m, o, o], 1]]</attributes>
</canvasRule>
<success></success>
<attributes>[[0, 0, 400, 300, [m, o, o], 1]]</attributes>
</start>
Canvas success: 1
<recordRule>
<try>#N canvas 0 0 400 30</try>
<canvasRule>
<try>#N canvas 0 0 400 30</try>
<fail/>
</canvasRule>
<arrayDataRule>
<try>#N canvas 0 0 400 30</try>
<fail/>
</arrayDataRule>
<fail/>
</recordRule>
Success: 0
可以看到,文件被独立的 PdCanvasGrammar 规则成功解析,但是当我尝试使用复合 PdRecordGrammar 规则时解析失败。
我假设我在将规则组合在一起时做错了什么,但我不知道是什么。
顺便说一句,定义为 PdRecordGrammar 一部分的 arrayDataRule 成功解析了它的输入:
Loaded file:
#A 1 2 3 4 5;
<start>
<try>#A 1 2 3 4 5;</try>
<canvasRule>
<try>#A 1 2 3 4 5;</try>
<fail/>
</canvasRule>
<fail/>
</start>
Canvas success: 0
<recordRule>
<try>#A 1 2 3 4 5;</try>
<canvasRule>
<try>#A 1 2 3 4 5;</try>
<fail/>
</canvasRule>
<arrayDataRule>
<try>#A 1 2 3 4 5;</try>
<success></success>
<attributes>[[[1, 2, 3, 4, 5]]]</attributes>
</arrayDataRule>
<success></success>
<attributes>[[[1, 2, 3, 4, 5]]]</attributes>
</recordRule>
Success: 1
所以我的假设是,如果我只是采用 PdCanvasGrammar 规则并将其本质上定义为 PdRecord 规则,它就会起作用。但我正在尝试了解如何正确组合规则,以免单个规则变得太大和笨拙。
所以我的问题是: 为什么定义的规则不起作用,将规则组合成复合规则的正确方法是什么?
版本:
提升:1.75.0
C++ 标准:11
GCC: gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
这个:
canvasRule = PdCanvasGrammar<Iterator>();
创建对悬空临时对象的引用。修复它:
template <typename Iterator>
struct PdRecordGrammar : qi::grammar<Iterator, PdRecord(), qi::space_type> {
PdRecordGrammar() : PdRecordGrammar::base_type(recordRule) {
arrayDataRule = "#A" >> +qi::float_ >> ";";
recordRule = canvasRule | arrayDataRule;
BOOST_SPIRIT_DEBUG_NODES((arrayDataRule)(recordRule))
}
private:
qi::rule<Iterator, PdArrayData(), qi::space_type> arrayDataRule;
PdCanvasGrammar<Iterator> canvasRule;
qi::rule<Iterator, PdRecord(), qi::space_type> recordRule;
};
adapt_adt
标题(这是最好的)。为什么包括那个?phrase_parse
。再次参见Boost spirit skipper issues了解上下文。lit()
,括号和;
using namespace qi
但仍然有资格qi::float_
.main
)。这会导致生命周期/变量重用错误。或者只是难以维护的代码。const_iterator
。应该。// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
struct PdCanvas { int topX, topY, wX, wY, openOnLoad; std::string name; };
struct PdArrayData { std::vector<float> data; };
BOOST_FUSION_ADAPT_STRUCT(PdCanvas, , topX, topY, wX, wY, name, openOnLoad)
BOOST_FUSION_ADAPT_STRUCT(PdArrayData, data)
using PdRecord = boost::variant<PdArrayData, PdCanvas>;
template <typename Iterator> struct PdCanvasGrammar : qi::grammar<Iterator, PdCanvas()> {
PdCanvasGrammar() : PdCanvasGrammar::base_type(start) {
using namespace qi;
start = skip(space)[canvasRule >> eoi];
name = +('\\' >> space | graph);
canvasRule = "#N canvas" >> int_ >> int_ >> int_ >> int_ >> name >> int_ >> ';';
BOOST_SPIRIT_DEBUG_NODES((start)(canvasRule)(name))
}
private:
qi::rule<Iterator, PdCanvas()> start;
qi::rule<Iterator, PdCanvas(), qi::space_type> canvasRule;
qi::rule<Iterator, std::string()> name;
};
template <typename Iterator>
struct PdRecordGrammar : qi::grammar<Iterator, PdRecord(), qi::space_type> {
PdRecordGrammar() : PdRecordGrammar::base_type(recordRule) {
arrayDataRule = "#A" >> +qi::float_ >> ";";
recordRule = canvasRule | arrayDataRule;
BOOST_SPIRIT_DEBUG_NODES((arrayDataRule)(recordRule))
}
private:
qi::rule<Iterator, PdArrayData(), qi::space_type> arrayDataRule;
PdCanvasGrammar<Iterator> canvasRule;
qi::rule<Iterator, PdRecord(), qi::space_type> recordRule;
};
int main() {
std::string const input = "#N canvas 0 0 400 300 moo 1; ";
std::cout << "Input:\n " << quoted(input) << std::endl;
{
PdCanvas obj;
PdCanvasGrammar<std::string::const_iterator> const canvParse;
std::cout << "Canvas success: " << parse(input.begin(), input.end(), canvParse, obj) << "\n";
}
{
PdRecord obj;
PdRecordGrammar<std::string::const_iterator> const parser;
std::cout << "Success: " << phrase_parse(input.begin(), input.end(), parser, qi::space, obj) << "\n";
}
}
输出:
Input:
"#N canvas 0 0 400 300 moo 1; "
Canvas success: 1
Success: 1