# tree-sitter-pgn
## Overview
Chess Portable Game Notation (PGN) grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter).
## Used in
* [Emacs pygn-mode](https://github.com/dwcoates/pygn-mode)
## Highlighting Example
<a href="/doc/images/pgn_syntax_highlight.png">
<img src="/doc/images/pgn_syntax_highlight.png" width=500/>
</a>
## Python Example
```python
import more_itertools
from tree_sitter import Language, Parser
import tree_sitter_pgn as ts_pgn
PGN_LANGUAGE = Language(ts_pgn.language())
parser = Parser(PGN_LANGUAGE)
query = PGN_LANGUAGE.query(
'''
(series_of_games
game: (game) @game)
(movetext
san_move: (san_move) @san_move)
(movetext
lan_move: (lan_move) @lan_move)
''')
with open('input_file.pgn', 'rb') as file:
tree = parser.parse(file.read())
matches = query.captures(tree.root_node)
merged_nodes = [
*matches.get('game', []),
*matches.get('san_move', []),
*matches.get('lan_move', []),
]
merged_nodes = sorted(merged_nodes, key=lambda elt: elt.start_byte)
for game in more_itertools.split_before(merged_nodes, lambda node: node.type == 'game'):
main_line = []
for node in game:
if node.type in ['san_move', 'lan_move'] and node.text is not None:
main_line.append(node.text.decode().strip())
continue
print(' '.join(main_line))
```
## References
* [PGN specification](http://www.saremba.de/chessgml/standards/pgn/pgn-complete.htm)
* [Commonly-used extensions](https://www.enpassant.dk/chess/palview/enhancedpgn.htm)
* [Commonly-used annotation glyphs](https://en.wikipedia.org/wiki/Numeric_Annotation_Glyphs)
Raw data
{
"_id": null,
"home_page": null,
"name": "tree-sitter-pgn",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "incremental, parsing, tree-sitter, PGN, chess",
"author": null,
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/ae/c1/a4359e7c2abb2823438956f6e5fe1fffc64f3cc1bdf83c1540439055c293/tree_sitter_pgn-1.2.11.tar.gz",
"platform": null,
"description": "# tree-sitter-pgn\n\n## Overview\n\nChess Portable Game Notation (PGN) grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter).\n\n## Used in\n\n * [Emacs pygn-mode](https://github.com/dwcoates/pygn-mode)\n\n## Highlighting Example\n\n<a href=\"/doc/images/pgn_syntax_highlight.png\">\n <img src=\"/doc/images/pgn_syntax_highlight.png\" width=500/>\n</a>\n\n## Python Example\n\n```python\nimport more_itertools\nfrom tree_sitter import Language, Parser\nimport tree_sitter_pgn as ts_pgn\n\nPGN_LANGUAGE = Language(ts_pgn.language())\nparser = Parser(PGN_LANGUAGE)\n\nquery = PGN_LANGUAGE.query(\n '''\n (series_of_games\n game: (game) @game)\n\n (movetext\n san_move: (san_move) @san_move)\n\n (movetext\n lan_move: (lan_move) @lan_move)\n ''')\n\nwith open('input_file.pgn', 'rb') as file:\n tree = parser.parse(file.read())\n\nmatches = query.captures(tree.root_node)\n\nmerged_nodes = [\n *matches.get('game', []),\n *matches.get('san_move', []),\n *matches.get('lan_move', []),\n]\nmerged_nodes = sorted(merged_nodes, key=lambda elt: elt.start_byte)\n\nfor game in more_itertools.split_before(merged_nodes, lambda node: node.type == 'game'):\n main_line = []\n for node in game:\n if node.type in ['san_move', 'lan_move'] and node.text is not None:\n main_line.append(node.text.decode().strip())\n continue\n print(' '.join(main_line))\n```\n\n## References\n\n * [PGN specification](http://www.saremba.de/chessgml/standards/pgn/pgn-complete.htm)\n * [Commonly-used extensions](https://www.enpassant.dk/chess/palview/enhancedpgn.htm)\n * [Commonly-used annotation glyphs](https://en.wikipedia.org/wiki/Numeric_Annotation_Glyphs)\n",
"bugtrack_url": null,
"license": "BSD-2-Clause",
"summary": "PGN grammar for tree-sitter",
"version": "1.2.11",
"project_urls": {
"Homepage": "https://github.com/rolandwalker/tree-sitter-pgn"
},
"split_keywords": [
"incremental",
" parsing",
" tree-sitter",
" pgn",
" chess"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e3a8929ca4b099acc8fd4f6021b7c004185985618385f0b51b57af29864bfc88",
"md5": "2edd9506858959053fe4731a6342f298",
"sha256": "3de29ccbefe3ecca9d13bb7e23a09ae683fcd030e54a9cac6b2ddb842508ccc3"
},
"downloads": -1,
"filename": "tree_sitter_pgn-1.2.11-cp39-abi3-macosx_10_9_x86_64.whl",
"has_sig": false,
"md5_digest": "2edd9506858959053fe4731a6342f298",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": ">=3.9",
"size": 76123,
"upload_time": "2025-07-29T14:51:19",
"upload_time_iso_8601": "2025-07-29T14:51:19.826822Z",
"url": "https://files.pythonhosted.org/packages/e3/a8/929ca4b099acc8fd4f6021b7c004185985618385f0b51b57af29864bfc88/tree_sitter_pgn-1.2.11-cp39-abi3-macosx_10_9_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "a97cd5c0c00e1ed82bd6c3bebc3d4415df124cb60ec73d26461c2647d5772816",
"md5": "968339b80aec0de13f39ab2796f88af2",
"sha256": "08a52afdefd7560f0457b68d276a55104405e83e48408295be0288f39d1197ea"
},
"downloads": -1,
"filename": "tree_sitter_pgn-1.2.11-cp39-abi3-macosx_11_0_arm64.whl",
"has_sig": false,
"md5_digest": "968339b80aec0de13f39ab2796f88af2",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": ">=3.9",
"size": 78631,
"upload_time": "2025-07-29T14:51:21",
"upload_time_iso_8601": "2025-07-29T14:51:21.581614Z",
"url": "https://files.pythonhosted.org/packages/a9/7c/d5c0c00e1ed82bd6c3bebc3d4415df124cb60ec73d26461c2647d5772816/tree_sitter_pgn-1.2.11-cp39-abi3-macosx_11_0_arm64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "25374f900439244b4ccfd9f4847ec1ef271bef11f0d5c28ec185dffbbaa463c6",
"md5": "ef0e8277d73174ebc57ca7a5e224ee7a",
"sha256": "f64621a0aedb69ce85f35cee964b4b7ddeb233ef1437fe1cbd2ecdaa88fc6c7a"
},
"downloads": -1,
"filename": "tree_sitter_pgn-1.2.11-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl",
"has_sig": false,
"md5_digest": "ef0e8277d73174ebc57ca7a5e224ee7a",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": ">=3.9",
"size": 92817,
"upload_time": "2025-07-29T14:51:22",
"upload_time_iso_8601": "2025-07-29T14:51:22.456554Z",
"url": "https://files.pythonhosted.org/packages/25/37/4f900439244b4ccfd9f4847ec1ef271bef11f0d5c28ec185dffbbaa463c6/tree_sitter_pgn-1.2.11-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "19af9f5ff48a3e1ffe6d6d63e28e0760ec96806fabf950df3726a4e54aae5cd7",
"md5": "953a3b0e3fb7c2577c0da2dcd8557a08",
"sha256": "78e00edb849d0641f256a0afbfb35911dd8888903a33bb3fd8e13d987a9f0cb2"
},
"downloads": -1,
"filename": "tree_sitter_pgn-1.2.11-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
"has_sig": false,
"md5_digest": "953a3b0e3fb7c2577c0da2dcd8557a08",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": ">=3.9",
"size": 89459,
"upload_time": "2025-07-29T14:51:23",
"upload_time_iso_8601": "2025-07-29T14:51:23.344390Z",
"url": "https://files.pythonhosted.org/packages/19/af/9f5ff48a3e1ffe6d6d63e28e0760ec96806fabf950df3726a4e54aae5cd7/tree_sitter_pgn-1.2.11-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "37008507f24ed4ca4c7c2002bcd88204fed8d34891e2077db240be47706fe769",
"md5": "fe51ff365a261a52dc0197322e7832cd",
"sha256": "caa40f352faa16e303d1d457ba548c2a190a66e10552424db19708731eb79c7d"
},
"downloads": -1,
"filename": "tree_sitter_pgn-1.2.11-cp39-abi3-musllinux_1_2_aarch64.whl",
"has_sig": false,
"md5_digest": "fe51ff365a261a52dc0197322e7832cd",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": ">=3.9",
"size": 88796,
"upload_time": "2025-07-29T14:51:24",
"upload_time_iso_8601": "2025-07-29T14:51:24.472522Z",
"url": "https://files.pythonhosted.org/packages/37/00/8507f24ed4ca4c7c2002bcd88204fed8d34891e2077db240be47706fe769/tree_sitter_pgn-1.2.11-cp39-abi3-musllinux_1_2_aarch64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "c12c25bb7ce7c59635be4da4f9ad90f96267a4156a78d36a7d010450f11861cc",
"md5": "b9795fb9fd1fcda9449fb1e3596bd184",
"sha256": "7ca46f9909085efc3cc707979660eb966ca855843d0cc2b38ae81fa7f2f36756"
},
"downloads": -1,
"filename": "tree_sitter_pgn-1.2.11-cp39-abi3-musllinux_1_2_x86_64.whl",
"has_sig": false,
"md5_digest": "b9795fb9fd1fcda9449fb1e3596bd184",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": ">=3.9",
"size": 92069,
"upload_time": "2025-07-29T14:51:25",
"upload_time_iso_8601": "2025-07-29T14:51:25.640628Z",
"url": "https://files.pythonhosted.org/packages/c1/2c/25bb7ce7c59635be4da4f9ad90f96267a4156a78d36a7d010450f11861cc/tree_sitter_pgn-1.2.11-cp39-abi3-musllinux_1_2_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "aec1a4359e7c2abb2823438956f6e5fe1fffc64f3cc1bdf83c1540439055c293",
"md5": "22216b2191e22e4f95d822f85d2511b9",
"sha256": "49386a049099c35187de042c8682d3fc29638cbbd6de276db5bd92a5600b1af0"
},
"downloads": -1,
"filename": "tree_sitter_pgn-1.2.11.tar.gz",
"has_sig": false,
"md5_digest": "22216b2191e22e4f95d822f85d2511b9",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 61886,
"upload_time": "2025-07-29T14:51:26",
"upload_time_iso_8601": "2025-07-29T14:51:26.761991Z",
"url": "https://files.pythonhosted.org/packages/ae/c1/a4359e7c2abb2823438956f6e5fe1fffc64f3cc1bdf83c1540439055c293/tree_sitter_pgn-1.2.11.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-29 14:51:26",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "rolandwalker",
"github_project": "tree-sitter-pgn",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "tree-sitter-pgn"
}