rain-lang 0.0.1

An implementation of an RVSDG in Rust with a concept of lifetimes
Documentation
/*!
Logical types and operations
*/

use std::fmt::{Debug, Display, Formatter, self};

/// The boolean type
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Bool;

impl Bool {
    /// Get the string representing the Boolean type
    #[inline(always)]
    pub fn get_string(&self) -> &'static str { "#bool" }
}

impl Debug for Bool {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        <Self as Display>::fmt(self, fmt)
    }
}

impl Display for Bool {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", self.get_string())
    }
}

/// Logical operations
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum LogicalOp {
    /// Binary logical operations
    Binary(Binary),
    /// Unary logical operations
    Unary(Unary)
}

impl Debug for LogicalOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        <Self as Display>::fmt(self, fmt)
    }
}

impl Display for LogicalOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        match self {
            Self::Binary(b) => <Binary as fmt::Display>::fmt(b, fmt),
            Self::Unary(u) => <Unary as fmt::Display>::fmt(u, fmt)
        }
    }
}

impl From<Binary> for LogicalOp {
    #[inline] fn from(b: Binary) -> LogicalOp { LogicalOp::Binary(b) }
}

impl PartialEq<Binary> for LogicalOp {
    #[inline] fn eq(&self, b: &Binary) -> bool { self.eq(&LogicalOp::from(*b)) }
}

impl From<Unary> for LogicalOp {
    #[inline] fn from(u: Unary) -> LogicalOp { LogicalOp::Unary(u) }
}

impl PartialEq<Unary> for LogicalOp {
    #[inline] fn eq(&self, u: &Unary) -> bool { self.eq(&LogicalOp::from(*u)) }
}

/// Binary logical operations
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum Binary {
    /// Logical AND
    And,
    /// Logical OR
    Or,
    /// Logical XOR
    Xor,
    /// Logical NAND
    Nand,
    /// Logical NOR
    Nor,
    /// Boolean equality
    Eq,
    /// Implication
    Implies,
    /// Reverse implication
    ImpliedBy
}

use Binary::*;
/// A list of all builtin binary logical operations
pub const BINARY_OPS: &[Binary] = &[And, Or, Xor, Nand, Nor, Eq, Implies, ImpliedBy];

impl Binary {
    /// Get the string representing each logical operation
    #[inline(always)]
    pub fn get_string(&self) -> &'static str {
        use Binary::*;
        match self {
            And => "#and",
            Or => "#or",
            Xor => "#xor",
            Nand => "#nand",
            Nor => "#nor",
            Eq => "#eq",
            Implies => "#implies",
            ImpliedBy => "#impliedby"
        }
    }
    /// Fully apply this logical operation
    #[inline(always)]
    pub fn apply(&self, left: bool, right: bool) -> bool {
        use Binary::*;
        match self {
            And => left & right,
            Or => left | right,
            Xor => left ^ right,
            Nand => !(left & right),
            Nor => !(left | right),
            Eq => left == right,
            Implies => !left | right,
            ImpliedBy => left | !right
        }
    }
    /// Partially apply this logical operation
    #[inline(always)]
    pub fn partial_apply(&self, arg: bool) -> Unary {
        use Unary::*;
        use Binary::*;
        match (self, arg) {
            (And, true) => Id,
            (And, false) => Constant(false),
            (Or, true) => Constant(true),
            (Or, false) => Id,
            (Xor, true) => Not,
            (Xor, false) => Id,
            (Nand, true) => Not,
            (Nand, false) => Constant(true),
            (Nor, true) => Constant(false),
            (Nor, false) => Not,
            (Eq, true) => Id,
            (Eq, false) => Not,
            (Implies, true) => Id,
            (Implies, false) => Constant(true),
            (ImpliedBy, true) => Constant(true),
            (ImpliedBy, false) => Not
        }
    }
}

impl Debug for Binary {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        <Self as Display>::fmt(self, fmt)
    }
}

impl Display for Binary {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", self.get_string())
    }
}

/// Unary logical operations
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum Unary {
    /// The identity operation on booleans
    Id,
    /// Logical negation
    Not,
    /// Boolean constant
    Constant(bool)
}

use Unary::*;
/// A list of all builtin unary logical operations
pub const UNARY_OPS: &[Unary] = &[Id, Not, Constant(true), Constant(false)];

impl Unary {
    /// Get the string representing logical not
    #[inline(always)]
    pub fn get_string(&self) -> &'static str {
        match self {
            Unary::Id => "#id", //TODO: think about this
            Unary::Not => "#not",
            //TODO: think about this
            Unary::Constant(c) => if *c { "#constant_true" } else { "#constant_false" }
        }
    }
    /// Apply this loigcal operation to a boolean
    #[inline(always)]
    pub fn apply(&self, arg: bool) -> bool {
        match self {
            Unary::Id => arg,
            Unary::Not => !arg,
            Unary::Constant(c) => *c
        }
    }
}

impl Debug for Unary {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        <Self as Display>::fmt(self, fmt)
    }
}

impl Display for Unary {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", self.get_string())
    }
}

#[cfg(test)]
pub mod tests {
    #[test]
    fn logical_operations_work() {
        use super::Binary::*;
        let f = false;
        let t = true;
        let ops = [
            (And, f, f, f),
            (And, t, f, f),
            (And, f, t, f),
            (And, t, t, t),
            (Or, f, f, f),
            (Or, t, f, t),
            (Or, f, t, t),
            (Or, t, t, t),
            (Xor, f, f, f),
            (Xor, t, f, t),
            (Xor, f, t, t),
            (Xor, t, t, f),
            (Nand, f, f, t),
            (Nand, t, f, t),
            (Nand, f, t, t),
            (Nand, t, t, f),
            (Nor, f, f, t),
            (Nor, t, f, f),
            (Nor, f, t, f),
            (Nor, t, t, f),
            (Eq, f, f, t),
            (Eq, t, f, f),
            (Eq, f, t, f),
            (Eq, t, t, t),
            (Implies, f, f, t),
            (Implies, t, f, f),
            (Implies, f, t, t),
            (Implies, t, t, t),
            (ImpliedBy, f, f, t),
            (ImpliedBy, t, f, t),
            (ImpliedBy, f, t, f),
            (ImpliedBy, t, t, t),
        ];
        for op in ops.iter() {
            assert_eq!(op.0.apply(op.1, op.2), op.3,
                "Incorrect value for ({} {} {}) = {}", op.0, op.1, op.2, op.3);
            assert_eq!(op.0.partial_apply(op.1).apply(op.2), op.3,
                "Incorrect value for (({} {}) {}) = ({} {}) = {}",
                op.0, op.1, op.2, op.0.partial_apply(op.1), op.2, op.3);
        }
    }
}