//! This module implements a Stack, which is useful for implementing a parser //! with variable lookahead, as it would allow to pop elements which are below //! the top-element, and maintain a top counter which would be in charge of //! moving these elements once shifted. use std::ptr; /// This container implements a stack and a queue in a single vector: /// - stack: buf[..top] /// - queue: buf[top + gap..] /// /// This structure is meant to avoid moving data when the head of the queue is /// transfered to the top of the stack. Also, sometimes we need to set items /// aside from the top of a stack, and then push them back onto the stack later. /// The queue is for storing these set-aside values. Since they live in the same /// buffer as the stack, values can be "set aside" and "pushed back on" without /// moving them at all. /// /// In the context of an LR parser, the stack contains shifted elements, and the /// queue contains the lookahead. If the lexer is completely independent of the /// parser, all tokens could be queued before starting the parser. /// /// The following statements describe how this structure is meant to be used and /// is described as a stack and a queue displayed as follow: /// [...stack...] [...queue...] /// /// New elements are always inserted in the queue with `enqueue`: /// [a, b] [] /// * enqueue(c) /// [a, b] [c] /// /// These elements are then moved to the stack with `shift`: /// [a, b] [c] /// * shift() /// [a, b, c] [] /// /// The stack top can be set aside in the queue with `unshift`: /// [a, b, c] [] /// * unshift() /// [a, b] [c] /// /// The stack top can be removed with `pop`: /// [a, b] [c] /// * pop() -> b /// [a] [c] /// * pop() -> a /// [] [c] /// /// New elements can be added to the front of the queue with `push_next`, which /// also moves the content of the queue to ensure that `shift` can be used /// afterward: /// [] [c] /// * push_next(d) /// [] [d, c] /// /// These operations are used by LR parser, to add lookahead with `enqueue`, to /// shift tokens with `shift`, to save tokens to be replayed with `unshift`, to /// reduce a set of tokens and replace it by a non-terminal with `pop` and /// `push_next`. pub struct QueueStack { /// Buffer containing the stack and the queue. /// /// [a, b, c, d, e, f, g, h, i, j] /// '-----------'<------>'-----' /// stack ^ gap queue /// | /// top -' buf: Vec, /// Length of the stack, self.buf[top - 1] being the last element of the /// stack. top: usize, /// Length of the gap between the stack top and the queue head. gap: usize, } impl QueueStack { /// Create a queue and stack with the given number of reserved elements. pub fn with_capacity(n: usize) -> QueueStack { QueueStack { buf: Vec::with_capacity(n), top: 0, gap: 0, } } /// Add an element to the back of the queue. pub fn enqueue(&mut self, value: T) { self.buf.push(value); } /// Add an element to the front of the queue. pub fn push_next(&mut self, value: T) { self.compact_with_gap(1); self.gap -= 1; unsafe { // Write over the gap without reading nor dropping the old entry. let ptr = self.buf.as_mut_ptr().add(self.top + self.gap); ptr.write(value); } } /// Whether elements can be shifted. pub fn can_shift(&self) -> bool { self.gap == 0 && !self.queue_empty() } /// Whether elements can be unshifted. pub fn can_unshift(&self) -> bool { self.gap == 0 && !self.stack_empty() } /// Transfer an element from the top of the stack to the front of the queue. /// /// The gap must be empty. This does not move the value from one address to /// another in memory; it just adjusts the boundary between the stack and /// the queue. /// /// # Panics /// If the stack is empty or there is a gap. pub fn unshift(&mut self) { assert!(self.can_unshift()); self.top -= 1; } /// Transfer an element from the front of the queue to the top of the stack. /// /// The gap must be empty. This does not move the value from one address to /// another in memory; it just adjusts the boundary between the stack and /// the queue. /// /// # Panics /// If the queue is empty or there is a gap. #[inline(always)] pub fn shift(&mut self) { assert!(self.can_shift()); self.top += 1; } /// Remove the top element of the stack and return it, or None if the stack /// is empty. /// /// This increases the gap size by 1. pub fn pop(&mut self) -> Option { if self.top == 0 { None } else { self.top -= 1; self.gap += 1; unsafe { // Take ownership of the content. let ptr = self.buf.as_mut_ptr().add(self.top); Some(ptr.read()) } } } /// Set the gap size to `new_gap`, memmove-ing the contents of the queue as /// needed. fn compact_with_gap(&mut self, new_gap: usize) { assert!(new_gap <= (std::isize::MAX as usize)); assert!(self.gap <= (std::isize::MAX as usize)); let diff = new_gap as isize - self.gap as isize; if diff == 0 { return; } // Ensure there is enough capacity. if diff > 0 { self.buf.reserve(diff as usize); } // Number of elements to be copied. let count = self.queue_len(); let new_len = self.top + new_gap + count; assert!(new_len < self.buf.capacity()); unsafe { let src_ptr = self.buf.as_mut_ptr().add(self.top + self.gap); let dst_ptr = src_ptr.offset(diff); // Shift everything down/up to have the expected gap. ptr::copy(src_ptr, dst_ptr, count); // Update the buffer length to newly copied elements. self.buf.set_len(new_len); // Update the gap to the new gap value. self.gap = new_gap; } debug_assert_eq!(self.queue_len(), count); } /// Returns a reference to the front element of the queue. pub fn next(&self) -> Option<&T> { if self.queue_empty() { None } else { Some(&self.buf[self.top + self.gap]) } } /// Returns a reference to the top element of the stack. #[allow(dead_code)] pub fn top(&self) -> Option<&T> { if self.top == 0 { None } else { Some(&self.buf[self.top - 1]) } } /// Returns a mutable reference to the top of the stack. #[allow(dead_code)] pub fn top_mut(&mut self) -> Option<&mut T> { if self.top == 0 { None } else { Some(&mut self.buf[self.top - 1]) } } /// Number of elements in the stack. pub fn stack_len(&self) -> usize { self.top } /// Number of elements in the queue. pub fn queue_len(&self) -> usize { self.buf.len() - self.top - self.gap } /// Whether the stack is empty. pub fn stack_empty(&self) -> bool { self.top == 0 } /// Whether the queue is empty. pub fn queue_empty(&self) -> bool { self.top == self.buf.len() } /// Create a slice which corresponds the stack. pub fn stack_slice(&self) -> &[T] { &self.buf[..self.top] } /// Create a slice which corresponds the queue. #[allow(dead_code)] pub fn queue_slice(&self) -> &[T] { &self.buf[self.top + self.gap..] } } impl Drop for QueueStack { fn drop(&mut self) { // QueueStack contains a gap of non-initialized values, before releasing // the vector, we move all initialized values from the queue into the // remaining gap. self.compact_with_gap(0); } }