use crate::semantics::Field;
use crate::Env;
use std::ops::Deref;
use std::ops::DerefMut;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Builtin {
pub name: Field,
pub is_special: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Frame {
Loop,
Subshell,
Condition,
Builtin(Builtin),
DotScript,
Trap(crate::trap::Condition),
}
impl From<Builtin> for Frame {
fn from(builtin: Builtin) -> Self {
Frame::Builtin(builtin)
}
}
impl From<crate::trap::Condition> for Frame {
fn from(condition: crate::trap::Condition) -> Self {
Frame::Trap(condition)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Stack {
inner: Vec<Frame>,
}
impl Deref for Stack {
type Target = Vec<Frame>;
fn deref(&self) -> &Vec<Frame> {
&self.inner
}
}
impl From<Vec<Frame>> for Stack {
fn from(vec: Vec<Frame>) -> Self {
Stack { inner: vec }
}
}
impl From<Stack> for Vec<Frame> {
fn from(vec: Stack) -> Self {
vec.inner
}
}
#[derive(Debug)]
#[must_use = "The frame is popped when the guard is dropped"]
pub struct StackFrameGuard<'a> {
stack: &'a mut Stack,
}
impl Stack {
#[inline]
pub fn push(&mut self, frame: Frame) -> StackFrameGuard<'_> {
self.inner.push(frame);
StackFrameGuard { stack: self }
}
#[inline]
pub fn pop(guard: StackFrameGuard<'_>) -> Frame {
let frame = guard.stack.inner.pop().unwrap();
std::mem::forget(guard);
frame
}
#[must_use]
pub fn loop_count(&self, max_count: usize) -> usize {
fn retains_context(frame: &Frame) -> bool {
match frame {
Frame::Loop | Frame::Condition | Frame::Builtin(_) => true,
Frame::Subshell | Frame::DotScript | Frame::Trap(_) => false,
}
}
self.inner
.iter()
.rev()
.take_while(|&frame| retains_context(frame))
.filter(|&frame| frame == &Frame::Loop)
.take(max_count)
.count()
}
#[must_use]
pub fn current_builtin(&self) -> Option<&Builtin> {
self.inner.iter().rev().find_map(|frame| match frame {
Frame::Builtin(builtin) => Some(builtin),
_ => None,
})
}
}
impl Drop for StackFrameGuard<'_> {
fn drop(&mut self) {
self.stack.inner.pop().unwrap();
}
}
impl Deref for StackFrameGuard<'_> {
type Target = Stack;
fn deref(&self) -> &Stack {
self.stack
}
}
impl DerefMut for StackFrameGuard<'_> {
fn deref_mut(&mut self) -> &mut Stack {
self.stack
}
}
#[derive(Debug)]
#[must_use = "The frame is popped when the guard is dropped"]
pub struct EnvFrameGuard<'a> {
env: &'a mut Env,
}
impl Env {
#[inline]
pub fn push_frame(&mut self, frame: Frame) -> EnvFrameGuard<'_> {
self.stack.inner.push(frame);
EnvFrameGuard { env: self }
}
#[inline]
pub fn pop_frame(guard: EnvFrameGuard<'_>) -> Frame {
let frame = guard.env.stack.inner.pop().unwrap();
std::mem::forget(guard);
frame
}
}
impl Drop for EnvFrameGuard<'_> {
fn drop(&mut self) {
self.env.stack.inner.pop().unwrap();
}
}
impl Deref for EnvFrameGuard<'_> {
type Target = Env;
fn deref(&self) -> &Env {
self.env
}
}
impl DerefMut for EnvFrameGuard<'_> {
fn deref_mut(&mut self) -> &mut Env {
self.env
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn loop_count_empty() {
let stack = Stack::default();
assert_eq!(stack.loop_count(usize::MAX), 0);
}
#[test]
fn loop_count_with_non_loop_frames() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Builtin(Builtin {
name: Field::dummy(""),
is_special: false,
}));
assert_eq!(stack.loop_count(usize::MAX), 0);
let stack = stack.push(Frame::Condition);
assert_eq!(stack.loop_count(usize::MAX), 0);
}
#[test]
fn loop_count_with_one_loop() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 1);
let stack = stack.push(Frame::Condition);
assert_eq!(stack.loop_count(usize::MAX), 1);
}
#[test]
fn loop_count_with_two_loops() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Loop);
let mut stack = stack.push(Frame::Condition);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 2);
let stack = stack.push(Frame::Condition);
assert_eq!(stack.loop_count(usize::MAX), 2);
}
#[test]
fn loop_count_with_subshells() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Loop);
let mut stack = stack.push(Frame::Subshell);
assert_eq!(stack.loop_count(usize::MAX), 0);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 1);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 2);
let mut stack = stack.push(Frame::Subshell);
assert_eq!(stack.loop_count(usize::MAX), 0);
let stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 1);
}
#[test]
fn loop_count_with_conditions() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Loop);
let mut stack = stack.push(Frame::Condition);
assert_eq!(stack.loop_count(usize::MAX), 1);
let stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 2);
}
#[test]
fn loop_count_with_builtins() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Loop);
let mut stack = stack.push(Frame::Builtin(Builtin {
name: Field::dummy(""),
is_special: false,
}));
assert_eq!(stack.loop_count(usize::MAX), 1);
let stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 2);
}
#[test]
fn loop_count_with_dot_scripts() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Loop);
let mut stack = stack.push(Frame::DotScript);
assert_eq!(stack.loop_count(usize::MAX), 0);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 1);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 2);
let mut stack = stack.push(Frame::DotScript);
assert_eq!(stack.loop_count(usize::MAX), 0);
let stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 1);
}
#[test]
fn loop_count_with_traps() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Loop);
let mut stack = stack.push(Frame::Trap(crate::trap::Condition::Exit));
assert_eq!(stack.loop_count(usize::MAX), 0);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 1);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 2);
let mut stack = stack.push(Frame::Trap(crate::trap::Condition::Signal(
crate::signal::Number::from_raw_unchecked(1.try_into().unwrap()),
)));
assert_eq!(stack.loop_count(usize::MAX), 0);
let stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 1);
}
#[test]
fn loop_count_with_small_max_count() {
let mut stack = Stack::default();
let mut stack = stack.push(Frame::Loop);
let mut stack = stack.push(Frame::Condition);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(usize::MAX), 2);
assert_eq!(stack.loop_count(3), 2);
assert_eq!(stack.loop_count(2), 2);
assert_eq!(stack.loop_count(1), 1);
assert_eq!(stack.loop_count(0), 0);
let stack = stack.push(Frame::Loop);
assert_eq!(stack.loop_count(4), 3);
assert_eq!(stack.loop_count(3), 3);
assert_eq!(stack.loop_count(2), 2);
}
#[test]
fn current_builtin() {
let mut stack = Stack::default();
assert_eq!(stack.current_builtin(), None);
let mut stack = stack.push(Frame::Loop);
assert_eq!(stack.current_builtin(), None);
let builtin = Builtin {
name: Field::dummy(""),
is_special: false,
};
let mut stack = stack.push(Frame::Builtin(builtin.clone()));
assert_eq!(stack.current_builtin(), Some(&builtin));
let builtin = Builtin {
name: Field::dummy("foo"),
is_special: true,
};
let stack = stack.push(Frame::Builtin(builtin.clone()));
assert_eq!(stack.current_builtin(), Some(&builtin));
}
}