psi-lang 0.3.0

A language for configuration and extension
Documentation
use crate::{Error, Identifier, Identifiers, Table};
use smartstring::alias::String;
use std::collections::VecDeque;
use std::fmt;
use std::ops::{Add, Div, Mul, Rem, Sub};

#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub enum Expr {
    Integer(i64),
    Float(f64),
    StringFmt(Identifiers),
    StringRaw(String),
    Identifier(Identifier),
    Boolean(bool),
    Inclusive(Box<Expr>, Box<Expr>),
    Exclusive(Box<Expr>, Box<Expr>),
    Array(Identifiers),
    Table(Table),
    Not(Box<Expr>),
    BinOp(Box<Expr>, Op, Box<Expr>),
    FnCall(Box<Expr>, Identifiers),
    VarIndex(Box<Expr>, VecDeque<Expr>),
    Impossible,
}

#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub enum Op {
    Add,
    Sub,
    Mul,
    Div,
    Pow,
    Rem,
    And,
    Or,
    In,
    Greater,
    Less,
    GreaterEq,
    LessEq,
    Equals,
    NotEquals,
}

impl Add for Expr {
    type Output = Self;
    fn add(self, by: Self) -> Self::Output {
        match (self, by) {
            (Self::Float(f1), Self::Float(f2)) => Self::Float(f1 + f2),
            (Self::Integer(i), Self::Float(f)) => Self::Float(i as f64 + f),
            (Self::Float(f), Self::Integer(i)) => Self::Float(f + i as f64),
            (Self::Integer(i1), Self::Integer(i2)) => Self::Integer(i1 + i2),
            _ => Self::Impossible,
        }
    }
}

impl Sub for Expr {
    type Output = Self;
    fn sub(self, by: Self) -> Self::Output {
        match (self, by) {
            (Self::Float(f1), Self::Float(f2)) => Self::Float(f1 - f2),
            (Self::Integer(i), Self::Float(f)) => Self::Float(i as f64 - f),
            (Self::Float(f), Self::Integer(i)) => Self::Float(f - i as f64),
            (Self::Integer(i1), Self::Integer(i2)) => Self::Integer(i1 - i2),
            _ => Self::Impossible,
        }
    }
}

impl Mul for Expr {
    type Output = Self;
    fn mul(self, by: Self) -> Self::Output {
        match (self, by) {
            (Self::Float(f1), Self::Float(f2)) => Self::Float(f1 * f2),
            (Self::Integer(i), Self::Float(f)) => Self::Float(i as f64 * f),
            (Self::Float(f), Self::Integer(i)) => Self::Float(f * i as f64),
            (Self::Integer(i1), Self::Integer(i2)) => Self::Integer(i1 * i2),
            _ => Self::Impossible,
        }
    }
}

impl Div for Expr {
    type Output = Self;
    fn div(self, by: Self) -> Self::Output {
        match (self, by) {
            (Self::Float(f1), Self::Float(f2)) => Self::Float(f1 / f2),
            (Self::Integer(i), Self::Float(f)) => Self::Float(i as f64 / f),
            (Self::Float(f), Self::Integer(i)) => Self::Float(f / i as f64),
            (Self::Integer(i1), Self::Integer(i2)) => Self::Float(i1 as f64 / i2 as f64),
            _ => Self::Impossible,
        }
    }
}

impl Rem for Expr {
    type Output = Self;
    fn rem(self, by: Self) -> Self::Output {
        match (self, by) {
            (Self::Float(f1), Self::Float(f2)) => Self::Float(f1 % f2),
            (Self::Integer(i), Self::Float(f)) => Self::Float(i as f64 % f),
            (Self::Float(f), Self::Integer(i)) => Self::Float(f % i as f64),
            (Self::Integer(i1), Self::Integer(i2)) => Self::Integer(i1 % i2),
            _ => Self::Impossible,
        }
    }
}

impl Expr {
    pub fn pow(self, by: Expr) -> Expr {
        match (self, by) {
            (Self::Float(f1), Self::Float(f2)) => Self::Float(f1.powf(f2)),
            (Self::Integer(i), Self::Float(f)) => Self::Float((i as f64).powf(f)),
            (Self::Float(f), Self::Integer(i)) => Self::Float(f.powf(i as f64)),
            (Self::Integer(i1), Self::Integer(i2)) => Self::Float((i1 as f64).powf(i2 as f64)),
            _ => Self::Impossible,
        }
    }

    pub fn as_bool(&self) -> Result<bool, Error> {
        if let Expr::Boolean(b) = self {
            Ok(*b)
        } else {
            Err(Error::EvalNotBool)
        }
    }
}

impl fmt::Display for Op {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Op::Add => write!(f, "+"),
            Op::Sub => write!(f, "-"),
            Op::Mul => write!(f, "*"),
            Op::Div => write!(f, "/"),
            Op::Pow => write!(f, "^"),
            Op::Rem => write!(f, "%"),
            Op::Equals => write!(f, "=="),
            Op::NotEquals => write!(f, "!="),
            Op::Greater => write!(f, ">"),
            Op::Less => write!(f, "<"),
            Op::GreaterEq => write!(f, ">="),
            Op::LessEq => write!(f, "<="),
            Op::Or => write!(f, "or"),
            Op::And => write!(f, "and"),
            Op::In => write!(f, "in"),
        }
    }
}

impl fmt::Display for Expr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Expr::Integer(i) => write!(f, "{}", i),
            Expr::Float(fl) => write!(f, "{}", fl),
            Expr::Boolean(b) => write!(f, "{}", b),
            Expr::Identifier(i) => write!(f, "{}", i.join(".")),
            Expr::VarIndex(i, l) => write!(
                f,
                "{}{}",
                i,
                l.iter()
                    .map(|x| format!("[{}]", x))
                    .collect::<Vec<_>>()
                    .join("")
            ),
            Expr::Exclusive(s, e) => write!(f, "{}..{}", s, e),
            Expr::Inclusive(s, e) => write!(f, "{}...{}", s, e),
            Expr::StringRaw(s) => write!(f, "{}", s),
            Expr::StringFmt(p) => write!(f, "{:?}", p),
            Expr::Array(s) => write!(
                f,
                "[{}]",
                s.iter()
                    .map(|i| format!("{}", i))
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
            Expr::Table(s) => write!(f, "{}", s),
            Expr::FnCall(i, a) => write!(
                f,
                "{}({})",
                i,
                a.iter()
                    .map(|i| format!("{}", i))
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
            Expr::BinOp(l, o, r) => write!(f, "{} {} {}", l, o, r),
            Expr::Not(e) => write!(f, "not {}", e),
            _ => unreachable!(),
        }
    }
}