1#![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 (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 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("&").next().unwrap();
822 assert_eq!(Token::And, t);
823
824 let t = Lexer::new("<").next().unwrap();
825 assert_eq!(Token::Lt, t);
826
827 let t = Lexer::new(">").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}