Compare commits
No commits in common. "52f663264e6b2ae439b480a3ccebe12de1ca248a" and "31e70bfc2a92939b2cb4005c194a7db923e342d6" have entirely different histories.
52f663264e
...
31e70bfc2a
121
sgf/src/game.rs
121
sgf/src/game.rs
|
@ -1,4 +1,4 @@
|
||||||
use crate::{parser, Color};
|
use crate::{tree, Color, Position};
|
||||||
use std::{collections::HashSet, time::Duration};
|
use std::{collections::HashSet, time::Duration};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -227,9 +227,9 @@ impl Node for GameNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&parser::Node> for GameNode {
|
impl TryFrom<&tree::Node> for GameNode {
|
||||||
type Error = ConversionError;
|
type Error = ConversionError;
|
||||||
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: &tree::Node) -> Result<Self, Self::Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,7 @@ impl Node for GameTree {
|
||||||
pub struct MoveNode {
|
pub struct MoveNode {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
color: Color,
|
color: Color,
|
||||||
position: String,
|
position: Position,
|
||||||
children: Vec<GameNode>,
|
children: Vec<GameNode>,
|
||||||
|
|
||||||
time_left: Option<Duration>,
|
time_left: Option<Duration>,
|
||||||
|
@ -274,7 +274,7 @@ pub struct MoveNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MoveNode {
|
impl MoveNode {
|
||||||
pub fn new(color: Color, position: String) -> Self {
|
pub fn new(color: Color, position: Position) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
color,
|
color,
|
||||||
|
@ -304,41 +304,42 @@ impl Node for MoveNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&parser::Node> for MoveNode {
|
impl TryFrom<&tree::Node> for MoveNode {
|
||||||
type Error = ConversionError;
|
type Error = ConversionError;
|
||||||
|
|
||||||
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: &tree::Node) -> Result<Self, Self::Error> {
|
||||||
match n.move_() {
|
let move_ = match (n.find_prop("W"), n.find_prop("B")) {
|
||||||
Some((color, position)) => Ok(MoveNode::new(color, position)),
|
(Some(white_move), _) => Some((Color::White, Position{ row: white_move
|
||||||
|
(None, Some(black_move)) => unimplemented!(),
|
||||||
|
(None, None) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match move_ {
|
||||||
|
Some((color, position)) => unimplemented!(),
|
||||||
None => Err(ConversionError::IncompatibleNodeType),
|
None => Err(ConversionError::IncompatibleNodeType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
fn parse_position(s: &str) -> Result<Position, ConversionError> {
|
fn parse_position(s: &str) -> Result<Position, ConversionError> {
|
||||||
if s.len() == 2 {
|
if s.len() == 2 {
|
||||||
Ok(Position {
|
Ok(Position{ row: s[0], column: s[1] })
|
||||||
row: s[0],
|
|
||||||
column: s[1],
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Err(ConversionError::InvalidPositionSyntax)
|
Err(ConversionError::InvalidPositionSyntax)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct SetupNode {
|
pub struct SetupNode {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
|
|
||||||
positions: Vec<(Option<Color>, String)>,
|
positions: Vec<(Option<Color>, Position)>,
|
||||||
children: Vec<GameNode>,
|
children: Vec<GameNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetupNode {
|
impl SetupNode {
|
||||||
pub fn new(positions: Vec<(Option<Color>, String)>) -> Result<Self, SetupError> {
|
pub fn new(positions: Vec<(Option<Color>, Position)>) -> Result<Self, SetupError> {
|
||||||
let mut coords: HashSet<String> = HashSet::new();
|
let mut coords: HashSet<Position> = HashSet::new();
|
||||||
for coord in positions.iter().map(|p| p.1.clone()) {
|
for coord in positions.iter().map(|p| p.1.clone()) {
|
||||||
if coords.contains(&coord) {
|
if coords.contains(&coord) {
|
||||||
return Err(SetupError::ConflictingPosition);
|
return Err(SetupError::ConflictingPosition);
|
||||||
|
@ -395,9 +396,21 @@ mod test {
|
||||||
fn it_can_add_moves_to_a_game() {
|
fn it_can_add_moves_to_a_game() {
|
||||||
let mut tree = GameTree::new();
|
let mut tree = GameTree::new();
|
||||||
|
|
||||||
let first_move = MoveNode::new(Color::Black, "dd".to_owned());
|
let first_move = MoveNode::new(
|
||||||
|
Color::Black,
|
||||||
|
Position {
|
||||||
|
row: 'd',
|
||||||
|
column: 'd',
|
||||||
|
},
|
||||||
|
);
|
||||||
let first_ = tree.add_child(GameNode::MoveNode(first_move.clone()));
|
let first_ = tree.add_child(GameNode::MoveNode(first_move.clone()));
|
||||||
let second_move = MoveNode::new(Color::White, "qq".to_owned());
|
let second_move = MoveNode::new(
|
||||||
|
Color::White,
|
||||||
|
Position {
|
||||||
|
row: 'q',
|
||||||
|
column: 'q',
|
||||||
|
},
|
||||||
|
);
|
||||||
first_.add_child(GameNode::MoveNode(second_move.clone()));
|
first_.add_child(GameNode::MoveNode(second_move.clone()));
|
||||||
|
|
||||||
let nodes = tree.nodes();
|
let nodes = tree.nodes();
|
||||||
|
@ -416,19 +429,20 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn game_node_can_parse_sgf_move_node() {
|
fn game_node_can_parse_sgf_move_node() {
|
||||||
let n = parser::Node {
|
let n = tree::Node {
|
||||||
properties: vec![
|
properties: vec![
|
||||||
parser::Property::Move((Color::White, "dp".to_owned())),
|
tree::Property {
|
||||||
/*
|
ident: "W".to_owned(),
|
||||||
parser::Property {
|
values: vec!["dp".to_owned()],
|
||||||
|
},
|
||||||
|
tree::Property {
|
||||||
ident: "WL".to_owned(),
|
ident: "WL".to_owned(),
|
||||||
values: vec!["176.099".to_owned()],
|
values: vec!["176.099".to_owned()],
|
||||||
},
|
},
|
||||||
parser::Property {
|
tree::Property {
|
||||||
ident: "C".to_owned(),
|
ident: "C".to_owned(),
|
||||||
values: vec!["Comments in the game".to_owned()],
|
values: vec!["Comments in the game".to_owned()],
|
||||||
},
|
},
|
||||||
*/
|
|
||||||
],
|
],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
|
@ -458,25 +472,26 @@ mod move_node_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_can_parse_an_sgf_move_node() {
|
fn it_can_parse_an_sgf_move_node() {
|
||||||
let n = parser::Node {
|
let n = tree::Node {
|
||||||
properties: vec![
|
properties: vec![
|
||||||
parser::Property::Move((Color::White, "dp".to_owned())),
|
tree::Property {
|
||||||
/*
|
ident: "W".to_owned(),
|
||||||
parser::Property {
|
values: vec!["dp".to_owned()],
|
||||||
|
},
|
||||||
|
tree::Property {
|
||||||
ident: "WL".to_owned(),
|
ident: "WL".to_owned(),
|
||||||
values: vec!["176.099".to_owned()],
|
values: vec!["176.099".to_owned()],
|
||||||
},
|
},
|
||||||
parser::Property {
|
tree::Property {
|
||||||
ident: "C".to_owned(),
|
ident: "C".to_owned(),
|
||||||
values: vec!["Comments in the game".to_owned()],
|
values: vec!["Comments in the game".to_owned()],
|
||||||
},
|
},
|
||||||
*/
|
|
||||||
],
|
],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
assert_matches!(MoveNode::try_from(&n), Ok(node) => {
|
assert_matches!(MoveNode::try_from(&n), Ok(node) => {
|
||||||
assert_eq!(node.color, Color::White);
|
assert_eq!(node.color, Color::White);
|
||||||
assert_eq!(node.position, "dp".to_owned());
|
assert_eq!(node.position, Position{ row: 'd', column: 'p' });
|
||||||
assert_eq!(node.children, vec![]);
|
assert_eq!(node.children, vec![]);
|
||||||
assert_eq!(node.time_left, Some(Duration::from_secs(176)));
|
assert_eq!(node.time_left, Some(Duration::from_secs(176)));
|
||||||
assert_eq!(node.comments, vec!["Comments in the game".to_owned()]);
|
assert_eq!(node.comments, vec!["Comments in the game".to_owned()]);
|
||||||
|
@ -502,16 +517,46 @@ mod setup_node_tests {
|
||||||
fn it_rejects_conflicting_placement_properties() {
|
fn it_rejects_conflicting_placement_properties() {
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
SetupNode::new(vec![
|
SetupNode::new(vec![
|
||||||
(Some(Color::Black), "dd".to_owned(),),
|
(
|
||||||
(Some(Color::Black), "dd".to_owned(),),
|
Some(Color::Black),
|
||||||
|
Position {
|
||||||
|
row: 'd',
|
||||||
|
column: 'd',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Some(Color::Black),
|
||||||
|
Position {
|
||||||
|
row: 'd',
|
||||||
|
column: 'd',
|
||||||
|
},
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
Err(SetupError::ConflictingPosition)
|
Err(SetupError::ConflictingPosition)
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
SetupNode::new(vec![
|
SetupNode::new(vec![
|
||||||
(Some(Color::Black), "dd".to_owned(),),
|
(
|
||||||
(Some(Color::Black), "ee".to_owned(),),
|
Some(Color::Black),
|
||||||
(Some(Color::White), "ee".to_owned(),),
|
Position {
|
||||||
|
row: 'd',
|
||||||
|
column: 'd',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Some(Color::Black),
|
||||||
|
Position {
|
||||||
|
row: 'e',
|
||||||
|
column: 'e',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Some(Color::White),
|
||||||
|
Position {
|
||||||
|
row: 'e',
|
||||||
|
column: 'e',
|
||||||
|
},
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
Err(SetupError::ConflictingPosition)
|
Err(SetupError::ConflictingPosition)
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,10 +3,10 @@ pub use date::Date;
|
||||||
|
|
||||||
// pub mod go;
|
// pub mod go;
|
||||||
|
|
||||||
mod game;
|
mod tree;
|
||||||
|
use tree::{parse_collection, Tree};
|
||||||
|
|
||||||
mod parser;
|
// mod game;
|
||||||
use parser::{parse_collection, Tree};
|
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use crate::{Color, Error, GameResult};
|
use crate::{Color, Error, GameResult};
|
||||||
use nom::{
|
use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
bytes::complete::{escaped_transform, tag, take_until1},
|
bytes::complete::{escaped_transform, tag},
|
||||||
character::complete::{alpha1, digit1, multispace0, multispace1, none_of},
|
character::complete::{alpha1, multispace0, multispace1, none_of},
|
||||||
combinator::{opt, value},
|
combinator::{opt, value},
|
||||||
error::ParseError,
|
|
||||||
multi::{many0, many1, separated_list1},
|
multi::{many0, many1, separated_list1},
|
||||||
IResult, Parser,
|
IResult,
|
||||||
};
|
};
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
@ -28,12 +27,6 @@ impl From<ParseIntError> for ParseSizeError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Double {
|
|
||||||
Normal,
|
|
||||||
Emphasized,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum GameType {
|
pub enum GameType {
|
||||||
Go,
|
Go,
|
||||||
|
@ -238,18 +231,6 @@ pub struct Node {
|
||||||
pub next: Vec<Node>,
|
pub next: Vec<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
|
||||||
pub fn move_(&self) -> Option<(Color, String)> {
|
|
||||||
self.properties
|
|
||||||
.iter()
|
|
||||||
.filter_map(|prop| match prop {
|
|
||||||
Property::Move(val) => Some(val.clone()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for Node {
|
impl ToString for Node {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
let props = self
|
let props = self
|
||||||
|
@ -275,6 +256,21 @@ impl ToString for Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
impl Node {
|
||||||
|
pub fn find_prop(&self, ident: &str) -> Option<Property> {
|
||||||
|
self.properties
|
||||||
|
.iter()
|
||||||
|
.find(|prop| prop.ident == ident)
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next<'a>(&'a self) -> Option<&'a Node> {
|
||||||
|
self.next.get(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// KO
|
// KO
|
||||||
// MN
|
// MN
|
||||||
// N
|
// N
|
||||||
|
@ -297,10 +293,10 @@ impl ToString for Node {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Property {
|
pub enum Property {
|
||||||
// B, W
|
// B, W
|
||||||
Move((Color, String)),
|
Move((Color, Position)),
|
||||||
|
|
||||||
// C
|
// C
|
||||||
Comment(String),
|
Comment(Vec<String>),
|
||||||
|
|
||||||
// BM
|
// BM
|
||||||
BadMove,
|
BadMove,
|
||||||
|
@ -321,7 +317,7 @@ pub enum Property {
|
||||||
Charset(String),
|
Charset(String),
|
||||||
|
|
||||||
// FF
|
// FF
|
||||||
FileFormat(i32),
|
FileFormat(u8),
|
||||||
|
|
||||||
// GM
|
// GM
|
||||||
GameType(GameType),
|
GameType(GameType),
|
||||||
|
@ -431,16 +427,30 @@ pub enum Property {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct UnknownProperty {
|
pub struct UnknownProperty {
|
||||||
ident: String,
|
ident: String,
|
||||||
value: String,
|
values: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Property {
|
||||||
|
pub ident: String,
|
||||||
|
pub values: Vec<String>,
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
impl ToString for Property {
|
impl ToString for Property {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Property::Move((color, position)) => {
|
Property::Move((color, position)) => {
|
||||||
format!("{}[{}]", color.abbreviation(), position)
|
format!("{}[{}]", color.abbreviation(), position.0)
|
||||||
}
|
}
|
||||||
Property::Comment(value) => format!("C[{}]", value),
|
Property::Comment(values) => format!(
|
||||||
|
"C{}",
|
||||||
|
values
|
||||||
|
.iter()
|
||||||
|
.map(|v| format!("[{}]", v))
|
||||||
|
.collect::<String>()
|
||||||
|
),
|
||||||
Property::BadMove => "BM[]".to_owned(),
|
Property::BadMove => "BM[]".to_owned(),
|
||||||
Property::DoubtfulMove => "DO[]".to_owned(),
|
Property::DoubtfulMove => "DO[]".to_owned(),
|
||||||
Property::InterestingMove => "IT[]".to_owned(),
|
Property::InterestingMove => "IT[]".to_owned(),
|
||||||
|
@ -494,10 +504,18 @@ impl ToString for Property {
|
||||||
Property::User(value) => format!("US[{}]", value),
|
Property::User(value) => format!("US[{}]", value),
|
||||||
Property::WhiteRank(value) => format!("WR[{}]", value),
|
Property::WhiteRank(value) => format!("WR[{}]", value),
|
||||||
Property::WhiteTeam(value) => format!("WT[{}]", value),
|
Property::WhiteTeam(value) => format!("WT[{}]", value),
|
||||||
Property::Unknown(UnknownProperty { ident, value }) => {
|
Property::Unknown(UnknownProperty { ident, values }) => {
|
||||||
format!("{}[{}]", ident, value)
|
let values = values
|
||||||
|
.iter()
|
||||||
|
.map(|val| format!("[{}]", val))
|
||||||
|
.collect::<String>();
|
||||||
|
format!("{}{}", ident, values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
let values = self
|
||||||
|
.collect::<String>();
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,133 +563,77 @@ fn parse_property<'a, E: nom::error::ParseError<&'a str>>(
|
||||||
) -> IResult<&'a str, Property, E> {
|
) -> IResult<&'a str, Property, E> {
|
||||||
let (input, _) = multispace0(input)?;
|
let (input, _) = multispace0(input)?;
|
||||||
let (input, ident) = alpha1(input)?;
|
let (input, ident) = alpha1(input)?;
|
||||||
|
let (input, values) = many1(parse_propval)(input)?;
|
||||||
|
let (input, _) = multispace0(input)?;
|
||||||
|
|
||||||
let (input, prop) = match ident {
|
let values = values
|
||||||
"W" => parse_propval(parse_move(Color::White))(input)?,
|
.into_iter()
|
||||||
"B" => parse_propval(parse_move(Color::Black))(input)?,
|
.map(|v| v.to_owned())
|
||||||
"C" => parse_propval(parse_comment())(input)?,
|
.collect::<Vec<String>>();
|
||||||
"BM" => discard_propval().map(|_| Property::BadMove).parse(input)?,
|
|
||||||
"DO" => discard_propval()
|
let prop = match ident {
|
||||||
.map(|_| Property::DoubtfulMove)
|
"W" => Property::Move((Color::White, Position(values[0].clone()))),
|
||||||
.parse(input)?,
|
"B" => Property::Move((Color::Black, Position(values[0].clone()))),
|
||||||
"IT" => discard_propval()
|
"C" => Property::Comment(values),
|
||||||
.map(|_| Property::InterestingMove)
|
"BM" => Property::BadMove,
|
||||||
.parse(input)?,
|
"DO" => Property::DoubtfulMove,
|
||||||
"TE" => discard_propval().map(|_| Property::Tesuji).parse(input)?,
|
"IT" => Property::InterestingMove,
|
||||||
"AP" => parse_propval(parse_simple_text().map(Property::Application))(input)?,
|
"TE" => Property::Tesuji,
|
||||||
"CA" => parse_propval(parse_simple_text().map(Property::Charset))(input)?,
|
"AP" => Property::Application(values.join(",")),
|
||||||
"FF" => parse_propval(parse_number().map(Property::FileFormat))(input)?,
|
"CA" => Property::Charset(values.join(",")),
|
||||||
"GM" => unimplemented!(),
|
"FF" => Property::FileFormat(values.join("").parse::<u8>().unwrap()),
|
||||||
|
"GM" => Property::GameType(GameType::from(values.join("").as_ref())),
|
||||||
"ST" => unimplemented!(),
|
"ST" => unimplemented!(),
|
||||||
"SZ" => unimplemented!(),
|
"SZ" => Property::BoardSize(Size::try_from(values.join("").as_ref()).unwrap()),
|
||||||
"DM" => discard_propval()
|
"DM" => Property::EvenResult,
|
||||||
.map(|_| Property::EvenResult)
|
"GB" => Property::GoodForBlack,
|
||||||
.parse(input)?,
|
"GW" => Property::GoodForWhite,
|
||||||
"GB" => discard_propval()
|
"UC" => Property::UnclearResult,
|
||||||
.map(|_| Property::GoodForBlack)
|
"V" => Property::Value(values.join("").parse::<f32>().unwrap()),
|
||||||
.parse(input)?,
|
"AN" => Property::Annotator(values.join("")),
|
||||||
"GW" => discard_propval()
|
"BR" => Property::BlackRank(values.join("")),
|
||||||
.map(|_| Property::GoodForWhite)
|
"BT" => Property::BlackTeam(values.join("")),
|
||||||
.parse(input)?,
|
"CP" => Property::Copyright(values.join("")),
|
||||||
"UC" => discard_propval()
|
|
||||||
.map(|_| Property::UnclearResult)
|
|
||||||
.parse(input)?,
|
|
||||||
"V" => unimplemented!(),
|
|
||||||
"AN" => parse_propval(parse_simple_text().map(Property::Annotator))(input)?,
|
|
||||||
"BR" => parse_propval(parse_simple_text().map(Property::BlackRank))(input)?,
|
|
||||||
"BT" => parse_propval(parse_simple_text().map(Property::BlackTeam))(input)?,
|
|
||||||
"CP" => parse_propval(parse_simple_text().map(Property::Copyright))(input)?,
|
|
||||||
"DT" => unimplemented!(),
|
"DT" => unimplemented!(),
|
||||||
"EV" => parse_propval(parse_simple_text().map(Property::EventName))(input)?,
|
"EV" => Property::EventName(values.join("")),
|
||||||
"GN" => parse_propval(parse_simple_text().map(Property::GameName))(input)?,
|
"GN" => Property::GameName(values.join("")),
|
||||||
"GC" => parse_propval(parse_simple_text().map(Property::ExtraGameInformation))(input)?,
|
"GC" => Property::ExtraGameInformation(values.join("")),
|
||||||
"ON" => parse_propval(parse_simple_text().map(Property::GameOpening))(input)?,
|
"ON" => Property::GameOpening(values.join("")),
|
||||||
"OT" => parse_propval(parse_simple_text().map(Property::Overtime))(input)?,
|
"OT" => Property::Overtime(values.join("")),
|
||||||
"PB" => parse_propval(parse_simple_text().map(Property::BlackPlayer))(input)?,
|
"PB" => Property::BlackPlayer(values.join("")),
|
||||||
"PC" => parse_propval(parse_simple_text().map(Property::GameLocation))(input)?,
|
"PC" => Property::GameLocation(values.join("")),
|
||||||
"PW" => parse_propval(parse_simple_text().map(Property::WhitePlayer))(input)?,
|
"PW" => Property::WhitePlayer(values.join("")),
|
||||||
"RE" => unimplemented!(),
|
"RE" => Property::Result(GameResult::try_from(values.join("").as_ref()).unwrap()),
|
||||||
"RO" => parse_propval(parse_simple_text().map(Property::Round))(input)?,
|
"RO" => Property::Round(values.join("")),
|
||||||
"RU" => parse_propval(parse_simple_text().map(Property::Ruleset))(input)?,
|
"RU" => Property::Ruleset(values.join("")),
|
||||||
"SO" => parse_propval(parse_simple_text().map(Property::Source))(input)?,
|
"SO" => Property::Source(values.join("")),
|
||||||
"TM" => unimplemented!(),
|
"TM" => unimplemented!(),
|
||||||
"US" => parse_propval(parse_simple_text().map(Property::User))(input)?,
|
"US" => Property::User(values.join("")),
|
||||||
"WR" => parse_propval(parse_simple_text().map(Property::WhiteRank))(input)?,
|
"WR" => Property::WhiteRank(values.join("")),
|
||||||
"WT" => parse_propval(parse_simple_text().map(Property::WhiteTeam))(input)?,
|
"WT" => Property::WhiteTeam(values.join("")),
|
||||||
_ => parse_propval(parse_simple_text().map(|value| {
|
_ => Property::Unknown(UnknownProperty {
|
||||||
Property::Unknown(UnknownProperty {
|
|
||||||
ident: ident.to_owned(),
|
ident: ident.to_owned(),
|
||||||
value,
|
values,
|
||||||
})
|
}),
|
||||||
}))(input)?,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((input, prop))
|
Ok((input, prop))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_comment<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a str, Property, E> {
|
|
||||||
parse_text().map(|text| Property::Comment(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_move<'a, E: nom::error::ParseError<&'a str>>(
|
|
||||||
color: Color,
|
|
||||||
) -> impl FnMut(&'a str) -> IResult<&'a str, Property, E> {
|
|
||||||
{
|
|
||||||
let color = color.clone();
|
|
||||||
move |input: &'a str| {
|
|
||||||
take_until1("]")
|
|
||||||
.map(|text: &'a str| Property::Move((color.clone(), text.to_owned())))
|
|
||||||
.parse(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_propval<'a, E: nom::error::ParseError<&'a str>>(
|
fn parse_propval<'a, E: nom::error::ParseError<&'a str>>(
|
||||||
mut parser: impl Parser<&'a str, Property, E>,
|
input: &'a str,
|
||||||
) -> impl FnMut(&'a str) -> IResult<&'a str, Property, E> {
|
) -> IResult<&'a str, String, E> {
|
||||||
move |input| {
|
|
||||||
let (input, _) = multispace0(input)?;
|
let (input, _) = multispace0(input)?;
|
||||||
let (input, _) = tag("[")(input)?;
|
let (input, _) = tag("[")(input)?;
|
||||||
let (input, value) = parser.parse(input)?;
|
let (input, value) = parse_propval_text(input)?;
|
||||||
let (input, _) = tag("]")(input)?;
|
let (input, _) = tag("]")(input)?;
|
||||||
|
|
||||||
Ok((input, value))
|
Ok((input, value.unwrap_or(String::new())))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discard_propval<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a str, (), E> {
|
fn parse_propval_text<'a, E: nom::error::ParseError<&'a str>>(
|
||||||
|input| {
|
input: &'a str,
|
||||||
let (input, _) = multispace0(input)?;
|
) -> IResult<&'a str, Option<String>, E> {
|
||||||
let (input, _) = tag("[")(input)?;
|
|
||||||
let (input, _) = parse_text().parse(input)?;
|
|
||||||
let (input, _) = tag("]")(input)?;
|
|
||||||
Ok((input, ()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_number<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, i32, E> {
|
|
||||||
|input| {
|
|
||||||
let (input, sign) = opt(alt((tag("+"), tag("-"))))(input)?;
|
|
||||||
let (input, value) = digit1(input)?;
|
|
||||||
let mult = if sign == Some("-") { -1 } else { 1 };
|
|
||||||
Ok((input, value.parse::<i32>().unwrap() * mult))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_real<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, f32, E> {
|
|
||||||
|input| unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_double<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, Double, E> {
|
|
||||||
|input| unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_simple_text<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, String, E> {
|
|
||||||
|input| unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_text<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, String, E> {
|
|
||||||
|input| {
|
|
||||||
let (input, value) = opt(escaped_transform(
|
let (input, value) = opt(escaped_transform(
|
||||||
none_of("\\]"),
|
none_of("\\]"),
|
||||||
'\\',
|
'\\',
|
||||||
|
@ -681,8 +643,7 @@ fn parse_text<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, String, E> {
|
||||||
value("", tag("\n")),
|
value("", tag("\n")),
|
||||||
)),
|
)),
|
||||||
))(input)?;
|
))(input)?;
|
||||||
Ok((input, value.unwrap_or("".to_owned())))
|
Ok((input, value.map(|v| v.to_owned())))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -714,7 +675,13 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn it_can_parse_properties() {
|
fn it_can_parse_properties() {
|
||||||
let (_, prop) = parse_property::<nom::error::VerboseError<&str>>("C[a]").unwrap();
|
let (_, prop) = parse_property::<nom::error::VerboseError<&str>>("C[a]").unwrap();
|
||||||
assert_eq!(prop, Property::Comment("a".to_owned()));
|
assert_eq!(prop, Property::Comment(vec!["a".to_owned()]));
|
||||||
|
|
||||||
|
let (_, prop) = parse_property::<nom::error::VerboseError<&str>>("C[a][b][c]").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
prop,
|
||||||
|
Property::Comment(vec!["a".to_owned(), "b".to_owned(), "c".to_owned()])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -724,7 +691,7 @@ mod test {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
node,
|
node,
|
||||||
Node {
|
Node {
|
||||||
properties: vec![Property::Move((Color::Black, "ab".to_owned()))],
|
properties: vec![Property::Move((Color::Black, Position("ab".to_owned())))],
|
||||||
next: vec![]
|
next: vec![]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -736,13 +703,13 @@ mod test {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
node,
|
node,
|
||||||
Node {
|
Node {
|
||||||
properties: vec![Property::Move((Color::Black, "ab".to_owned()))],
|
properties: vec![Property::Move((Color::Black, Position("ab".to_owned())))],
|
||||||
next: vec![Node {
|
next: vec![Node {
|
||||||
properties: vec![Property::Move((Color::White, "dp".to_owned()))],
|
properties: vec![Property::Move((Color::White, Position("dp".to_owned())))],
|
||||||
next: vec![Node {
|
next: vec![Node {
|
||||||
properties: vec![
|
properties: vec![
|
||||||
Property::Move((Color::Black, "pq".to_owned())),
|
Property::Move((Color::Black, Position("pq".to_owned()))),
|
||||||
Property::Comment("some comments".to_owned())
|
Property::Comment(vec!["some comments".to_owned()])
|
||||||
],
|
],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
}]
|
}]
|
||||||
|
@ -760,13 +727,13 @@ mod test {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sequence,
|
sequence,
|
||||||
Node {
|
Node {
|
||||||
properties: vec![Property::Move((Color::Black, "ab".to_owned()))],
|
properties: vec![Property::Move((Color::Black, Position("ab".to_owned())))],
|
||||||
next: vec![Node {
|
next: vec![Node {
|
||||||
properties: vec![Property::Move((Color::White, "dp".to_owned()))],
|
properties: vec![Property::Move((Color::White, Position("dp".to_owned())))],
|
||||||
next: vec![Node {
|
next: vec![Node {
|
||||||
properties: vec![
|
properties: vec![
|
||||||
Property::Move((Color::Black, "pq".to_owned())),
|
Property::Move((Color::Black, Position("pq".to_owned()))),
|
||||||
Property::Comment("some comments".to_owned())
|
Property::Comment(vec!["some comments".to_owned()])
|
||||||
],
|
],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
}]
|
}]
|
||||||
|
@ -781,18 +748,18 @@ mod test {
|
||||||
let (_, tree) = parse_tree::<nom::error::VerboseError<&str>>(text).unwrap();
|
let (_, tree) = parse_tree::<nom::error::VerboseError<&str>>(text).unwrap();
|
||||||
|
|
||||||
let expected = Node {
|
let expected = Node {
|
||||||
properties: vec![Property::Comment("a".to_owned())],
|
properties: vec![Property::Comment(vec!["a".to_owned()])],
|
||||||
next: vec![Node {
|
next: vec![Node {
|
||||||
properties: vec![Property::Comment("b".to_owned())],
|
properties: vec![Property::Comment(vec!["b".to_owned()])],
|
||||||
next: vec![
|
next: vec![
|
||||||
Node {
|
Node {
|
||||||
properties: vec![Property::Comment("c".to_owned())],
|
properties: vec![Property::Comment(vec!["c".to_owned()])],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
},
|
},
|
||||||
Node {
|
Node {
|
||||||
properties: vec![Property::Comment("d".to_owned())],
|
properties: vec![Property::Comment(vec!["d".to_owned()])],
|
||||||
next: vec![Node {
|
next: vec![Node {
|
||||||
properties: vec![Property::Comment("e".to_owned())],
|
properties: vec![Property::Comment(vec!["e".to_owned()])],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
|
@ -808,49 +775,49 @@ mod test {
|
||||||
let (_, tree) = parse_tree::<nom::error::VerboseError<&str>>(EXAMPLE).unwrap();
|
let (_, tree) = parse_tree::<nom::error::VerboseError<&str>>(EXAMPLE).unwrap();
|
||||||
|
|
||||||
let j = Node {
|
let j = Node {
|
||||||
properties: vec![Property::Comment("j".to_owned())],
|
properties: vec![Property::Comment(vec!["j".to_owned()])],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
let i = Node {
|
let i = Node {
|
||||||
properties: vec![Property::Comment("i".to_owned())],
|
properties: vec![Property::Comment(vec!["i".to_owned()])],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
let h = Node {
|
let h = Node {
|
||||||
properties: vec![Property::Comment("h".to_owned())],
|
properties: vec![Property::Comment(vec!["h".to_owned()])],
|
||||||
next: vec![i],
|
next: vec![i],
|
||||||
};
|
};
|
||||||
let g = Node {
|
let g = Node {
|
||||||
properties: vec![Property::Comment("g".to_owned())],
|
properties: vec![Property::Comment(vec!["g".to_owned()])],
|
||||||
next: vec![h],
|
next: vec![h],
|
||||||
};
|
};
|
||||||
let f = Node {
|
let f = Node {
|
||||||
properties: vec![Property::Comment("f".to_owned())],
|
properties: vec![Property::Comment(vec!["f".to_owned()])],
|
||||||
next: vec![g, j],
|
next: vec![g, j],
|
||||||
};
|
};
|
||||||
let e = Node {
|
let e = Node {
|
||||||
properties: vec![Property::Comment("e".to_owned())],
|
properties: vec![Property::Comment(vec!["e".to_owned()])],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
let d = Node {
|
let d = Node {
|
||||||
properties: vec![Property::Comment("d".to_owned())],
|
properties: vec![Property::Comment(vec!["d".to_owned()])],
|
||||||
next: vec![e],
|
next: vec![e],
|
||||||
};
|
};
|
||||||
let c = Node {
|
let c = Node {
|
||||||
properties: vec![Property::Comment("c".to_owned())],
|
properties: vec![Property::Comment(vec!["c".to_owned()])],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
let b = Node {
|
let b = Node {
|
||||||
properties: vec![Property::Comment("b".to_owned())],
|
properties: vec![Property::Comment(vec!["b".to_owned()])],
|
||||||
next: vec![c, d],
|
next: vec![c, d],
|
||||||
};
|
};
|
||||||
let a = Node {
|
let a = Node {
|
||||||
properties: vec![Property::Comment("a".to_owned())],
|
properties: vec![Property::Comment(vec!["a".to_owned()])],
|
||||||
next: vec![b],
|
next: vec![b],
|
||||||
};
|
};
|
||||||
let expected = Node {
|
let expected = Node {
|
||||||
properties: vec![
|
properties: vec![
|
||||||
Property::FileFormat(4),
|
Property::FileFormat(4),
|
||||||
Property::Comment("root".to_owned()),
|
Property::Comment(vec!["root".to_owned()]),
|
||||||
],
|
],
|
||||||
next: vec![a, f],
|
next: vec![a, f],
|
||||||
};
|
};
|
||||||
|
@ -872,59 +839,58 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_parses_propvals() {
|
fn it_parses_propvals() {
|
||||||
let (_, propval) = parse_propval::<nom::error::VerboseError<&str>>(parse_comment())
|
let (_, propval) = parse_propval::<nom::error::VerboseError<&str>>("[]").unwrap();
|
||||||
.parse("[]")
|
assert_eq!(propval, "".to_owned());
|
||||||
.unwrap();
|
|
||||||
assert_eq!(propval, Property::Comment("".to_owned()));
|
|
||||||
|
|
||||||
let (_, propval) = parse_propval::<nom::error::VerboseError<&str>>(parse_comment())
|
let (_, propval) =
|
||||||
.parse("[normal propval]")
|
parse_propval::<nom::error::VerboseError<&str>>("[normal propval]").unwrap();
|
||||||
.unwrap();
|
assert_eq!(propval, "normal propval".to_owned());
|
||||||
assert_eq!(propval, Property::Comment("normal propval".to_owned()));
|
|
||||||
|
|
||||||
let (_, propval) = parse_propval::<nom::error::VerboseError<&str>>(parse_comment())
|
let (_, propval) =
|
||||||
.parse(r"[need an [escape\] in the propval]")
|
parse_propval::<nom::error::VerboseError<&str>>(r"[need an [escape\] in the propval]")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(propval, "need an [escape] in the propval".to_owned());
|
||||||
propval,
|
|
||||||
Property::Comment("need an [escape] in the propval".to_owned())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_parses_propvals_with_hard_linebreaks() {
|
fn it_parses_propvals_with_hard_linebreaks() {
|
||||||
let (_, propval) = parse_text::<nom::error::VerboseError<&str>>()
|
let (_, propval) = parse_propval_text::<nom::error::VerboseError<&str>>(
|
||||||
.parse(
|
|
||||||
"There are hard linebreaks & soft linebreaks.
|
"There are hard linebreaks & soft linebreaks.
|
||||||
Soft linebreaks...",
|
Soft linebreaks...",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
propval,
|
propval,
|
||||||
|
Some(
|
||||||
"There are hard linebreaks & soft linebreaks.
|
"There are hard linebreaks & soft linebreaks.
|
||||||
Soft linebreaks..."
|
Soft linebreaks..."
|
||||||
|
.to_owned()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_parses_propvals_with_escaped_closing_brackets() {
|
fn it_parses_propvals_with_escaped_closing_brackets() {
|
||||||
let (_, propval) = parse_text::<nom::error::VerboseError<&str>>()
|
let (_, propval) =
|
||||||
.parse(r"escaped closing \] bracket")
|
parse_propval_text::<nom::error::VerboseError<&str>>(r"escaped closing \] bracket")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(propval, r"escaped closing ] bracket".to_owned());
|
assert_eq!(
|
||||||
|
propval,
|
||||||
|
Some(r"escaped closing ] bracket".to_owned()).to_owned()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_parses_propvals_with_soft_linebreaks() {
|
fn it_parses_propvals_with_soft_linebreaks() {
|
||||||
let (_, propval) = parse_text::<nom::error::VerboseError<&str>>()
|
let (_, propval) = parse_propval_text::<nom::error::VerboseError<&str>>(
|
||||||
.parse(
|
|
||||||
r"Soft linebreaks are linebreaks preceeded by '\\' like this one >o\
|
r"Soft linebreaks are linebreaks preceeded by '\\' like this one >o\
|
||||||
k<. Hard line breaks are all other linebreaks.",
|
k<. Hard line breaks are all other linebreaks.",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
propval,
|
propval,
|
||||||
"Soft linebreaks are linebreaks preceeded by '\\' like this one >ok<. Hard line breaks are all other linebreaks."
|
Some("Soft linebreaks are linebreaks preceeded by '\\' like this one >ok<. Hard line breaks are all other linebreaks.".to_owned())
|
||||||
|
.to_owned()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue