1use cyfs_base::*;
2
3#[derive(Debug, Eq, PartialEq)]
4enum ExpLexItem {
5 Op(ExpOp),
6 LeftParen,
7 RightParen,
8 Token(String),
9}
10
11pub enum ExpOpArity {
13 Unary,
14 Binary,
15}
16
17#[derive(Debug, Eq, PartialEq, Clone)]
18pub enum ExpOp {
19 EQ,
21
22 NE,
24
25 LT,
27
28 LE,
30
31 GT,
33
34 GE,
36
37 NOT,
39
40 AND,
42
43 BAND,
46
47 BOR,
49
50 BXOR,
52
53 OR,
55}
56
57impl ExpOp {
58 fn arity(&self) -> ExpOpArity {
59 match &self {
60 ExpOp::NOT => ExpOpArity::Unary,
61 _ => ExpOpArity::Binary,
62 }
63 }
64
65 fn priority(&self) -> u8 {
67 match &self {
68 ExpOp::NOT => 17,
69 ExpOp::LT | ExpOp::LE | ExpOp::GT | ExpOp::GE => 12,
70 ExpOp::NE | ExpOp::EQ => 11,
71 ExpOp::BAND => 10,
72 ExpOp::BXOR => 9,
73 ExpOp::BOR => 8,
74 ExpOp::AND => 7,
75 ExpOp::OR => 6,
76 }
77 }
78
79 fn from_str(s: &str) -> Option<Self> {
80 let ret = match s {
81 "==" => Self::EQ,
82 "!=" => Self::NE,
83
84 "<" => Self::LT,
85 "<=" => Self::LE,
86
87 ">" => Self::GT,
88 ">=" => Self::GE,
89
90 "!" => Self::NOT,
91
92 "&&" => Self::AND,
93 "||" => Self::OR,
94
95 "&" => Self::BAND,
96 "^" => Self::BXOR,
97 "|" => Self::BOR,
98
99 _ => {
100 return None;
101 }
102 };
103
104 Some(ret)
105 }
106
107 fn to_str(&self) -> &str {
108 match *self {
109 Self::EQ => "==",
110 Self::NE => "!=",
111
112 Self::LT => "<",
113 Self::LE => "<=",
114
115 Self::GT => ">",
116 Self::GE => ">=",
117
118 Self::NOT => "!",
119
120 Self::AND => "&&",
121 Self::OR => "||",
122
123 Self::BAND => "&",
124 Self::BXOR => "^",
125 Self::BOR => "|",
126 }
127 }
128
129 pub fn parse(ch: char, it: &mut std::str::Chars<'_>) -> Option<Self> {
130 let result = match ch {
131 '!' => match it.clone().next() {
132 Some('=') => {
133 it.next();
134 Self::NE
135 }
136 _ => Self::NOT,
137 },
138 '=' => match it.next() {
139 Some('=') => Self::EQ,
140 _ => return None,
141 },
142 '>' => match it.clone().next() {
143 Some('=') => {
144 it.next();
145 Self::GE
146 }
147 _ => Self::GT,
148 },
149 '<' => match it.clone().next() {
150 Some('=') => {
151 it.next();
152 Self::LE
153 }
154 _ => Self::LT,
155 },
156 '&' => match it.clone().next() {
157 Some('&') => {
158 it.next();
159 Self::AND
160 }
161 _ => Self::BAND,
162 },
163 '|' => match it.clone().next() {
164 Some('|') => {
165 it.next();
166 Self::OR
167 }
168 _ => Self::BOR,
169 },
170 '^' => Self::BXOR,
171 _ => return None,
172 };
173
174 Some(result)
175 }
176}
177
178#[derive(Eq, PartialEq)]
180enum ExpTokenQuote {
181 Single,
182 Double,
183 None,
184}
185
186enum ExpTokenQuoteParserResult {
187 Continue,
188 Begin,
189 Token,
190 End,
191}
192
193impl ExpTokenQuote {
194 fn from_char(c: char) -> Self {
195 match c {
196 '"' => Self::Double,
197 '\'' => Self::Single,
198 _ => Self::None,
199 }
200 }
201}
202
203struct ExpTokenQuoteParser {
204 state: ExpTokenQuote,
205}
206
207impl ExpTokenQuoteParser {
208 pub fn new() -> Self {
209 Self {
210 state: ExpTokenQuote::None,
211 }
212 }
213
214 fn next(&mut self, exp: &str, c: char) -> BuckyResult<ExpTokenQuoteParserResult> {
216 match ExpTokenQuote::from_char(c) {
217 ExpTokenQuote::None => match self.state {
218 ExpTokenQuote::None => return Ok(ExpTokenQuoteParserResult::Continue),
219 _ => Ok(ExpTokenQuoteParserResult::Token),
220 },
221 state @ _ => match self.state {
222 ExpTokenQuote::None => {
223 self.state = state;
224 Ok(ExpTokenQuoteParserResult::Begin)
225 }
226 _ => {
227 if self.state != state {
228 let msg = format!("filter exp quote unmatch! exp={}", exp);
229 error!("{}", msg);
230 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
231 }
232
233 self.state = ExpTokenQuote::None;
234 Ok(ExpTokenQuoteParserResult::End)
235 }
236 },
237 }
238 }
239}
240
241struct ExpParser;
244
245const EXP_TOKEN_QUOTES: [char; 2] = ['\'', '"'];
246
247impl ExpParser {
248 fn is_operand_char(ch: char) -> bool {
250 ch.is_ascii_alphabetic()
251 || ch.is_numeric()
252 || ch == '-'
253 || ch == '_'
254 || ch == '.'
255 || ch == '$'
256 || ch == '*'
257 || ch == '/'
258 || ch == '\\'
259 }
260
261 fn parse_token(token: &[char]) -> BuckyResult<ExpLexItem> {
262 let s: String = token.iter().collect();
263
264 match ExpOp::from_str(&s) {
265 Some(op) => Ok(ExpLexItem::Op(op)),
266 None => {
267 for ch in token {
269 if !Self::is_operand_char(*ch) {
270 let msg = format!("invalid exp token: {}", s);
271 error!("{}", msg);
272
273 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
274 }
275 }
276
277 let s = s
279 .trim_start_matches(&EXP_TOKEN_QUOTES[..])
280 .trim_end_matches(&EXP_TOKEN_QUOTES[..]);
281
282 Ok(ExpLexItem::Token(s.to_owned()))
283 }
284 }
285 }
286
287 fn direct_parse_token(token: &[char]) -> BuckyResult<ExpLexItem> {
288 let s: String = token.iter().collect();
289
290 let s = s
292 .trim_start_matches(&EXP_TOKEN_QUOTES[..])
293 .trim_end_matches(&EXP_TOKEN_QUOTES[..]);
294
295 Ok(ExpLexItem::Token(s.to_owned()))
296 }
297
298 fn convert_to_rpn(exp: &str, list: Vec<ExpLexItem>) -> BuckyResult<Vec<ExpLexItem>> {
300 let mut operands = vec![];
301 let mut operators = vec![];
302
303 for item in list {
304 match item {
305 ExpLexItem::Token(ref _v) => {
306 operands.push(item);
307 }
308 ExpLexItem::LeftParen => {
309 operators.push(item);
310 }
311 ExpLexItem::RightParen => {
312 loop {
314 match operators.pop() {
315 Some(v) => match v {
316 ExpLexItem::LeftParen => {
317 break;
318 }
319 _ => {
320 operands.push(v);
321 }
322 },
323 None => {
324 let msg = format!("unmatch exp paren: {}", exp);
325 error!("{}", msg);
326
327 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
328 }
329 }
330 }
331 }
332
333 ExpLexItem::Op(v) => {
334 loop {
335 let last_op = operators.last();
336
337 if last_op.is_none() {
339 operators.push(ExpLexItem::Op(v));
340 break;
341 }
342
343 let last_op = last_op.unwrap();
344
345 if v == ExpOp::NOT {
347 if let ExpLexItem::Op(prev) = last_op {
348 if *prev == v {
349 operators.pop();
350 break;
351 }
352 }
353 }
354
355 match last_op {
357 ExpLexItem::Op(lv) => {
358 if v.priority() > lv.priority() {
360 operators.push(ExpLexItem::Op(v));
361 break;
362 } else {
363 let last_op = operators.pop().unwrap();
364 operands.push(last_op);
365 }
366 }
367 ExpLexItem::LeftParen => {
369 operators.push(ExpLexItem::Op(v));
370 break;
371 }
372 _ => unreachable!(),
373 }
374 }
375 }
376 }
377 }
378
379 while !operators.is_empty() {
381 let op = operators.pop().unwrap();
382 operands.push(op);
383 }
384
385 Ok(operands)
395 }
396
397 pub fn parse_lex(exp: &str) -> BuckyResult<Vec<ExpLexItem>> {
398 let mut it = exp.chars();
399
400 let mut token_list = vec![];
401 let mut token = vec![];
402 let mut quote_parse = ExpTokenQuoteParser::new();
403 loop {
404 match it.next() {
405 Some(c) => {
406 match quote_parse.next(exp, c)? {
408 ExpTokenQuoteParserResult::Continue => {
409 }
411 ExpTokenQuoteParserResult::Token => {
412 token.push(c);
413 continue;
414 }
415 ExpTokenQuoteParserResult::Begin => {
416 continue;
417 }
418 ExpTokenQuoteParserResult::End => {
419 token_list.push(Self::direct_parse_token(&token)?);
420 token.clear();
421 continue;
422 }
423 }
424 if c.is_whitespace() {
425 if !token.is_empty() {
426 token_list.push(Self::parse_token(&token)?);
427 token.clear();
428 }
429 continue;
430 }
431
432 match c {
433 '(' => {
434 if !token.is_empty() {
435 token_list.push(Self::parse_token(&token)?);
436 token.clear();
437 }
438 token_list.push(ExpLexItem::LeftParen);
439 }
440 ')' => {
441 if !token.is_empty() {
442 token_list.push(Self::parse_token(&token)?);
443 token.clear();
444 }
445 token_list.push(ExpLexItem::RightParen);
446 }
447 _ => {
448 if Self::is_operand_char(c) {
449 token.push(c);
450 } else {
451 match ExpOp::parse(c, &mut it) {
452 Some(op) => {
453 if !token.is_empty() {
454 token_list.push(Self::parse_token(&token)?);
455 token.clear();
456 }
457
458 token_list.push(ExpLexItem::Op(op));
459 }
460 None => {
461 let msg = format!(
462 "invalid operand or operator: exp={}, char={}, token={}",
463 exp,
464 c,
465 it.as_str()
466 );
467 error!("{}", msg);
468 return Err(BuckyError::new(
469 BuckyErrorCode::InvalidFormat,
470 msg,
471 ));
472 }
473 }
474 }
475 }
476 }
477 }
478 None => {
479 break;
480 }
481 }
482 }
483 if !token.is_empty() {
484 token_list.push(Self::parse_token(&token)?);
485 }
486
487 debug!("exp to lex list: exp={}, list={:?}", exp, token_list);
488
489 Self::convert_to_rpn(exp, token_list)
491 }
492}
493
494pub struct ExpReservedTokenList {
495 list: Vec<(String, ExpTokenEvalValue)>,
496}
497
498impl ExpReservedTokenList {
499 pub fn new() -> Self {
500 Self { list: Vec::new() }
501 }
502
503 pub fn translate(&mut self, prefix: &str) {
505 self.list
506 .iter_mut()
507 .for_each(|v| v.0 = format!("{}.{}", prefix, v.0));
508 }
509
510 pub fn translate_resp(&mut self) {
512 self.translate("resp");
513 }
514
515 pub fn append(&mut self, other: Self) {
516 for item in other.list {
517 assert!(!self.is_reserved_token(&item.0));
518
519 self.list.push(item);
520 }
521 }
522
523 fn add_token(&mut self, token: &str, default_value: ExpTokenEvalValue) {
524 assert!(!self.is_reserved_token(token));
525
526 self.list.push((token.to_owned(), default_value));
527 }
528
529 pub fn add_string(&mut self, token: &str) {
530 self.add_token(token, ExpTokenEvalValue::String(String::default()))
531 }
532
533 pub fn add_glob(&mut self, token: &str) {
534 self.add_token(token, ExpTokenEvalValue::Glob(ExpGlobToken::default()))
535 }
536
537 pub fn add_bool(&mut self, token: &str) {
538 self.add_token(token, ExpTokenEvalValue::Bool(false))
539 }
540
541 pub fn add_i8(&mut self, token: &str) {
542 self.add_token(token, ExpTokenEvalValue::I8(0))
543 }
544 pub fn add_i16(&mut self, token: &str) {
545 self.add_token(token, ExpTokenEvalValue::I16(0))
546 }
547 pub fn add_i32(&mut self, token: &str) {
548 self.add_token(token, ExpTokenEvalValue::I32(0))
549 }
550 pub fn add_i64(&mut self, token: &str) {
551 self.add_token(token, ExpTokenEvalValue::I64(0))
552 }
553
554 pub fn add_u8(&mut self, token: &str) {
555 self.add_token(token, ExpTokenEvalValue::U8(0))
556 }
557 pub fn add_u16(&mut self, token: &str) {
558 self.add_token(token, ExpTokenEvalValue::U16(0))
559 }
560 pub fn add_u32(&mut self, token: &str) {
561 self.add_token(token, ExpTokenEvalValue::U32(0))
562 }
563 pub fn add_u64(&mut self, token: &str) {
564 self.add_token(token, ExpTokenEvalValue::U64(0))
565 }
566
567 pub fn is_reserved_token(&self, token: &str) -> bool {
568 self.list.iter().find(|v| v.0.as_str() == token).is_some()
569 }
570
571 pub fn default_value(&self, token: &str) -> Option<ExpTokenEvalValue> {
572 self.list
573 .iter()
574 .find(|v| v.0.as_str() == token)
575 .map(|v| v.1.clone())
576 }
577}
578
579impl ExpReservedTokenTranslator for ExpReservedTokenList {
580 fn trans(&self, token: &str) -> ExpTokenEvalValue {
581 self.default_value(token).unwrap()
582 }
583}
584
585impl ExpReservedTokenTranslator for &ExpReservedTokenList {
586 fn trans(&self, token: &str) -> ExpTokenEvalValue {
587 self.default_value(token).unwrap()
588 }
589}
590
591#[derive(Clone, Debug)]
592pub enum ExpGlobToken {
593 Glob(globset::GlobMatcher),
594 StringList(Vec<String>),
595 String(String),
596}
597
598impl ExpGlobToken {
599 pub fn new_glob(token: &str) -> BuckyResult<Self> {
600 let glob = globset::GlobBuilder::new(token)
601 .case_insensitive(true)
602 .literal_separator(true)
603 .build()
604 .map_err(|e| {
605 let msg = format!("parse filter glob as glob error! token={}, {}", token, e);
606 error!("{}", msg);
607 BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
608 })?;
609
610 Ok(Self::Glob(glob.compile_matcher()))
611 }
612
613 fn append_string(list: &mut Vec<String>, token: String) {
615 let s = token.trim_end_matches('/');
616 if s.len() == token.len() {
617 list.push(format!("{}/", s));
618 drop(s);
619 list.push(token);
620 } else {
621 list.push(s.to_owned());
622 if s.len() + 1 == token.len() {
623 drop(s);
624 list.push(token);
625 } else {
626 list.push(format!("{}/", s));
627 }
628 }
629 }
630
631 pub fn new_string(token: String) -> Self {
632 let mut list = Vec::with_capacity(2);
633 Self::append_string(&mut list, token);
634 Self::StringList(list)
635 }
636
637 pub fn new_string_list(tokens: Vec<String>) -> Self {
638 let mut list = Vec::with_capacity(tokens.len() * 2);
639 for token in tokens {
640 Self::append_string(&mut list, token);
641 }
642 Self::StringList(list)
643 }
644
645 pub fn is_glob(&self) -> bool {
646 match *self {
647 Self::Glob(_) => true,
648 _ => false,
649 }
650 }
651
652 pub fn as_glob(&self) -> &globset::GlobMatcher {
653 match self {
654 Self::Glob(v) => v,
655 _ => unreachable!(),
656 }
657 }
658
659 pub fn eq(left: &Self, right: &Self) -> bool {
661 match left {
662 Self::Glob(_) => Self::eq(right, left),
663 Self::String(s) => right.as_glob().is_match(s),
664 Self::StringList(list) => {
665 for s in list {
666 if right.as_glob().is_match(&s) {
667 return true;
668 }
669 }
670
671 false
672 }
673 }
674 }
675}
676
677impl Default for ExpGlobToken {
678 fn default() -> Self {
679 Self::new_string("".to_owned())
680 }
681}
682
683impl PartialEq for ExpGlobToken {
684 fn eq(&self, other: &Self) -> bool {
685 Self::eq(&self, other)
686 }
687}
688impl Eq for ExpGlobToken {}
689
690impl PartialOrd for ExpGlobToken {
691 fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
692 unreachable!();
693 }
694}
695
696impl Ord for ExpGlobToken {
697 fn cmp(&self, _other: &Self) -> std::cmp::Ordering {
698 unreachable!();
699 }
700}
701
702#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
704pub enum ExpTokenEvalValue {
705 None,
707
708 String(String),
709 Glob(ExpGlobToken),
710 Bool(bool),
711
712 I8(i8),
713 I16(i16),
714 I32(i32),
715 I64(i64),
716
717 U8(u8),
718 U16(u16),
719 U32(u32),
720 U64(u64),
721}
722
723impl Into<i8> for ExpTokenEvalValue {
724 fn into(self) -> i8 {
725 match self {
726 Self::I8(v) => v,
727 _ => unreachable!(),
728 }
729 }
730}
731impl Into<i16> for ExpTokenEvalValue {
732 fn into(self) -> i16 {
733 match self {
734 Self::I16(v) => v,
735 _ => unreachable!(),
736 }
737 }
738}
739impl Into<i32> for ExpTokenEvalValue {
740 fn into(self) -> i32 {
741 match self {
742 Self::I32(v) => v,
743 _ => unreachable!(),
744 }
745 }
746}
747impl Into<i64> for ExpTokenEvalValue {
748 fn into(self) -> i64 {
749 match self {
750 Self::I64(v) => v,
751 _ => unreachable!(),
752 }
753 }
754}
755
756impl Into<u8> for ExpTokenEvalValue {
757 fn into(self) -> u8 {
758 match self {
759 Self::U8(v) => v,
760 _ => unreachable!(),
761 }
762 }
763}
764impl Into<u16> for ExpTokenEvalValue {
765 fn into(self) -> u16 {
766 match self {
767 Self::U16(v) => v,
768 _ => unreachable!(),
769 }
770 }
771}
772impl Into<u32> for ExpTokenEvalValue {
773 fn into(self) -> u32 {
774 match self {
775 Self::U32(v) => v,
776 _ => unreachable!(),
777 }
778 }
779}
780impl Into<u64> for ExpTokenEvalValue {
781 fn into(self) -> u64 {
782 match self {
783 Self::U64(v) => v,
784 _ => unreachable!(),
785 }
786 }
787}
788
789trait FromStrRadix<T> {
791 fn from_str_radix(src: &str, radix: u32) -> Result<T, std::num::ParseIntError>;
792}
793
794macro_rules! from_str_radix_trait_impl {
795 ($T:ty) => {
796 impl FromStrRadix<$T> for $T {
797 fn from_str_radix(src: &str, radix: u32) -> Result<$T, std::num::ParseIntError> {
798 <$T>::from_str_radix(src, radix)
799 }
800 }
801 };
802}
803from_str_radix_trait_impl!(i8);
804from_str_radix_trait_impl!(i16);
805from_str_radix_trait_impl!(i32);
806from_str_radix_trait_impl!(i64);
807from_str_radix_trait_impl!(u8);
808from_str_radix_trait_impl!(u16);
809from_str_radix_trait_impl!(u32);
810from_str_radix_trait_impl!(u64);
811
812impl ExpTokenEvalValue {
813 pub fn from_string<T>(v: &T) -> Self
814 where
815 T: ToString,
816 {
817 Self::String(v.to_string())
818 }
819
820 pub fn from_opt_string<T>(v: &Option<T>) -> Self
821 where
822 T: ToString,
823 {
824 match v {
825 Some(v) => Self::String(v.to_string()),
826 None => Self::None,
827 }
828 }
829
830 pub fn from_glob_list<T>(v: &Vec<T>) -> Self
831 where
832 T: ToString,
833 {
834 let list: Vec<String> = v.iter().map(|v| v.to_string()).collect();
835 Self::Glob(ExpGlobToken::new_string_list(list))
836 }
837
838 pub fn from_opt_glob<T>(v: &Option<T>) -> Self
839 where
840 T: ToString,
841 {
842 match v {
843 Some(v) => Self::Glob(ExpGlobToken::new_string(v.to_string())),
844 None => Self::None,
845 }
846 }
847
848 pub fn from_opt_u64(v: Option<u64>) -> Self
849 {
850 match v {
851 Some(v) => Self::U64(v),
852 None => Self::None,
853 }
854 }
855
856 pub fn is_none(&self) -> bool {
857 match *self {
858 Self::None => true,
859 _ => false,
860 }
861 }
862
863 pub fn try_from_single_const_token(token: &str) -> Option<Self> {
865 if token == "*" {
866 Some(Self::Bool(true))
867 } else {
868 None
869 }
870 }
871
872 pub fn new_from_const_token(target: &ExpTokenEvalValue, token: &str) -> BuckyResult<Self> {
873 if token == "$none" {
875 return Ok(Self::None);
876 }
877
878 let ret = match target {
879 Self::None => unreachable!(),
880 Self::String(_) => Self::String(token.to_owned()),
881 Self::Glob(_) => Self::Glob(ExpGlobToken::new_glob(token)?),
882 Self::Bool(_) => {
883 let v;
884 if token == "true" || token == "1" {
885 v = true;
886 } else if token == "false" || token == "0" {
887 v = false;
888 } else {
889 let msg = format!("invalid const value, bool expected: {}", token);
890 error!("{}", msg);
891 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
892 }
893 Self::Bool(v)
894 }
895 Self::I8(_) => {
896 let v = Self::parse_number(token, "i8")?;
897 Self::I8(v)
898 }
899 Self::I16(_) => {
900 let v = Self::parse_number(token, "i16")?;
901 Self::I16(v)
902 }
903 Self::I32(_) => {
904 let v = Self::parse_number(token, "i32")?;
905 Self::I32(v)
906 }
907 Self::I64(_) => {
908 let v = Self::parse_number(token, "i64")?;
909 Self::I64(v)
910 }
911
912 Self::U8(_) => {
913 let v = Self::parse_number(token, "u8")?;
914 Self::U8(v)
915 }
916 Self::U16(_) => {
917 let v = Self::parse_number(token, "u16")?;
918 Self::U16(v)
919 }
920 Self::U32(_) => {
921 let v = Self::parse_number(token, "u32")?;
922 Self::U32(v)
923 }
924 Self::U64(_) => {
925 let v = Self::parse_number(token, "u64")?;
926 Self::U64(v)
927 }
928 };
929
930 Ok(ret)
931 }
932
933 fn parse_number<I>(token: &str, type_name: &str) -> BuckyResult<I>
951 where
952 I: FromStrRadix<I>,
953 {
954 let radix = if token.starts_with("0x") || token.starts_with("0X") {
955 16
956 } else if token.starts_with("0o") || token.starts_with("0O") {
957 8
958 } else if token.starts_with("0b") || token.starts_with("0B") {
959 2
960 } else {
961 10
962 };
963
964 let token = if radix != 10 {
965 token.split_at(2).1
966 } else {
967 token
968 };
969
970 I::from_str_radix(token, radix).map_err(|e| {
971 let msg = format!(
972 "invalid number value, {} expected: {}, {}",
973 type_name, token, e
974 );
975 error!("{}", msg);
976 BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
977 })
978 }
979
980 pub fn support_ops(&self) -> Vec<ExpOp> {
981 match *self {
982 Self::String(_) | Self::Glob(_) | Self::None => vec![ExpOp::EQ, ExpOp::NE],
983 Self::Bool(_) => vec![
984 ExpOp::EQ,
985 ExpOp::NE,
986 ExpOp::LT,
987 ExpOp::LE,
988 ExpOp::GT,
989 ExpOp::GE,
990 ExpOp::NOT,
991 ExpOp::AND,
992 ExpOp::OR,
993 ],
994
995 _ => vec![
997 ExpOp::EQ,
998 ExpOp::NE,
999 ExpOp::LT,
1000 ExpOp::LE,
1001 ExpOp::GT,
1002 ExpOp::GE,
1003 ExpOp::NOT,
1004 ExpOp::BAND,
1005 ExpOp::BOR,
1006 ExpOp::BXOR,
1007 ],
1008 }
1009 }
1010
1011 pub fn is_support_op(&self, op: &ExpOp) -> bool {
1012 self.support_ops().contains(op)
1013 }
1014
1015 pub fn as_bool(&self) -> Option<bool> {
1016 match self {
1017 Self::Bool(v) => Some(*v),
1018 _ => None,
1019 }
1020 }
1021
1022 fn bitor(&self, rhs: &Self) -> bool {
1024 let rhs = rhs.to_owned();
1025 match *self {
1026 Self::I8(v) => v | Into::<i8>::into(rhs) != 0,
1027 Self::I16(v) => v | Into::<i16>::into(rhs) != 0,
1028 Self::I32(v) => v | Into::<i32>::into(rhs) != 0,
1029 Self::I64(v) => v | Into::<i64>::into(rhs) != 0,
1030
1031 Self::U8(v) => v | Into::<u8>::into(rhs) != 0,
1032 Self::U16(v) => v | Into::<u16>::into(rhs) != 0,
1033 Self::U32(v) => v | Into::<u32>::into(rhs) != 0,
1034 Self::U64(v) => v | Into::<u64>::into(rhs) != 0,
1035 _ => {
1036 unreachable!("bitor only for int numbers");
1037 }
1038 }
1039 }
1040
1041 fn bitand(&self, rhs: &Self) -> bool {
1042 let rhs = rhs.to_owned();
1043 match *self {
1044 Self::I8(v) => v & Into::<i8>::into(rhs) != 0,
1045 Self::I16(v) => v & Into::<i16>::into(rhs) != 0,
1046 Self::I32(v) => v & Into::<i32>::into(rhs) != 0,
1047 Self::I64(v) => v & Into::<i64>::into(rhs) != 0,
1048
1049 Self::U8(v) => v & Into::<u8>::into(rhs) != 0,
1050 Self::U16(v) => v & Into::<u16>::into(rhs) != 0,
1051 Self::U32(v) => v & Into::<u32>::into(rhs) != 0,
1052 Self::U64(v) => v & Into::<u64>::into(rhs) != 0,
1053 _ => {
1054 unreachable!("bitand only for int numbers");
1055 }
1056 }
1057 }
1058
1059 fn bitxor(&self, rhs: &Self) -> bool {
1060 let rhs = rhs.to_owned();
1061 match *self {
1062 Self::I8(v) => v ^ Into::<i8>::into(rhs) != 0,
1063 Self::I16(v) => v ^ Into::<i16>::into(rhs) != 0,
1064 Self::I32(v) => v ^ Into::<i32>::into(rhs) != 0,
1065 Self::I64(v) => v ^ Into::<i64>::into(rhs) != 0,
1066
1067 Self::U8(v) => v ^ Into::<u8>::into(rhs) != 0,
1068 Self::U16(v) => v ^ Into::<u16>::into(rhs) != 0,
1069 Self::U32(v) => v ^ Into::<u32>::into(rhs) != 0,
1070 Self::U64(v) => v ^ Into::<u64>::into(rhs) != 0,
1071 _ => {
1072 unreachable!("bitxor only for int numbers");
1073 }
1074 }
1075 }
1076}
1077
1078pub trait ExpReservedTokenTranslator {
1080 fn trans(&self, token: &str) -> ExpTokenEvalValue;
1081}
1082
1083#[derive(Debug, Clone)]
1084enum ExpEvalItem {
1085 Op(ExpOp),
1086
1087 ReservedToken(String),
1088
1089 ConstToken(String),
1091
1092 EvalToken(ExpTokenEvalValue),
1094}
1095
1096impl ExpEvalItem {
1097 pub fn trans(self, translator: &impl ExpReservedTokenTranslator) -> Self {
1098 match self {
1099 Self::ReservedToken(v) => ExpEvalItem::EvalToken(translator.trans(&v)),
1100 _ => self,
1101 }
1102 }
1103
1104 pub fn is_token(&self) -> bool {
1105 match self {
1106 Self::Op(_) => false,
1107 _ => true,
1108 }
1109 }
1110
1111 pub fn is_const_token(&self) -> bool {
1112 match self {
1113 Self::ConstToken(_) => true,
1114 _ => false,
1115 }
1116 }
1117
1118 pub fn is_reserved_token(&self) -> bool {
1119 match self {
1120 Self::ReservedToken(_) => true,
1121 _ => false,
1122 }
1123 }
1124
1125 pub fn is_eval_token(&self) -> bool {
1126 match self {
1127 Self::EvalToken(_) => true,
1128 _ => false,
1129 }
1130 }
1131
1132 pub fn into_const_token(self) -> String {
1133 match self {
1134 Self::ConstToken(v) => v,
1135 _ => unreachable!(),
1136 }
1137 }
1138
1139 pub fn into_eval_value(self) -> ExpTokenEvalValue {
1140 match self {
1141 Self::EvalToken(v) => v,
1142 _ => unreachable!(),
1143 }
1144 }
1145}
1146
1147pub struct ExpEvaluator {
1148 exp: String,
1149 rpn: Vec<ExpEvalItem>,
1150}
1151
1152impl std::fmt::Debug for ExpEvaluator {
1153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1154 write!(f, "{}", self.exp)?;
1155
1156 Ok(())
1157 }
1158}
1159
1160impl std::fmt::Display for ExpEvaluator {
1161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1162 write!(f, "{}", self.exp)?;
1163
1164 Ok(())
1165 }
1166}
1167
1168impl PartialEq for ExpEvaluator {
1169 fn eq(&self, other: &Self) -> bool {
1170 self.exp() == other.exp()
1171 }
1172}
1173impl Eq for ExpEvaluator {}
1174
1175use std::cmp::Ordering;
1176impl PartialOrd for ExpEvaluator {
1177 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1178 self.exp().partial_cmp(other.exp())
1179 }
1180}
1181
1182impl Ord for ExpEvaluator {
1183 fn cmp(&self, other: &Self) -> Ordering {
1184 self.partial_cmp(other).unwrap()
1185 }
1186}
1187
1188
1189impl ExpEvaluator {
1190 pub fn new(exp: impl Into<String>, reserved_token_list: &ExpReservedTokenList) -> BuckyResult<Self> {
1191 let exp = exp.into();
1192 let lex_list = ExpParser::parse_lex(&exp)?;
1193 if lex_list.is_empty() {
1195 let msg = format!("empty exp filter not supported! exp={}", exp);
1196 error!("{}", msg);
1197 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
1198 }
1199
1200 debug!("exp to rpn lex list: exp={}, list={:?}", exp, lex_list);
1201
1202 let rpn = Self::convert(&exp, lex_list, reserved_token_list)?;
1203
1204 Ok(Self {
1205 exp,
1206 rpn,
1207 })
1208 }
1209
1210 pub fn new_uninit(exp: impl Into<String>) -> Self {
1211 let exp = exp.into();
1212
1213 Self {
1214 exp,
1215 rpn: vec![],
1216 }
1217 }
1218
1219 pub fn exp(&self) -> &str {
1220 &self.exp
1221 }
1222
1223 pub fn into_exp(self) -> String {
1224 self.exp
1225 }
1226
1227 fn convert(
1228 exp: &str,
1229 lex_list: Vec<ExpLexItem>,
1230 reserved_token_list: &ExpReservedTokenList,
1231 ) -> BuckyResult<Vec<ExpEvalItem>> {
1232 let mut result = vec![];
1234 for item in lex_list.into_iter() {
1235 match item {
1236 ExpLexItem::Op(v) => {
1237 result.push(ExpEvalItem::Op(v));
1238 }
1239 ExpLexItem::Token(v) => {
1240 if reserved_token_list.is_reserved_token(&v) {
1241 result.push(ExpEvalItem::ReservedToken(v));
1242 } else {
1243 result.push(ExpEvalItem::ConstToken(v));
1244 }
1245 }
1246 _ => unreachable!(),
1247 }
1248 }
1249
1250 debug!("exp lex to eval: {:?}", result);
1251
1252 Self::check_and_convert(exp, result, reserved_token_list)
1254 }
1255
1256 fn check_and_convert(
1257 exp: &str,
1258 mut rpn: Vec<ExpEvalItem>,
1259 translator: &impl ExpReservedTokenTranslator,
1260 ) -> BuckyResult<Vec<ExpEvalItem>> {
1261 let mid_result = ExpEvalItem::EvalToken(ExpTokenEvalValue::Bool(false));
1263
1264 let mut operands: Vec<(ExpEvalItem, usize)> = vec![];
1265 let mut result = vec![];
1266
1267 for (i, item) in rpn.iter().enumerate() {
1268 match item {
1269 ExpEvalItem::Op(op) => {
1270 match op.arity() {
1271 ExpOpArity::Unary => {
1272 if operands.len() < 1 {
1273 let msg = format!(
1274 "exp operator need one operands, got none: exp={}, op='{}'",
1275 exp,
1276 op.to_str()
1277 );
1278 error!("{}", msg);
1279 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
1280 }
1281
1282 let (operand, _) = operands.pop().unwrap();
1284 if operand.is_const_token() {
1285 let msg = format!(
1286 "unary operator not support const token: exp={}, op='{}'",
1287 exp,
1288 op.to_str()
1289 );
1290 error!("{}", msg);
1291 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
1292 }
1293
1294 let value = operand.into_eval_value();
1295
1296 if !value.is_support_op(op) {
1298 let msg = format!("operand not support operator: exp={}, operator={}, operand={:?}", exp, op.to_str(), value);
1299 error!("{}", msg);
1300 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
1301 }
1302
1303 operands.push((mid_result.clone(), usize::MAX));
1305 }
1306 ExpOpArity::Binary => {
1307 if operands.len() < 2 {
1308 let msg = format!(
1309 "exp operator need two operands, got one: {}",
1310 op.to_str()
1311 );
1312 error!("{}", msg);
1313 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
1314 }
1315
1316 let (right, right_index) = operands.pop().unwrap();
1317 let (left, left_index) = operands.pop().unwrap();
1318
1319 if left.is_const_token() && right.is_const_token() {
1321 let msg = format!(
1322 "binary operator not support two const token: exp={}, op='{}'",
1323 exp,
1324 op.to_str()
1325 );
1326 error!("{}", msg);
1327 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
1328 }
1329
1330 let (left, _left_index, right, right_index) = if left.is_eval_token() {
1332 (left, left_index, right, right_index)
1333 } else {
1334 (right, right_index, left, left_index)
1335 };
1336
1337 assert!(!left.is_const_token());
1338
1339 let left_value = left.into_eval_value();
1340 if !left_value.is_support_op(op) {
1342 let msg = format!("operand not support operator: exp={}, operator={}, operand={:?}",
1343 exp, op.to_str(), left_value);
1344 error!("{}", msg);
1345 return Err(BuckyError::new(BuckyErrorCode::InvalidFormat, msg));
1346 }
1347
1348 if right.is_eval_token() {
1350 let right_value = right.into_eval_value();
1351
1352 if !left_value.is_none()
1354 && !right_value.is_none()
1355 && left_value != right_value
1356 {
1357 let msg = format!("binary operator left type and right type not equal: exp={}, op='{}', left={:?}, right={:?}",
1358 exp, op.to_str(), left_value, right_value);
1359 error!("{}", msg);
1360 return Err(BuckyError::new(
1361 BuckyErrorCode::InvalidFormat,
1362 msg,
1363 ));
1364 }
1365 } else {
1366 assert!(right.is_const_token());
1367
1368 let right_token = right.into_const_token();
1370 let right_value = ExpTokenEvalValue::new_from_const_token(
1371 &left_value,
1372 &right_token,
1373 )?;
1374
1375 assert!(right_index != usize::MAX);
1377 result.push((ExpEvalItem::EvalToken(right_value), right_index));
1378 }
1379
1380 operands.push((mid_result.clone(), usize::MAX));
1382 }
1383 }
1384 }
1385 ExpEvalItem::ReservedToken(v) => {
1386 let value = translator.trans(&v);
1388 operands.push((ExpEvalItem::EvalToken(value), i));
1389 }
1390 ExpEvalItem::ConstToken(v) => {
1391 if let Some(value) = ExpTokenEvalValue::try_from_single_const_token(&v) {
1392 trace!("got const token={}, value={:?}", v, value);
1393
1394 result.push((ExpEvalItem::EvalToken(value), i))
1396 } else {
1397 operands.push((item.clone(), i));
1399 }
1400 }
1401 ExpEvalItem::EvalToken(_) => {
1402 unreachable!();
1404 }
1405 }
1406 }
1407
1408 for (item, i) in result.into_iter() {
1410 assert!(rpn[i].is_const_token());
1411 rpn[i] = item;
1412 }
1413
1414 Ok(rpn)
1415 }
1416
1417 pub fn eval(&self, translator: &impl ExpReservedTokenTranslator) -> BuckyResult<bool> {
1419 let mut operands: Vec<ExpTokenEvalValue> = vec![];
1420
1421 for item in self.rpn.iter() {
1422 match item {
1423 ExpEvalItem::Op(op) => match op.arity() {
1424 ExpOpArity::Unary => {
1425 assert!(operands.len() >= 1);
1426 let operand = operands.pop().unwrap();
1427 assert!(operand.is_support_op(op));
1428
1429 let result = Self::unary_eval(op, operand);
1431 operands.push(result);
1432 }
1433 ExpOpArity::Binary => {
1434 assert!(operands.len() >= 2);
1435
1436 let right = operands.pop().unwrap();
1438 let left = operands.pop().unwrap();
1439
1440 assert!(left.is_support_op(op) && right.is_support_op(op));
1441
1442 let result = Self::binary_eval(op, left, right);
1443
1444 operands.push(result);
1446 }
1447 },
1448 ExpEvalItem::ReservedToken(v) => {
1449 let value = translator.trans(&v);
1451 operands.push(value);
1452 }
1453 ExpEvalItem::EvalToken(v) => {
1454 operands.push(v.clone());
1455 }
1456 _ => unreachable!(),
1467 }
1468 }
1469
1470 assert!(operands.len() == 1);
1471 let result = operands.pop().unwrap();
1472 match result {
1473 ExpTokenEvalValue::Bool(v) => Ok(v),
1474 _ => unreachable!(),
1475 }
1476 }
1477
1478 fn unary_eval(op: &ExpOp, operand: ExpTokenEvalValue) -> ExpTokenEvalValue {
1479 let value = match *op {
1480 ExpOp::NOT => match operand {
1481 ExpTokenEvalValue::Bool(v) => ExpTokenEvalValue::Bool(!v),
1482 ExpTokenEvalValue::I8(v) => ExpTokenEvalValue::Bool(v == 0),
1483 ExpTokenEvalValue::I16(v) => ExpTokenEvalValue::Bool(v == 0),
1484 ExpTokenEvalValue::I32(v) => ExpTokenEvalValue::Bool(v == 0),
1485 ExpTokenEvalValue::I64(v) => ExpTokenEvalValue::Bool(v == 0),
1486 ExpTokenEvalValue::U8(v) => ExpTokenEvalValue::Bool(v == 0),
1487 ExpTokenEvalValue::U16(v) => ExpTokenEvalValue::Bool(v == 0),
1488 ExpTokenEvalValue::U32(v) => ExpTokenEvalValue::Bool(v == 0),
1489 ExpTokenEvalValue::U64(v) => ExpTokenEvalValue::Bool(v == 0),
1490 _ => unreachable!(),
1491 },
1492 _ => {
1493 unreachable!();
1494 }
1495 };
1496
1497 value
1498 }
1499
1500 fn binary_eval(
1501 op: &ExpOp,
1502 left_operand: ExpTokenEvalValue,
1503 right_operand: ExpTokenEvalValue,
1504 ) -> ExpTokenEvalValue {
1505 let value = match *op {
1506 ExpOp::EQ => ExpTokenEvalValue::Bool(left_operand == right_operand),
1507 ExpOp::NE => ExpTokenEvalValue::Bool(left_operand != right_operand),
1508 ExpOp::LT => ExpTokenEvalValue::Bool(left_operand < right_operand),
1509 ExpOp::LE => ExpTokenEvalValue::Bool(left_operand <= right_operand),
1510 ExpOp::GT => ExpTokenEvalValue::Bool(left_operand > right_operand),
1511 ExpOp::GE => ExpTokenEvalValue::Bool(left_operand >= right_operand),
1512
1513 ExpOp::AND => {
1514 let left = left_operand.as_bool().unwrap();
1515 let right = right_operand.as_bool().unwrap();
1516 ExpTokenEvalValue::Bool(left && right)
1517 }
1518 ExpOp::OR => {
1519 let left = left_operand.as_bool().unwrap();
1520 let right = right_operand.as_bool().unwrap();
1521 ExpTokenEvalValue::Bool(left || right)
1522 }
1523
1524 ExpOp::BAND => ExpTokenEvalValue::Bool(left_operand.bitand(&right_operand)),
1525 ExpOp::BOR => ExpTokenEvalValue::Bool(left_operand.bitor(&right_operand)),
1526 ExpOp::BXOR => ExpTokenEvalValue::Bool(left_operand.bitxor(&right_operand)),
1527
1528 _ => unreachable!(),
1529 };
1530
1531 value
1541 }
1542}
1543
1544
1545#[cfg(test)]
1546mod exp_tests {
1547 use super::*;
1548
1549 struct TestTranslator {}
1550
1551 impl ExpReservedTokenTranslator for TestTranslator {
1552 fn trans(&self, token: &str) -> ExpTokenEvalValue {
1553 match token {
1554 "a" => ExpTokenEvalValue::I8(10),
1555 "b" => ExpTokenEvalValue::I32(1),
1556 "b1" => ExpTokenEvalValue::I32(1),
1557 "c" => ExpTokenEvalValue::Bool(true),
1558 "d" => ExpTokenEvalValue::I16(100),
1559 "x" => ExpTokenEvalValue::None,
1560 "req_path" => {
1561 ExpTokenEvalValue::Glob(ExpGlobToken::new_string("/hello/world.js".to_owned()))
1562 }
1563 "req_path2" => {
1564 ExpTokenEvalValue::Glob(ExpGlobToken::new_string("/test/some/globs".to_owned()))
1565 }
1566 _ => {
1567 unreachable!("unknown token {}", token);
1568 }
1569 }
1570 }
1571 }
1572
1573 #[test]
1575 fn test() {
1576 cyfs_base::init_simple_log("test-exp-filter", Some("trace"));
1577
1578 let mut token_list = ExpReservedTokenList::new();
1579 token_list.add_string("object-id");
1580 token_list.add_string("source");
1581 token_list.add_string("target");
1582 token_list.add_glob("req_path");
1583 token_list.add_glob("req_path2");
1584
1585 token_list.add_i8("a");
1586 token_list.add_i32("b");
1587 token_list.add_i32("b1");
1588 token_list.add_bool("c");
1589 token_list.add_u32("d");
1590 token_list.add_u32("x");
1591
1592 let translator = TestTranslator {};
1593
1594 let ret = ExpEvaluator::new(" ", &token_list);
1596 assert!(ret.is_err());
1597
1598 let exp = ExpEvaluator::new("(* )", &token_list).unwrap();
1600 let result = exp.eval(&translator).unwrap();
1601 assert_eq!(result, true);
1602
1603 let exp = ExpEvaluator::new("req_path == '/**/*.js'", &token_list).unwrap();
1605 let result = exp.eval(&translator).unwrap();
1606 assert_eq!(result, true);
1607
1608 let exp = ExpEvaluator::new("req_path2 == '/**/*.js'", &token_list).unwrap();
1609 let result = exp.eval(&translator).unwrap();
1610 assert_eq!(result, false);
1611
1612 let exp = ExpEvaluator::new("(x != $none)", &token_list).unwrap();
1614 let result = exp.eval(&translator).unwrap();
1615 assert_eq!(result, false);
1616
1617 let exp = ExpEvaluator::new("(a != $none)", &token_list).unwrap();
1618 let result = exp.eval(&translator).unwrap();
1619 assert_eq!(result, true);
1620
1621 let exp = ExpEvaluator::new("(x == $none)", &token_list).unwrap();
1622 let result = exp.eval(&translator).unwrap();
1623 assert_eq!(result, true);
1624
1625 let exp = ExpEvaluator::new("(a != 0 && (b == 0 || c))", &token_list).unwrap();
1627 let result = exp.eval(&translator).unwrap();
1628 assert_eq!(result, true);
1629
1630 let exp = ExpEvaluator::new("(a != 0 && (b != 1 || !c))", &token_list).unwrap();
1631 let result = exp.eval(&translator).unwrap();
1632 assert_eq!(result, false);
1633
1634 let exp = ExpEvaluator::new(
1635 "(a != 0 && (a!=1) && a == a && (b != b1 || (!c) || d == 1))",
1636 &token_list,
1637 )
1638 .unwrap();
1639 let result = exp.eval(&translator).unwrap();
1640 assert_eq!(result, false);
1641
1642 let exp = ExpEvaluator::new("(a != 0 && (a!=1) && a <= 10) && c", &token_list).unwrap();
1643 let result = exp.eval(&translator).unwrap();
1644 assert_eq!(result, true);
1645
1646 let exp = ExpEvaluator::new("((a < 11)) && a >= 10", &token_list).unwrap();
1647 let result = exp.eval(&translator).unwrap();
1648 assert_eq!(result, true);
1649 let exp = ExpEvaluator::new("a < 11 && a > 10", &token_list).unwrap();
1650 let result = exp.eval(&translator).unwrap();
1651 assert_eq!(result, false);
1652 let exp = ExpEvaluator::new("a >=10 && a == 10 && a <= 10", &token_list).unwrap();
1653 let result = exp.eval(&translator).unwrap();
1654 assert_eq!(result, true);
1655 let exp = ExpEvaluator::new("a < 11 || (a!=10) && a > 10", &token_list).unwrap();
1656 let result = exp.eval(&translator).unwrap();
1657 assert_eq!(result, true);
1658
1659 let exp = ExpEvaluator::new("a >= 11 || (a==10) && a < 10", &token_list).unwrap();
1660 let result = exp.eval(&translator).unwrap();
1661 assert_eq!(result, false);
1662 let exp = ExpEvaluator::new("(a >= 11 || (a==10)) && a <= 10", &token_list).unwrap();
1663 let result = exp.eval(&translator).unwrap();
1664 assert_eq!(result, true);
1665
1666 let exp = ExpEvaluator::new("!c", &token_list).unwrap();
1667 let result = exp.eval(&translator).unwrap();
1668 assert_eq!(result, false);
1669
1670 let exp = ExpEvaluator::new("!!c", &token_list).unwrap();
1671 let result = exp.eval(&translator).unwrap();
1672 assert_eq!(result, true);
1673
1674 let exp = ExpEvaluator::new("!!!c", &token_list).unwrap();
1675 let result = exp.eval(&translator).unwrap();
1676 assert_eq!(result, false);
1677
1678 let exp = ExpEvaluator::new("!!(a == 10)", &token_list).unwrap();
1679 let result = exp.eval(&translator).unwrap();
1680 assert_eq!(result, true);
1681
1682 let exp = ExpEvaluator::new("c && !!c", &token_list).unwrap();
1683 let result = exp.eval(&translator).unwrap();
1684 assert_eq!(result, true);
1685 let exp = ExpEvaluator::new("!(c && !!c)", &token_list).unwrap();
1686 let result = exp.eval(&translator).unwrap();
1687 assert_eq!(result, false);
1688
1689 let exp = ExpEvaluator::new("(a != 0 && (a!=1) && a <10)", &token_list).unwrap();
1690 let result = exp.eval(&translator).unwrap();
1691 assert_eq!(result, false);
1692
1693 let exp = ExpEvaluator::new("a & 0x10", &token_list).unwrap();
1694 let result = exp.eval(&translator).unwrap();
1695 assert_eq!(result, false);
1696 let exp = ExpEvaluator::new("a & 10", &token_list).unwrap();
1697 let result = exp.eval(&translator).unwrap();
1698 assert_eq!(result, true);
1699 }
1700}