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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
//! Data information to build the Flat AST
//! And some functions to work with it
//!
//! The AST is a tree, because... it has to be during the parsing
//! and building.
//!
//! But once the input has been processed, we will want to follow
//! the tree in a specific order.
//!
//! It's not complicated visit the elements in a tree, but could
//! be easier if the AST has been "flattened"
//!
//! In order to flatten the tree, is necessary to add a new kind
//! of node. The end 'Rule'
//!

use ast::{self, error, Error};
use idata::cont::IVec;
use std::result::Result;

// -------------------------------------------------------------------------------------
//  T Y P E S

/// Information of a node when ast has been flattened
#[derive(Debug, PartialEq)]
pub enum Node {
    /// The node is terminal (atom) with a name
    Val(String),
    /// Starts a rule
    BeginRule(String),
    /// Ends a rule
    EndRule(String),
    /// Reached end of file
    EOF,
}

impl ast::Node {
    /// Flattening tree
    ///
    /// It's very commont to visit nodes in order
    /// The grammar checked the consistency of input
    /// Flattening the AST, could be interesting in order to process the tree
    ///
    /// ```
    ///    use dynparser::ast::{self, flat};
    ///    
    ///    let ast_before_flatten = ast::Node::Rule((
    ///        "first".to_string(),
    ///        vec![
    ///            ast::Node::Rule((
    ///                "node1".to_string(),
    ///                vec![
    ///                    ast::Node::Val("hello".to_string()),
    ///                    ast::Node::Rule(("node1.1".to_string(), vec![ast::Node::Val(" ".to_string())])),
    ///                ],
    ///            )),
    ///            ast::Node::Rule((
    ///                "node2".to_string(),
    ///                vec![ast::Node::Val("world".to_string())],
    ///            )),
    ///        ],
    ///    ));
    ///
    ///    let vec_after_flatten =
    ///        vec![
    ///            flat::Node::BeginRule("first".to_string()),
    ///            flat::Node::BeginRule("node1".to_string()),
    ///            flat::Node::Val("hello".to_string()),
    ///            flat::Node::BeginRule("node1.1".to_string()),
    ///            flat::Node::Val(" ".to_string()),
    ///            flat::Node::EndRule("node1.1".to_string()),
    ///            flat::Node::EndRule("node1".to_string()),
    ///            flat::Node::BeginRule("node2".to_string()),
    ///            flat::Node::Val("world".to_string()),
    ///            flat::Node::EndRule("node2".to_string()),
    ///            flat::Node::EndRule("first".to_string()),
    ///        ];
    ///
    ///    assert!(ast_before_flatten.flatten() == vec_after_flatten)
    ///```
    pub fn flatten(&self) -> Vec<Node> {
        fn flatten_acc(acc: Vec<Node>, next: &ast::Node) -> Vec<Node> {
            match next {
                ast::Node::EOF => acc,
                ast::Node::Val(v) => acc.ipush(Node::Val(v.clone())),
                ast::Node::Rule((n, vn)) => {
                    let acc = acc.ipush(Node::BeginRule(n.to_string()));
                    let acc = vn.iter().fold(acc, |facc, n| flatten_acc(facc, n));
                    acc.ipush(Node::EndRule(n.to_string()))
                }
            }
        }

        flatten_acc(vec![], self)
    }
}

/// Consume a ast::flat::Node if it's a Rule kind with a specific value
/// and return the rest of nodes
///
///```
///     use dynparser::ast::flat;
///     let nodes = vec![
///                    flat::Node::BeginRule("hello".to_string()),
///                    flat::Node::Val("world".to_string()),
///                ];
///     
///     let nodes = flat::consume_node_start_rule_name("hello", &nodes).unwrap();
///
///     let (node, nodes)  = flat::consume_val(nodes).unwrap();
///     assert!(node == "world");
///     assert!(nodes.len() == 0);
///```
///
pub fn consume_node_start_rule_name<'a>(
    name: &str,
    nodes: &'a [Node],
) -> Result<&'a [Node], Error> {
    let (node, nodes) = split_first_nodes(nodes)?;
    let node_name = match node {
        Node::BeginRule(n) => Ok(n),
        _ => Err(error(&format!("expected begin rule for {}", name), None)),
    }?;
    if node_name == name {
        Ok(nodes)
    } else {
        Err(error(
            &format!("expected {} node, received {}", name, node_name),
            None,
        ))
    }
}

/// Consume a Node indicating the end of a rule,
/// if it's a Rule kind with a specific name
/// and return the rest of nodes
///
///```
///     use dynparser::ast::flat;
///     let nodes = vec![
///                 flat::Node::EndRule("hello".to_string()),
///                 flat::Node::EndRule("hi".to_string()),
///     ];
///     
///     let nodes = flat::consume_node_end_rule_name("hello", &nodes).unwrap();
///     let nodes = flat::consume_node_end_rule_name("hi", &nodes).unwrap();
///     assert!(nodes.len() == 0);
///```
///
pub fn consume_node_end_rule_name<'a>(name: &str, nodes: &'a [Node]) -> Result<&'a [Node], Error> {
    let (node, nodes) = split_first_nodes(nodes)?;
    let node_name = match node {
        Node::EndRule(n) => Ok(n),
        _ => Err(error(&format!("expected end rule for {}", name), None)),
    }?;
    if node_name == name {
        Ok(nodes)
    } else {
        Err(error(
            &format!("expected {} node, received {}", name, node_name),
            None,
        ))
    }
}

/// Given a list of nodes, return the first and the rest on a tuple
///
///```
///     use dynparser::ast::flat;
///     let nodes = vec![
///                 flat::Node::Val("hello".to_string()),
///                 flat::Node::Val("world".to_string()),
///                 flat::Node::Val(".".to_string()),
///     ];
///     
///     let (node, nodes) = flat::split_first_nodes(&nodes).unwrap();
///     assert!(flat::get_node_val(node).unwrap() == "hello");
///     assert!(nodes.len() == 2);
///
///     let (node, nodes) = flat::split_first_nodes(&nodes).unwrap();
///     assert!(flat::get_node_val(node).unwrap() == "world");
///     assert!(nodes.len() == 1);
///
///     let (node, nodes) = flat::split_first_nodes(&nodes).unwrap();
///     assert!(flat::get_node_val(node).unwrap() == ".");
///     assert!(nodes.len() == 0);
///```
///
pub fn split_first_nodes(nodes: &[Node]) -> Result<(&Node, &[Node]), Error> {
    nodes
        .split_first()
        .ok_or_else(|| error("trying get first element from nodes on empty slice", None))
}

/// Return a reference to first node
///
///```
///     use dynparser::ast::flat;
///     let nodes = vec![
///                    flat::Node::BeginRule("hello".to_string()),
///                    flat::Node::Val("world".to_string()),
///                ];
///     
///     let first = flat::peek_first_node(&nodes).unwrap();
///     assert!(first == &flat::Node::BeginRule("hello".to_string()));
///```
///
pub fn peek_first_node(nodes: &[Node]) -> Result<&Node, Error> {
    if nodes.is_empty() {
        Err(error("exptected node on peek_first_node", None))
    } else {
        Ok(&nodes[0])
    }
}

/// Get the value of the Node
/// If node is not a Node::Val, it will return an error
///```
///     use dynparser::ast::flat;
///     let node = flat::Node::Val("hello".to_string());
///     
///     let val = flat::get_node_val(&node).unwrap();
///     
///     assert!(val == "hello");
///```
pub fn get_node_val(node: &Node) -> Result<&str, Error> {
    match node {
        Node::Val(v) => Ok(v),
        _ => Err(error("expected node::Val", None)),
    }
}

/// Consume a node if it's a Val kind and the vaule is
/// equal to the provider one
///
///```
///     use dynparser::ast::flat;
///     let nodes = vec![
///                 flat::Node::Val("hello".to_string()),
///                 flat::Node::Val("world".to_string()),
///                 flat::Node::Val(".".to_string()),
///     ];
///     
///     let nodes = flat::consume_this_value("hello", &nodes).unwrap();
///     let nodes = flat::consume_this_value("world", &nodes).unwrap();
///```
///
pub fn consume_this_value<'a>(v: &str, nodes: &'a [Node]) -> Result<&'a [Node], Error> {
    let (node, nodes) = split_first_nodes(nodes)?;

    let nv = get_node_val(node)?;
    if nv == v {
        Ok(nodes)
    } else {
        Err(error(
            "trying get first element from nodes on empty slice",
            None,
        ))
    }
}

/// It will get the node name of a node if it's a rule one (begin or end).
/// In other case, it will return an error
///
/// ```
///    use dynparser::ast::flat;
///
///    let node = flat::Node::BeginRule("aaa".to_string());
///
///    let node_name = flat::get_nodename(&node).unwrap();
///
///    assert!(node_name == "aaa");
/// ```
pub fn get_nodename(node: &Node) -> Result<&str, Error> {
    match node {
        Node::BeginRule(nname) => Ok(nname),
        Node::EndRule(nname) => Ok(nname),
        _ => Err(error("expected node::Rule", None)),
    }
}

/// Given a slice of nodes, return the value (&str) of first
/// node if it is a Node::Val and return the rest of nodes
///
/// If it's not possible, it returns an error
///
///```
///     use dynparser::ast::flat;
///     let nodes = vec![
///                 flat::Node::Val("hello".to_string()),
///                 flat::Node::Val("world".to_string()),
///     ];
///     
///     let (val, nodes) = flat::consume_val(&nodes).unwrap();
///     assert!(val == "hello");
///     assert!(nodes.len() == 1);
///
///     let (val, nodes) = flat::consume_val(&nodes).unwrap();
///     assert!(val == "world");
///     assert!(nodes.len() == 0);
///```
///
pub fn consume_val(nodes: &[Node]) -> Result<(&str, &[Node]), Error> {
    let (node, nodes) = split_first_nodes(nodes)?;
    match node {
        Node::Val(v) => Ok((&v, nodes)),
        _ => Err(error("expected Val node", None)),
    }
}