Added Shared Parser support
This commit is contained in:
@@ -4,6 +4,7 @@ import de.nowchess.api.board.*
|
|||||||
import de.nowchess.api.game.GameContext
|
import de.nowchess.api.game.GameContext
|
||||||
import de.nowchess.io.GameContextImport
|
import de.nowchess.io.GameContextImport
|
||||||
import scala.util.parsing.combinator.RegexParsers
|
import scala.util.parsing.combinator.RegexParsers
|
||||||
|
import FenParserSupport.*
|
||||||
|
|
||||||
object FenParserCombinators extends RegexParsers with GameContextImport:
|
object FenParserCombinators extends RegexParsers with GameContextImport:
|
||||||
|
|
||||||
@@ -11,15 +12,6 @@ object FenParserCombinators extends RegexParsers with GameContextImport:
|
|||||||
|
|
||||||
// ── Piece character ──────────────────────────────────────────────────────
|
// ── Piece character ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
private val charToPieceType: Map[Char, PieceType] = Map(
|
|
||||||
'p' -> PieceType.Pawn,
|
|
||||||
'r' -> PieceType.Rook,
|
|
||||||
'n' -> PieceType.Knight,
|
|
||||||
'b' -> PieceType.Bishop,
|
|
||||||
'q' -> PieceType.Queen,
|
|
||||||
'k' -> PieceType.King
|
|
||||||
)
|
|
||||||
|
|
||||||
private def pieceChar: Parser[Piece] =
|
private def pieceChar: Parser[Piece] =
|
||||||
"[prnbqkPRNBQK]".r ^^ { s =>
|
"[prnbqkPRNBQK]".r ^^ { s =>
|
||||||
val c = s.head
|
val c = s.head
|
||||||
@@ -32,11 +24,6 @@ object FenParserCombinators extends RegexParsers with GameContextImport:
|
|||||||
|
|
||||||
// ── Rank parser ──────────────────────────────────────────────────────────
|
// ── Rank parser ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Parse a sequence of piece-chars and empty-counts, returning tagged tokens. */
|
|
||||||
private sealed trait RankToken
|
|
||||||
private case class PieceToken(piece: Piece) extends RankToken
|
|
||||||
private case class EmptyToken(count: Int) extends RankToken
|
|
||||||
|
|
||||||
private def rankToken: Parser[RankToken] =
|
private def rankToken: Parser[RankToken] =
|
||||||
pieceChar ^^ PieceToken.apply | emptyCount ^^ EmptyToken.apply
|
pieceChar ^^ PieceToken.apply | emptyCount ^^ EmptyToken.apply
|
||||||
|
|
||||||
@@ -46,21 +33,9 @@ object FenParserCombinators extends RegexParsers with GameContextImport:
|
|||||||
* Fails if total file count != 8 or any piece placement exceeds board bounds. */
|
* Fails if total file count != 8 or any piece placement exceeds board bounds. */
|
||||||
private def rankParser(rank: Rank): Parser[List[(Square, Piece)]] =
|
private def rankParser(rank: Rank): Parser[List[(Square, Piece)]] =
|
||||||
rankTokens >> { tokens =>
|
rankTokens >> { tokens =>
|
||||||
val result = tokens.foldLeft(Option((List.empty[(Square, Piece)], 0))):
|
buildSquares(rank, tokens) match
|
||||||
case (None, _) => None
|
case Some(squares) => success(squares)
|
||||||
case (Some((acc, fileIdx)), PieceToken(piece)) =>
|
case None => failure(s"Rank $rank is invalid")
|
||||||
if fileIdx > 7 then None
|
|
||||||
else
|
|
||||||
val sq = Square(File.values(fileIdx), rank)
|
|
||||||
Some((acc :+ (sq -> piece), fileIdx + 1))
|
|
||||||
case (Some((acc, fileIdx)), EmptyToken(n)) =>
|
|
||||||
val next = fileIdx + n
|
|
||||||
if next > 8 then None
|
|
||||||
else Some((acc, next))
|
|
||||||
result match
|
|
||||||
case Some((squares, 8)) => success(squares)
|
|
||||||
case Some((_, total)) => failure(s"Rank $rank has $total files, expected 8")
|
|
||||||
case None => failure(s"Rank $rank exceeds board width")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Board parser ─────────────────────────────────────────────────────────
|
// ── Board parser ─────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -5,26 +5,10 @@ import fastparse.NoWhitespace.*
|
|||||||
import de.nowchess.api.board.*
|
import de.nowchess.api.board.*
|
||||||
import de.nowchess.api.game.GameContext
|
import de.nowchess.api.game.GameContext
|
||||||
import de.nowchess.io.GameContextImport
|
import de.nowchess.io.GameContextImport
|
||||||
|
import FenParserSupport.*
|
||||||
|
|
||||||
object FenParserFastParse extends GameContextImport:
|
object FenParserFastParse extends GameContextImport:
|
||||||
|
|
||||||
// ── Rank token ADT ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private sealed trait RankToken
|
|
||||||
private case class PieceToken(piece: Piece) extends RankToken
|
|
||||||
private case class EmptyToken(count: Int) extends RankToken
|
|
||||||
|
|
||||||
// ── Char-to-piece mapping ────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private val charToPieceType: Map[Char, PieceType] = Map(
|
|
||||||
'p' -> PieceType.Pawn,
|
|
||||||
'r' -> PieceType.Rook,
|
|
||||||
'n' -> PieceType.Knight,
|
|
||||||
'b' -> PieceType.Bishop,
|
|
||||||
'q' -> PieceType.Queen,
|
|
||||||
'k' -> PieceType.King
|
|
||||||
)
|
|
||||||
|
|
||||||
// ── Low-level parsers ────────────────────────────────────────────────────
|
// ── Low-level parsers ────────────────────────────────────────────────────
|
||||||
|
|
||||||
private def pieceChar(using P[Any]): P[Piece] =
|
private def pieceChar(using P[Any]): P[Piece] =
|
||||||
@@ -40,22 +24,6 @@ object FenParserFastParse extends GameContextImport:
|
|||||||
private def rankToken(using P[Any]): P[RankToken] =
|
private def rankToken(using P[Any]): P[RankToken] =
|
||||||
pieceChar.map(PieceToken.apply) | emptyCount.map(EmptyToken.apply)
|
pieceChar.map(PieceToken.apply) | emptyCount.map(EmptyToken.apply)
|
||||||
|
|
||||||
// ── Rank validation helper ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
private def buildSquares(rank: Rank, tokens: Seq[RankToken]): Option[List[(Square, Piece)]] =
|
|
||||||
tokens.foldLeft(Option((List.empty[(Square, Piece)], 0))):
|
|
||||||
case (None, _) => None
|
|
||||||
case (Some((acc, fileIdx)), PieceToken(piece)) =>
|
|
||||||
if fileIdx > 7 then None
|
|
||||||
else
|
|
||||||
val sq = Square(File.values(fileIdx), rank)
|
|
||||||
Some((acc :+ (sq -> piece), fileIdx + 1))
|
|
||||||
case (Some((acc, fileIdx)), EmptyToken(n)) =>
|
|
||||||
val next = fileIdx + n
|
|
||||||
if next > 8 then None
|
|
||||||
else Some((acc, next))
|
|
||||||
.flatMap { case (squares, total) => if total == 8 then Some(squares) else None }
|
|
||||||
|
|
||||||
// ── Rank parser ──────────────────────────────────────────────────────────
|
// ── Rank parser ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private def rankParser(rank: Rank)(using P[Any]): P[List[(Square, Piece)]] =
|
private def rankParser(rank: Rank)(using P[Any]): P[List[(Square, Piece)]] =
|
||||||
|
|||||||
Reference in New Issue
Block a user