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