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
// Copyright 2018 Eduardo Sánchez Muñoz
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use crate::Node;
/// Base struct from which `Builder` are created.
/// See `Builder` example.
pub struct BuilderBase {
stack: Vec<Vec<Node>>,
current: Vec<Node>,
}
/// Helper struct to build SISE trees and get index paths
/// of the inserted nodes.
///
/// # Example
///
/// ```
/// use sise::sise_expr;
///
/// let mut builder_base = sise::BuilderBase::new();
/// let mut builder = builder_base.builder();
///
/// // Build (atom-1 atom-2 (atom-3 atom-4) atom-5)
/// builder.add_node("atom-1");
/// assert_eq!(builder.last_index_path(), [0]);
/// builder.add_node("atom-2");
/// assert_eq!(builder.last_index_path(), [1]);
/// builder.begin_list();
/// builder.add_node("atom-3");
/// assert_eq!(builder.last_index_path(), [2, 0]);
/// builder.add_node("atom-4");
/// assert_eq!(builder.last_index_path(), [2, 1]);
/// builder.end_list();
/// assert_eq!(builder.last_index_path(), [2]);
/// builder.add_node("atom-5");
/// assert_eq!(builder.last_index_path(), [3]);
/// builder.finish();
///
/// let root_node = builder_base.into_node();
/// let expected = sise_expr!(["atom-1", "atom-2", ["atom-3", "atom-4"], "atom-5"]);
/// assert_eq!(root_node, expected);
/// ```
pub struct Builder<'a> {
base: &'a mut BuilderBase,
min_depth: usize,
}
impl BuilderBase {
#[inline]
pub fn new() -> Self {
Self {
stack: Vec::new(),
current: Vec::new(),
}
}
#[inline]
pub fn builder(&mut self) -> Builder {
assert!(self.stack.is_empty());
assert!(self.current.is_empty());
Builder {
base: self,
min_depth: 0,
}
}
#[inline]
pub fn into_node(self) -> Node {
assert!(self.stack.is_empty());
Node::List(self.current)
}
}
impl Default for BuilderBase {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<'a> Builder<'a> {
/// Returns the index path of the last inserted node.
pub fn last_index_path(&self) -> Vec<usize> {
let mut path = Vec::with_capacity(self.base.stack.len() + 1);
for stack_item in self.base.stack.iter() {
path.push(stack_item.len());
}
if !self.base.current.is_empty() {
path.push(self.base.current.len() - 1);
}
path
}
/// Creates a builder that won't allow to pop further.
///
/// # Example
///
/// ```
/// let r = std::panic::catch_unwind(|| {
/// let mut builder_base = sise::BuilderBase::new();
/// let mut builder = builder_base.builder();
///
/// builder.begin_list();
/// let mut builder2 = builder.sub_builder();
/// builder2.end_list();
/// });
/// assert!(r.is_err());
/// ```
#[inline]
pub fn sub_builder<'b>(&'b mut self) -> Builder<'b> {
let min_depth = self.base.stack.len();
Builder {
base: self.base,
min_depth: min_depth,
}
}
/// Adds `node` into the current list.
pub fn add_node<T: Into<Node>>(&mut self, node: T) {
self.base.current.push(node.into());
}
/// Creates a new list, pushing the current one into a stack.
/// This new list will be pushed into the current one.
pub fn begin_list(&mut self) {
self.base.stack.push(std::mem::replace(&mut self.base.current, Vec::new()));
}
/// Finishes the current list, popping a list from the
/// stack and setting it as current.
pub fn end_list(&mut self) {
assert!(self.base.stack.len() > self.min_depth);
let parent_list = self.base.stack.pop().unwrap();
let current_list = std::mem::replace(&mut self.base.current, parent_list);
self.base.current.push(Node::List(current_list));
}
/// Finishes the builder, making sure that the stack depth
/// is the same as when it was created.
pub fn finish(self) {
assert_eq!(self.base.stack.len(), self.min_depth);
}
}