You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

220 lines
5.9 KiB

//! 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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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)
}
}
fn schema(&self) -> TileSchema {
TileSchema::value("Operation", "CmpOp")
}
}
impl Default for Operation {
fn default() -> Self {
Self::Eq
}
}