diff --git a/sgf/src/parser.rs b/sgf/src/parser.rs index 7ff1d23..3fc59f9 100644 --- a/sgf/src/parser.rs +++ b/sgf/src/parser.rs @@ -620,9 +620,11 @@ fn parse_property<'a, E: nom::error::ParseError<&'a str>>( "AP" => parse_propval(parse_simple_text().map(Property::Application))(input)?, "CA" => parse_propval(parse_simple_text().map(Property::Charset))(input)?, "FF" => parse_propval(parse_number().map(Property::FileFormat))(input)?, - "GM" => unimplemented!(), - "ST" => unimplemented!(), - "SZ" => unimplemented!(), + "GM" => parse_propval(parse_gametype().map(Property::GameType))(input)?, + "ST" => discard_propval() + .map(|_| Property::VariationDisplay) + .parse(input)?, + "SZ" => parse_propval(parse_size().map(Property::BoardSize))(input)?, "DM" => discard_propval() .map(|_| Property::Evaluation(Evaluation::Even)) .parse(input)?, @@ -653,7 +655,9 @@ fn parse_property<'a, E: nom::error::ParseError<&'a str>>( "RO" => parse_propval(parse_simple_text().map(Property::Round))(input)?, "RU" => parse_propval(parse_simple_text().map(Property::Ruleset))(input)?, "SO" => parse_propval(parse_simple_text().map(Property::Source))(input)?, - "TM" => unimplemented!(), + "TM" => parse_propval( + parse_real().map(|seconds| Property::TimeLimit(Duration::from_secs(seconds as u64))), + )(input)?, "US" => parse_propval(parse_simple_text().map(Property::User))(input)?, "WR" => parse_propval(parse_simple_text().map(Property::WhiteRank))(input)?, "WT" => parse_propval(parse_simple_text().map(Property::WhiteTeam))(input)?, @@ -674,7 +678,7 @@ fn parse_comment<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a st fn parse_move<'a, E: nom::error::ParseError<&'a str>>( color: Color, -) -> impl FnMut(&'a str) -> IResult<&'a str, Property, E> { +) -> impl Parser<&'a str, Property, E> { { let color = color.clone(); move |input: &'a str| { @@ -685,6 +689,14 @@ fn parse_move<'a, E: nom::error::ParseError<&'a str>>( } } +fn parse_gametype<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a str, GameType, E> { + |input: &'a str| { + take_until1("]") + .map(|text: &'a str| GameType::from(text)) + .parse(input) + } +} + fn parse_time_left<'a, E: ParseError<&'a str>>( color: Color, ) -> impl FnMut(&'a str) -> IResult<&'a str, Property, E> { @@ -701,6 +713,21 @@ fn parse_time_left<'a, E: ParseError<&'a str>>( } } +pub fn parse_size<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a str, Size, E> { + |input: &'a str| { + let (input, dimensions) = separated_list1(tag(":"), digit1)(input)?; + let (width, height) = match dimensions.as_slice() { + [width] => (width.parse::().unwrap(), width.parse::().unwrap()), + [width, height] => ( + width.parse::().unwrap(), + height.parse::().unwrap(), + ), + _ => (19, 19), + }; + Ok((input, Size { width, height })) + } +} + fn parse_propval<'a, E: nom::error::ParseError<&'a str>>( mut parser: impl Parser<&'a str, Property, E>, ) -> impl FnMut(&'a str) -> IResult<&'a str, Property, E> { @@ -734,7 +761,24 @@ fn parse_number<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, i32, E> { } fn parse_real<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, f32, E> { - |input| unimplemented!() + |input| { + let (input, sign) = opt(alt((tag("+"), tag("-"))))(input)?; + let (input, whole_value) = digit1(input)?; + let (input, fractional_value) = opt(|input| { + let (input, _) = tag(".")(input)?; + let (input, fractional_value) = digit1(input)?; + Ok((input, format!(".{}", fractional_value))) + })(input)?; + let value = format!( + "{}{}{}", + sign.unwrap_or("+"), + whole_value, + fractional_value.unwrap_or("".to_owned()) + ) + .parse::() + .unwrap(); + Ok((input, value)) + } } fn parse_double<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, Double, E> { @@ -742,7 +786,18 @@ fn parse_double<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, Double, E> } fn parse_simple_text<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, String, E> { - |input| unimplemented!() + |input| { + let (input, value) = opt(escaped_transform( + none_of("\\]"), + '\\', + alt(( + value("]", tag("]")), + value("\\", tag("\\")), + value("", tag("\n")), + )), + ))(input)?; + Ok((input, value.unwrap_or("".to_owned()))) + } } fn parse_text<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, String, E> { @@ -760,23 +815,6 @@ fn parse_text<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, String, E> { } } -/* -pub fn parse_size<'a, E: nom::error::ParseError<&'a str>>( - input: &'a str, -) -> IResult<&'a str, Size, E> { - let (input, dimensions) = separated_list1(tag(":"), digit1)(input)?; - let (width, height) = match dimensions.as_slice() { - [width] => (width.parse::().unwrap(), width.parse::().unwrap()), - [width, height] => ( - width.parse::().unwrap(), - height.parse::().unwrap(), - ), - _ => (19, 19), - }; - Ok((input, Size { width, height })) -} -*/ - #[cfg(test)] mod test { use super::*;