PgnDatabase.cc
Go to the documentation of this file.
1 // cwchessboard -- A C++ chessboard tool set
2 //
3 //! @file PgnDatabase.cc This file contains the implementation of class pgn::Database.
4 //
5 // Copyright (C) 2010, by
6 //
7 // Carlo Wood, Run on IRC <carlo@alinoe.com>
8 // RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt
9 // Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 2 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 
24 #ifdef CWDEBUG
25 #define BOOST_SPIRIT_DEBUG
26 #endif
27 
28 #ifndef USE_PCH
29 #include "sys.h"
30 #include <cstring>
31 #include <ctime> // Needed for clock_gettime.
32 #include <iomanip>
33 #include "debug.h"
34 #include <glib.h>
35 #ifdef CWDEBUG
36 #include <libcwd/buf2str.h>
37 #endif
38 #endif
39 
40 #include "PgnDatabase.h"
41 #include "PgnGrammar.h"
42 #include "chattr.h"
43 #include "Color.h"
44 
45 namespace cwchess {
46 namespace pgn {
47 
48 void Database::process_next_data_block(char const* data, size_t size)
49 {
50 }
51 
52 void DatabaseSeekable::load(void)
53 {
54  if (!Glib::thread_supported())
55  DoutFatal(dc::fatal, "DatabaseSeekable::load: Threading not initialized. Call Glib::init_thread() at the start of main().");
56  M_file->read_async(sigc::mem_fun(this,& DatabaseSeekable::read_async_open_ready), M_cancellable);
57 }
58 
59 void DatabaseSeekable::read_async_open_ready(Glib::RefPtr<Gio::AsyncResult>& result)
60 {
61  M_file_input_stream = M_file->read_finish(result);
62  M_buffer = new MemoryBlockList(sigc::mem_fun(*this,& DatabaseSeekable::need_more_data));
63  M_read_thread = Glib::Thread::create(sigc::mem_fun(*this,& DatabaseSeekable::read_thread), false);
64  M_new_block = MemoryBlockNode::create(S_buffer_size);
65  // We're using the glib API for the inner loop in order to avoid unnecessary calls to new/delete.
66  GInputStream* stream = M_file_input_stream->InputStream::gobj();
67  g_input_stream_read_async(stream, M_new_block->block_begin(), S_buffer_size,
68  G_PRIORITY_DEFAULT, M_cancellable->gobj(),& DatabaseSeekable::read_async_ready, this);
69  M_processing_finished.connect(sigc::mem_fun(*this,& DatabaseSeekable::processing_finished));
70 }
71 
72 void DatabaseSeekable::need_more_data(void)
73 {
74  M_new_block = MemoryBlockNode::create(S_buffer_size);
75  g_input_stream_read_async(M_file_input_stream->InputStream::gobj(), M_new_block->block_begin(), S_buffer_size,
76  G_PRIORITY_DEFAULT, M_cancellable->gobj(),& DatabaseSeekable::read_async_ready, this);
77 }
78 
79 // The inner loop.
80 inline void DatabaseSeekable::read_async_ready(GObject* source_object, GAsyncResult* async_res)
81 {
82  GInputStream* stream = M_file_input_stream->InputStream::gobj();
83  GError* error = NULL;
84  gssize len = g_input_stream_read_finish(stream, async_res,& error);
85  if (len == -1)
86  DoutFatal(dc::core, "read_finish() returned -1");
87  if (len > 0)
88  {
89  M_bytes_read += len;
90  Dout(dc::notice, "Appending a block with " << len << " bytes to the buffer.");
91  // This is the only place where append is called, which is the only
92  // function that increments MemoryBlockList::M_blocks.
93  M_buffer->append(M_new_block, len);
94  }
95  else
96  {
97  Dout(dc::notice, "g_input_stream_read_finish() returned 0. Closing buffer.");
98  // If we get here, we're completely done reading the file.
99  // Hence, the read thread is the only thread accessing the buffer and
100  // we can allow it to process the last block, after which it
101  // will destroy the buffer.
102  M_buffer->close();
103  }
104 }
105 
106 void DatabaseSeekable::read_async_ready(GObject* source_object, GAsyncResult* async_res, gpointer user_data)
107 {
108  DatabaseSeekable* database_seekable = reinterpret_cast<DatabaseSeekable*>(user_data);
109  database_seekable->read_async_ready(source_object, async_res);
110 }
111 
112 DatabaseSeekable::~DatabaseSeekable()
113 {
114  // Just in case. Normally this should already be freed after loading of the database finished.
115  if (M_buffer)
116  delete M_buffer;
117 }
118 
119 namespace {
120 
121 timespec& operator-=(timespec& t1, timespec const& t2)
122 {
123  t1.tv_sec -= t2.tv_sec;
124  t1.tv_nsec -= t2.tv_nsec;
125  if (t1.tv_nsec < 0)
126  {
127  --t1.tv_sec;
128  t1.tv_nsec += 1000000000L;
129  }
130  return t1;
131 }
132 
133 timespec& operator+=(timespec& t1, timespec const& t2)
134 {
135  t1.tv_sec += t2.tv_sec;
136  t1.tv_nsec += t2.tv_nsec;
137  if (t1.tv_nsec > 999999999L)
138  {
139  ++t1.tv_sec;
140  t1.tv_nsec -= 1000000000L;
141  }
142  return t1;
143 }
144 
145 std::ostream& operator<<(std::ostream& os, timespec const& t1)
146 {
147  return os << t1.tv_sec << '.' << std::setfill('0') << std::setw(9) << t1.tv_nsec;
148 }
149 
150 } // namespace
151 
152 //
153 // The read thread.
154 //
155 // The code below belongs to a different thread. It reads and processes the buffer.
156 //
157 
158 #define DEBUG_PARSER 0
159 
160 //! @brief Variable data of a Scanner.
161 template<class ForwardIterator>
162 struct ScannerData {
163  ForwardIterator* M_iter; //!< The current position.
164  unsigned int M_line; //!< The current line number, starts at 1.
165  unsigned int M_column; //!< The current column, starts at 0.
166  unsigned int M_number_of_characters; //!< The number of characters before the current position.
167  Color M_to_move; //!< The color that is too move at this point in the game.
168 #if DEBUG_PARSER
169  ForwardIterator M_line_start; //!< Pointer to the start of the current line.
170 
171  ScannerData(ForwardIterator* iter) : M_iter(iter), M_line(1), M_column(0), M_number_of_characters(0), M_line_start(iter->buffer()) { }
172 #else
173  void init(ForwardIterator* iter)
174  {
175  M_iter = iter;
176  M_line = 1;
177  M_column = 0;
178  M_number_of_characters = 0;
179  }
180 #endif
181 };
182 
183 class EndOfFileReached : public std::exception {
184 };
185 
186 class ParseError : public std::exception {
187 };
188 
189 static EndOfFileReached const end_of_file_reached;
190 
191 //! @brief A class used to read input from a PGN database.
192 template<class ForwardIterator>
193 class Scanner {
194  private:
195  ScannerData<ForwardIterator> M_current_position; //!< The current position.
196  ForwardIterator const M_end; //!< The one-past-the-end position.
197  std::vector<ScannerData<ForwardIterator> > M_stack; //!< A stack of stored positions.
198  int M_stack_index; //!< Next free place on stack.
199  public:
200  //! @brief Construct a Scanner object.
201  //
202  // @param iter : Iterator to the first character.
203  // @param iter : Iterator one-past-the-end.
204  Scanner(ForwardIterator& iter, ForwardIterator const end) :
205 #if DEBUG_PARSER
206  M_current_position(&iter),
207 #endif
208  M_end(end), M_stack_index(0)
209 #if DEBUG_PARSER
210  { }
211 #else
212  { M_current_position.init(&iter); }
213 #endif
214 
215  int push_position(void)
216  {
217  size_t size = M_stack.size();
218  if (G_UNLIKELY(size <= M_stack_index))
219  M_stack.push_back(M_current_position);
220  else
221  M_stack[M_stack_index] = M_current_position;
222  return M_stack_index++;
223  }
224 
225  void pop_position(int index)
226  {
227  M_current_position = M_stack[index];
228  M_stack_index = index;
229  }
230 
231 #if DEBUG_PARSER
232  //! @brief A debug helper routine.
233  //
234  // This function is intended for debugging only.
235  // It writes to debug channel dc::parser and escapes non-printable characters.
236  void print_line(void)
237  {
238  std::string s(M_current_position.M_line_start,* M_current_position.M_iter);
239  Dout(dc::parser, "Parsed: \"" << buf2str(s.data(), s.length()) << "\".");
240  }
241 #endif
242 
243  //! @brief Return the first character.
244  //
245  // This function must be called directly after creation of the Scanner object,
246  // before calling any of the other member functions. It should only be called
247  // once.
248  //
249  // @returns The first character.
250  typename ForwardIterator::value_type first_character(void) throw(EndOfFileReached)
251  {
252  if (G_UNLIKELY(*M_current_position.M_iter == M_end))
253  throw end_of_file_reached;
254  M_current_position.M_number_of_characters = 1;
255 #if DEBUG_PARSER
256  M_current_position.M_line_start =* M_current_position.M_iter;
257 #endif
258  return** M_current_position.M_iter;
259  }
260 
261  //! @brief Make the next character the current character.
262  //
263  // @returns The new current character.
264  typename ForwardIterator::value_type next_character(void) //throw(EndOfFileReached)
265  {
266  if (G_UNLIKELY(++*M_current_position.M_iter == M_end))
267  {
268 #if DEBUG_PARSER
269  print_line();
270 #endif
271 // throw end_of_file_reached;
272  }
273  ++M_current_position.M_column;
274  return** M_current_position.M_iter;
275  }
276 
277  //! @brief Eat all white space character, return the first non-white-space.
278  //
279  // @param c : A reference to the current character.
280  //
281  // Upon return, \a current_character will contain the first non-white-space character.
282  // If \a current_character is not a space upon entry, then the function does nothing.
283  void eat_white_space(typename ForwardIterator::value_type& current_character)
284  {
285  while (is_white_space(current_character))
286  {
287  if (G_UNLIKELY(is_eol(current_character)))
288  eat_eol(current_character);
289  else
290  current_character = next_character();
291  }
292  }
293 
294  //! @brief Eat all characters left in the current line up till but not including the EOL.
295  void eat_line(typename ForwardIterator::value_type& current_character)
296  {
297  while (!is_eol(current_character))
298  current_character = next_character();
299  }
300 
301  //! @brief Parse the next character and return true if it equals \a literal.
302  bool parse_char(typename ForwardIterator::value_type& current_character, char literal)
303  {
304  current_character = next_character();
305  if (current_character != literal)
306  return false;
307  current_character = next_character();
308  return true;
309  }
310 
311  //! @brief Return true if the string after the current character matches \a literal.
312  bool parse_str(typename ForwardIterator::value_type& current_character, char const* literal)
313  {
314  current_character = next_character();
315  for (char const* p = literal;* p; ++p)
316  {
317  if (current_character !=* p)
318  return false;
319  current_character = next_character();
320  }
321  return true;
322  }
323 
324  //! @brief Eat a single comment, if any.
325  //
326  // This function should only ever be called directly after
327  // a call to eat_white_space.
328  //
329  // @returns True if a comment was eaten.
330  bool eat_comment(typename ForwardIterator::value_type& current_character)
331  {
332 #if DEBUG_PARSER
333  assert(!is_white_space(current_character));
334 #endif
335  if (G_UNLIKELY(is_comment_start(current_character)))
336  {
337  if (current_character == '{')
338  {
339  while (current_character != '}')
340  current_character = next_character();
341  current_character = next_character();
342  }
343  else // current_character == ';'
344  {
345  eat_line(current_character);
346  eat_eol(current_character);
347  }
348  return true;
349  }
350  return false;
351  }
352 
353  //! Eat all white space and all comments encountered, if any.
354  void eat_white_space_and_comments(typename ForwardIterator::value_type& current_character)
355  {
356  eat_white_space(current_character);
357  while(eat_comment(current_character))
358  eat_white_space(current_character);
359  }
360 
361  //! @brief Eat one or more EOL sequences.
362  //
363  // The current position must be on an EOL character (is_eol(c) is true).
364  // This function also eats escaped lines (lines starting with a '%'),
365  // such lines are completely ignored and not counted as empty lines.
366  //
367  // @returns True if more than one EOL sequence was eaten.
368  bool eat_eol(typename ForwardIterator::value_type& current_character)
369  {
370 #if DEBUG_PARSER
371  assert(is_eol(current_character));
372 #endif
373  unsigned int line = M_current_position.M_line + 1;
374  do
375  {
376  ++M_current_position.M_line;
377  bool saw_carriage_return = (current_character == '\r');
378  current_character = next_character();
379  if (saw_carriage_return && current_character == '\n')
380  current_character = next_character();
381  if (current_character == '%')
382  {
383  eat_line(current_character);
384  ++line; // We don't count escaped lines as empty lines.
385  }
386  }
387  while (is_eol(current_character));
388 #if DEBUG_PARSER
389  print_line();
390  M_current_position.M_line_start =* M_current_position.M_iter;
391 #endif
392  M_current_position.M_number_of_characters += M_current_position.M_column;
393  M_current_position.M_column = 0;
394  return M_current_position.M_line > line;
395  }
396 
397  //! @brief Decodes a string.
398  //
399  // The current position must be a quote character.
400  // After this function returns, the current position
401  // is the character after the second quote.
402  void decode_string(typename ForwardIterator::value_type& current_character)
403  {
404  do
405  {
406  current_character = next_character();
407  }
408  while(current_character != '"');
409  // Eat closing quote.
410  current_character = next_character();
411  }
412 
413  //! @brief Return the current line number.
414  unsigned int line(void) const { return M_current_position.M_line; }
415  //! @brief Return the column.
416  unsigned int column(void) const { return M_current_position.M_column + 1; }
417  //! @brief Return the total number of characters parsed thus far.
418  //
419  // The current character is not counted.
420  unsigned int number_of_characters(void) const { return M_current_position.M_number_of_characters + M_current_position.M_column; }
421 
422  //! @brief Return who is expected to move at this moment.
423  Color to_move(void) const { return M_current_position.M_to_move; }
424 
425  //! @brief Reset the game state.
426  void reset_game_state(void)
427  {
428  M_current_position.M_to_move = white;
429  }
430 
431 #ifdef CWDEBUG
432  template<typename T>
433  friend std::ostream& operator<<(std::ostream& os, Scanner<T> const& scanner);
434 #endif
435  };
436 
437 #ifdef CWDEBUG
438 //! @brief Debug helper function.
439 template<typename T>
440 std::ostream& operator<<(std::ostream& os, Scanner<T> const& const_scanner)
441 {
442  Scanner<T> scanner(const_scanner);
443  if (*scanner.M_current_position.M_iter == scanner.M_end)
444  os << "<EOF>";
445  else
446  {
447  char c =** scanner.M_current_position.M_iter;
448  try
449  {
450  do
451  {
452  os << libcwd::char2str(c);
453  }
454  while (!is_eol(c = scanner.next_character()));
455  }
456  catch(EndOfFileReached&)
457  {
458  os << "<EOF>";
459  }
460  }
461  return os;
462 }
463 #endif
464 
466 
467 namespace {
468 
469 //! @brief Decode a tagname.
470 //
471 // A tagname must begin with an alpha-numeric character.
472 // The rest of the characters are either alpha-numberic or underscores.
473 //
474 // @returns True if a non-empty tagname was found.
475 inline bool decode_tagname(char& c, scanner_t& scanner)
476 {
477  if (G_UNLIKELY(!is_tagname_begin(c)))
478  return false;
479  while(is_tagname_continuation(c))
480  c = scanner.next_character();
481  return true;
482 }
483 
484 //! @brief Decode a string, if any.
485 //
486 // This function demands that the string is on one line: EOL characters are not allowed in the string.
487 //
488 // @returns True if a string was found and decoded.
489 inline bool correct_string(char& c, scanner_t& scanner)
490 {
491  if (c != '"')
492  return false;
493  // Eat the first quote.
494  c = scanner.next_character();
495  // Find the second quote, but also stop if we run into an EOL.
496  while(!is_quote_or_eol(c))
497  c = scanner.next_character();
498  // Note a correct string if we ran into an EOL.
499  if (c != '"')
500  return false;
501  // Eat the second quote.
502  c = scanner.next_character();
503  return true;
504 }
505 
506 //! @brief Decode a tag pair.
507 //
508 // The current position must be on a '['.
509 // @returns True if a correctly formatted tag pair was found and decoded.
510 inline bool correct_tag_pair(char& c, scanner_t& scanner)
511 {
512 #if DEBUG_PARSER
513  assert(c == '[');
514 #endif
515  // Skip the '['.
516  c = scanner.next_character();
517  scanner.eat_white_space(c);
518  if (G_UNLIKELY(!decode_tagname(c, scanner)))
519  return false;
520  scanner.eat_white_space(c);
521  if (G_UNLIKELY(!correct_string(c, scanner)))
522  return false;
523  scanner.eat_white_space(c);
524  if (G_UNLIKELY(c != ']'))
525  return false;
526  // Skip the ']'.
527  c = scanner.next_character();
528  return true;
529 }
530 
531 inline bool tag_pair(char& c, scanner_t& scanner)
532 {
533 #if DEBUG_PARSER
534  assert(c == '[');
535 #endif
536  // Skip the '['.
537  c = scanner.next_character();
538  scanner.eat_white_space_and_comments(c);
539  if (G_UNLIKELY(!decode_tagname(c, scanner)))
540  return false;
541  scanner.eat_white_space_and_comments(c);
542  if (G_UNLIKELY(is_tag_separator_junk(c)))
543  {
544  // Allow stupidity like [Annotator: "Me"], or [Result = "1-0"].
545  c = scanner.next_character();
546  scanner.eat_white_space_and_comments(c);
547  }
548  if (G_UNLIKELY(c != '"'))
549  return false;
550  scanner.decode_string(c);
551  scanner.eat_white_space_and_comments(c);
552  if (G_UNLIKELY(c != ']'))
553  return false;
554  // Skip the ']'.
555  c = scanner.next_character();
556  return true;
557 }
558 
559 bool decode_movetext_section_white(char& c, scanner_t& scanner) throw(ParseError)
560 {
561  throw ParseError();
562 }
563 
564 bool decode_movetext_section_black(char& c, scanner_t& scanner) throw(ParseError)
565 {
566  throw ParseError();
567 }
568 
569 bool decode_movetext_section(char& c, scanner_t& scanner)
570 {
571  if (scanner.to_move() == white)
572  return decode_movetext_section_white(c, scanner);
573  else
574  return decode_movetext_section_black(c, scanner);
575 }
576 
577 bool decode_game_termination(char& c, scanner_t& scanner)
578 {
579  char d = c;
580  c = scanner.next_character();
581  if (d == '0' && c == '-')
582  return scanner.parse_char(c, '1');
583  else if (d == '1' && c == '/')
584  return scanner.parse_str(c, "2-1/2");
585  return d == '*';
586 }
587 
588 } // namespace
589 
590 void DatabaseSeekable::read_thread(void)
591 {
592  Debug(debug::init_thread());
593  Dout(dc::notice, "DatabaseSeekable::read_thread started.");
594 
595  timespec start_time_real, end_time_real;
596  timespec start_time_process, end_time_process;
597  timespec start_time_thread, end_time_thread;
598 
599  clock_gettime(CLOCK_REALTIME,& start_time_real);
600  clock_gettime(CLOCK_PROCESS_CPUTIME_ID,& start_time_process);
601  clock_gettime(CLOCK_THREAD_CPUTIME_ID,& start_time_thread);
602 
603  scanner_t scanner(M_buffer->begin(), M_buffer->end());
604 
605  try
606  {
607  int PGN_game_start;
608  int PGN_movetext_section_start;
609 
610  bool saw_empty_line = true; // The start of the file has the same status as empty line.
611 
612  // Read first character if any.
613  char c = scanner.first_character();
614 
615  // Loop to find the start of the next game in case of parse errors.
616  for (;;)
617  {
618  // We're going to parse a new game. Start with resetting the game state.
619  scanner.reset_game_state();
620 
621  try
622  {
623 
624  //
625  // Start with eating leading junk.
626  //
627 
628  // Eat leading white spaces.
629  scanner.eat_white_space(c);
630 
631  do
632  {
633  if (c == '[')
634  {
635  PGN_game_start = scanner.push_position();
636 
637  // Demand a syntactically correct tag pair if we saw junk and there is no separating empty line before it.
638  // Otherwise our less restrictive tag pair parser is used.
639  if ((saw_empty_line && tag_pair(c, scanner)) ||
640  (!saw_empty_line && correct_tag_pair(c, scanner)))
641  {
642  // Found the start of a PGN game.
643  Dout(dc::parser, "After first tag pair of PGN game: " << scanner.line() << ':' << scanner.column());
644  break;
645  }
646  }
647  // Eat this whole line.
648  scanner.eat_line(c);
649  // Plus the EOL, and possible following empty lines.
650  saw_empty_line = scanner.eat_eol(c);
651  }
652  while(1);
653 
654  //
655  // We found the beginning of the first PGN file and parsed the first tag pair.
656  // Next, parse all remaining tag pairs.
657  //
658 
659  scanner.eat_white_space_and_comments(c);
660  while(c == '[')
661  {
662  if (G_UNLIKELY(!tag_pair(c, scanner)))
663  break;
664  scanner.eat_white_space_and_comments(c);
665  }
666 
667  // Decode optional movetext section.
668  if (c == '1')
669  {
670  // Because it is optional, we have to remember the position
671  // in case it fails. This could be a game termination (1-0 or 1/2-1/2).
672  PGN_movetext_section_start = scanner.push_position();
673  }
674  if (!decode_movetext_section(c, scanner))
675  {
676  // It failed, so pop the position.
677  scanner.pop_position(PGN_movetext_section_start);
678  c = '1';
679  }
680 
681  if (decode_game_termination(c, scanner))
682  {
683  // Eat any possible final comments.
684  scanner.eat_white_space_and_comments(c);
685 
686  // Since this was a clean exit, start processing of
687  // next game as if we just saw an empty line (we probably did anyway).
688  saw_empty_line = true;
689  continue;
690  }
691 
692  // Missing game termination, or parse error...
693  Dout(dc::parser, "Parsing stopped at " << scanner.line() << ':' << scanner.column() << " at \"" << scanner << "\".");
694  }
695  catch(ParseError&)
696  {
697  // Eat the rest until the next start of a PGN game.
698  continue;
699  }
700  break;
701  }
702  }
703  catch(EndOfFileReached&)
704  {
705  }
706 
707  clock_gettime(CLOCK_REALTIME,& end_time_real);
708  clock_gettime(CLOCK_PROCESS_CPUTIME_ID,& end_time_process);
709  clock_gettime(CLOCK_THREAD_CPUTIME_ID,& end_time_thread);
710 
711  end_time_real -= start_time_real;
712  end_time_process -= start_time_process;
713  end_time_thread -= start_time_thread;
714 
715 #if 0
716  if (!info.hit)
717  {
718  std::cout << "Failure to parse anything." << std::endl;
719  }
720  else
721  std::cout << info.length << " characters have been parsed successfully." << std::endl;
722 #endif
723 
724  std::cout << "Number of characters: " << scanner.number_of_characters() << '\n';
725  std::cout << "Number of lines: " << scanner.line() << '\n';
726 
727  std::cout << "Real time : " << end_time_real << " seconds.\n";
728  std::cout << "Process time : " << end_time_process << " seconds.\n";
729  std::cout << "Run time read_thread : " << end_time_thread << " seconds.\n";
730 
731  double t = end_time_thread.tv_sec + end_time_thread.tv_nsec * 1e-9;
732  std::cout << "Speed: " << (scanner.number_of_characters() / t / 1048576) << " MB/s." << std::endl;
733 
734  M_processing_finished.emit();
735 }
736 
737 void DatabaseSeekable::processing_finished(void)
738 {
739  assert(M_buffer->closed());
740  delete M_buffer;
741  M_buffer = NULL;
742  M_slot_open_finished(M_bytes_read);
743 }
744 
745 } // namespace pgn
746 } // namespace cwchess
This file contains the declaration of class PgnGrammar.
unsigned int M_line
The current line number, starts at 1.
Definition: PgnDatabase.cc:164
A namespace for all chess related objects that are not related to the GUI.
Definition: Array.h:39
This file contains the declaration of class pgn::Database.
Scanner(ForwardIterator& iter, ForwardIterator const end)
Construct a Scanner object.
Definition: PgnDatabase.cc:204
Glib::RefPtr< MemoryBlockNode > M_new_block
Definition: PgnDatabase.h:63
attr_t is_quote_or_eol(char c)
Test whether a character is a quote or EOL.
Definition: chattr.h:105
attr_t is_tagname_begin(char c)
Test whether a character could be the first character of a tagname.
Definition: chattr.h:97
ColorData const white
A constant representing the color white.
Definition: Color.h:55
ForwardIterator * M_iter
The current position.
Definition: PgnDatabase.cc:163
Character attribute definitions and arrays.
bool eat_eol(typename ForwardIterator::value_type& current_character)
Eat one or more EOL sequences.
Definition: PgnDatabase.cc:368
Color M_to_move
The color that is too move at this point in the game.
Definition: PgnDatabase.cc:167
unsigned int number_of_characters(void) const
Return the total number of characters parsed thus far.
Definition: PgnDatabase.cc:420
attr_t is_white_space(char c)
Test whether a character is white space.
Definition: chattr.h:79
void eat_white_space(typename ForwardIterator::value_type& current_character)
Eat all white space character, return the first non-white-space.
Definition: PgnDatabase.cc:283
ForwardIterator::value_type first_character(void)
Return the first character.
Definition: PgnDatabase.cc:250
bool eat_comment(typename ForwardIterator::value_type& current_character)
Eat a single comment, if any.
Definition: PgnDatabase.cc:330
unsigned int M_column
The current column, starts at 0.
Definition: PgnDatabase.cc:165
bool parse_str(typename ForwardIterator::value_type& current_character, char const* literal)
Return true if the string after the current character matches literal.
Definition: PgnDatabase.cc:312
unsigned int M_number_of_characters
The number of characters before the current position.
Definition: PgnDatabase.cc:166
unsigned int column(void) const
Return the column.
Definition: PgnDatabase.cc:416
Color to_move(void) const
Return who is expected to move at this moment.
Definition: PgnDatabase.cc:423
void decode_string(typename ForwardIterator::value_type& current_character)
Decodes a string.
Definition: PgnDatabase.cc:402
This file contains the declaration of class Color.
Variable data of a Scanner.
Definition: PgnDatabase.cc:162
unsigned int line(void) const
Return the current line number.
Definition: PgnDatabase.cc:414
ForwardIterator::value_type next_character(void)
Make the next character the current character.
Definition: PgnDatabase.cc:264
attr_t is_comment_start(char c)
Test whether a character is a comment start.
Definition: chattr.h:107
MemoryBlockList * M_buffer
Linked list of blocks with valid data.
Definition: PgnDatabase.h:62
A color (black or white).
Definition: Color.h:67
void process_next_data_block(char const* data, size_t size)
Process next data block.
Definition: PgnDatabase.cc:48
A class used to read input from a PGN database.
Definition: PgnDatabase.cc:193
void eat_line(typename ForwardIterator::value_type& current_character)
Eat all characters left in the current line up till but not including the EOL.
Definition: PgnDatabase.cc:295
void eat_white_space_and_comments(typename ForwardIterator::value_type& current_character)
Eat all white space and all comments encountered, if any.
Definition: PgnDatabase.cc:354
attr_t is_eol(char c)
Test whether a character is an end-of-line character.
Definition: chattr.h:77
attr_t is_tagname_continuation(char c)
Test whether a character could be part of a tagname.
Definition: chattr.h:99
void reset_game_state(void)
Reset the game state.
Definition: PgnDatabase.cc:426
bool parse_char(typename ForwardIterator::value_type& current_character, char literal)
Parse the next character and return true if it equals literal.
Definition: PgnDatabase.cc:302
attr_t is_tag_separator_junk(char c)
Test whether a character could tag separator junk.
Definition: chattr.h:101

Copyright © 2006 - 2010 Carlo Wood.  All rights reserved.