From 40c2103dbbb238a63b5825a7488fd0814c30c051 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Thu, 22 Jun 2023 22:45:31 -0400 Subject: [PATCH] Set up node parsing without any interpretation --- go-sgf/src/lib.rs | 269 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 265 insertions(+), 4 deletions(-) diff --git a/go-sgf/src/lib.rs b/go-sgf/src/lib.rs index 38f3f47..47f146f 100644 --- a/go-sgf/src/lib.rs +++ b/go-sgf/src/lib.rs @@ -67,7 +67,14 @@ // PM // VW -use nom; +use nom::{ + bytes::complete::{tag, take_until}, + character::complete::{alpha1, anychar, multispace0}, + combinator::eof, + multi::{many0, many1, many_till}, + sequence::{delimited, terminated}, + IResult, Parser, +}; use thiserror::Error; pub enum Warning {} @@ -148,13 +155,16 @@ pub enum GameType { struct Sequence(Node); +/* struct Node { // properties } +*/ -struct Property { +#[derive(Debug, PartialEq)] +pub struct Property { ident: String, - value: Vec, + values: Vec, } enum PropType { @@ -177,12 +187,84 @@ enum PropValue { Stone, } +#[derive(Debug, PartialEq)] +struct Tree { + sequence: Vec, + sub_sequences: Vec, +} + +#[derive(Debug, PartialEq)] +struct Node { + properties: Vec, +} + // note: must preserve unknown properties // note: must fix or preserve illegally formatted game-info properties // note: must correct or delete illegally foramtted properties, but display a warning +/* pub fn parse_sgf(input: &str) -> Result<(GameTree, Vec), ParseError> { - Err(ParseError::UnknownError) + let (_, gameinfo) = parse_gametree(input).unwrap(); + Ok((gameinfo, vec![])) } +*/ + +fn parse_tree(input: &str) -> IResult<&str, Tree> { + println!("parse_tree: {}", input); + let (input, _) = multispace0(input)?; + delimited(tag("("), parse_sequence, tag(")"))(input) +} + +fn parse_sequence(input: &str) -> IResult<&str, Tree> { + println!("parse_sequence: {}", input); + let (input, _) = multispace0(input)?; + let (input, nodes) = many1(parse_node)(input)?; + let (input, sub_sequences) = many0(parse_tree)(input)?; + + Ok(( + input, + Tree { + sequence: nodes, + sub_sequences, + }, + )) +} + +fn parse_node(input: &str) -> IResult<&str, Node> { + println!("parse_node: {}", input); + let (input, _) = multispace0(input)?; + let (input, _) = tag(";")(input)?; + let (input, properties) = many1(parse_property)(input)?; + Ok((input, Node { properties })) +} + +fn parse_property(input: &str) -> IResult<&str, Property> { + println!("parse_property: {}", input); + let (input, ident) = alpha1(input)?; + let (input, values) = many1(delimited(tag("["), take_until("]"), tag("]")))(input)?; + + let values = values + .into_iter() + .map(|v| v.to_owned()) + .collect::>(); + Ok(( + input, + Property { + ident: ident.to_owned(), + values, + }, + )) +} + +/* +fn parse_gametree(input: &str) -> IResult<&str, GameTree> { + let (input, _) = tag("(;")(input)?; + let (input, properties) = many1(parse_property)(input)?; + let (input, _) = tag(")")(input)?; + println!("properties: {:?}", properties); + Ok((input, unimplemented!())) +} + +*/ pub fn add(left: usize, right: usize) -> usize { left + right @@ -216,6 +298,184 @@ Usually B plays the 3,3 invasion - see variation];W[qo];B[qp] ... (;W[dq]N[wrong direction];B[qo];W[qp]))"; + #[test] + fn it_can_parse_properties() { + let (_, prop) = parse_property("C[a]").unwrap(); + assert_eq!( + prop, + Property { + ident: "C".to_owned(), + values: vec!["a".to_owned()] + } + ); + + let (_, prop) = parse_property("C[a][b][c]").unwrap(); + assert_eq!( + prop, + Property { + ident: "C".to_owned(), + values: vec!["a".to_owned(), "b".to_owned(), "c".to_owned()] + } + ); + } + + #[test] + fn it_can_parse_a_standalone_node() { + let (_, node) = parse_node(";B[ab]").unwrap(); + + assert_eq!( + node, + Node { + properties: vec![Property { + ident: "B".to_owned(), + values: vec!["ab".to_owned()] + }] + } + ); + + let (_, node) = parse_node(";B[ab];W[dp];B[pq]C[some comments]").unwrap(); + + assert_eq!( + node, + Node { + properties: vec![Property { + ident: "B".to_owned(), + values: vec!["ab".to_owned()] + }] + } + ); + } + + #[test] + fn it_can_parse_a_simple_sequence() { + let (_, sequence) = parse_tree("(;B[ab];W[dp];B[pq]C[some comments])").unwrap(); + + assert_eq!( + sequence, + Tree { + sequence: vec![ + Node { + properties: vec![Property { + ident: "B".to_owned(), + values: vec!["ab".to_owned()] + }] + }, + Node { + properties: vec![Property { + ident: "W".to_owned(), + values: vec!["dp".to_owned()] + }] + }, + Node { + properties: vec![ + Property { + ident: "B".to_owned(), + values: vec!["pq".to_owned()] + }, + Property { + ident: "C".to_owned(), + values: vec!["some comments".to_owned()] + } + ] + } + ], + sub_sequences: vec![], + } + ); + } + + #[test] + fn it_can_parse_a_sequence_with_subsequences() { + let text = "(;C[a];C[b](;C[c])(;C[d];C[e]))"; + let (_, sequence) = parse_tree(text).unwrap(); + + let main_sequence = vec![ + Node { + properties: vec![Property { + ident: "C".to_owned(), + values: vec!["a".to_owned()], + }], + }, + Node { + properties: vec![Property { + ident: "C".to_owned(), + values: vec!["b".to_owned()], + }], + }, + ]; + let subsequence_1 = Tree { + sequence: vec![Node { + properties: vec![Property { + ident: "C".to_owned(), + values: vec!["c".to_owned()], + }], + }], + sub_sequences: vec![], + }; + let subsequence_2 = Tree { + sequence: vec![ + Node { + properties: vec![Property { + ident: "C".to_owned(), + values: vec!["d".to_owned()], + }], + }, + Node { + properties: vec![Property { + ident: "C".to_owned(), + values: vec!["e".to_owned()], + }], + }, + ], + sub_sequences: vec![], + }; + + assert_eq!( + sequence, + Tree { + sequence: main_sequence, + sub_sequences: vec![subsequence_1, subsequence_2], + } + ); + } + + #[test] + fn it_can_parse_example_1() { + let (_, ex_tree) = parse_tree(EXAMPLE_1).unwrap(); + assert_eq!(ex_tree.sequence.len(), 1); + + assert_eq!(ex_tree.sequence[0].properties.len(), 2); + assert_eq!( + ex_tree.sequence[0].properties[0], + Property { + ident: "FF".to_owned(), + values: vec!["4".to_owned()] + } + ); + assert_eq!(ex_tree.sub_sequences.len(), 2); + + assert_eq!(ex_tree.sub_sequences[0].sequence.len(), 2); + assert_eq!( + ex_tree.sub_sequences[0].sequence, + vec![ + Node { + properties: vec![Property { + ident: "C".to_owned(), + values: vec!["a".to_owned()] + }] + }, + Node { + properties: vec![Property { + ident: "C".to_owned(), + values: vec!["b".to_owned()] + }] + }, + ] + ); + assert_eq!(ex_tree.sub_sequences[0].sub_sequences.len(), 2); + } + + /* fn with_examples(f: impl FnOnce(GameTree, GameTree)) { let (example_1, _) = parse_sgf(EXAMPLE_1).unwrap(); let (example_2, _) = parse_sgf(EXAMPLE_2).unwrap(); @@ -251,4 +511,5 @@ Usually B plays the 3,3 invasion - see variation];W[qo];B[qp] assert_eq!(ex_2.text, EXAMPLE_2.to_owned()); }); } + */ }