tytanic_filter/ast/
expr.rs

1use std::sync::Arc;
2
3use pest::iterators::Pair;
4use pest::pratt_parser::PrattParser;
5
6use super::{Atom, Error, Func, Id, Num, Pat, Rule, Str};
7use crate::eval::{self, Context, Eval, Set, Test, Value};
8
9/// A unary prefix operator.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub enum PrefixOp {
12    /// The negation operator. Matches the symbols `not` and `!`.
13    Not,
14}
15
16/// A binary infix operator.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub enum InfixOp {
19    /// The union/or operator. Matches the symbols `or` and `|`.
20    Union,
21
22    /// The intersection/and operator. Matches the symbols `and` and `&`.
23    Inter,
24
25    /// The difference operator. Matches the symbols `diff` and `~`.
26    Diff,
27
28    /// The symmetric difference/xor operator. Matches the symbols `xor` and
29    /// `^`.
30    SymDiff,
31}
32
33/// An expression node.
34#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35pub enum Expr {
36    /// An expression atom.
37    Atom(Atom),
38
39    /// A function call expression.
40    Func(Func),
41
42    /// A prefix expression.
43    Prefix {
44        /// The unary prefix operator.
45        op: PrefixOp,
46
47        /// The inner expression.
48        expr: Arc<Expr>,
49    },
50
51    /// An infix expression.
52    Infix {
53        /// The binary infix operator.
54        op: InfixOp,
55
56        /// The left hand side of this binary expression.
57        lhs: Arc<Expr>,
58
59        /// The right hand side of this binary expression.
60        rhs: Arc<Expr>,
61    },
62}
63
64// TODO(tinger): flatten intersection and union chains
65impl<T: Test> Eval<T> for Expr {
66    fn eval(&self, ctx: &Context<T>) -> Result<Value<T>, eval::Error> {
67        match self {
68            Self::Atom(atom) => atom.eval(ctx),
69            Self::Func(func) => func.eval(ctx),
70            Self::Prefix { op, expr } => {
71                // unary prefix operator is only valid for test sets
72                let set: Set<T> = expr.eval(ctx)?.expect_type()?;
73
74                Ok(Value::Set(match op {
75                    PrefixOp::Not => Set::expr_comp(set),
76                }))
77            }
78            Self::Infix { op, lhs, rhs } => {
79                // binary infix operator is only valid for test sets
80                let lhs: Set<T> = lhs.eval(ctx)?.expect_type()?;
81                let rhs: Set<T> = rhs.eval(ctx)?.expect_type()?;
82
83                Ok(Value::Set(match op {
84                    InfixOp::Union => Set::expr_union(lhs, rhs, []),
85                    InfixOp::Inter => Set::expr_inter(lhs, rhs, []),
86                    InfixOp::Diff => Set::expr_diff(lhs, rhs),
87                    InfixOp::SymDiff => Set::expr_sym_diff(lhs, rhs),
88                }))
89            }
90        }
91    }
92}
93
94impl Expr {
95    pub(super) fn parse(pair: Pair<'_, Rule>, pratt: &PrattParser<Rule>) -> Result<Expr, Error> {
96        pratt
97            .map_primary(|primary| {
98                Ok(match primary.as_rule() {
99                    Rule::id => Expr::Atom(Atom::Id(Id::parse(primary)?)),
100                    Rule::pat_inner => Expr::Atom(Atom::Pat(Pat::parse(primary)?)),
101                    Rule::str_single | Rule::str_double => {
102                        Expr::Atom(Atom::Str(Str::parse(primary)?))
103                    }
104                    Rule::num_inner => Expr::Atom(Atom::Num(Num::parse(primary)?)),
105                    Rule::func => Expr::Func(Func::parse(primary, pratt)?),
106                    Rule::expr => Self::parse(primary, pratt)?,
107                    x => unreachable!("unhandled primary expression {x:?}"),
108                })
109            })
110            .map_prefix(|op, expr| match op.as_rule().to_prefix() {
111                Some(op) => Ok(Expr::Prefix {
112                    op,
113                    expr: Arc::new(expr?),
114                }),
115                None => unreachable!("unhandled prefix operator {:?}", op.as_rule()),
116            })
117            .map_infix(|lhs, op, rhs| match op.as_rule().to_infix() {
118                Some(op) => Ok(Expr::Infix {
119                    op,
120                    lhs: Arc::new(lhs?),
121                    rhs: Arc::new(rhs?),
122                }),
123                None => unreachable!("unhandled infix operator {:?}", op.as_rule()),
124            })
125            .parse(pair.into_inner())
126    }
127}