1c67d6573Sopenharmony_ciuse std::fmt;
2c67d6573Sopenharmony_ci
3c67d6573Sopenharmony_ciuse crate::ast::{self, Ast};
4c67d6573Sopenharmony_ci
5c67d6573Sopenharmony_ci/// A trait for visiting an abstract syntax tree (AST) in depth first order.
6c67d6573Sopenharmony_ci///
7c67d6573Sopenharmony_ci/// The principle aim of this trait is to enable callers to perform case
8c67d6573Sopenharmony_ci/// analysis on an abstract syntax tree without necessarily using recursion.
9c67d6573Sopenharmony_ci/// In particular, this permits callers to do case analysis with constant stack
10c67d6573Sopenharmony_ci/// usage, which can be important since the size of an abstract syntax tree
11c67d6573Sopenharmony_ci/// may be proportional to end user input.
12c67d6573Sopenharmony_ci///
13c67d6573Sopenharmony_ci/// Typical usage of this trait involves providing an implementation and then
14c67d6573Sopenharmony_ci/// running it using the [`visit`](fn.visit.html) function.
15c67d6573Sopenharmony_ci///
16c67d6573Sopenharmony_ci/// Note that the abstract syntax tree for a regular expression is quite
17c67d6573Sopenharmony_ci/// complex. Unless you specifically need it, you might be able to use the
18c67d6573Sopenharmony_ci/// much simpler
19c67d6573Sopenharmony_ci/// [high-level intermediate representation](../hir/struct.Hir.html)
20c67d6573Sopenharmony_ci/// and its
21c67d6573Sopenharmony_ci/// [corresponding `Visitor` trait](../hir/trait.Visitor.html)
22c67d6573Sopenharmony_ci/// instead.
23c67d6573Sopenharmony_cipub trait Visitor {
24c67d6573Sopenharmony_ci    /// The result of visiting an AST.
25c67d6573Sopenharmony_ci    type Output;
26c67d6573Sopenharmony_ci    /// An error that visiting an AST might return.
27c67d6573Sopenharmony_ci    type Err;
28c67d6573Sopenharmony_ci
29c67d6573Sopenharmony_ci    /// All implementors of `Visitor` must provide a `finish` method, which
30c67d6573Sopenharmony_ci    /// yields the result of visiting the AST or an error.
31c67d6573Sopenharmony_ci    fn finish(self) -> Result<Self::Output, Self::Err>;
32c67d6573Sopenharmony_ci
33c67d6573Sopenharmony_ci    /// This method is called before beginning traversal of the AST.
34c67d6573Sopenharmony_ci    fn start(&mut self) {}
35c67d6573Sopenharmony_ci
36c67d6573Sopenharmony_ci    /// This method is called on an `Ast` before descending into child `Ast`
37c67d6573Sopenharmony_ci    /// nodes.
38c67d6573Sopenharmony_ci    fn visit_pre(&mut self, _ast: &Ast) -> Result<(), Self::Err> {
39c67d6573Sopenharmony_ci        Ok(())
40c67d6573Sopenharmony_ci    }
41c67d6573Sopenharmony_ci
42c67d6573Sopenharmony_ci    /// This method is called on an `Ast` after descending all of its child
43c67d6573Sopenharmony_ci    /// `Ast` nodes.
44c67d6573Sopenharmony_ci    fn visit_post(&mut self, _ast: &Ast) -> Result<(), Self::Err> {
45c67d6573Sopenharmony_ci        Ok(())
46c67d6573Sopenharmony_ci    }
47c67d6573Sopenharmony_ci
48c67d6573Sopenharmony_ci    /// This method is called between child nodes of an
49c67d6573Sopenharmony_ci    /// [`Alternation`](struct.Alternation.html).
50c67d6573Sopenharmony_ci    fn visit_alternation_in(&mut self) -> Result<(), Self::Err> {
51c67d6573Sopenharmony_ci        Ok(())
52c67d6573Sopenharmony_ci    }
53c67d6573Sopenharmony_ci
54c67d6573Sopenharmony_ci    /// This method is called on every
55c67d6573Sopenharmony_ci    /// [`ClassSetItem`](enum.ClassSetItem.html)
56c67d6573Sopenharmony_ci    /// before descending into child nodes.
57c67d6573Sopenharmony_ci    fn visit_class_set_item_pre(
58c67d6573Sopenharmony_ci        &mut self,
59c67d6573Sopenharmony_ci        _ast: &ast::ClassSetItem,
60c67d6573Sopenharmony_ci    ) -> Result<(), Self::Err> {
61c67d6573Sopenharmony_ci        Ok(())
62c67d6573Sopenharmony_ci    }
63c67d6573Sopenharmony_ci
64c67d6573Sopenharmony_ci    /// This method is called on every
65c67d6573Sopenharmony_ci    /// [`ClassSetItem`](enum.ClassSetItem.html)
66c67d6573Sopenharmony_ci    /// after descending into child nodes.
67c67d6573Sopenharmony_ci    fn visit_class_set_item_post(
68c67d6573Sopenharmony_ci        &mut self,
69c67d6573Sopenharmony_ci        _ast: &ast::ClassSetItem,
70c67d6573Sopenharmony_ci    ) -> Result<(), Self::Err> {
71c67d6573Sopenharmony_ci        Ok(())
72c67d6573Sopenharmony_ci    }
73c67d6573Sopenharmony_ci
74c67d6573Sopenharmony_ci    /// This method is called on every
75c67d6573Sopenharmony_ci    /// [`ClassSetBinaryOp`](struct.ClassSetBinaryOp.html)
76c67d6573Sopenharmony_ci    /// before descending into child nodes.
77c67d6573Sopenharmony_ci    fn visit_class_set_binary_op_pre(
78c67d6573Sopenharmony_ci        &mut self,
79c67d6573Sopenharmony_ci        _ast: &ast::ClassSetBinaryOp,
80c67d6573Sopenharmony_ci    ) -> Result<(), Self::Err> {
81c67d6573Sopenharmony_ci        Ok(())
82c67d6573Sopenharmony_ci    }
83c67d6573Sopenharmony_ci
84c67d6573Sopenharmony_ci    /// This method is called on every
85c67d6573Sopenharmony_ci    /// [`ClassSetBinaryOp`](struct.ClassSetBinaryOp.html)
86c67d6573Sopenharmony_ci    /// after descending into child nodes.
87c67d6573Sopenharmony_ci    fn visit_class_set_binary_op_post(
88c67d6573Sopenharmony_ci        &mut self,
89c67d6573Sopenharmony_ci        _ast: &ast::ClassSetBinaryOp,
90c67d6573Sopenharmony_ci    ) -> Result<(), Self::Err> {
91c67d6573Sopenharmony_ci        Ok(())
92c67d6573Sopenharmony_ci    }
93c67d6573Sopenharmony_ci
94c67d6573Sopenharmony_ci    /// This method is called between the left hand and right hand child nodes
95c67d6573Sopenharmony_ci    /// of a [`ClassSetBinaryOp`](struct.ClassSetBinaryOp.html).
96c67d6573Sopenharmony_ci    fn visit_class_set_binary_op_in(
97c67d6573Sopenharmony_ci        &mut self,
98c67d6573Sopenharmony_ci        _ast: &ast::ClassSetBinaryOp,
99c67d6573Sopenharmony_ci    ) -> Result<(), Self::Err> {
100c67d6573Sopenharmony_ci        Ok(())
101c67d6573Sopenharmony_ci    }
102c67d6573Sopenharmony_ci}
103c67d6573Sopenharmony_ci
104c67d6573Sopenharmony_ci/// Executes an implementation of `Visitor` in constant stack space.
105c67d6573Sopenharmony_ci///
106c67d6573Sopenharmony_ci/// This function will visit every node in the given `Ast` while calling the
107c67d6573Sopenharmony_ci/// appropriate methods provided by the
108c67d6573Sopenharmony_ci/// [`Visitor`](trait.Visitor.html) trait.
109c67d6573Sopenharmony_ci///
110c67d6573Sopenharmony_ci/// The primary use case for this method is when one wants to perform case
111c67d6573Sopenharmony_ci/// analysis over an `Ast` without using a stack size proportional to the depth
112c67d6573Sopenharmony_ci/// of the `Ast`. Namely, this method will instead use constant stack size, but
113c67d6573Sopenharmony_ci/// will use heap space proportional to the size of the `Ast`. This may be
114c67d6573Sopenharmony_ci/// desirable in cases where the size of `Ast` is proportional to end user
115c67d6573Sopenharmony_ci/// input.
116c67d6573Sopenharmony_ci///
117c67d6573Sopenharmony_ci/// If the visitor returns an error at any point, then visiting is stopped and
118c67d6573Sopenharmony_ci/// the error is returned.
119c67d6573Sopenharmony_cipub fn visit<V: Visitor>(ast: &Ast, visitor: V) -> Result<V::Output, V::Err> {
120c67d6573Sopenharmony_ci    HeapVisitor::new().visit(ast, visitor)
121c67d6573Sopenharmony_ci}
122c67d6573Sopenharmony_ci
123c67d6573Sopenharmony_ci/// HeapVisitor visits every item in an `Ast` recursively using constant stack
124c67d6573Sopenharmony_ci/// size and a heap size proportional to the size of the `Ast`.
125c67d6573Sopenharmony_cistruct HeapVisitor<'a> {
126c67d6573Sopenharmony_ci    /// A stack of `Ast` nodes. This is roughly analogous to the call stack
127c67d6573Sopenharmony_ci    /// used in a typical recursive visitor.
128c67d6573Sopenharmony_ci    stack: Vec<(&'a Ast, Frame<'a>)>,
129c67d6573Sopenharmony_ci    /// Similar to the `Ast` stack above, but is used only for character
130c67d6573Sopenharmony_ci    /// classes. In particular, character classes embed their own mini
131c67d6573Sopenharmony_ci    /// recursive syntax.
132c67d6573Sopenharmony_ci    stack_class: Vec<(ClassInduct<'a>, ClassFrame<'a>)>,
133c67d6573Sopenharmony_ci}
134c67d6573Sopenharmony_ci
135c67d6573Sopenharmony_ci/// Represents a single stack frame while performing structural induction over
136c67d6573Sopenharmony_ci/// an `Ast`.
137c67d6573Sopenharmony_cienum Frame<'a> {
138c67d6573Sopenharmony_ci    /// A stack frame allocated just before descending into a repetition
139c67d6573Sopenharmony_ci    /// operator's child node.
140c67d6573Sopenharmony_ci    Repetition(&'a ast::Repetition),
141c67d6573Sopenharmony_ci    /// A stack frame allocated just before descending into a group's child
142c67d6573Sopenharmony_ci    /// node.
143c67d6573Sopenharmony_ci    Group(&'a ast::Group),
144c67d6573Sopenharmony_ci    /// The stack frame used while visiting every child node of a concatenation
145c67d6573Sopenharmony_ci    /// of expressions.
146c67d6573Sopenharmony_ci    Concat {
147c67d6573Sopenharmony_ci        /// The child node we are currently visiting.
148c67d6573Sopenharmony_ci        head: &'a Ast,
149c67d6573Sopenharmony_ci        /// The remaining child nodes to visit (which may be empty).
150c67d6573Sopenharmony_ci        tail: &'a [Ast],
151c67d6573Sopenharmony_ci    },
152c67d6573Sopenharmony_ci    /// The stack frame used while visiting every child node of an alternation
153c67d6573Sopenharmony_ci    /// of expressions.
154c67d6573Sopenharmony_ci    Alternation {
155c67d6573Sopenharmony_ci        /// The child node we are currently visiting.
156c67d6573Sopenharmony_ci        head: &'a Ast,
157c67d6573Sopenharmony_ci        /// The remaining child nodes to visit (which may be empty).
158c67d6573Sopenharmony_ci        tail: &'a [Ast],
159c67d6573Sopenharmony_ci    },
160c67d6573Sopenharmony_ci}
161c67d6573Sopenharmony_ci
162c67d6573Sopenharmony_ci/// Represents a single stack frame while performing structural induction over
163c67d6573Sopenharmony_ci/// a character class.
164c67d6573Sopenharmony_cienum ClassFrame<'a> {
165c67d6573Sopenharmony_ci    /// The stack frame used while visiting every child node of a union of
166c67d6573Sopenharmony_ci    /// character class items.
167c67d6573Sopenharmony_ci    Union {
168c67d6573Sopenharmony_ci        /// The child node we are currently visiting.
169c67d6573Sopenharmony_ci        head: &'a ast::ClassSetItem,
170c67d6573Sopenharmony_ci        /// The remaining child nodes to visit (which may be empty).
171c67d6573Sopenharmony_ci        tail: &'a [ast::ClassSetItem],
172c67d6573Sopenharmony_ci    },
173c67d6573Sopenharmony_ci    /// The stack frame used while a binary class operation.
174c67d6573Sopenharmony_ci    Binary { op: &'a ast::ClassSetBinaryOp },
175c67d6573Sopenharmony_ci    /// A stack frame allocated just before descending into a binary operator's
176c67d6573Sopenharmony_ci    /// left hand child node.
177c67d6573Sopenharmony_ci    BinaryLHS {
178c67d6573Sopenharmony_ci        op: &'a ast::ClassSetBinaryOp,
179c67d6573Sopenharmony_ci        lhs: &'a ast::ClassSet,
180c67d6573Sopenharmony_ci        rhs: &'a ast::ClassSet,
181c67d6573Sopenharmony_ci    },
182c67d6573Sopenharmony_ci    /// A stack frame allocated just before descending into a binary operator's
183c67d6573Sopenharmony_ci    /// right hand child node.
184c67d6573Sopenharmony_ci    BinaryRHS { op: &'a ast::ClassSetBinaryOp, rhs: &'a ast::ClassSet },
185c67d6573Sopenharmony_ci}
186c67d6573Sopenharmony_ci
187c67d6573Sopenharmony_ci/// A representation of the inductive step when performing structural induction
188c67d6573Sopenharmony_ci/// over a character class.
189c67d6573Sopenharmony_ci///
190c67d6573Sopenharmony_ci/// Note that there is no analogous explicit type for the inductive step for
191c67d6573Sopenharmony_ci/// `Ast` nodes because the inductive step is just an `Ast`. For character
192c67d6573Sopenharmony_ci/// classes, the inductive step can produce one of two possible child nodes:
193c67d6573Sopenharmony_ci/// an item or a binary operation. (An item cannot be a binary operation
194c67d6573Sopenharmony_ci/// because that would imply binary operations can be unioned in the concrete
195c67d6573Sopenharmony_ci/// syntax, which is not possible.)
196c67d6573Sopenharmony_cienum ClassInduct<'a> {
197c67d6573Sopenharmony_ci    Item(&'a ast::ClassSetItem),
198c67d6573Sopenharmony_ci    BinaryOp(&'a ast::ClassSetBinaryOp),
199c67d6573Sopenharmony_ci}
200c67d6573Sopenharmony_ci
201c67d6573Sopenharmony_ciimpl<'a> HeapVisitor<'a> {
202c67d6573Sopenharmony_ci    fn new() -> HeapVisitor<'a> {
203c67d6573Sopenharmony_ci        HeapVisitor { stack: vec![], stack_class: vec![] }
204c67d6573Sopenharmony_ci    }
205c67d6573Sopenharmony_ci
206c67d6573Sopenharmony_ci    fn visit<V: Visitor>(
207c67d6573Sopenharmony_ci        &mut self,
208c67d6573Sopenharmony_ci        mut ast: &'a Ast,
209c67d6573Sopenharmony_ci        mut visitor: V,
210c67d6573Sopenharmony_ci    ) -> Result<V::Output, V::Err> {
211c67d6573Sopenharmony_ci        self.stack.clear();
212c67d6573Sopenharmony_ci        self.stack_class.clear();
213c67d6573Sopenharmony_ci
214c67d6573Sopenharmony_ci        visitor.start();
215c67d6573Sopenharmony_ci        loop {
216c67d6573Sopenharmony_ci            visitor.visit_pre(ast)?;
217c67d6573Sopenharmony_ci            if let Some(x) = self.induct(ast, &mut visitor)? {
218c67d6573Sopenharmony_ci                let child = x.child();
219c67d6573Sopenharmony_ci                self.stack.push((ast, x));
220c67d6573Sopenharmony_ci                ast = child;
221c67d6573Sopenharmony_ci                continue;
222c67d6573Sopenharmony_ci            }
223c67d6573Sopenharmony_ci            // No induction means we have a base case, so we can post visit
224c67d6573Sopenharmony_ci            // it now.
225c67d6573Sopenharmony_ci            visitor.visit_post(ast)?;
226c67d6573Sopenharmony_ci
227c67d6573Sopenharmony_ci            // At this point, we now try to pop our call stack until it is
228c67d6573Sopenharmony_ci            // either empty or we hit another inductive case.
229c67d6573Sopenharmony_ci            loop {
230c67d6573Sopenharmony_ci                let (post_ast, frame) = match self.stack.pop() {
231c67d6573Sopenharmony_ci                    None => return visitor.finish(),
232c67d6573Sopenharmony_ci                    Some((post_ast, frame)) => (post_ast, frame),
233c67d6573Sopenharmony_ci                };
234c67d6573Sopenharmony_ci                // If this is a concat/alternate, then we might have additional
235c67d6573Sopenharmony_ci                // inductive steps to process.
236c67d6573Sopenharmony_ci                if let Some(x) = self.pop(frame) {
237c67d6573Sopenharmony_ci                    if let Frame::Alternation { .. } = x {
238c67d6573Sopenharmony_ci                        visitor.visit_alternation_in()?;
239c67d6573Sopenharmony_ci                    }
240c67d6573Sopenharmony_ci                    ast = x.child();
241c67d6573Sopenharmony_ci                    self.stack.push((post_ast, x));
242c67d6573Sopenharmony_ci                    break;
243c67d6573Sopenharmony_ci                }
244c67d6573Sopenharmony_ci                // Otherwise, we've finished visiting all the child nodes for
245c67d6573Sopenharmony_ci                // this AST, so we can post visit it now.
246c67d6573Sopenharmony_ci                visitor.visit_post(post_ast)?;
247c67d6573Sopenharmony_ci            }
248c67d6573Sopenharmony_ci        }
249c67d6573Sopenharmony_ci    }
250c67d6573Sopenharmony_ci
251c67d6573Sopenharmony_ci    /// Build a stack frame for the given AST if one is needed (which occurs if
252c67d6573Sopenharmony_ci    /// and only if there are child nodes in the AST). Otherwise, return None.
253c67d6573Sopenharmony_ci    ///
254c67d6573Sopenharmony_ci    /// If this visits a class, then the underlying visitor implementation may
255c67d6573Sopenharmony_ci    /// return an error which will be passed on here.
256c67d6573Sopenharmony_ci    fn induct<V: Visitor>(
257c67d6573Sopenharmony_ci        &mut self,
258c67d6573Sopenharmony_ci        ast: &'a Ast,
259c67d6573Sopenharmony_ci        visitor: &mut V,
260c67d6573Sopenharmony_ci    ) -> Result<Option<Frame<'a>>, V::Err> {
261c67d6573Sopenharmony_ci        Ok(match *ast {
262c67d6573Sopenharmony_ci            Ast::Class(ast::Class::Bracketed(ref x)) => {
263c67d6573Sopenharmony_ci                self.visit_class(x, visitor)?;
264c67d6573Sopenharmony_ci                None
265c67d6573Sopenharmony_ci            }
266c67d6573Sopenharmony_ci            Ast::Repetition(ref x) => Some(Frame::Repetition(x)),
267c67d6573Sopenharmony_ci            Ast::Group(ref x) => Some(Frame::Group(x)),
268c67d6573Sopenharmony_ci            Ast::Concat(ref x) if x.asts.is_empty() => None,
269c67d6573Sopenharmony_ci            Ast::Concat(ref x) => {
270c67d6573Sopenharmony_ci                Some(Frame::Concat { head: &x.asts[0], tail: &x.asts[1..] })
271c67d6573Sopenharmony_ci            }
272c67d6573Sopenharmony_ci            Ast::Alternation(ref x) if x.asts.is_empty() => None,
273c67d6573Sopenharmony_ci            Ast::Alternation(ref x) => Some(Frame::Alternation {
274c67d6573Sopenharmony_ci                head: &x.asts[0],
275c67d6573Sopenharmony_ci                tail: &x.asts[1..],
276c67d6573Sopenharmony_ci            }),
277c67d6573Sopenharmony_ci            _ => None,
278c67d6573Sopenharmony_ci        })
279c67d6573Sopenharmony_ci    }
280c67d6573Sopenharmony_ci
281c67d6573Sopenharmony_ci    /// Pops the given frame. If the frame has an additional inductive step,
282c67d6573Sopenharmony_ci    /// then return it, otherwise return `None`.
283c67d6573Sopenharmony_ci    fn pop(&self, induct: Frame<'a>) -> Option<Frame<'a>> {
284c67d6573Sopenharmony_ci        match induct {
285c67d6573Sopenharmony_ci            Frame::Repetition(_) => None,
286c67d6573Sopenharmony_ci            Frame::Group(_) => None,
287c67d6573Sopenharmony_ci            Frame::Concat { tail, .. } => {
288c67d6573Sopenharmony_ci                if tail.is_empty() {
289c67d6573Sopenharmony_ci                    None
290c67d6573Sopenharmony_ci                } else {
291c67d6573Sopenharmony_ci                    Some(Frame::Concat { head: &tail[0], tail: &tail[1..] })
292c67d6573Sopenharmony_ci                }
293c67d6573Sopenharmony_ci            }
294c67d6573Sopenharmony_ci            Frame::Alternation { tail, .. } => {
295c67d6573Sopenharmony_ci                if tail.is_empty() {
296c67d6573Sopenharmony_ci                    None
297c67d6573Sopenharmony_ci                } else {
298c67d6573Sopenharmony_ci                    Some(Frame::Alternation {
299c67d6573Sopenharmony_ci                        head: &tail[0],
300c67d6573Sopenharmony_ci                        tail: &tail[1..],
301c67d6573Sopenharmony_ci                    })
302c67d6573Sopenharmony_ci                }
303c67d6573Sopenharmony_ci            }
304c67d6573Sopenharmony_ci        }
305c67d6573Sopenharmony_ci    }
306c67d6573Sopenharmony_ci
307c67d6573Sopenharmony_ci    fn visit_class<V: Visitor>(
308c67d6573Sopenharmony_ci        &mut self,
309c67d6573Sopenharmony_ci        ast: &'a ast::ClassBracketed,
310c67d6573Sopenharmony_ci        visitor: &mut V,
311c67d6573Sopenharmony_ci    ) -> Result<(), V::Err> {
312c67d6573Sopenharmony_ci        let mut ast = ClassInduct::from_bracketed(ast);
313c67d6573Sopenharmony_ci        loop {
314c67d6573Sopenharmony_ci            self.visit_class_pre(&ast, visitor)?;
315c67d6573Sopenharmony_ci            if let Some(x) = self.induct_class(&ast) {
316c67d6573Sopenharmony_ci                let child = x.child();
317c67d6573Sopenharmony_ci                self.stack_class.push((ast, x));
318c67d6573Sopenharmony_ci                ast = child;
319c67d6573Sopenharmony_ci                continue;
320c67d6573Sopenharmony_ci            }
321c67d6573Sopenharmony_ci            self.visit_class_post(&ast, visitor)?;
322c67d6573Sopenharmony_ci
323c67d6573Sopenharmony_ci            // At this point, we now try to pop our call stack until it is
324c67d6573Sopenharmony_ci            // either empty or we hit another inductive case.
325c67d6573Sopenharmony_ci            loop {
326c67d6573Sopenharmony_ci                let (post_ast, frame) = match self.stack_class.pop() {
327c67d6573Sopenharmony_ci                    None => return Ok(()),
328c67d6573Sopenharmony_ci                    Some((post_ast, frame)) => (post_ast, frame),
329c67d6573Sopenharmony_ci                };
330c67d6573Sopenharmony_ci                // If this is a union or a binary op, then we might have
331c67d6573Sopenharmony_ci                // additional inductive steps to process.
332c67d6573Sopenharmony_ci                if let Some(x) = self.pop_class(frame) {
333c67d6573Sopenharmony_ci                    if let ClassFrame::BinaryRHS { ref op, .. } = x {
334c67d6573Sopenharmony_ci                        visitor.visit_class_set_binary_op_in(op)?;
335c67d6573Sopenharmony_ci                    }
336c67d6573Sopenharmony_ci                    ast = x.child();
337c67d6573Sopenharmony_ci                    self.stack_class.push((post_ast, x));
338c67d6573Sopenharmony_ci                    break;
339c67d6573Sopenharmony_ci                }
340c67d6573Sopenharmony_ci                // Otherwise, we've finished visiting all the child nodes for
341c67d6573Sopenharmony_ci                // this class node, so we can post visit it now.
342c67d6573Sopenharmony_ci                self.visit_class_post(&post_ast, visitor)?;
343c67d6573Sopenharmony_ci            }
344c67d6573Sopenharmony_ci        }
345c67d6573Sopenharmony_ci    }
346c67d6573Sopenharmony_ci
347c67d6573Sopenharmony_ci    /// Call the appropriate `Visitor` methods given an inductive step.
348c67d6573Sopenharmony_ci    fn visit_class_pre<V: Visitor>(
349c67d6573Sopenharmony_ci        &self,
350c67d6573Sopenharmony_ci        ast: &ClassInduct<'a>,
351c67d6573Sopenharmony_ci        visitor: &mut V,
352c67d6573Sopenharmony_ci    ) -> Result<(), V::Err> {
353c67d6573Sopenharmony_ci        match *ast {
354c67d6573Sopenharmony_ci            ClassInduct::Item(item) => {
355c67d6573Sopenharmony_ci                visitor.visit_class_set_item_pre(item)?;
356c67d6573Sopenharmony_ci            }
357c67d6573Sopenharmony_ci            ClassInduct::BinaryOp(op) => {
358c67d6573Sopenharmony_ci                visitor.visit_class_set_binary_op_pre(op)?;
359c67d6573Sopenharmony_ci            }
360c67d6573Sopenharmony_ci        }
361c67d6573Sopenharmony_ci        Ok(())
362c67d6573Sopenharmony_ci    }
363c67d6573Sopenharmony_ci
364c67d6573Sopenharmony_ci    /// Call the appropriate `Visitor` methods given an inductive step.
365c67d6573Sopenharmony_ci    fn visit_class_post<V: Visitor>(
366c67d6573Sopenharmony_ci        &self,
367c67d6573Sopenharmony_ci        ast: &ClassInduct<'a>,
368c67d6573Sopenharmony_ci        visitor: &mut V,
369c67d6573Sopenharmony_ci    ) -> Result<(), V::Err> {
370c67d6573Sopenharmony_ci        match *ast {
371c67d6573Sopenharmony_ci            ClassInduct::Item(item) => {
372c67d6573Sopenharmony_ci                visitor.visit_class_set_item_post(item)?;
373c67d6573Sopenharmony_ci            }
374c67d6573Sopenharmony_ci            ClassInduct::BinaryOp(op) => {
375c67d6573Sopenharmony_ci                visitor.visit_class_set_binary_op_post(op)?;
376c67d6573Sopenharmony_ci            }
377c67d6573Sopenharmony_ci        }
378c67d6573Sopenharmony_ci        Ok(())
379c67d6573Sopenharmony_ci    }
380c67d6573Sopenharmony_ci
381c67d6573Sopenharmony_ci    /// Build a stack frame for the given class node if one is needed (which
382c67d6573Sopenharmony_ci    /// occurs if and only if there are child nodes). Otherwise, return None.
383c67d6573Sopenharmony_ci    fn induct_class(&self, ast: &ClassInduct<'a>) -> Option<ClassFrame<'a>> {
384c67d6573Sopenharmony_ci        match *ast {
385c67d6573Sopenharmony_ci            ClassInduct::Item(&ast::ClassSetItem::Bracketed(ref x)) => {
386c67d6573Sopenharmony_ci                match x.kind {
387c67d6573Sopenharmony_ci                    ast::ClassSet::Item(ref item) => {
388c67d6573Sopenharmony_ci                        Some(ClassFrame::Union { head: item, tail: &[] })
389c67d6573Sopenharmony_ci                    }
390c67d6573Sopenharmony_ci                    ast::ClassSet::BinaryOp(ref op) => {
391c67d6573Sopenharmony_ci                        Some(ClassFrame::Binary { op })
392c67d6573Sopenharmony_ci                    }
393c67d6573Sopenharmony_ci                }
394c67d6573Sopenharmony_ci            }
395c67d6573Sopenharmony_ci            ClassInduct::Item(&ast::ClassSetItem::Union(ref x)) => {
396c67d6573Sopenharmony_ci                if x.items.is_empty() {
397c67d6573Sopenharmony_ci                    None
398c67d6573Sopenharmony_ci                } else {
399c67d6573Sopenharmony_ci                    Some(ClassFrame::Union {
400c67d6573Sopenharmony_ci                        head: &x.items[0],
401c67d6573Sopenharmony_ci                        tail: &x.items[1..],
402c67d6573Sopenharmony_ci                    })
403c67d6573Sopenharmony_ci                }
404c67d6573Sopenharmony_ci            }
405c67d6573Sopenharmony_ci            ClassInduct::BinaryOp(op) => {
406c67d6573Sopenharmony_ci                Some(ClassFrame::BinaryLHS { op, lhs: &op.lhs, rhs: &op.rhs })
407c67d6573Sopenharmony_ci            }
408c67d6573Sopenharmony_ci            _ => None,
409c67d6573Sopenharmony_ci        }
410c67d6573Sopenharmony_ci    }
411c67d6573Sopenharmony_ci
412c67d6573Sopenharmony_ci    /// Pops the given frame. If the frame has an additional inductive step,
413c67d6573Sopenharmony_ci    /// then return it, otherwise return `None`.
414c67d6573Sopenharmony_ci    fn pop_class(&self, induct: ClassFrame<'a>) -> Option<ClassFrame<'a>> {
415c67d6573Sopenharmony_ci        match induct {
416c67d6573Sopenharmony_ci            ClassFrame::Union { tail, .. } => {
417c67d6573Sopenharmony_ci                if tail.is_empty() {
418c67d6573Sopenharmony_ci                    None
419c67d6573Sopenharmony_ci                } else {
420c67d6573Sopenharmony_ci                    Some(ClassFrame::Union {
421c67d6573Sopenharmony_ci                        head: &tail[0],
422c67d6573Sopenharmony_ci                        tail: &tail[1..],
423c67d6573Sopenharmony_ci                    })
424c67d6573Sopenharmony_ci                }
425c67d6573Sopenharmony_ci            }
426c67d6573Sopenharmony_ci            ClassFrame::Binary { .. } => None,
427c67d6573Sopenharmony_ci            ClassFrame::BinaryLHS { op, rhs, .. } => {
428c67d6573Sopenharmony_ci                Some(ClassFrame::BinaryRHS { op, rhs })
429c67d6573Sopenharmony_ci            }
430c67d6573Sopenharmony_ci            ClassFrame::BinaryRHS { .. } => None,
431c67d6573Sopenharmony_ci        }
432c67d6573Sopenharmony_ci    }
433c67d6573Sopenharmony_ci}
434c67d6573Sopenharmony_ci
435c67d6573Sopenharmony_ciimpl<'a> Frame<'a> {
436c67d6573Sopenharmony_ci    /// Perform the next inductive step on this frame and return the next
437c67d6573Sopenharmony_ci    /// child AST node to visit.
438c67d6573Sopenharmony_ci    fn child(&self) -> &'a Ast {
439c67d6573Sopenharmony_ci        match *self {
440c67d6573Sopenharmony_ci            Frame::Repetition(rep) => &rep.ast,
441c67d6573Sopenharmony_ci            Frame::Group(group) => &group.ast,
442c67d6573Sopenharmony_ci            Frame::Concat { head, .. } => head,
443c67d6573Sopenharmony_ci            Frame::Alternation { head, .. } => head,
444c67d6573Sopenharmony_ci        }
445c67d6573Sopenharmony_ci    }
446c67d6573Sopenharmony_ci}
447c67d6573Sopenharmony_ci
448c67d6573Sopenharmony_ciimpl<'a> ClassFrame<'a> {
449c67d6573Sopenharmony_ci    /// Perform the next inductive step on this frame and return the next
450c67d6573Sopenharmony_ci    /// child class node to visit.
451c67d6573Sopenharmony_ci    fn child(&self) -> ClassInduct<'a> {
452c67d6573Sopenharmony_ci        match *self {
453c67d6573Sopenharmony_ci            ClassFrame::Union { head, .. } => ClassInduct::Item(head),
454c67d6573Sopenharmony_ci            ClassFrame::Binary { op, .. } => ClassInduct::BinaryOp(op),
455c67d6573Sopenharmony_ci            ClassFrame::BinaryLHS { ref lhs, .. } => {
456c67d6573Sopenharmony_ci                ClassInduct::from_set(lhs)
457c67d6573Sopenharmony_ci            }
458c67d6573Sopenharmony_ci            ClassFrame::BinaryRHS { ref rhs, .. } => {
459c67d6573Sopenharmony_ci                ClassInduct::from_set(rhs)
460c67d6573Sopenharmony_ci            }
461c67d6573Sopenharmony_ci        }
462c67d6573Sopenharmony_ci    }
463c67d6573Sopenharmony_ci}
464c67d6573Sopenharmony_ci
465c67d6573Sopenharmony_ciimpl<'a> ClassInduct<'a> {
466c67d6573Sopenharmony_ci    fn from_bracketed(ast: &'a ast::ClassBracketed) -> ClassInduct<'a> {
467c67d6573Sopenharmony_ci        ClassInduct::from_set(&ast.kind)
468c67d6573Sopenharmony_ci    }
469c67d6573Sopenharmony_ci
470c67d6573Sopenharmony_ci    fn from_set(ast: &'a ast::ClassSet) -> ClassInduct<'a> {
471c67d6573Sopenharmony_ci        match *ast {
472c67d6573Sopenharmony_ci            ast::ClassSet::Item(ref item) => ClassInduct::Item(item),
473c67d6573Sopenharmony_ci            ast::ClassSet::BinaryOp(ref op) => ClassInduct::BinaryOp(op),
474c67d6573Sopenharmony_ci        }
475c67d6573Sopenharmony_ci    }
476c67d6573Sopenharmony_ci}
477c67d6573Sopenharmony_ci
478c67d6573Sopenharmony_ciimpl<'a> fmt::Debug for ClassFrame<'a> {
479c67d6573Sopenharmony_ci    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
480c67d6573Sopenharmony_ci        let x = match *self {
481c67d6573Sopenharmony_ci            ClassFrame::Union { .. } => "Union",
482c67d6573Sopenharmony_ci            ClassFrame::Binary { .. } => "Binary",
483c67d6573Sopenharmony_ci            ClassFrame::BinaryLHS { .. } => "BinaryLHS",
484c67d6573Sopenharmony_ci            ClassFrame::BinaryRHS { .. } => "BinaryRHS",
485c67d6573Sopenharmony_ci        };
486c67d6573Sopenharmony_ci        write!(f, "{}", x)
487c67d6573Sopenharmony_ci    }
488c67d6573Sopenharmony_ci}
489c67d6573Sopenharmony_ci
490c67d6573Sopenharmony_ciimpl<'a> fmt::Debug for ClassInduct<'a> {
491c67d6573Sopenharmony_ci    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492c67d6573Sopenharmony_ci        let x = match *self {
493c67d6573Sopenharmony_ci            ClassInduct::Item(it) => match *it {
494c67d6573Sopenharmony_ci                ast::ClassSetItem::Empty(_) => "Item(Empty)",
495c67d6573Sopenharmony_ci                ast::ClassSetItem::Literal(_) => "Item(Literal)",
496c67d6573Sopenharmony_ci                ast::ClassSetItem::Range(_) => "Item(Range)",
497c67d6573Sopenharmony_ci                ast::ClassSetItem::Ascii(_) => "Item(Ascii)",
498c67d6573Sopenharmony_ci                ast::ClassSetItem::Perl(_) => "Item(Perl)",
499c67d6573Sopenharmony_ci                ast::ClassSetItem::Unicode(_) => "Item(Unicode)",
500c67d6573Sopenharmony_ci                ast::ClassSetItem::Bracketed(_) => "Item(Bracketed)",
501c67d6573Sopenharmony_ci                ast::ClassSetItem::Union(_) => "Item(Union)",
502c67d6573Sopenharmony_ci            },
503c67d6573Sopenharmony_ci            ClassInduct::BinaryOp(it) => match it.kind {
504c67d6573Sopenharmony_ci                ast::ClassSetBinaryOpKind::Intersection => {
505c67d6573Sopenharmony_ci                    "BinaryOp(Intersection)"
506c67d6573Sopenharmony_ci                }
507c67d6573Sopenharmony_ci                ast::ClassSetBinaryOpKind::Difference => {
508c67d6573Sopenharmony_ci                    "BinaryOp(Difference)"
509c67d6573Sopenharmony_ci                }
510c67d6573Sopenharmony_ci                ast::ClassSetBinaryOpKind::SymmetricDifference => {
511c67d6573Sopenharmony_ci                    "BinaryOp(SymmetricDifference)"
512c67d6573Sopenharmony_ci                }
513c67d6573Sopenharmony_ci            },
514c67d6573Sopenharmony_ci        };
515c67d6573Sopenharmony_ci        write!(f, "{}", x)
516c67d6573Sopenharmony_ci    }
517c67d6573Sopenharmony_ci}
518