logical_expression_parser 0.1.1

This is a simple logical expression parser written in Rust using pest library
Documentation
use lazy_static::lazy_static;
pub use pest::Parser;
use pest_derive::Parser;

pub use pest::iterators::Pairs;
use pest::pratt_parser::PrattParser;

#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct CalculatorParser;

lazy_static! {
    static ref PRATT_PARSER: PrattParser<Rule> = {
        use pest::pratt_parser::{Assoc::*, Op};
        use Rule::*;

        PrattParser::new()
            .op(Op::infix(implication, Right))
            .op(Op::infix(equivalence, Left) | Op::infix(xor, Left))
            .op(Op::infix(logical_or, Left))
            .op(Op::infix(logical_and, Left))
            .op(Op::prefix(unary_not))
    };
}

#[derive(Debug, PartialEq, Eq)]
pub enum Expr {
    Integer(i32),
    UnaryNot(Box<Expr>),
    LogicalOp {
        lhs: Box<Expr>,
        op: Op,
        rhs: Box<Expr>,
    },
}

#[derive(Debug, PartialEq, Eq)]
pub enum Op {
    LogicalAnd,
    LogicalOr,
    Implication,
    Equivalence,
    Xor,
}

pub fn parse_expr(pairs: Pairs<Rule>) -> Expr {
    PRATT_PARSER
        .map_primary(|primary| match primary.as_rule() {
            Rule::binary_digit => Expr::Integer(primary.as_str().parse::<i32>().unwrap()),
            Rule::expr => parse_expr(primary.into_inner()),
            rule => unreachable!("Expr::parse expected atom, found {:?}", rule),
        })
        .map_infix(|lhs, op, rhs| {
            let op = match op.as_rule() {
                Rule::logical_or => Op::LogicalOr,
                Rule::logical_and => Op::LogicalAnd,
                Rule::implication => Op::Implication,
                Rule::equivalence => Op::Equivalence,
                Rule::xor => Op::Xor,
                rule => unreachable!("Expr::parse expected infix operation, found {:?}", rule),
            };
            Expr::LogicalOp {
                lhs: Box::new(lhs),
                op,
                rhs: Box::new(rhs),
            }
        })
        .map_prefix(|op, rhs| match op.as_rule() {
            Rule::unary_not => Expr::UnaryNot(Box::new(rhs)),
            _ => unreachable!(),
        })
        .parse(pairs)
}

impl Expr {
    pub fn evaluate(&self) -> bool {
        match self {
            Expr::Integer(val) => *val != 0,
            Expr::UnaryNot(expr) => !expr.evaluate(),
            Expr::LogicalOp { lhs, op, rhs } => {
                let lhs_val = lhs.evaluate();
                let rhs_val = rhs.evaluate();
                match op {
                    Op::LogicalAnd => lhs_val && rhs_val,
                    Op::LogicalOr => lhs_val || rhs_val,
                    Op::Implication => !lhs_val || rhs_val,
                    Op::Equivalence => (lhs_val && rhs_val) || (!lhs_val && !rhs_val),
                    Op::Xor => (lhs_val || rhs_val) && !(lhs_val && rhs_val),
                }
            }
        }
    }
}