//! Arithmetic operations: add, subtract, etc. use crate::prelude::*; use crate::tile::prelude::*; macro_rules! binary_op { () => { fn update<'b>(&'b mut self, mut context: UpdateContext<'b>) { if let Some(mut signal) = context.take_signal() { if signal.len() >= 2 { let first = signal.pop().unwrap_or_else(|| unreachable!()); let second = signal.pop().unwrap_or_else(|| unreachable!()); if let Some(res) = self.binary_op(second, first) { signal.push(res); } } 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(); } } } } /// Adds two values together: `[..., a, b] -> [..., a+b]` #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Add; impl Add { fn binary_op(&self, left: Value, right: Value) -> Option { Some(left + right) } } impl Tile for Add { binary_op!(); fn draw_simple(&self, ctx: DrawContext) -> TextChar { TextChar::from_state('\u{2295}', ctx.state) // CIRCLED PLUS } } /// Subtracts two values: `[..., a, b] -> [..., a-b]` #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Sub; impl Sub { fn binary_op(&self, left: Value, right: Value) -> Option { use Value::*; match (left, right) { (Number(x), Number(y)) => Some(Number(x - y)), (String(mut x), Number(y)) => { x.truncate(y as usize); Some(String(x)) } (String(x), String(y)) => { Some(String(x.split(&y).collect())) } _ => None } } } impl Tile for Sub { binary_op!(); fn draw_simple(&self, ctx: DrawContext) -> TextChar { TextChar::from_state('\u{2296}', ctx.state) // CIRCLED MINUS } } /// Multiplies two values together: `[..., a, b] -> [..., a*b]` #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Mul; impl Mul { fn binary_op(&self, left: Value, right: Value) -> Option { use Value::*; match (left, right) { (Number(x), Number(y)) => Some(Number(x * y)), (String(x), Number(y)) => Some(String(x.repeat(y as usize))), _ => None } } } impl Tile for Mul { binary_op!(); fn draw_simple(&self, ctx: DrawContext) -> TextChar { TextChar::from_state('\u{2297}', ctx.state) // CIRCLED TIMES } } /// Divides two values together: `[..., a, b] -> [..., a/b]` #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Div; impl Div { fn binary_op(&self, left: Value, right: Value) -> Option { use Value::*; match (left, right) { (Number(x), Number(y)) => Some(Number(x / y)), _ => None } } } impl Tile for Div { binary_op!(); fn draw_simple(&self, ctx: DrawContext) -> TextChar { TextChar::from_state('\u{2298}', ctx.state) // CIRCLED DIVISION SLASH } } /// Computes the modulo of two values: `[..., a, b] -> [..., a%b]` #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Mod; impl Mod { fn binary_op(&self, left: Value, right: Value) -> Option { use Value::*; match (left, right) { (Number(x), Number(y)) => Some(Number(x % y)), _ => None } } } impl Tile for Mod { binary_op!(); fn draw_simple(&self, ctx: DrawContext) -> TextChar { TextChar::from_state('\u{2299}', ctx.state) // CIRCLED DOT OPERATOR } } /// Compares the top two values: `[..., a, b] -> [..., a ?? b]` #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Cmp(Operation); #[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq, Eq)] pub enum Operation { Eq, Neq, Gt, Gte, Lt, Lte, } impl Cmp { fn binary_op(&self, left: Value, right: Value) -> Option { use Value::*; use Operation::*; let res = match (self.0, left, right) { (Eq, Number(x), Number(y)) => Some(x == y), (Eq, String(x), String(y)) => Some(x == y), (Neq, Number(x), Number(y)) => Some(x != y), (Neq, String(x), String(y)) => Some(x != y), (Gt, Number(x), Number(y)) => Some(x > y), (Gt, String(x), String(y)) => Some(x > y), (Gte, Number(x), Number(y)) => Some(x >= y), (Gte, String(x), String(y)) => Some(x >= y), (Lt, Number(x), Number(y)) => Some(x < y), (Lt, String(x), String(y)) => Some(x < y), (Lte, Number(x), Number(y)) => Some(x <= y), (Lte, String(x), String(y)) => Some(x <= y), _ => None }?; Some(Number(if res { 1.0 } else { 0.0 })) } } impl Tile for Cmp { binary_op!(); fn draw_simple(&self, ctx: DrawContext) -> TextChar { use Operation::*; match self.0 { Eq => TextChar::from_state('\u{229c}', ctx.state), // CIRCLED EQUALS Neq => TextChar::from_state('\u{2260}', ctx.state), // NOT EQUALS (the circled version doesn't exist) Lt => TextChar::from_state('\u{29c0}', ctx.state), // CIRCLED LESS-THAN Lte => TextChar::from_state('\u{2264}', ctx.state), // LESS-THAN OR EQUAL TO (no circled version) Gt => TextChar::from_state('\u{29c1}', ctx.state), // CIRCLED GREATER-THAN Gte => TextChar::from_state('\u{2265}', ctx.state), // GREATER-THAN OR EQUAL TO (no circled version) } } } impl Default for Operation { fn default() -> Self { Self::Eq } }