cameleon_genapi/
formula.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![allow(
6    clippy::cast_sign_loss,
7    clippy::cast_precision_loss,
8    clippy::cast_possible_truncation
9)]
10
11use std::{borrow::Borrow, collections::HashMap, fmt, hash::Hash, str::FromStr};
12
13use tracing::debug;
14
15use super::{GenApiError, GenApiResult};
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct Formula {
19    pub(crate) expr: Expr,
20}
21
22impl Formula {
23    #[must_use]
24    pub fn expr(&self) -> &Expr {
25        &self.expr
26    }
27
28    pub fn eval<K, V>(&self, var_env: &HashMap<K, V>) -> GenApiResult<EvaluationResult>
29    where
30        K: Borrow<str> + Eq + Hash + fmt::Debug,
31        V: Borrow<Expr> + fmt::Debug,
32    {
33        self.expr.eval(var_env)
34    }
35}
36
37#[derive(Debug, Clone, PartialEq)]
38pub enum Expr {
39    BinOp {
40        kind: BinOpKind,
41        lhs: Box<Expr>,
42        rhs: Box<Expr>,
43    },
44    UnOp {
45        kind: UnOpKind,
46        expr: Box<Expr>,
47    },
48    If {
49        cond: Box<Expr>,
50        then: Box<Expr>,
51        else_: Box<Expr>,
52    },
53    Integer(i64),
54    Float(f64),
55    Ident(String),
56}
57
58impl From<i64> for Expr {
59    fn from(i: i64) -> Self {
60        Self::Integer(i)
61    }
62}
63
64impl From<f64> for Expr {
65    fn from(f: f64) -> Self {
66        Self::Float(f)
67    }
68}
69
70impl From<bool> for Expr {
71    fn from(b: bool) -> Self {
72        if b {
73            Self::Integer(1)
74        } else {
75            Self::Integer(0)
76        }
77    }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq)]
81pub enum EvaluationResult {
82    Integer(i64),
83    Float(f64),
84}
85
86impl From<i64> for EvaluationResult {
87    fn from(i: i64) -> Self {
88        Self::Integer(i)
89    }
90}
91
92impl From<f64> for EvaluationResult {
93    fn from(f: f64) -> Self {
94        Self::Float(f)
95    }
96}
97
98impl From<bool> for EvaluationResult {
99    fn from(b: bool) -> Self {
100        if b {
101            Self::Integer(1)
102        } else {
103            Self::Integer(0)
104        }
105    }
106}
107
108impl EvaluationResult {
109    #[must_use]
110    pub fn as_integer(self) -> i64 {
111        match self {
112            Self::Integer(i) => i,
113            Self::Float(f) => f as i64,
114        }
115    }
116
117    #[must_use]
118    pub fn as_float(self) -> f64 {
119        match self {
120            Self::Integer(i) => i as f64,
121            Self::Float(f) => f,
122        }
123    }
124
125    #[must_use]
126    pub fn as_bool(self) -> bool {
127        match self {
128            Self::Integer(i) => i != 0,
129            Self::Float(f) => f != 0_f64,
130        }
131    }
132
133    fn is_integer(&self) -> bool {
134        matches!(self, Self::Integer(..))
135    }
136}
137
138impl Expr {
139    pub fn eval<K, V>(&self, var_env: &HashMap<K, V>) -> GenApiResult<EvaluationResult>
140    where
141        K: Borrow<str> + Eq + Hash + fmt::Debug,
142        V: Borrow<Expr> + fmt::Debug,
143    {
144        match self {
145            Self::BinOp { kind, lhs, rhs } => lhs.eval_binop(*kind, rhs, var_env),
146            Self::UnOp { kind, expr } => expr.eval_unop(*kind, var_env),
147            Self::If { cond, then, else_ } => {
148                if cond.eval(var_env)?.as_bool() {
149                    then.eval(var_env)
150                } else {
151                    else_.eval(var_env)
152                }
153            }
154            &Self::Integer(i) => Ok(i.into()),
155            &Self::Float(f) => Ok(f.into()),
156            Self::Ident(s) => var_env
157                .get(s.as_str())
158                .ok_or_else(|| {
159                    GenApiError::invalid_node(
160                        format!("ident not found in variable env: {} not found", s).into(),
161                    )
162                })?
163                .borrow()
164                .eval(var_env),
165        }
166    }
167
168    fn eval_binop<K, V>(
169        &self,
170        op: BinOpKind,
171        rhs: &Self,
172        var_env: &HashMap<K, V>,
173    ) -> GenApiResult<EvaluationResult>
174    where
175        K: Borrow<str> + Eq + Hash + fmt::Debug,
176        V: Borrow<Expr> + fmt::Debug,
177    {
178        use std::ops::{Add, Mul, Rem, Sub};
179
180        Ok(match op {
181            BinOpKind::And => {
182                (self.eval(var_env)?.as_bool() && rhs.eval(var_env)?.as_bool()).into()
183            }
184            BinOpKind::Or => (self.eval(var_env)?.as_bool() || rhs.eval(var_env)?.as_bool()).into(),
185
186            _ => {
187                let lhs = self.eval(var_env)?;
188                let rhs = rhs.eval(var_env)?;
189
190                macro_rules! apply_arithmetic_op {
191                    ($fint:ident, $ffloat:ident) => {{
192                        if lhs.is_integer() && rhs.is_integer() {
193                            (lhs.as_integer().$fint(rhs.as_integer())).0.into()
194                        } else {
195                            (lhs.as_float().$ffloat(rhs.as_float())).into()
196                        }
197                    }};
198                }
199
200                macro_rules! apply_cmp_op {
201                    ($fint:ident, $ffloat:ident) => {{
202                        if lhs.is_integer() && rhs.is_integer() {
203                            (lhs.as_integer().$fint(&rhs.as_integer())).into()
204                        } else {
205                            (lhs.as_float().$ffloat(&rhs.as_float())).into()
206                        }
207                    }};
208                }
209                match op {
210                    BinOpKind::Add => apply_arithmetic_op!(overflowing_add, add),
211                    BinOpKind::Sub => apply_arithmetic_op!(overflowing_sub, sub),
212                    BinOpKind::Mul => apply_arithmetic_op!(overflowing_mul, mul),
213                    BinOpKind::Div => {
214                        // Division must be treated as floating points.
215                        // e.g. Converter node with `<FormulaFrom>TO/(1&lt;&lt;P1)</FormulaFrom>` where `P1` points to integer node are commonplace.
216                        (lhs.as_float() / rhs.as_float()).into()
217                    }
218                    BinOpKind::Rem => apply_arithmetic_op!(overflowing_rem, rem),
219                    BinOpKind::Pow => {
220                        if lhs.is_integer() && rhs.is_integer() && rhs.as_integer() >= 0 {
221                            lhs.as_integer()
222                                .overflowing_pow(rhs.as_integer() as u32)
223                                .0
224                                .into()
225                        } else {
226                            lhs.as_float().powf(rhs.as_float()).into()
227                        }
228                    }
229                    BinOpKind::Eq => apply_cmp_op!(eq, eq),
230                    BinOpKind::Ne => apply_cmp_op!(ne, ne),
231                    BinOpKind::Lt => apply_cmp_op!(lt, lt),
232                    BinOpKind::Le => apply_cmp_op!(le, le),
233                    BinOpKind::Gt => apply_cmp_op!(gt, gt),
234                    BinOpKind::Ge => apply_cmp_op!(ge, ge),
235                    BinOpKind::Shl => lhs
236                        .as_integer()
237                        .overflowing_shl(rhs.as_integer() as u32)
238                        .0
239                        .into(),
240                    BinOpKind::Shr => lhs
241                        .as_integer()
242                        .overflowing_shr(rhs.as_integer() as u32)
243                        .0
244                        .into(),
245                    BinOpKind::BitAnd => (lhs.as_integer() & rhs.as_integer()).into(),
246                    BinOpKind::BitOr => (lhs.as_integer() | rhs.as_integer()).into(),
247                    BinOpKind::Xor => (lhs.as_integer() ^ rhs.as_integer()).into(),
248                    _ => unreachable!(),
249                }
250            }
251        })
252    }
253
254    fn eval_unop<K, V>(
255        &self,
256        op: UnOpKind,
257        var_env: &HashMap<K, V>,
258    ) -> GenApiResult<EvaluationResult>
259    where
260        K: Borrow<str> + Eq + Hash + fmt::Debug,
261        V: Borrow<Expr> + fmt::Debug,
262    {
263        use std::ops::Neg;
264
265        let res = self.eval(var_env)?;
266        macro_rules! apply_op {
267            ($f:ident) => {
268                match res {
269                    EvaluationResult::Integer(i) => EvaluationResult::from(i.$f()),
270                    EvaluationResult::Float(f) => EvaluationResult::from(f.$f()),
271                }
272            };
273        }
274
275        Ok(match op {
276            UnOpKind::Not => (!res.as_integer()).into(),
277            UnOpKind::Abs => apply_op!(abs),
278            UnOpKind::Sgn => apply_op!(signum),
279            UnOpKind::Neg => apply_op!(neg),
280            UnOpKind::Sin => res.as_float().sin().into(),
281            UnOpKind::Cos => res.as_float().cos().into(),
282            UnOpKind::Tan => res.as_float().tan().into(),
283            UnOpKind::Asin => res.as_float().asin().into(),
284            UnOpKind::Acos => res.as_float().acos().into(),
285            UnOpKind::Atan => res.as_float().atan().into(),
286            UnOpKind::Exp => res.as_float().exp().into(),
287            UnOpKind::Ln => res.as_float().ln().into(),
288            UnOpKind::Lg => res.as_float().log10().into(),
289            UnOpKind::Sqrt => res.as_float().sqrt().into(),
290            UnOpKind::Trunc => res.as_float().trunc().into(),
291            UnOpKind::Floor => res.as_float().floor().into(),
292            UnOpKind::Ceil => res.as_float().ceil().into(),
293            UnOpKind::Round => res.as_float().round().into(),
294        })
295    }
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq)]
299pub enum BinOpKind {
300    Add,
301    Sub,
302    Mul,
303    Div,
304    Rem,
305    Pow,
306    Shl,
307    Shr,
308    And,
309    Or,
310    Eq,
311    Ne,
312    Lt,
313    Le,
314    Gt,
315    Ge,
316    BitAnd,
317    BitOr,
318    Xor,
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq)]
322pub enum UnOpKind {
323    Not,
324    Abs,
325    Sgn,
326    Neg,
327    Sin,
328    Cos,
329    Tan,
330    Asin,
331    Acos,
332    Atan,
333    Exp,
334    Ln,
335    Lg,
336    Sqrt,
337    Trunc,
338    Floor,
339    Ceil,
340    Round,
341}
342
343#[must_use]
344#[tracing::instrument(level = "trace")]
345pub fn parse(s: &str) -> Expr {
346    debug!("start parsing expression in `formula`");
347    let lexer = Lexer::new(s);
348    Parser { lexer }.expr()
349}
350
351struct Parser<'a> {
352    lexer: Lexer<'a>,
353}
354
355macro_rules! parse_binop {
356    ($self:ident.$f:ident, ($token:expr, $op:expr) $(,($token_rep:expr, $op_rep:expr))*) => {
357        {
358        let mut expr = $self.$f();
359        loop {
360            let (op_kind, rhs) = if $self.eat(&$token) {
361                ($op, $self.$f())
362            } $(else if $self.eat(&$token_rep) {
363                ($op_rep, $self.$f())
364            })* else {
365                break;
366            };
367            expr = Expr::BinOp {
368                kind: op_kind,
369                lhs: expr.into(),
370                rhs: rhs.into(),
371            };
372        }
373        expr
374        }
375    }
376}
377
378impl Parser<'_> {
379    fn expr(&mut self) -> Expr {
380        let expr = self.logical_or();
381        if self.eat(&Token::Question) {
382            let then = self.expr();
383            self.expect(&Token::Colon);
384            let else_ = self.expr();
385            Expr::If {
386                cond: expr.into(),
387                then: then.into(),
388                else_: else_.into(),
389            }
390        } else {
391            expr
392        }
393    }
394
395    fn logical_or(&mut self) -> Expr {
396        parse_binop!(self.logical_and, (Token::DoubleOr, BinOpKind::Or))
397    }
398
399    fn logical_and(&mut self) -> Expr {
400        parse_binop!(self.bitwise_or, (Token::DoubleAnd, BinOpKind::And))
401    }
402
403    fn bitwise_or(&mut self) -> Expr {
404        parse_binop!(self.bitwise_xor, (Token::Or, BinOpKind::BitOr))
405    }
406
407    fn bitwise_xor(&mut self) -> Expr {
408        parse_binop!(self.bitwise_and, (Token::Caret, BinOpKind::Xor))
409    }
410
411    fn bitwise_and(&mut self) -> Expr {
412        parse_binop!(self.eq, (Token::And, BinOpKind::BitAnd))
413    }
414
415    fn eq(&mut self) -> Expr {
416        parse_binop!(
417            self.rel,
418            (Token::Eq, BinOpKind::Eq),
419            (Token::Ne, BinOpKind::Ne)
420        )
421    }
422
423    fn rel(&mut self) -> Expr {
424        parse_binop!(
425            self.bit_shift,
426            (Token::Lt, BinOpKind::Lt),
427            (Token::Le, BinOpKind::Le),
428            (Token::Gt, BinOpKind::Gt),
429            (Token::Ge, BinOpKind::Ge)
430        )
431    }
432
433    fn bit_shift(&mut self) -> Expr {
434        parse_binop!(
435            self.term,
436            (Token::Shl, BinOpKind::Shl),
437            (Token::Shr, BinOpKind::Shr)
438        )
439    }
440
441    fn term(&mut self) -> Expr {
442        parse_binop!(
443            self.factor,
444            (Token::Plus, BinOpKind::Add),
445            (Token::Minus, BinOpKind::Sub)
446        )
447    }
448
449    fn factor(&mut self) -> Expr {
450        parse_binop!(
451            self.unop,
452            (Token::Star, BinOpKind::Mul),
453            (Token::Slash, BinOpKind::Div),
454            (Token::Percent, BinOpKind::Rem)
455        )
456    }
457
458    fn unop(&mut self) -> Expr {
459        if self.eat(&Token::Tilde) {
460            let expr = self.unop();
461            Expr::UnOp {
462                kind: UnOpKind::Not,
463                expr: expr.into(),
464            }
465        } else if self.eat(&Token::Minus) {
466            let expr = self.unop();
467            Expr::UnOp {
468                kind: UnOpKind::Neg,
469                expr: expr.into(),
470            }
471        } else {
472            // Eat unary `+` if exists.
473            self.eat(&Token::Plus);
474            self.pow()
475        }
476    }
477
478    fn pow(&mut self) -> Expr {
479        let expr = self.primary();
480        if self.eat(&Token::DoubleStar) {
481            let rhs = self.unop();
482            Expr::BinOp {
483                kind: BinOpKind::Pow,
484                lhs: expr.into(),
485                rhs: rhs.into(),
486            }
487        } else {
488            expr
489        }
490    }
491
492    fn primary(&mut self) -> Expr {
493        if self.eat(&Token::LParen) {
494            let expr = self.expr();
495            self.expect(&Token::RParen);
496            expr
497        } else if let Some(i) = self.next_integer() {
498            Expr::Integer(i)
499        } else if let Some(f) = self.next_float() {
500            Expr::Float(f)
501        } else {
502            let s = self.next_ident().unwrap();
503            if self.eat(&Token::LParen) {
504                let op = match s.as_str() {
505                    "NEG" => UnOpKind::Neg,
506                    "SIN" => UnOpKind::Sin,
507                    "COS" => UnOpKind::Cos,
508                    "TAN" => UnOpKind::Tan,
509                    "ASIN" => UnOpKind::Asin,
510                    "ACOS" => UnOpKind::Acos,
511                    "ATAN" => UnOpKind::Atan,
512                    "ABS" => UnOpKind::Abs,
513                    "EXP" => UnOpKind::Exp,
514                    "LN" => UnOpKind::Ln,
515                    "LG" => UnOpKind::Lg,
516                    "SQRT" => UnOpKind::Sqrt,
517                    "TRUNC" => UnOpKind::Trunc,
518                    "FLOOR" => UnOpKind::Floor,
519                    "CEIL" => UnOpKind::Ceil,
520                    "ROUND" => UnOpKind::Round,
521                    other => panic!("{} is not a keyword or function name", other),
522                };
523                let expr = self.expr();
524                self.expect(&Token::RParen);
525                Expr::UnOp {
526                    kind: op,
527                    expr: expr.into(),
528                }
529            } else {
530                Expr::Ident(s)
531            }
532        }
533    }
534
535    fn eat(&mut self, tok: &Token) -> bool {
536        match self.lexer.peek() {
537            Some(peek) if peek == tok => {
538                self.lexer.next();
539                true
540            }
541            _ => false,
542        }
543    }
544
545    fn next_integer(&mut self) -> Option<i64> {
546        if let Some(&Token::Integer(i)) = self.lexer.peek() {
547            self.lexer.next();
548            Some(i)
549        } else {
550            None
551        }
552    }
553
554    fn next_float(&mut self) -> Option<f64> {
555        if let Some(&Token::Float(f)) = self.lexer.peek() {
556            self.lexer.next();
557            Some(f)
558        } else if let Some(Token::Ident(s)) = self.lexer.peek() {
559            let f = match s.as_str() {
560                "PI" => std::f64::consts::PI,
561                "E" => std::f64::consts::E,
562                _ => return None,
563            };
564            self.lexer.next();
565            Some(f)
566        } else {
567            None
568        }
569    }
570
571    fn next_ident(&mut self) -> Option<String> {
572        if let Some(Token::Ident(s)) = self.lexer.peek() {
573            let s = s.to_string();
574            self.lexer.next();
575            Some(s)
576        } else {
577            None
578        }
579    }
580
581    fn expect(&mut self, tok: &Token) {
582        assert!(self.eat(tok))
583    }
584}
585
586#[derive(Debug, Clone, PartialEq)]
587enum Token {
588    LParen,
589    RParen,
590    Plus,
591    Minus,
592    Star,
593    DoubleStar,
594    Slash,
595    Percent,
596    And,
597    DoubleAnd,
598    Or,
599    DoubleOr,
600    Caret,
601    Tilde,
602    Eq,
603    Ne,
604    Colon,
605    Question,
606    Lt,
607    Le,
608    Gt,
609    Ge,
610    Shl,
611    Shr,
612    Ident(String),
613    Float(f64),
614    Integer(i64),
615}
616
617struct Lexer<'a> {
618    src: &'a [u8],
619    peek: Option<Token>,
620    cur: usize,
621    peek_char: Option<(char, usize)>,
622}
623
624impl<'a> Lexer<'a> {
625    fn new(src: &'a str) -> Self {
626        tracing::trace!(src);
627        Self {
628            src: src.as_bytes(),
629            peek: None,
630            cur: 0,
631            peek_char: None,
632        }
633    }
634
635    fn next(&mut self) -> Option<Token> {
636        self.peek();
637        self.peek.take()
638    }
639
640    fn peek(&mut self) -> Option<&Token> {
641        if let Some(ref peek) = self.peek {
642            return Some(peek);
643        }
644
645        while self.eat_char(|c| c.is_whitespace() || c.is_ascii_control()) {}
646
647        self.peek = Some(match self.next_char()? {
648            '(' => Token::LParen,
649            ')' => Token::RParen,
650            '+' => Token::Plus,
651            '-' => Token::Minus,
652            '*' => {
653                if self.eat_char(|c| c == '*') {
654                    Token::DoubleStar
655                } else {
656                    Token::Star
657                }
658            }
659            '/' => Token::Slash,
660            '%' => Token::Percent,
661            '&' => {
662                if self.eat_char(|c| c == '&') {
663                    Token::DoubleAnd
664                } else {
665                    Token::And
666                }
667            }
668            '|' => {
669                if self.eat_char(|c| c == '|') {
670                    Token::DoubleOr
671                } else {
672                    Token::Or
673                }
674            }
675            '^' => Token::Caret,
676            '~' => Token::Tilde,
677            '=' => Token::Eq,
678            ':' => Token::Colon,
679            '?' => Token::Question,
680            '<' => {
681                if self.eat_char(|c| c == '>') {
682                    Token::Ne
683                } else if self.eat_char(|c| c == '=') {
684                    Token::Le
685                } else if self.eat_char(|c| c == '<') {
686                    Token::Shl
687                } else {
688                    Token::Lt
689                }
690            }
691            '>' => {
692                if self.eat_char(|c| c == '=') {
693                    Token::Ge
694                } else if self.eat_char(|c| c == '>') {
695                    Token::Shr
696                } else {
697                    Token::Gt
698                }
699            }
700            '.' => {
701                let start_pos = self.cur - 1;
702                while self.eat_char(char::is_numeric) {}
703                let end_pos = self.cur;
704                let f = f64::from_str(self.sub_string(start_pos, end_pos)).unwrap();
705                Token::Float(f)
706            }
707
708            c if c.is_alphabetic() => {
709                let start_pos = self.cur - 1;
710                while self.eat_char(|c| c.is_alphanumeric() || c == '.' || c == '_') {}
711                let end_pos = self.cur;
712                Token::Ident(self.sub_string(start_pos, end_pos).into())
713            }
714
715            c if c.is_numeric() => {
716                if c == '0' && self.eat_char(|c| c == 'x') {
717                    let start_pos = self.cur;
718                    while self.eat_char(|c| c.is_ascii_hexdigit()) {}
719                    let end_pos = self.cur;
720                    let i = i64::from_str_radix(self.sub_string(start_pos, end_pos), 16).unwrap();
721                    Token::Integer(i)
722                } else {
723                    let start_pos = self.cur - 1;
724                    let mut is_integer = true;
725                    let mut check_digit = |c: char| {
726                        if c == '.' {
727                            is_integer = false;
728                            true
729                        } else {
730                            c.is_numeric()
731                        }
732                    };
733                    while self.eat_char(&mut check_digit) {}
734                    let end_pos = self.cur;
735                    let s = self.sub_string(start_pos, end_pos);
736                    if is_integer {
737                        Token::Integer(i64::from_str(s).unwrap())
738                    } else {
739                        Token::Float(f64::from_str(s).unwrap())
740                    }
741                }
742            }
743
744            c => panic!("unexpected character `{}` in formula", c),
745        });
746
747        self.peek.as_ref()
748    }
749
750    fn next_char(&mut self) -> Option<char> {
751        self.peek_char();
752        if let Some((peek, idx)) = self.peek_char.take() {
753            self.cur = idx;
754            Some(peek)
755        } else {
756            None
757        }
758    }
759
760    fn eat_char(&mut self, f: impl FnOnce(char) -> bool) -> bool {
761        match self.peek_char() {
762            Some(peek) if f(peek) => {
763                self.next_char();
764                true
765            }
766            _ => false,
767        }
768    }
769
770    fn peek_char(&mut self) -> Option<char> {
771        if let Some((peek, _)) = self.peek_char {
772            return Some(peek);
773        }
774
775        let (peek, idx) = if self.peek_char_raw('&', 0)
776            && self.peek_char_raw('a', 1)
777            && self.peek_char_raw('m', 2)
778            && self.peek_char_raw('p', 3)
779            && self.peek_char_raw(';', 4)
780        {
781            ('&', self.cur + 5)
782        } else if self.peek_char_raw('&', 0)
783            && self.peek_char_raw('l', 1)
784            && self.peek_char_raw('t', 2)
785            && self.peek_char_raw(';', 3)
786        {
787            ('<', self.cur + 4)
788        } else if self.peek_char_raw('&', 0)
789            && self.peek_char_raw('g', 1)
790            && self.peek_char_raw('t', 2)
791            && self.peek_char_raw(';', 3)
792        {
793            ('>', self.cur + 4)
794        } else if let Some(c) = self.src.get(self.cur).map(|c| *c as char) {
795            (c, self.cur + 1)
796        } else {
797            return None;
798        };
799
800        self.peek_char = Some((peek, idx));
801        Some(peek)
802    }
803
804    fn peek_char_raw(&self, c: char, n: usize) -> bool {
805        self.src
806            .get(self.cur + n)
807            .map_or(false, |next| c == *next as char)
808    }
809
810    fn sub_string(&self, start_pos: usize, end_pos: usize) -> &str {
811        std::str::from_utf8(&self.src[start_pos..end_pos]).unwrap()
812    }
813}
814
815#[cfg(test)]
816mod tests {
817    use super::*;
818
819    #[test]
820    fn test_lexer() {
821        let t = Lexer::new("&amp;").next().unwrap();
822        assert_eq!(Token::And, t);
823
824        let t = Lexer::new("&lt;").next().unwrap();
825        assert_eq!(Token::Lt, t);
826
827        let t = Lexer::new("&gt;").next().unwrap();
828        assert_eq!(Token::Gt, t);
829
830        let t = Lexer::new("Foo1.Max").next().unwrap();
831        assert_eq!(Token::Ident("Foo1.Max".into()), t);
832
833        let t = Lexer::new("0xa").next().unwrap();
834        assert_eq!(Token::Integer(0xa), t);
835
836        let t = Lexer::new("10").next().unwrap();
837        assert_eq!(Token::Integer(10), t);
838
839        let t = Lexer::new("0.1").next().unwrap();
840        assert!(matches!(t, Token::Float(_)));
841
842        let t = Lexer::new(".1").next().unwrap();
843        assert!(matches!(t, Token::Float(_)));
844
845        let t = Lexer::new("  10 ").next().unwrap();
846        assert_eq!(Token::Integer(10), t);
847
848        let mut lexer = Lexer::new("&&||<>**>><<");
849        assert_eq!(Token::DoubleAnd, lexer.next().unwrap());
850        assert_eq!(Token::DoubleOr, lexer.next().unwrap());
851        assert_eq!(Token::Ne, lexer.next().unwrap());
852        assert_eq!(Token::DoubleStar, lexer.next().unwrap());
853        assert_eq!(Token::Shr, lexer.next().unwrap());
854        assert_eq!(Token::Shl, lexer.next().unwrap());
855    }
856
857    fn test_eval_impl(expr: &str, var_env: &HashMap<&str, Expr>) {
858        let expr = parse(expr);
859        assert!(matches!(
860            expr.eval(var_env).unwrap(),
861            EvaluationResult::Integer(1)
862        ));
863    }
864
865    fn test_eval_no_var_impl(expr: &str) {
866        test_eval_impl(expr, &HashMap::new());
867    }
868
869    #[test]
870    fn test_eval_no_env() {
871        test_eval_no_var_impl("(1 + 2 * 3 - 6) = 1 ");
872        test_eval_no_var_impl("(10 % 3) = 1");
873        test_eval_no_var_impl("(2 * 3 ** 2) = 18");
874        test_eval_no_var_impl("(2 ** 3 ** 2) = 512");
875        test_eval_no_var_impl("-1 ** 2 = -1");
876        test_eval_no_var_impl("(1 << 2 + 2 >> 1) = 8");
877        test_eval_no_var_impl("(1 || 1 && 0) = 1");
878        test_eval_no_var_impl("((1 <> 0) + (1 = 1)) = 2");
879        test_eval_no_var_impl("((1 > 0) + (1 > 1) + (1 >= 1) + (1 >= 2)) = 2");
880        test_eval_no_var_impl("((0 < 1) + (1 < 1) + (1 <= 1) + (2 <= 1)) = 2");
881        test_eval_no_var_impl("(0xff00 & 0xf0f0) = 0xf000");
882        test_eval_no_var_impl("(0xff00 | 0xf0f0) = 0xfff0");
883        test_eval_no_var_impl("(0xff00 ^ 0xf0f0) = 0x0ff0");
884        test_eval_no_var_impl("(~0) = (0 - 1)");
885    }
886
887    #[test]
888    fn test_eval_with_env() {
889        let env = vec![
890            ("VAR1", Expr::Integer(1)),
891            ("EPS", Expr::Float(f64::EPSILON)),
892            ("EXP", Expr::Float(1.0)),
893        ]
894        .into_iter()
895        .collect();
896
897        test_eval_impl("ABS(SIN(PI / 2) - VAR1) < EPS", &env);
898        test_eval_impl("ABS(LN(E) - 1) < EPS", &env);
899        test_eval_impl("ABS(1. / 2. - 0.5) < EPS", &env);
900        test_eval_impl("ABS(2 ** -1 - 1. / 2.) < EPS", &env);
901        test_eval_impl("ABS(2 ** -1 ** 2 - 1. / 2.) < EPS", &env);
902        test_eval_impl("ABS(VAR1 + 1 / 4 - 1.25) < EPS", &env);
903        test_eval_impl("( EXP = 1 ) ? 1 : 0", &env);
904    }
905}