//! 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()) { let _ = context.send(coords, signal.direction(), signal); } } if context.state() != State::Idle { context.next_state(); } } } } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Arithmetic(ArithOp); impl Arithmetic { pub fn new(op: ArithOp) -> Self { Self(op) } fn binary_op(&self, left: Value, right: Value) -> Option { self.0.binary_op(left, right) } } impl Tile for Arithmetic { binary_op!(); fn draw_simple(&self, ctx: DrawContext) -> TextChar { use ArithOp::*; match self.0 { Add => TextChar::from_state('\u{2295}', ctx.state), // CIRCLED PLUS Sub => TextChar::from_state('\u{2296}', ctx.state), // CIRCLED MINUS Mul => TextChar::from_state('\u{2297}', ctx.state), // CIRCLED TIMES Div => TextChar::from_state('\u{2298}', ctx.state), // CIRCLED DIVISION SLASH Mod => TextChar::from_state('\u{2299}', ctx.state), // CIRCLED DOT OPERATOR } } fn schema(&self) -> TileSchema { TileSchema::value("Operator", "ArithOp") } } #[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq, Eq)] pub enum ArithOp { Add, Sub, Mul, Div, Mod } impl ArithOp { fn binary_op(&self, left: Value, right: Value) -> Option { use Value::*; use ArithOp::*; match (self, left, right) { // == Add == (Add, left, right) => Some(left + right), // == Sub == (Sub, Number(x), Number(y)) => Some(Number(x - y)), (Sub, String(mut x), Number(y)) => { x.truncate(y as usize); Some(String(x)) } (Sub, String(x), String(y)) => { Some(String(x.split(&y).collect())) } // == Mul == (Mul, Number(x), Number(y)) => Some(Number(x * y)), (Mul, String(x), Number(y)) => Some(String(x.repeat(y as usize))), // == Div == (Div, Number(x), Number(y)) => Some(Number(x / y)), // == Mod == (Mod, Number(x), Number(y)) => Some(Number(x % y)), // Default _ => None } } } impl Default for ArithOp { fn default() -> Self { Self::Add } } /// Compares the top two values: `[..., a, b] -> [..., a ?? b]` #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Cmp(CmpOp); #[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq, Eq)] pub enum CmpOp { Eq, Neq, Gt, Gte, Lt, Lte, } impl Cmp { fn binary_op(&self, left: Value, right: Value) -> Option { use Value::*; use CmpOp::*; 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 CmpOp::*; 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) } } fn schema(&self) -> TileSchema { TileSchema::value("CmpOp", "CmpOp") } } impl Default for CmpOp { fn default() -> Self { Self::Eq } }