/// <summary> /// Constructor the initializes our parser. /// </summary> public PGNReader() { _Regex = new Regex("^\\[([A-Za-z]*) \"(.*)\"", RegexOptions.Compiled); _Value = new StringBuilder(); _State = Enums.PGNReaderState.Header; _SaveState = new Stack(); _PeriodCount = 0; }
/// <summary> /// Calls the correct event based on the parsers state. /// </summary> /// <param name="state"></param> private void CallEvent(Enums.PGNReaderState state) { if (_Value.Length > 0) { switch (state) { case Enums.PGNReaderState.Comment: if (EventCommentParsed != null) { EventCommentParsed(this); } State = _PrevState; break; case Enums.PGNReaderState.Nags: if (EventNagParsed != null) { EventNagParsed(this); } State = _PrevState; break; case Enums.PGNReaderState.ConvertNag: string nag = _Value.ToString(); _Value.Length = 0; if (nag.Equals("!")) { _Value.Append('1'); } else if (nag.Equals("?")) { _Value.Append('2'); } else if (nag.Equals("!!")) { _Value.Append('3'); } else if (nag.Equals("??")) { _Value.Append('4'); } else if (nag.Equals("!?")) { _Value.Append('5'); } else if (nag.Equals("?!")) { _Value.Append('6'); } else { _Value.Append('0'); } if (EventNagParsed != null) { EventNagParsed(this); } State = _PrevState; break; case Enums.PGNReaderState.Number: if (EventMoveParsed != null) { EventMoveParsed(this); } State = Enums.PGNReaderState.Color; break; case Enums.PGNReaderState.White: if (EventMoveParsed != null) { EventMoveParsed(this); } State = Enums.PGNReaderState.Black; break; case Enums.PGNReaderState.Black: if (EventMoveParsed != null) { EventMoveParsed(this); } State = Enums.PGNReaderState.Number; break; case Enums.PGNReaderState.EndMarker: if (EventendMarkerParsed != null) { EventendMarkerParsed(this); } State = Enums.PGNReaderState.Header; break; } } // Always clear out our data as the handler should have used this value during the event. _Value.Length = 0; }
/// <summary> /// Handles individual charaters which may signal a change in the /// parser's state. /// </summary> /// <param name="aChar"></param> private void HandleChar(char aChar) { switch (aChar) { case '{': CallEvent(State); _PrevState = State; State = Enums.PGNReaderState.Comment; break; case '(': if (EventEnterVariation != null) { EventEnterVariation(this); } _Value.Length = 0; _SaveState.Push(State); break; case ')': if (EventExitVariation != null) { EventExitVariation(this); } _Value.Length = 0; State = (Enums.PGNReaderState)_SaveState.Pop(); break; case ' ': // Only if we have some data do we want to fire an event. CallEvent(State); break; case '.': // We may have come across 6. e4 6... d5 as in our example data. State = Enums.PGNReaderState.Number; CallEvent(State); _PeriodCount = 1; break; case '$': CallEvent(State); _PrevState = State; State = Enums.PGNReaderState.Nags; break; case '!': case '?': if (State != Enums.PGNReaderState.ConvertNag) { CallEvent(State); _PrevState = State; State = Enums.PGNReaderState.ConvertNag; } _Value.Append(aChar); break; case '-': if (State != Enums.PGNReaderState.EndMarker && _Value.Length >= 1) { if ("012".IndexOf(_Value[_Value.Length - 1]) >= 0) { State = Enums.PGNReaderState.EndMarker; } } _Value.Append(aChar); break; case '*': State = Enums.PGNReaderState.EndMarker; _Value.Append(aChar); break; case '\t': break; default: _Value.Append(aChar); break; } }
/// <summary> /// Handles the parsing of the actual game notation of the PGN file. /// </summary> /// <param name="line"></param> public void ParseDetail(string line) { foreach (char aChar in line) { // Handle any special processing of our text. switch (_State) { case Enums.PGNReaderState.Comment: if (aChar == '}') { CallEvent(_State); } else { _Value.Append(aChar); } break; case Enums.PGNReaderState.Nags: if (aChar >= '0' && aChar <= '9') { _Value.Append(aChar); } else { CallEvent(_State); HandleChar(aChar); } break; case Enums.PGNReaderState.Color: if (aChar == '.') { _PeriodCount++; } else { _Value.Length = 0; if (_PeriodCount == 1) { _State = Enums.PGNReaderState.White; } else if (_PeriodCount > 1) { _State = Enums.PGNReaderState.Black; } HandleChar(aChar); _PeriodCount = 0; } break; default: HandleChar(aChar); break; } } // Ensure we add a space between comment lines that are broken appart. if (_State == Enums.PGNReaderState.Comment) { _Value.Append(' '); } else { CallEvent(_State); } }
/// <summary> /// Responsible for the main driver loop of the parser. Here we read /// in the PGN file and produce events for the listening parties. /// </summary> public void Parse() { StringBuilder builder = new StringBuilder(1024); StreamReader reader = null; if (EventStarting != null) { EventStarting(this); } try { Encoding fileEncoding = TextFileEncoding.GetEncoding(_filename); reader = new StreamReader(_filename, fileEncoding); while (!reader.EndOfStream) { #region while char aChar = (char)reader.Read(); switch (aChar) { #region case case '\n': _Data = builder.ToString(); builder.Length = 0; if (_Data.Length > 0) { if (_State != Enums.PGNReaderState.Comment && Regex.IsMatch(_Data, "^\\[")) { if (_NextGame == false) { CallEvent(_State); _NextGame = true; if (EventNewGame != null) { EventNewGame(this); } } _State = Enums.PGNReaderState.Header; ParseTag(_Data); _Value.Length = 0; } else { if (_NextGame) { _NextGame = false; if (EventExitHeader != null) { EventExitHeader(this); } } ParseDetail(_Data); } } break; case '\r': break; default: builder.Append(aChar); if (reader.EndOfStream) { goto case '\n'; } break; #endregion } #endregion }//while CallEvent(_State); } catch (ApplicationException) { throw; } finally { if (reader != null) { reader.Close(); reader = null; } } if (EventFinished != null) { EventFinished(this); } }
/// <summary> /// Handles individual charaters which may signal a change in the /// parser's state. /// </summary> /// <param name="aChar"></param> private void HandleChar(char aChar) { switch (aChar) { case '{': CallEvent(_State); _PrevState = _State; _State = Enums.PGNReaderState.Comment; break; case '(': if (EventEnterVariation != null) EventEnterVariation(this); _Value.Length = 0; _SaveState.Push(_State); break; case ')': if (EventExitVariation != null) EventExitVariation(this); _Value.Length = 0; _State = (Enums.PGNReaderState)_SaveState.Pop(); break; case ' ': // Only if we have some data do we want to fire an event. CallEvent(_State); break; case '.': // We may have come across 6. e4 6... d5 as in our example data. _State = Enums.PGNReaderState.Number; CallEvent(_State); _PeriodCount = 1; break; case '$': CallEvent(_State); _PrevState = _State; _State = Enums.PGNReaderState.Nags; break; case '!': case '?': if (_State != Enums.PGNReaderState.ConvertNag) { CallEvent(_State); _PrevState = _State; _State = Enums.PGNReaderState.ConvertNag; } _Value.Append(aChar); break; case '-': if (_State != Enums.PGNReaderState.EndMarker && _Value.Length >= 1) { if ("012".IndexOf(_Value[_Value.Length - 1]) >= 0) _State = Enums.PGNReaderState.EndMarker; } _Value.Append(aChar); break; case '*': _State = Enums.PGNReaderState.EndMarker; _Value.Append(aChar); break; case '\t': break; default: _Value.Append(aChar); break; } }
/// <summary> /// Calls the correct event based on the parsers state. /// </summary> /// <param name="state"></param> private void CallEvent(Enums.PGNReaderState state) { if (_Value.Length > 0) { switch (state) { case Enums.PGNReaderState.Comment: if (EventCommentParsed != null) EventCommentParsed(this); _State = _PrevState; break; case Enums.PGNReaderState.Nags: if (EventNagParsed != null) EventNagParsed(this); _State = _PrevState; break; case Enums.PGNReaderState.ConvertNag: string nag = _Value.ToString(); _Value.Length = 0; if (nag.Equals("!")) _Value.Append('1'); else if (nag.Equals("?")) _Value.Append('2'); else if (nag.Equals("!!")) _Value.Append('3'); else if (nag.Equals("??")) _Value.Append('4'); else if (nag.Equals("!?")) _Value.Append('5'); else if (nag.Equals("?!")) _Value.Append('6'); else _Value.Append('0'); if (EventNagParsed != null) EventNagParsed(this); _State = _PrevState; break; case Enums.PGNReaderState.Number: if (EventMoveParsed != null) EventMoveParsed(this); _State = Enums.PGNReaderState.Color; break; case Enums.PGNReaderState.White: if (EventMoveParsed != null) EventMoveParsed(this); _State = Enums.PGNReaderState.Black; break; case Enums.PGNReaderState.Black: if (EventMoveParsed != null) EventMoveParsed(this); _State = Enums.PGNReaderState.Number; break; case Enums.PGNReaderState.EndMarker: if (EventendMarkerParsed != null) EventendMarkerParsed(this); _State = Enums.PGNReaderState.Header; break; } } // Always clear out our data as the handler should have used this value during the event. _Value.Length = 0; }
/// <summary> /// Handles the parsing of the actual game notation of the PGN file. /// </summary> /// <param name="line"></param> public void ParseDetail(string line) { foreach (char aChar in line) { // Handle any special processing of our text. switch (_State) { case Enums.PGNReaderState.Comment: if (aChar == '}') { CallEvent(_State); } else _Value.Append(aChar); break; case Enums.PGNReaderState.Nags: if (aChar >= '0' && aChar <= '9') _Value.Append(aChar); else { CallEvent(_State); HandleChar(aChar); } break; case Enums.PGNReaderState.Color: if (aChar == '.') { _PeriodCount++; } else { _Value.Length = 0; if (_PeriodCount == 1) { _State = Enums.PGNReaderState.White; } else if (_PeriodCount > 1) { _State = Enums.PGNReaderState.Black; } HandleChar(aChar); _PeriodCount = 0; } break; default: HandleChar(aChar); break; } } // Ensure we add a space between comment lines that are broken appart. if (_State == Enums.PGNReaderState.Comment) _Value.Append(' '); else CallEvent(_State); }
/// <summary> /// Responsible for the main driver loop of the parser. Here we read /// in the PGN file and produce events for the listening parties. /// </summary> public void Parse() { StringBuilder builder = new StringBuilder(1024); StreamReader reader = null; if (EventStarting != null) EventStarting(this); try { Encoding fileEncoding = TextFileEncoding.GetEncoding(_filename); reader = new StreamReader(_filename, fileEncoding); while (!reader.EndOfStream) { #region while char aChar = (char)reader.Read(); switch (aChar) { #region case case '\n': _Data = builder.ToString(); builder.Length = 0; if (_Data.Length > 0) { if (_State != Enums.PGNReaderState.Comment && Regex.IsMatch(_Data, "^\\[")) { if (_NextGame == false) { CallEvent(_State); _NextGame = true; if (EventNewGame != null) { EventNewGame(this); } } _State = Enums.PGNReaderState.Header; ParseTag(_Data); _Value.Length = 0; } else { if (_NextGame) { _NextGame = false; if (EventExitHeader != null) { EventExitHeader(this); } } ParseDetail(_Data); } } break; case '\r': break; default: builder.Append(aChar); if (reader.EndOfStream) { goto case '\n'; } break; #endregion } #endregion }//while CallEvent(_State); } catch (ApplicationException) { throw; } finally { if (reader != null) { reader.Close(); reader = null; } } if (EventFinished != null) EventFinished(this); }