Skip to main content

litcheck_filecheck/expr/
mod.rs

1mod cli;
2mod error;
3pub mod num;
4mod parser;
5mod value;
6
7pub use self::cli::CliVariable;
8pub use self::error::*;
9pub use self::num::{CasingStyle, FormatSpecifier, Number, NumberFormat, ParseNumberError};
10pub use self::value::{Value, ValueType};
11pub use litcheck::variables::VariableName;
12
13use litcheck::{Symbol, variables};
14
15use crate::common::*;
16
17pub type Variable<'a> = variables::Variable<Value<'a>>;
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
20pub struct TypedVariable {
21    pub name: VariableName,
22    pub ty: ValueType,
23}
24
25#[derive(Debug, Clone)]
26pub enum Expr {
27    Num(Number),
28    Var(VariableName),
29    Binary {
30        span: SourceSpan,
31        op: BinaryOp,
32        lhs: Box<Expr>,
33        rhs: Box<Expr>,
34    },
35}
36impl Spanned for Expr {
37    fn span(&self) -> SourceSpan {
38        match self {
39            Self::Num(spanned) => spanned.span(),
40            Self::Var(spanned) => spanned.span(),
41            Self::Binary { span, .. } => *span,
42        }
43    }
44}
45impl Expr {
46    pub fn from_call(
47        span: SourceSpan,
48        callee: Span<Symbol>,
49        mut args: Vec<Expr>,
50    ) -> Result<Self, InvalidCallExprError> {
51        match args.len() {
52            2 => {
53                let op = match callee.as_str() {
54                    "add" => BinaryOp::Add,
55                    "sub" => BinaryOp::Sub,
56                    "mul" => BinaryOp::Mul,
57                    "div" => BinaryOp::Div,
58                    "min" => BinaryOp::Min,
59                    "max" => BinaryOp::Max,
60                    callee => {
61                        return Err(InvalidCallExprError::Undefined {
62                            span,
63                            callee: callee.to_string(),
64                        });
65                    }
66                };
67                let rhs = Box::new(args.pop().unwrap());
68                let lhs = Box::new(args.pop().unwrap());
69                Ok(Self::Binary { span, op, lhs, rhs })
70            }
71            arity => match callee.as_str() {
72                callee @ ("add" | "sub" | "mul" | "div" | "min" | "max") => {
73                    Err(InvalidCallExprError::InvalidArity {
74                        span,
75                        callee: callee.to_string(),
76                        expected: 2,
77                        given: arity as u8,
78                    })
79                }
80                callee => Err(InvalidCallExprError::Undefined {
81                    span,
82                    callee: callee.to_string(),
83                }),
84            },
85        }
86    }
87}
88impl Eq for Expr {}
89impl PartialEq for Expr {
90    fn eq(&self, other: &Self) -> bool {
91        match (self, other) {
92            (Self::Num(a), Self::Num(b)) => a == b,
93            (Self::Var(a), Self::Var(b)) => a == b,
94            (
95                Self::Binary {
96                    op: aop,
97                    lhs: al,
98                    rhs: ar,
99                    ..
100                },
101                Self::Binary {
102                    op: bop,
103                    lhs: bl,
104                    rhs: br,
105                    ..
106                },
107            ) => aop == bop && al == bl && ar == br,
108            _ => false,
109        }
110    }
111}
112impl PartialOrd for Expr {
113    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
114        Some(self.cmp(other))
115    }
116}
117impl Ord for Expr {
118    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
119        use core::cmp::Ordering;
120        match (self, other) {
121            (Self::Num(l), Self::Num(r)) => l.cmp(r),
122            (Self::Num(_), _) => Ordering::Less,
123            (_, Self::Num(_)) => Ordering::Greater,
124            (Self::Var(l), Self::Var(r)) => l.cmp(r),
125            (Self::Var(_), _) => Ordering::Less,
126            (_, Self::Var(_)) => Ordering::Greater,
127            (
128                Self::Binary {
129                    op: lop,
130                    lhs: ll,
131                    rhs: lr,
132                    ..
133                },
134                Self::Binary {
135                    op: rop,
136                    lhs: rl,
137                    rhs: rr,
138                    ..
139                },
140            ) => lop
141                .cmp(rop)
142                .then_with(|| ll.cmp(rl))
143                .then_with(|| lr.cmp(rr)),
144        }
145    }
146}
147
148impl fmt::Display for Expr {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        match self {
151            Self::Var(variables::VariableName::Global(v)) => write!(f, "${v}"),
152            Self::Var(variables::VariableName::Pseudo(v)) => write!(f, "@{v}"),
153            Self::Var(variables::VariableName::User(v)) => write!(f, "{v}"),
154            Self::Num(n) => write!(f, "{n}"),
155            Self::Binary { op, lhs, rhs, .. } => {
156                if matches!(op, BinaryOp::Min | BinaryOp::Max) {
157                    write!(f, "{op}({lhs}, {rhs})")
158                } else {
159                    write!(f, "{lhs} {op} {rhs}")
160                }
161            }
162        }
163    }
164}
165
166#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
167pub enum BinaryOp {
168    Eq,
169    Add,
170    Sub,
171    Mul,
172    Div,
173    Min,
174    Max,
175}
176
177impl fmt::Display for BinaryOp {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        match self {
180            Self::Eq => f.write_str("=="),
181            Self::Add => f.write_str("+"),
182            Self::Sub => f.write_str("-"),
183            Self::Mul => f.write_str("*"),
184            Self::Div => f.write_str("/"),
185            Self::Min => f.write_str("min"),
186            Self::Max => f.write_str("max"),
187        }
188    }
189}