[英]boost spirit 2 : is there a way to know what is the parser progression percentage?
[英]boost spirit 2 : following progression percentage on qi::parser. What's bad in my code?
我使用Boost Spirit 2進行開發,我嘗試遵循示例以在我的pgn解析器中獲得進展(稍后將添加語義動作)(另請參閱相關的先前問題 )。 但是我無法避免編譯錯誤: cpp
#include "pgn_games_extractor.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <tuple>
#include <iostream>
BOOST_FUSION_ADAPT_STRUCT(loloof64::pgn_tag, key, value)
BOOST_FUSION_ADAPT_STRUCT(loloof64::game_move, move_number, white_move, black_move, result)
BOOST_FUSION_ADAPT_STRUCT(loloof64::pgn_game, header, moves)
namespace loloof64 {
namespace qi = boost::spirit::qi;
typedef std::tuple<std::size_t, game_move> move_t;
typedef std::tuple<std::vector<pgn_tag>, std::vector<move_t>> game_t;
typedef std::tuple<std::size_t, std::vector<game_t>> pgn_t;
template <typename Iterator> struct pgn_parser : qi::grammar<Iterator, std::vector<pgn_game>, qi::space_type> {
pgn_parser() : pgn_parser::base_type(games) {
using namespace qi;
CurrentPos<Iterator> filepos;
const std::string no_move;
result.add
("1-0", result_t::white_won)
("0-1", result_t::black_won)
("1/2-1/2", result_t::draw)
("*", result_t::undecided);
quoted_string = '"' >> *~char_('"') >> '"';
tag = '[' >> +alnum >> quoted_string >> ']';
header = +tag;
regular_move = lit("O-O-O") | "O-O" | (+char_("a-hNBRQK") >> +char_("a-h1-8x=NBRQK") >> -lit("e.p."));
single_move = raw [ regular_move >> -char_("+#") ];
full_move = filepos.current_pos >> uint_
>> (lexeme["..." >> attr(no_move)] | "." >> single_move)
>> (single_move | attr(no_move))
>> -result;
game_description = +full_move;
single_game = -header >> game_description;
games = filepos.save_start_pos >> *single_game;
BOOST_SPIRIT_DEBUG_NODES(
(tag)(header)(quoted_string)(regular_move)(single_move)
(full_move)(game_description)(single_game)(games)
)
}
private:
qi::rule<Iterator, pgn_tag(), qi::space_type> tag;
qi::rule<Iterator, std::vector<pgn_tag>, qi::space_type> header;
qi::rule<Iterator, move_t(), qi::space_type> full_move;
qi::rule<Iterator, std::vector<move_t>, qi::space_type> game_description;
qi::rule<Iterator, game_t(), qi::space_type> single_game;
qi::rule<Iterator, pgn_t(), qi::space_type> games;
// lexemes
qi::symbols<char, result_t> result;
qi::rule<Iterator, std::string()> quoted_string;
qi::rule<Iterator> regular_move;
qi::rule<Iterator, std::string()> single_move;
};
}
loloof64::PgnGamesExtractor::PgnGamesExtractor(std::string inputFilePath) {
std::ifstream inputFile(inputFilePath);
parseInput(inputFile);
}
loloof64::PgnGamesExtractor::PgnGamesExtractor(std::istream &inputFile) { parseInput(inputFile); }
loloof64::PgnGamesExtractor::~PgnGamesExtractor() {
// dtor
}
void loloof64::PgnGamesExtractor::parseInput(std::istream &inputFile) {
if (inputFile.fail() || inputFile.bad())
throw new InputFileException("Could not read the input file !");
typedef boost::spirit::istream_iterator It;
loloof64::pgn_parser<It> parser;
std::vector<loloof64::pgn_game> temp_games;
It iter(inputFile >> std::noskipws), end;
//////////////////////////////////
std::cout << "About to parse the file" << std::endl;
//////////////////////////////////
bool success = boost::spirit::qi::phrase_parse(iter, end, parser, boost::spirit::qi::space, temp_games);
//////////////////////////////////
std::cout << "Finished to parse the file" << std::endl;
//////////////////////////////////
if (success && iter == end) {
games.swap(temp_games);
} else {
std::string error_fragment(iter, end);
throw PgnParsingException("Failed to parse the input at :'" + error_fragment + "' !");
}
}
和頭文件: header 。
#ifndef PGNGAMESEXTRACTOR_HPP
#define PGNGAMESEXTRACTOR_HPP
#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
namespace loloof64 {
namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi;
/*
* This class has been taken from http://marko-editor.com/articles/position_tracking/
*/
template<typename Iterator>
struct CurrentPos {
CurrentPos() {
save_start_pos = qi::omit[boost::spirit::repository::qi::iter_pos[
phx::bind(&CurrentPos::setStartPos, this, qi::_1)]];
current_pos = boost::spirit::repository::qi::iter_pos[
qi::_val = phx::bind(&CurrentPos::getCurrentPos, this, qi::_1)];
}
qi::rule<Iterator> save_start_pos;
qi::rule<Iterator, std::size_t()> current_pos;
private:
void setStartPos(const Iterator &iterator) {
start_pos_ = iterator;
}
std::size_t getCurrentPos(const Iterator &iterator) {
return std::distance(start_pos_, iterator);
}
Iterator start_pos_;
};
enum result_t { white_won, black_won, draw, undecided };
struct pgn_tag {
std::string key;
std::string value;
};
struct game_move {
unsigned move_number;
std::string white_move;
std::string black_move;
result_t result;
};
struct pgn_game {
std::vector<pgn_tag> header;
std::vector<game_move> moves;
};
class PgnGamesExtractor {
public:
PgnGamesExtractor(std::string inputFilePath);
PgnGamesExtractor(std::istream &inputFile);
/*
Both constructos may throw PgnParsingException (if bad pgn format) and InputFileException (if missing file)
*/
std::vector<pgn_game> getGames() const { return games; }
virtual ~PgnGamesExtractor();
protected:
private:
std::vector<pgn_game> games;
void parseInput(std::istream &inputFile);
};
class PgnParsingException : public virtual std::runtime_error {
public:
PgnParsingException(std::string message) : std::runtime_error(message) {}
};
class InputFileException : public virtual std::runtime_error {
public:
InputFileException(std::string message) : std::runtime_error(message) {}
};
}
#endif // PGNGAMESEXTRACTOR_HPP
我沒有發布編譯錯誤,因為有太多錯誤並且可以輕松測試文件。
當然,它不能與流接口一起很好地工作。 您可以保留啟動迭代器,但是
您將不會提前知道流的長度(除非您將其帶外)
每次計算當前位置(與起始迭代器的距離)都會非常低效。
既然您在注釋中提到過您正在解析文件,則應考慮使用內存映射(例如boost::iostream::mapped_file_source
或mmap
)。 這樣,在隨機訪問迭代器上使用指針算法即可立即進行距離計算。
這是一個工作示例,具有以下更改/注釋:
save_start_pos
omit[]
是無用的(沒有聲明的屬性) getCurrentPos
效率低下(以至於在full_move
規則中僅使用omit[current_pos]
full_move
解析速度降低了幾個數量級。
這是因為
boost::spirit::istream_iterator
以雙端隊列保持所有先前讀取的狀態,並且在執行std::distance
時遍歷它們並不是免費的
您的CurrentPos<Iterator> filepos;
實例在構建后超出范圍! 這意味着調用save_start_pos
/ current_pos
是未定義行為 ¹。 將其移出構造函數。
當您添加語義操作時, full_move %= ...
一點是使用full_move %= ...
(請參閱docs和blog )
您更改了某些規則的類型,以在AST類型旁邊包含位置信息。 這既沒有必要,也有缺陷:AST類型與規則的tuple<size_t, T>
版本不兼容。
此外,例如, games
規則甚至都沒有公開位置,因為save_start_pos
合成未使用的unused_type
(無屬性)。
因此,刪除整個元組業務,然后在您的語義動作中處理filepos
成員的狀態:
full_move %= omit[filepos.current_pos [ reportProgress(_1) ]] >> uint_ >> (lexeme["..." >> attr(no_move)] | "." >> single_move) >> (single_move | attr(no_move)) >> -result;
最后,作為一個演示如何報告嚴格增加的進度指示²,我包括了一個簡單的鳳凰演員:
struct reportProgress_f { size_t total_; mutable double pct = 0.0; reportProgress_f(size_t total) : total_(total) {} template<typename T> void operator()(T pos) const { double newpct = pos * 100.0 / total_; if ((newpct - pct) > 10) { //sleep(1); // because it's way too fast otherwise... pct = newpct; std::cerr << "\\rProgress " << std::fixed << std::setprecision(1) << pct << std::flush; }; } }; phx::function<reportProgress_f> reportProgress;
注意
reportProgress
需要使用有關開始和結束迭代器的知識來構造,請參見pgn_parser
的構造pgn_parser
¹在錄制的實時流中,您可以看到我在第一次閱讀時發現了錯誤,然后在進行編譯后就忘記了。 該程序崩潰,盡職:)然后我想起來了。
²即使面對回溯
3 (並非嚴格要求,但我想目標不是要使其變得如此緩慢以至於您實際上需要進度指示器嗎?)
#ifndef PGNGAMESEXTRACTOR_HPP
#define PGNGAMESEXTRACTOR_HPP
#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
namespace loloof64 {
namespace phx = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace qr = boost::spirit::repository::qi;
/*
* This class has been taken from http://marko-editor.com/articles/position_tracking/
*/
template<typename Iterator>
struct CurrentPos {
CurrentPos() {
save_start_pos = qr::iter_pos [phx::bind(&CurrentPos::setStartPos, this, qi::_1)] >> qi::eps;
current_pos = qr::iter_pos [qi::_val = phx::bind(&CurrentPos::getCurrentPos, this, qi::_1)] >> qi::eps;
}
qi::rule<Iterator> save_start_pos;
qi::rule<Iterator, std::size_t()> current_pos;
private:
void setStartPos(const Iterator &iterator) {
start_pos_ = iterator;
}
std::size_t getCurrentPos(const Iterator &iterator) {
return std::distance(start_pos_, iterator);
}
Iterator start_pos_;
};
enum result_t { white_won, black_won, draw, undecided };
struct pgn_tag {
std::string key;
std::string value;
};
struct game_move {
unsigned move_number;
std::string white_move;
std::string black_move;
result_t result;
};
struct pgn_game {
std::vector<pgn_tag> header;
std::vector<game_move> moves;
};
class PgnGamesExtractor {
public:
PgnGamesExtractor(std::string const& inputFilePath);
/*
Both constructos may throw PgnParsingException (if bad pgn format) and InputFileException (if missing file)
*/
std::vector<pgn_game> getGames() const { return games; }
virtual ~PgnGamesExtractor();
protected:
private:
std::vector<pgn_game> games;
void parseInput(std::string const&);
};
class PgnParsingException : public virtual std::runtime_error {
public:
PgnParsingException(std::string message) : std::runtime_error(message) {}
};
class InputFileException : public virtual std::runtime_error {
public:
InputFileException(std::string message) : std::runtime_error(message) {}
};
}
#endif // PGNGAMESEXTRACTOR_HPP
//#include "pgn_games_extractor.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <iomanip>
BOOST_FUSION_ADAPT_STRUCT(loloof64::pgn_tag, key, value)
BOOST_FUSION_ADAPT_STRUCT(loloof64::game_move, move_number, white_move, black_move, result)
BOOST_FUSION_ADAPT_STRUCT(loloof64::pgn_game, header, moves)
namespace loloof64 {
namespace qi = boost::spirit::qi;
template <typename Iterator> struct pgn_parser : qi::grammar<Iterator, std::vector<pgn_game>(), qi::space_type> {
pgn_parser(Iterator start, Iterator end)
: pgn_parser::base_type(games),
reportProgress(std::distance(start, end))
{
using namespace qi;
const std::string no_move;
result.add
("1-0", result_t::white_won)
("0-1", result_t::black_won)
("1/2-1/2", result_t::draw)
("*", result_t::undecided);
quoted_string = '"' >> *~char_('"') >> '"';
tag = '[' >> +alnum >> quoted_string >> ']';
header = +tag;
regular_move = lit("O-O-O") | "O-O" | (+char_("a-hNBRQK") >> +char_("a-h1-8x=NBRQK") >> -lit("e.p."));
single_move = raw [ regular_move >> -char_("+#") ];
full_move %=
omit[filepos.current_pos [ reportProgress(_1) ]] >>
uint_
>> (lexeme["..." >> attr(no_move)] | "." >> single_move)
>> (single_move | attr(no_move))
>> -result;
game_description = +full_move;
single_game = -header >> game_description;
games = filepos.save_start_pos >> *single_game;
BOOST_SPIRIT_DEBUG_NODES(
(tag)(header)(quoted_string)(regular_move)(single_move)
(full_move)(game_description)(single_game)(games)
)
}
private:
struct reportProgress_f {
size_t total_;
mutable double pct = 0.0;
reportProgress_f(size_t total) : total_(total) {}
template<typename T>
void operator()(T pos) const {
double newpct = pos * 100.0 / total_;
if ((newpct - pct) > 10) {
//sleep(1); // because it's way too fast otherwise...
pct = newpct;
std::cerr << "\rProgress " << std::fixed << std::setprecision(1) << pct << " " << std::flush;
};
}
};
phx::function<reportProgress_f> reportProgress;
CurrentPos<Iterator> filepos;
qi::rule<Iterator, pgn_tag(), qi::space_type> tag;
qi::rule<Iterator, std::vector<pgn_tag>, qi::space_type> header;
qi::rule<Iterator, game_move(), qi::space_type> full_move;
qi::rule<Iterator, std::vector<game_move>, qi::space_type> game_description;
qi::rule<Iterator, pgn_game(), qi::space_type> single_game;
qi::rule<Iterator, std::vector<pgn_game>(), qi::space_type> games;
// lexemes
qi::symbols<char, result_t> result;
qi::rule<Iterator, std::string()> quoted_string;
qi::rule<Iterator> regular_move;
qi::rule<Iterator, std::string()> single_move;
};
}
#include <boost/iostreams/device/mapped_file.hpp>
loloof64::PgnGamesExtractor::~PgnGamesExtractor() {
// dtor
}
loloof64::PgnGamesExtractor::PgnGamesExtractor(std::string const& inputFilePath) {
parseInput(inputFilePath);
}
void loloof64::PgnGamesExtractor::parseInput(std::string const& inputFilePath) {
boost::iostreams::mapped_file_source mf(inputFilePath);
//if (inputFile.fail() || inputFile.bad())
//throw new InputFileException("Could not read the input file !");
typedef char const* It;
std::vector<loloof64::pgn_game> temp_games;
/* It iter(inputFile >> std::noskipws), end; */
auto iter = mf.begin();
auto end = mf.end();
loloof64::pgn_parser<It> parser(iter, end);
//////////////////////////////////
//std::cout << "About to parse the file" << std::endl;
//////////////////////////////////
bool success = boost::spirit::qi::phrase_parse(iter, end, parser, boost::spirit::qi::space, temp_games);
//////////////////////////////////
//std::cout << "Finished to parse the file" << std::endl;
//////////////////////////////////
if (success && iter == end) {
games.swap(temp_games);
} else {
std::string error_fragment(iter, end);
throw PgnParsingException("Failed to parse the input at :'" + error_fragment + "' !");
}
}
int main() {
loloof64::PgnGamesExtractor pge("ScotchGambit.pgn");
std::cout << "Parsed " << pge.getGames().size() << " games\n";
for (auto& g : pge.getGames())
for (auto& m : g.moves)
std::cout << m.move_number << ".\t" << m.white_move << "\t" << m.black_move << "\n";
}
帶樣品輸出
Progress 32.6
Progress 44.5
Progress 55.5
Progress 67.2
Progress 77.2
Progress 89.1
Progress 100.0Parsed 1 games
1. e4 e5
2. Nf3 Nc6
3. d4 exd4
4. Bc4 Qf6
5. O-O d6
6. Ng5 Nh6
7. f4 Be7
8. e5 Qg6
9. exd6 cxd6
10. c3 dxc3
11. Nxc3 O-O
12. Nd5 Bd7
13. Rf3 Bg4
14. Bd3 Bxf3
15. Qxf3 f5
16. Bc4 Kh8
17. Nxe7 Nxe7
18. Qxb7 Qf6
19. Be3 Rfb8
20. Qd7 Rd8
21. Qb7 d5
22. Bb3 Nc6
23. Bxd5 Nd4
24. Rd1 Ne2+
25. Kf1 Rab8
26. Qxa7 Rxb2
27. Ne6 Qxe6
28. Bxe6 Rxd1+
29. Kf2
請注意,在終端上,進度指示將使用回車鍵進行自我更新,而不是打印單獨的行
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.