Set up SGF reading and start on the game database #47
|
@ -67,7 +67,14 @@
|
||||||
// PM
|
// PM
|
||||||
// VW
|
// 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;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub enum Warning {}
|
pub enum Warning {}
|
||||||
|
@ -148,13 +155,16 @@ pub enum GameType {
|
||||||
|
|
||||||
struct Sequence(Node);
|
struct Sequence(Node);
|
||||||
|
|
||||||
|
/*
|
||||||
struct Node {
|
struct Node {
|
||||||
// properties
|
// properties
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
struct Property {
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Property {
|
||||||
ident: String,
|
ident: String,
|
||||||
value: Vec<PropValue>,
|
values: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PropType {
|
enum PropType {
|
||||||
|
@ -177,12 +187,84 @@ enum PropValue {
|
||||||
Stone,
|
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 preserve unknown properties
|
||||||
// note: must fix or preserve illegally formatted game-info properties
|
// note: must fix or preserve illegally formatted game-info properties
|
||||||
// note: must correct or delete illegally foramtted properties, but display a warning
|
// note: must correct or delete illegally foramtted properties, but display a warning
|
||||||
|
/*
|
||||||
pub fn parse_sgf(input: &str) -> Result<(GameTree, Vec<Warning>), ParseError> {
|
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 {
|
pub fn add(left: usize, right: usize) -> usize {
|
||||||
left + right
|
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]))";
|
(;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)) {
|
fn with_examples(f: impl FnOnce(GameTree, GameTree)) {
|
||||||
let (example_1, _) = parse_sgf(EXAMPLE_1).unwrap();
|
let (example_1, _) = parse_sgf(EXAMPLE_1).unwrap();
|
||||||
let (example_2, _) = parse_sgf(EXAMPLE_2).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());
|
assert_eq!(ex_2.text, EXAMPLE_2.to_owned());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue