Set up SGF reading and start on the game database #47
|
@ -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<PropValue>,
|
||||
values: Vec<String>,
|
||||
}
|
||||
|
||||
enum PropType {
|
||||
|
@ -177,12 +187,84 @@ enum PropValue {
|
|||
Stone,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Tree {
|
||||
sequence: Vec<Node>,
|
||||
sub_sequences: Vec<Tree>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Node {
|
||||
properties: Vec<Property>,
|
||||
}
|
||||
|
||||
// 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<Warning>), 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::<Vec<String>>();
|
||||
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());
|
||||
});
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue