summaryrefslogtreecommitdiff
path: root/trunk/src/examples/pgn.py
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/examples/pgn.py')
-rw-r--r--trunk/src/examples/pgn.py94
1 files changed, 94 insertions, 0 deletions
diff --git a/trunk/src/examples/pgn.py b/trunk/src/examples/pgn.py
new file mode 100644
index 0000000..d13f83e
--- /dev/null
+++ b/trunk/src/examples/pgn.py
@@ -0,0 +1,94 @@
+# pgn.py rel. 1.1 17-sep-2004
+#
+# Demonstration of the parsing module, implementing a pgn parser.
+#
+# The aim of this parser is not to support database application,
+# but to create automagically a pgn annotated reading the log console file
+# of a lecture of ICC (Internet Chess Club), saved by Blitzin.
+# Of course you can modify the Abstract Syntax Tree to your purpose.
+#
+# Copyright 2004, by Alberto Santini http://www.albertosantini.it/chess/
+#
+from pyparsing import alphanums, nums, quotedString
+from pyparsing import Combine, Forward, Group, Literal, oneOf, OneOrMore, Optional, Suppress, ZeroOrMore, White, Word
+from pyparsing import ParseException
+
+#
+# define pgn grammar
+#
+
+tag = Suppress("[") + Word(alphanums) + Combine(quotedString) + Suppress("]")
+comment = Suppress("{") + Word(alphanums + " ") + Suppress("}")
+
+dot = Literal(".")
+piece = oneOf("K Q B N R")
+file_coord = oneOf("a b c d e f g h")
+rank_coord = oneOf("1 2 3 4 5 6 7 8")
+capture = oneOf("x :")
+promote = Literal("=")
+castle_queenside = oneOf("O-O-O 0-0-0 o-o-o")
+castle_kingside = oneOf("O-O 0-0 o-o")
+
+move_number = Optional(comment) + Word(nums) + dot
+m1 = file_coord + rank_coord # pawn move e.g. d4
+m2 = file_coord + capture + file_coord + rank_coord # pawn capture move e.g. dxe5
+m3 = file_coord + "8" + promote + piece # pawn promotion e.g. e8=Q
+m4 = piece + file_coord + rank_coord # piece move e.g. Be6
+m5 = piece + file_coord + file_coord + rank_coord # piece move e.g. Nbd2
+m6 = piece + rank_coord + file_coord + rank_coord # piece move e.g. R4a7
+m7 = piece + capture + file_coord + rank_coord # piece capture move e.g. Bxh7
+m8 = castle_queenside | castle_kingside # castling e.g. o-o
+
+check = oneOf("+ ++")
+mate = Literal("#")
+annotation = Word("!?", max=2)
+nag = " $" + Word(nums)
+decoration = check | mate | annotation | nag
+
+variant = Forward()
+half_move = Combine((m3 | m1 | m2 | m4 | m5 | m6 | m7 | m8) + Optional(decoration)) \
+ + Optional(comment) +Optional(variant)
+move = Suppress(move_number) + half_move + Optional(half_move)
+variant << "(" + OneOrMore(move) + ")"
+# grouping the plies (half-moves) for each move: useful to group annotations, variants...
+# suggested by Paul McGuire :)
+move = Group(Suppress(move_number) + half_move + Optional(half_move))
+variant << Group("(" + OneOrMore(move) + ")")
+game_terminator = oneOf("1-0 0-1 1/2-1/2 *")
+
+pgnGrammar = Suppress(ZeroOrMore(tag)) + ZeroOrMore(move) + Optional(Suppress(game_terminator))
+
+def parsePGN( pgn, bnf=pgnGrammar, fn=None ):
+ try:
+ return bnf.parseString( pgn )
+ except ParseException as err:
+ print(err.line)
+ print(" "*(err.column-1) + "^")
+ print(err)
+
+if __name__ == "__main__":
+ # input string
+ pgn = """
+[Event "ICC 5 0 u"]
+[Site "Internet Chess Club"]
+[Date "2004.01.25"]
+[Round "-"]
+[White "guest920"]
+[Black "IceBox"]
+[Result "0-1"]
+[ICCResult "White checkmated"]
+[BlackElo "1498"]
+[Opening "French defense"]
+[ECO "C00"]
+[NIC "FR.01"]
+[Time "04:44:56"]
+[TimeControl "300+0"]
+
+1. e4 e6 2. Nf3 d5 $2 3. exd5 (3. e5 g6 4. h4) exd5 4. Qe2+ Qe7 5. Qxe7+ Bxe7 6. d3 Nf6 7. Be3
+Bg4 8. Nbd2 c5 9. h3 Be6 10. O-O-O Nc6 11. g4 Bd6 12. g5 Nd7 13. Rg1 d4 14.
+g6 fxg6 15. Bg5 Rf8 16. a3 Bd5 17. Re1+ Nde5 18. Nxe5 Nxe5 19. Bf4 Rf5 20.
+Bxe5 Rxe5 21. Rg5 Rxe1# {Black wins} 0-1
+"""
+ # parse input string
+ tokens = parsePGN(pgn, pgnGrammar)
+ print(tokens.dump())