A live board you can step through, edit and take apart, with every option demonstrated. It is the fastest way to see what this is.
A grammar plugin that teaches the Tabnas parser to read chess notation: PGN games and the SAN moves inside them. Available for both TypeScript and Go, built on the same grammar.
Chess notation looks like this:
[Event "F/S Return Match"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]
1. e4 e5 2. Nf3 {The main line.} Nc6 $1 (2... d6 3. d4) 3. Bb5 a6 1/2-1/2
# TypeScript / JavaScript
npm install @tabnas/parser @tabnas/chess
# Go
go get github.com/tabnas/chess/go@latestTypeScript β the plugin layers onto a Tabnas engine:
const { Tabnas } = require('@tabnas/parser')
const { Chess } = require('@tabnas/chess')
const tn = new Tabnas().use(Chess)
const move = tn.parse('1. e4 e5 *')[0].moves[0]
move.san // => 'e4'
move.piece // => 'P'
move.to // => 'e4'
move.number // => 1
move.side // => 'w'There is a one-call entry point too, for when you do not need the engine:
const { parseGame } = require('@tabnas/chess')
const capture = parseGame('1. e4 Nc6 2. d4 exd5 *').moves[3]
capture.san // => 'exd5'
capture.piece // => 'P'
capture.capture // => true
capture.to // => 'd5'
capture.disambiguation // => ({ file: 'e' })Go β chess.Parse is the one-call entry point:
import chess "github.com/tabnas/chess/go"
db, _ := chess.Parse("1. e4 e5 *")
// db[0].Moves[0] == &chess.Move{San: "e4", Piece: "P", To: "e4", Number: 1, Side: "w"}A plain, JSON-serialisable game model β no classes, no cycles, nothing to unwrap:
const { parseGame } = require('@tabnas/chess')
const game = parseGame('[Event "Casual"]\n\n1. e4 {Best by test.} e5 (1... c5) 1-0')
game.tags.Event // => 'Casual'
game.result // => '1-0'
game.moves.length // => 2
game.moves[0].comments[0].text // => 'Best by test.'
game.moves[1].variations[0].moves[0].san // => 'c5'Each move is decomposed into the vocabulary the PGN standard itself uses β piece, disambiguation, capture, destination, promotion, check indicator:
const { parseSan } = require('@tabnas/chess')
const move = parseSan('Qa6xb7#')
move.piece // => 'Q'
move.disambiguation // => ({ file: 'a', rank: 6 })
move.capture // => true
move.to // => 'b7'
move.check // => '#'Every field is something the notation actually said. This is a parser,
not a chess engine: it has no board, so it cannot tell you which knight
played Nf3, and it does not pretend to. disambiguation holds as much of
the origin square as was written, and nothing more.
ts/doc/concepts.md explains why the model is shaped
this way, and how it compares with the alternatives.
@tabnas/chess implements the notation described by the
PGN standard
(Steven J. Edwards, 1994), section by section:
| Section | Feature | |
|---|---|---|
| 4, 7 | Character codes and token classes | β |
| 5 | Brace {β¦} and rest-of-line ;β¦ commentary |
β kept, not discarded |
| 6 | The % escape mechanism (first column only) |
β |
| 8.1 | Tag pairs, with \" and \\ string escapes |
β raw string values |
| 8.2.2 | Move number indications | β counted where unwritten |
| 8.2.3 | SAN moves, in full | β decomposed |
| 8.2.4 | Numeric annotation glyphs | β |
| 8.2.5 | Recursive annotation variations | β nested |
| 8.2.6 | Game termination markers | β |
| 9.7 | The FEN tag, read for the starting move and side |
β |
| 18 | Databases: many games in one source | β |
| 3 | Import format (lax) and export format (strict) | β
strict option |
Plus one thing the 1994 standard does not define. The [%clk 0:05:00] /
[%eval β¦] / [%cal β¦] markup that lichess, chess.com and ChessBase put
inside comments is parsed into Comment.commands, with the comment text
still kept verbatim. Its grammar comes from the
PGN Specification Supplement
(final draft, 2001), which defines the [%name operand,operand] syntax
and four time commands β clk, egt, emt, mct. Everything else
borrows the syntax without being in it, so this parses the syntax and
interprets none of the names.
The supplement's second kind of operand is the reason this needs a scanner
rather than a regular expression: a double-quoted operand may contain the
comma and the right bracket a bare one may not, so in
[%src "Lasker, 1896]"] the command ends at the last bracket and holds
one operand, not two.
Not included, deliberately: move legality. Nothing here knows the rules
of chess, so 1. Qh8 parses happily and 1. e9 does not β the first is a
well-formed move, the second is not a move at all. Feed the output to a
board library if you need the difference. Also out of scope: FEN and EPD as
standalone documents (sections 16.1 and 16.2), and the non-standard --
null move and (=) draw offer some tools emit.
web/ is a self-contained <chess-view> web component built on
this parser: a classic 2D board, controls to step through the game, and the
notation highlighted move by move.
<script src="https://cdn.jsdelivr.net/npm/@tabnas/chess-view@0.1.3"></script>
<chess-view>1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 1/2-1/2</chess-view>One tag, no dependencies, no second request β or npm install @tabnas/chess-view for a bundler, types included.
It is also where the parser's one hard limit becomes concrete. Nf3 names
a piece and a destination, and a parser with no board cannot know which
knight β so the component supplies the missing half, a small legal move
generator that resolves each parsed move against the running position. See
web/README.md.
Full documentation follows the DiΓ‘taxis framework β one file per quadrant:
| Tutorial (learning) | ts/doc/tutorial.md |
| How-to guide (tasks) | ts/doc/guide.md |
| Reference (API + options + syntax) | ts/doc/reference.md |
| Concepts (explanation) | ts/doc/concepts.md |
The docs' examples are TypeScript, but the model, the options and the accepted notation are the same in both runtimes.
Package hubs: ts/README.md, go/README.md.
The grammar is defined once in the top-level
chess-grammar.jsonic and embedded into both
implementations β TypeScript (ts/src/chess.ts) and Go
(go/chess.go) β by
ts/embed-grammar.js during the TypeScript build.
Edit the grammar there, not in the generated sources.
As a railroad/syntax diagram, generated from the live grammar with
@tabnas/railroad:
ASCII version: ts/doc/grammar.txt.
MIT. Copyright (c) Richard Rodger.
