WHITESPACE = _{ " " | "\t" | "\n" | "\r" }
COMMENT = { "{" ~ (!"}" ~ ANY)* ~ "}" }
// Constants
key = @{ (!("]" | "\"") ~ ASCII_ALPHA)+ }
value = @{ (!("]" | "\"") ~ ANY)+ }
file = @{ 'a'..'h' }
rank = @{ '1'..'8' }
piece = @{ ("K" | "Q" | "R" | "B" | "N") }
square = @{ file ~ rank }
option_square = @{ file? ~ rank? }
// Move information
check_state = @{ ("+" | "#") }
promotion = @{ "=" ~ (!"K" ~ piece) }
castle = @{ ("O-O-O" | "O-O") }
move_number = @{ ('1'..'9') ~ ('0'..'9')* ~ "." }
second_move_number = @{ move_number ~ ".." }
// Move types
basic_move = @{ piece? ~ ((option_square ~ square) | square) }
capture = @{ (square | file | rank | (piece ~ option_square)) ~ "x" ~ square }
move = @{ (((capture | basic_move) ~ promotion?) | castle) ~ check_state? }
// Move sequences
partial_move = { move_number ~ move }
full_move = { move_number ~ move{2} }
half_move = { second_move_number ~ move }
line = { (full_move* ~ partial_move) | full_move+ }
// Metadata
metadata = ${ "[" ~ key ~ " \"" ~ value? ~ "\"]" }
result = { "1/2-1/2" | "1-0" | "0-1" }
// Sequences
sequence = {
white_sequence
| black_sequence
| line
}
subsequence = { "(" ~ sequence ~ ")" }
half_sequence = { (half_move | move) ~ sequence? }
half_subsequence = { "(" ~ half_sequence ~ ")" }
multi_subsequence = { subsequence+ }
multi_half_subsequence = { half_subsequence+ }
white_sequence = { (full_move* ~ partial_move) ~ multi_subsequence ~ half_sequence? }
black_sequence = { (full_move+ ~ multi_half_subsequence ~ sequence?) }
// PGN
pgn = !{ metadata* ~ sequence ~ result? }
pgn_file = ${ SOI ~ WHITESPACE* ~ pgn ~ (WHITESPACE* ~ pgn)* ~ WHITESPACE* ~ EOI }