use pest::Parser;
use pest::iterators::Pair;
use pest_derive::*;
use crate::*;
#[derive(Parser)]
#[grammar = "../sgf.pest"]
struct SGFParser;
pub fn parse(input: &str) -> Result<GameTree, SgfError> {
let mut parse_roots =
SGFParser::parse(Rule::game_tree, input).map_err(SgfError::parse_error)?;
if let Some(game_tree) = parse_roots.next() {
let tree = parse_pair(game_tree);
let game = create_game_tree(tree, true)?;
Ok(game)
} else {
Ok(GameTree::default())
}
}
fn create_game_tree(parser_node: ParserNode<'_>, is_root: bool) -> Result<GameTree, SgfError> {
if let ParserNode::GameTree(tree_nodes) = parser_node {
let mut nodes: Vec<GameNode> = vec![];
let mut variations: Vec<GameTree> = vec![];
for node in tree_nodes {
match node {
ParserNode::Sequence(sequence_nodes) => {
nodes.extend(parse_sequence(sequence_nodes)?)
}
ParserNode::GameTree(_) => {
variations.push(create_game_tree(node, false)?);
}
_ => {
return Err(SgfErrorKind::ParseError.into());
}
}
}
let mut iter = nodes.iter();
if is_root {
iter.next();
}
let in_valid = iter.any(|node| node.tokens.iter().any(|token| token.is_root_token()));
if in_valid {
Err(SgfErrorKind::InvalidRootTokenPlacement.into())
} else {
Ok(GameTree { nodes, variations })
}
} else {
Err(SgfErrorKind::ParseError.into())
}
}
fn parse_sequence(sequence_nodes: Vec<ParserNode<'_>>) -> Result<Vec<GameNode>, SgfError> {
let mut nodes = vec![];
for sequence_node in &sequence_nodes {
if let ParserNode::Node(node_tokens) = sequence_node {
let mut tokens: Vec<SgfToken> = vec![];
for t in node_tokens {
if let ParserNode::Token(new_tokens) = t {
tokens.extend(new_tokens.clone());
} else {
return Err(SgfErrorKind::ParseError.into());
}
}
nodes.push(GameNode { tokens });
} else {
return Err(SgfErrorKind::ParseError.into());
}
}
Ok(nodes)
}
#[derive(Debug, PartialEq, Clone)]
enum ParserNode<'a> {
Token(Vec<SgfToken>),
Text(&'a str),
Node(Vec<ParserNode<'a>>),
Sequence(Vec<ParserNode<'a>>),
GameTree(Vec<ParserNode<'a>>),
}
fn parse_pair(pair: Pair<'_, Rule>) -> ParserNode<'_> {
match pair.as_rule() {
Rule::game_tree => ParserNode::GameTree(pair.into_inner().map(parse_pair).collect()),
Rule::sequence => ParserNode::Sequence(pair.into_inner().map(parse_pair).collect()),
Rule::node => ParserNode::Node(pair.into_inner().map(parse_pair).collect()),
Rule::property => {
let text_nodes = pair.into_inner().map(parse_pair).collect::<Vec<_>>();
let (_, ts) = text_nodes
.iter()
.try_fold((None, vec![]), |(ident, mut tokens), value| {
if let ParserNode::Text(value) = value {
match ident {
None => Some((Some(*value), tokens)),
Some(id) => {
tokens.push(SgfToken::from_pair(id, value));
Some((ident, tokens))
}
}
} else {
None
}
})
.expect(
"Pest parsing guarantee that all properties have an identifier and a value",
);
ParserNode::Token(ts)
}
Rule::property_identifier => ParserNode::Text(pair.as_str()),
Rule::property_value => {
let value = pair.as_str();
let end = value.len() - 1;
ParserNode::Text(&value[1..end])
}
Rule::inner => {
unreachable!();
}
Rule::char => {
unreachable!();
}
Rule::WHITESPACE => {
unreachable!();
}
}
}