1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use pest::Parser;

use pest::iterators::Pair;
use pest_derive::*;

use crate::*;

#[derive(Parser)]
#[grammar = "../sgf.pest"]
struct SGFParser;

///
/// Main entry point to the library. Parses an SGF string, and returns a `GameTree`.
///
/// Returns an `SgfError` when parsing failed, but it tries to recover from most kind of invalid input and insert `SgfToken::Invalid` or `SgfToken::Unknown` rather than failing
///
/// ```rust
/// use sgf_parser::*;
///
/// let tree: Result<GameTree, SgfError> = parse("(;EV[event]PB[black]PW[white]C[comment];B[aa];W[bb])");
///
/// let tree = tree.unwrap();
/// assert_eq!(tree.count_max_nodes(), 3);
/// ```
///
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())
    }
}

/// Creates a `GameTree` from the Pest result
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())
    }
}

/// Parses a sequence of nodes to be added to a `GameTree`
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)
}

/// Intermediate nodes from parsing the SGF file
#[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!();
        }
    }
}