//! Basic stack operations: push a value, pop a value, swap, etc. use crate::prelude::*; use crate::tile::prelude::*; /// Pushes a value on the stack, then lets the signal through #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Push { value: Value } impl Push { pub fn new(value: Value) -> Self { Self { value } } } impl Default for Push { fn default() -> Self { Self::new(Value::Number(0.0)) } } impl Tile for Push { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { if let Some(mut signal) = context.take_signal() { signal.push(self.value.clone()); if let Some(coords) = context.offset(signal.direction().into_offset()) { context.send(coords, signal.direction(), signal).unwrap_or_else(|_| unreachable!()); } } if context.state() != State::Idle { context.next_state(); } } fn draw_simple(&self, ctx: DrawContext) -> TextChar { TextChar::from_state('p', ctx.state) } } /// Pops `n` values from the top of the stack, then lets the signal through #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Pop { amount: usize, } impl Pop { pub fn new(amount: usize) -> Self { Self { amount } } } impl Default for Pop { fn default() -> Self { Self::new(1) } } impl Tile for Pop { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { if let Some(mut signal) = context.take_signal() { for _ in 0..self.amount { let _ = signal.pop(); } if let Some(coords) = context.offset(signal.direction().into_offset()) { context.send(coords, signal.direction(), signal).unwrap_or_else(|_| unreachable!()); } } if context.state() != State::Idle { context.next_state(); } } fn draw_simple(&self, ctx: DrawContext) -> TextChar { if self.amount == 1 { TextChar::from_state('P', ctx.state) } else { TextChar::from_state(ctx.blink.scroll(&format!("PPP{}", self.amount)), ctx.state) } } } /// Reverses the order of the top `n` values from the stack, then lets the signal through #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Swap { amount: usize, } impl Swap { pub fn new(amount: usize) -> Self { Self { amount } } } impl Default for Swap { fn default() -> Self { Self::new(2) } } impl Tile for Swap { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { if let Some(mut signal) = context.take_signal() { let len = signal.stack().len(); let last_elements = if self.amount > len { &mut signal.stack_mut()[..] } else { &mut signal.stack_mut()[(len - self.amount)..len] }; last_elements.reverse(); if let Some(coords) = context.offset(signal.direction().into_offset()) { context.send(coords, signal.direction(), signal).unwrap_or_else(|_| unreachable!()); } } if context.state() != State::Idle { context.next_state(); } } fn draw_simple(&self, ctx: DrawContext) -> TextChar { if self.amount == 2 { TextChar::from_state('s', ctx.state) } else { TextChar::from_state(ctx.blink.scroll(&format!("sss{}", self.amount)), ctx.state) } } } // TODO: SwapN, RotateLeft, RotateRight, RotateLeftN, RotateRightN