225 lines
3.8 KiB
Go
225 lines
3.8 KiB
Go
package move
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"code.c-base.org/gochess/libchess/pkg/board"
|
|
)
|
|
|
|
// Parser implements a parser for PGN moves.
|
|
type Parser struct {
|
|
lexer *Lexer
|
|
}
|
|
|
|
// NewParser returns an initialized parser for the given move.
|
|
func NewParser(m string) *Parser {
|
|
return &Parser{
|
|
lexer: NewLexer(m),
|
|
}
|
|
}
|
|
|
|
// Move parses the move and returns it or an error.
|
|
func (p *Parser) Move() (*board.Move, error) {
|
|
var (
|
|
stateCastles bool
|
|
statePiece bool
|
|
stateDisambiguity bool
|
|
stateCaptures bool
|
|
stateSquare bool
|
|
stateCheck bool
|
|
move = &board.Move{}
|
|
)
|
|
parsing:
|
|
for {
|
|
t := p.lexer.NextToken()
|
|
if t.Type == TokenEOF {
|
|
if move.To == board.NoSquare {
|
|
if !move.HasProp(board.KingSideCastle) && !move.HasProp(board.QueenSideCastle) {
|
|
return nil, p.throwToken(t)
|
|
}
|
|
}
|
|
return move, nil
|
|
}
|
|
|
|
if !stateCastles {
|
|
stateCastles = true
|
|
if parseCastles(t, move) {
|
|
continue parsing
|
|
}
|
|
}
|
|
|
|
if !statePiece {
|
|
statePiece = true
|
|
if parsePiece(t, move) {
|
|
continue parsing
|
|
}
|
|
}
|
|
|
|
if !stateDisambiguity {
|
|
stateDisambiguity = true
|
|
if parseDisambiguity(t, move) {
|
|
continue parsing
|
|
}
|
|
}
|
|
|
|
if !stateCaptures {
|
|
stateCaptures = true
|
|
if parseCaptures(t, move) {
|
|
continue parsing
|
|
}
|
|
}
|
|
|
|
if !stateSquare {
|
|
stateSquare = true
|
|
if parseSquare(t, move) {
|
|
continue parsing
|
|
}
|
|
}
|
|
|
|
if !stateCheck {
|
|
stateCheck = true
|
|
if parseCheckMate(t, move) {
|
|
continue parsing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p Parser) throwToken(t Token) error {
|
|
return fmt.Errorf("invalid token at pos %d: %s", t.Pos, t.Value)
|
|
}
|
|
|
|
///////////////////////
|
|
//// PARSE CASTLES ////
|
|
///////////////////////
|
|
|
|
func parseCastles(t Token, m *board.Move) bool {
|
|
if t.Type == TokenCastles {
|
|
switch t.Value {
|
|
case "O-O", "0-0":
|
|
m.AddProp(board.KingSideCastle)
|
|
return true
|
|
case "O-O-O", "0-0-0":
|
|
m.AddProp(board.QueenSideCastle)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
/////////////////////
|
|
//// PARSE PIECE ////
|
|
/////////////////////
|
|
|
|
var legalPieces = map[string]board.PieceType{
|
|
"K": board.King,
|
|
"Q": board.Queen,
|
|
"B": board.Bishop,
|
|
"N": board.Knight,
|
|
"R": board.Rook,
|
|
}
|
|
|
|
func parsePiece(t Token, m *board.Move) bool {
|
|
if t.Type != TokenPiece {
|
|
return false
|
|
}
|
|
p, ok := legalPieces[t.Value]
|
|
if ok {
|
|
m.Piece = p
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
///////////////////////
|
|
//// PARSE SQUARES ////
|
|
///////////////////////
|
|
|
|
var (
|
|
legalFiles = map[string]board.File{
|
|
"a": board.FileA,
|
|
"b": board.FileB,
|
|
"c": board.FileC,
|
|
"d": board.FileD,
|
|
"e": board.FileE,
|
|
"f": board.FileF,
|
|
"g": board.FileG,
|
|
"h": board.FileH,
|
|
}
|
|
legalRanks = map[string]board.Rank{
|
|
"1": board.Rank1,
|
|
"2": board.Rank2,
|
|
"3": board.Rank3,
|
|
"4": board.Rank4,
|
|
"5": board.Rank5,
|
|
"6": board.Rank6,
|
|
"7": board.Rank7,
|
|
"8": board.Rank8,
|
|
}
|
|
)
|
|
|
|
func parseDisambiguity(t Token, m *board.Move) bool {
|
|
if t.Type == TokenFile {
|
|
f, ok := legalFiles[t.Value]
|
|
if ok {
|
|
m.FromFile = &f
|
|
return true
|
|
}
|
|
}
|
|
if t.Type == TokenRank {
|
|
r, ok := legalRanks[t.Value]
|
|
if ok {
|
|
m.FromRank = &r
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func parseSquare(t Token, m *board.Move) bool {
|
|
if t.Type == TokenSquare {
|
|
m.To = board.StrToSquareMap[t.Value]
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
///////////////////////
|
|
//// PARSE CAPTURE ////
|
|
///////////////////////
|
|
|
|
var legalCapture = map[string]struct{}{
|
|
"x": {},
|
|
}
|
|
|
|
func parseCaptures(t Token, m *board.Move) bool {
|
|
if t.Type == TokenCapture {
|
|
_, ok := legalCapture[t.Value]
|
|
if ok {
|
|
m.AddProp(board.Capture)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
//////////////////////////
|
|
//// PARSE CHECK/MATE ////
|
|
//////////////////////////
|
|
|
|
func parseCheckMate(t Token, m *board.Move) bool {
|
|
if t.Type == TokenCheck {
|
|
if t.Value == "+" {
|
|
m.AddProp(board.Check)
|
|
return true
|
|
}
|
|
}
|
|
if t.Type == TokenMate {
|
|
if t.Value == "#" {
|
|
m.AddProp(board.Mate)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|