1use crate::error::{ OfficeError, Result, XlsxError };
5use crate::xlsx::cell::{ CellReference, CellValue };
6use std::collections::{ HashMap, HashSet };
7use std::fmt;
8
9mod formula_calculator;
11mod formula_manager;
12
13pub use formula_manager::*;
15
16#[derive(Debug, Clone, PartialEq)]
18pub enum FormulaValue {
19 Number(f64),
21 Text(String),
23 Boolean(bool),
25 Error(FormulaError),
27 Array(Vec<Vec<FormulaValue>>),
29}
30
31#[derive(Debug, Clone, PartialEq)]
33pub enum FormulaError {
34 DivisionByZero,
36 ValueError,
38 ReferenceError,
40 NameError,
42 NumError,
44 NotAvailable,
46 NullError,
48 SpillError,
50}
51
52#[derive(Debug, Clone, PartialEq)]
54pub enum FormulaExpression {
55 Constant(FormulaValue),
57 CellRef(CellReference),
59 RangeRef(CellReference, CellReference),
61 Function {
63 name: String,
64 args: Vec<FormulaExpression>,
65 },
66 BinaryOp {
68 op: BinaryOperator,
69 left: Box<FormulaExpression>,
70 right: Box<FormulaExpression>,
71 },
72 UnaryOp {
74 op: UnaryOperator,
75 operand: Box<FormulaExpression>,
76 },
77}
78
79#[derive(Debug, Clone, PartialEq)]
81pub enum BinaryOperator {
82 Add,
84 Subtract,
86 Multiply,
88 Divide,
90 Power,
92 Equal,
94 NotEqual,
96 LessThan,
98 LessThanOrEqual,
100 GreaterThan,
102 GreaterThanOrEqual,
104 Concatenate,
106 LogicalOr,
108 LogicalAnd,
110}
111
112#[derive(Debug, Clone, PartialEq)]
114pub enum UnaryOperator {
115 Plus,
117 Minus,
119 Percent,
121 Factorial,
123}
124
125#[derive(Debug, Clone, PartialEq)]
127pub enum Token {
128 Number(f64),
130 String(String),
132 Identifier(String),
134 CellReference(String),
136 Operator(String),
138 LeftParen,
140 RightParen,
142 Comma,
144 Colon,
146 Semicolon,
148 Eof,
150}
151
152pub struct FormulaParser {
154 tokens: Vec<Token>,
155 current: usize,
156}
157
158pub struct FormulaCalculator {
160 cell_provider: Box<dyn CellProvider>,
162 function_library: FunctionLibrary,
164 cache: HashMap<String, FormulaValue>,
166}
167
168pub trait CellProvider {
170 fn get_cell_value(&self, reference: &CellReference) -> Result<CellValue>;
172
173 fn get_range_values(
175 &self,
176 start: &CellReference,
177 end: &CellReference
178 ) -> Result<Vec<Vec<CellValue>>>;
179}
180
181pub struct FunctionLibrary {
183 functions: HashMap<String, Box<dyn FormulaFunction>>,
184}
185
186pub trait FormulaFunction {
188 fn name(&self) -> &str;
190
191 fn min_args(&self) -> usize;
193
194 fn max_args(&self) -> Option<usize>;
196
197 fn execute(&self, args: &[FormulaValue]) -> Result<FormulaValue>;
199}
200
201#[derive(Debug, Clone)]
203pub struct FormulaDependency {
204 pub formula_cell: CellReference,
206 pub dependent_cells: HashSet<CellReference>,
208}
209
210pub struct FormulaManager {
212 formulas: HashMap<CellReference, FormulaExpression>,
214 dependencies: HashMap<CellReference, FormulaDependency>,
216 calculator: FormulaCalculator,
218}
219
220impl fmt::Display for FormulaError {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 match self {
223 FormulaError::DivisionByZero => write!(f, "#DIV/0!"),
224 FormulaError::ValueError => write!(f, "#VALUE!"),
225 FormulaError::ReferenceError => write!(f, "#REF!"),
226 FormulaError::NameError => write!(f, "#NAME?"),
227 FormulaError::NumError => write!(f, "#NUM!"),
228 FormulaError::NotAvailable => write!(f, "#N/A"),
229 FormulaError::NullError => write!(f, "#NULL!"),
230 FormulaError::SpillError => write!(f, "#SPILL!"),
231 }
232 }
233}
234
235impl FormulaValue {
236 pub fn as_number(&self) -> Result<f64> {
238 match self {
239 FormulaValue::Number(n) => Ok(*n),
240 FormulaValue::Boolean(b) => Ok(if *b { 1.0 } else { 0.0 }),
241 FormulaValue::Text(s) =>
242 s.parse::<f64>().map_err(|_| {
243 OfficeError::Xlsx(XlsxError::InvalidFormula {
244 formula: format!("Cannot convert '{}' to number", s),
245 })
246 }),
247 FormulaValue::Error(e) =>
248 Err(
249 OfficeError::Xlsx(XlsxError::InvalidFormula {
250 formula: format!("Formula error: {:?}", e),
251 })
252 ),
253 FormulaValue::Array(_) =>
254 Err(
255 OfficeError::Xlsx(XlsxError::InvalidFormula {
256 formula: "Cannot convert array to number".to_string(),
257 })
258 ),
259 }
260 }
261
262 pub fn as_text(&self) -> String {
264 match self {
265 FormulaValue::Number(n) => n.to_string(),
266 FormulaValue::Text(s) => s.clone(),
267 FormulaValue::Boolean(b) => b.to_string().to_uppercase(),
268 FormulaValue::Error(e) => e.to_string(),
269 FormulaValue::Array(_) => "#VALUE!".to_string(),
270 }
271 }
272
273 pub fn as_boolean(&self) -> Result<bool> {
275 match self {
276 FormulaValue::Boolean(b) => Ok(*b),
277 FormulaValue::Number(n) => Ok(*n != 0.0),
278 FormulaValue::Text(s) =>
279 match s.to_uppercase().as_str() {
280 "TRUE" => Ok(true),
281 "FALSE" => Ok(false),
282 _ =>
283 Err(
284 OfficeError::Xlsx(XlsxError::InvalidFormula {
285 formula: format!("Cannot convert '{}' to boolean", s),
286 })
287 ),
288 }
289 FormulaValue::Error(e) =>
290 Err(
291 OfficeError::Xlsx(XlsxError::InvalidFormula {
292 formula: format!("Formula error: {:?}", e),
293 })
294 ),
295 FormulaValue::Array(_) =>
296 Err(
297 OfficeError::Xlsx(XlsxError::InvalidFormula {
298 formula: "Cannot convert array to boolean".to_string(),
299 })
300 ),
301 }
302 }
303
304 pub fn is_error(&self) -> bool {
306 matches!(self, FormulaValue::Error(_))
307 }
308
309 pub fn is_number(&self) -> bool {
311 matches!(self, FormulaValue::Number(_))
312 }
313
314 pub fn is_text(&self) -> bool {
316 matches!(self, FormulaValue::Text(_))
317 }
318
319 pub fn is_boolean(&self) -> bool {
321 matches!(self, FormulaValue::Boolean(_))
322 }
323}
324
325impl FormulaParser {
326 pub fn new(formula: &str) -> Result<Self> {
328 let tokens = Self::tokenize(formula)?;
329 Ok(Self { tokens, current: 0 })
330 }
331
332 pub fn parse(&mut self) -> Result<FormulaExpression> {
334 self.parse_expression()
335 }
336
337 fn tokenize(formula: &str) -> Result<Vec<Token>> {
339 let mut tokens = Vec::new();
340 let mut chars = formula.chars().peekable();
341
342 while let Some(&ch) = chars.peek() {
343 match ch {
344 ' ' | '\t' | '\n' | '\r' => {
345 chars.next();
346 }
347 '(' => {
348 tokens.push(Token::LeftParen);
349 chars.next();
350 }
351 ')' => {
352 tokens.push(Token::RightParen);
353 chars.next();
354 }
355 ',' => {
356 tokens.push(Token::Comma);
357 chars.next();
358 }
359 ':' => {
360 tokens.push(Token::Colon);
361 chars.next();
362 }
363 ';' => {
364 tokens.push(Token::Semicolon);
365 chars.next();
366 }
367 '+' | '-' | '*' | '/' | '^' | '=' | '<' | '>' | '&' | '|' | '%' => {
368 let mut op = String::new();
369 op.push(chars.next().unwrap());
370
371 let valid_double_ops = [
390 "<=", ">=", "<>", ];
394
395 if let Some(&next_ch) = chars.peek() {
396 let potential_op = format!("{}{}", ch, next_ch);
397 if valid_double_ops.contains(&potential_op.as_str()) {
398 op.push(chars.next().unwrap()); }
400 }
401
402 tokens.push(Token::Operator(op));
403 }
404 '"' => {
405 chars.next(); let mut string_val = String::new();
407
408 while let Some(ch) = chars.next() {
409 if ch == '"' {
410 if chars.peek() == Some(&'"') {
412 string_val.push('"');
413 chars.next();
414 } else {
415 break;
416 }
417 } else {
418 string_val.push(ch);
419 }
420 }
421
422 tokens.push(Token::String(string_val));
423 }
424 '0'..='9' | '.' => {
425 let mut number = String::new();
426
427 while let Some(&ch) = chars.peek() {
428 if ch.is_ascii_digit() || ch == '.' {
429 number.push(chars.next().unwrap());
430 } else {
431 break;
432 }
433 }
434
435 let num_val = number
436 .parse::<f64>()
437 .map_err(|_| {
438 OfficeError::Xlsx(XlsxError::InvalidFormula { formula: number })
439 })?;
440
441 tokens.push(Token::Number(num_val));
442 }
443 'A'..='Z' | 'a'..='z' | '$' => {
444 let mut identifier = String::new();
445
446 while let Some(&ch) = chars.peek() {
448 if ch.is_ascii_alphanumeric() || ch == '$' || ch == '_' {
449 identifier.push(chars.next().unwrap());
450 } else {
451 break;
452 }
453 }
454
455 if Self::is_cell_reference(&identifier) {
457 tokens.push(Token::CellReference(identifier));
458 } else if
459 identifier.to_uppercase() == "TRUE" ||
460 identifier.to_uppercase() == "FALSE"
461 {
462 tokens.push(Token::Identifier(identifier));
463 } else {
464 tokens.push(Token::Identifier(identifier));
465 }
466 }
467 _ => {
468 return Err(
469 OfficeError::Xlsx(XlsxError::InvalidFormula {
470 formula: format!("Unexpected character: {}", ch),
471 })
472 );
473 }
474 }
475 }
476
477 tokens.push(Token::Eof);
478 Ok(tokens)
479 }
480
481 fn is_cell_reference(text: &str) -> bool {
483 let text = text.trim();
484
485 if text.is_empty() {
487 return false;
488 }
489
490 if text.ends_with("$") {
492 return false;
493 }
494
495 let mut chars = text.chars().peekable();
496 let mut dollar_count = 0;
497
498 if chars.peek() == Some(&'$') {
500 chars.next();
501 dollar_count += 1;
502 }
503
504 let mut has_col_letter = false;
506 while let Some(ch) = chars.peek() {
507 if ch.is_ascii_uppercase() {
508 chars.next();
509 has_col_letter = true;
510 } else {
511 break;
512 }
513 }
514
515 if !has_col_letter {
517 return false;
518 }
519
520 if chars.peek() == Some(&'$') {
522 chars.next();
523 dollar_count += 1;
524 }
525
526 let mut has_row_number = false;
528 while let Some(ch) = chars.peek() {
529 if ch.is_ascii_digit() {
530 chars.next();
531 has_row_number = true;
532 } else {
533 break;
534 }
535 }
536
537 if !has_row_number {
539 return false;
540 }
541
542 if chars.peek() == Some(&'$') {
544 chars.next();
545 dollar_count += 1;
546 if chars.peek().is_some() {
548 return false;
549 }
550 }
551
552 chars.next().is_none() && dollar_count <= 2
554 }
555
556 fn parse_expression(&mut self) -> Result<FormulaExpression> {
558 self.parse_logical_or()
559 }
560
561 fn parse_logical_or(&mut self) -> Result<FormulaExpression> {
563 let mut expr = self.parse_logical_and()?;
564
565 while self.match_operator("|") {
566 let right = self.parse_logical_and()?;
567 expr = FormulaExpression::BinaryOp {
568 op: BinaryOperator::LogicalOr,
569 left: Box::new(expr),
570 right: Box::new(right),
571 };
572 }
573
574 Ok(expr)
575 }
576
577 fn parse_logical_and(&mut self) -> Result<FormulaExpression> {
579 let mut expr = self.parse_equality()?;
580
581 while self.match_operator("&") {
582 let right = self.parse_equality()?;
583 expr = FormulaExpression::BinaryOp {
584 op: BinaryOperator::LogicalAnd,
585 left: Box::new(expr),
586 right: Box::new(right),
587 };
588 }
589
590 Ok(expr)
591 }
592
593 fn parse_equality(&mut self) -> Result<FormulaExpression> {
595 let mut expr = self.parse_comparison()?;
596
597 while let Some(op) = self.match_equality_operator() {
598 let right = self.parse_comparison()?;
599 expr = FormulaExpression::BinaryOp {
600 op,
601 left: Box::new(expr),
602 right: Box::new(right),
603 };
604 }
605
606 Ok(expr)
607 }
608
609 fn parse_comparison(&mut self) -> Result<FormulaExpression> {
611 let mut expr = self.parse_addition()?;
612
613 while let Some(op) = self.match_comparison_operator() {
614 let right = self.parse_addition()?;
615 expr = FormulaExpression::BinaryOp {
616 op,
617 left: Box::new(expr),
618 right: Box::new(right),
619 };
620 }
621
622 Ok(expr)
623 }
624
625 fn parse_addition(&mut self) -> Result<FormulaExpression> {
627 let mut expr = self.parse_multiplication()?;
628
629 while let Some(op) = self.match_addition_operator() {
630 let right = self.parse_multiplication()?;
631 expr = FormulaExpression::BinaryOp {
632 op,
633 left: Box::new(expr),
634 right: Box::new(right),
635 };
636 }
637
638 Ok(expr)
639 }
640
641 fn parse_multiplication(&mut self) -> Result<FormulaExpression> {
643 let mut expr = self.parse_power()?;
644
645 while let Some(op) = self.match_multiplication_operator() {
646 let right = self.parse_power()?;
647 expr = FormulaExpression::BinaryOp {
648 op,
649 left: Box::new(expr),
650 right: Box::new(right),
651 };
652 }
653
654 Ok(expr)
655 }
656
657 fn parse_power(&mut self) -> Result<FormulaExpression> {
659 let mut expr = self.parse_unary()?;
660
661 if self.match_operator("%") {
663 expr = FormulaExpression::UnaryOp {
664 op: UnaryOperator::Percent,
665 operand: Box::new(expr),
666 };
667 }
668
669 if self.match_operator("^") {
670 let right = self.parse_power()?; expr = FormulaExpression::BinaryOp {
672 op: BinaryOperator::Power,
673 left: Box::new(expr),
674 right: Box::new(right),
675 };
676 }
677
678 Ok(expr)
679 }
680
681 fn parse_unary(&mut self) -> Result<FormulaExpression> {
683 if let Some(op) = self.match_unary_operator() {
685 if matches!(op, UnaryOperator::Plus | UnaryOperator::Minus) {
687 let operand = self.parse_unary()?;
688 return Ok(FormulaExpression::UnaryOp {
689 op,
690 operand: Box::new(operand),
691 });
692 } else {
693 self.current -= 1;
695 }
696 }
697
698 self.parse_primary()
699 }
700
701 fn parse_primary(&mut self) -> Result<FormulaExpression> {
703 match &self.current_token()? {
704 Token::Number(n) => {
705 let value = *n;
706 self.advance();
707 Ok(FormulaExpression::Constant(FormulaValue::Number(value)))
708 }
709 Token::String(s) => {
710 let value = s.clone();
711 self.advance();
712 Ok(FormulaExpression::Constant(FormulaValue::Text(value)))
713 }
714 Token::CellReference(ref_str) => {
715 let cell_ref = CellReference::from_a1(ref_str)?;
716 self.advance();
717
718 if self.match_token(&Token::Colon) {
720 if let Token::CellReference(end_ref_str) = &self.current_token()? {
721 let end_ref = CellReference::from_a1(end_ref_str)?;
722 self.advance();
723 Ok(FormulaExpression::RangeRef(cell_ref, end_ref))
724 } else {
725 Err(
726 OfficeError::Xlsx(XlsxError::InvalidFormula {
727 formula: "Expected cell reference after colon".to_string(),
728 })
729 )
730 }
731 } else {
732 Ok(FormulaExpression::CellRef(cell_ref))
733 }
734 }
735 Token::Identifier(name) => {
736 let func_name = name.clone();
737 self.advance();
738
739 if func_name.to_uppercase() == "TRUE" {
741 return Ok(FormulaExpression::Constant(FormulaValue::Boolean(true)));
742 } else if func_name.to_uppercase() == "FALSE" {
743 return Ok(FormulaExpression::Constant(FormulaValue::Boolean(false)));
744 }
745
746 if self.match_token(&Token::LeftParen) {
747 let mut args = Vec::new();
749
750 if !self.check_token(&Token::RightParen) {
751 loop {
752 args.push(self.parse_expression()?);
753
754 if !self.match_token(&Token::Comma) {
755 break;
756 }
757 }
758 }
759
760 if !self.match_token(&Token::RightParen) {
761 return Err(
762 OfficeError::Xlsx(XlsxError::InvalidFormula {
763 formula: "Expected ')' after function arguments".to_string(),
764 })
765 );
766 }
767
768 Ok(FormulaExpression::Function {
769 name: func_name,
770 args,
771 })
772 } else {
773 Err(
775 OfficeError::Xlsx(XlsxError::InvalidFormula {
776 formula: format!("Unknown identifier: {}", func_name),
777 })
778 )
779 }
780 }
781 Token::LeftParen => {
782 self.advance();
783 let expr = self.parse_expression()?;
784
785 if !self.match_token(&Token::RightParen) {
786 return Err(
787 OfficeError::Xlsx(XlsxError::InvalidFormula {
788 formula: "Expected ')'".to_string(),
789 })
790 );
791 }
792
793 Ok(expr)
794 }
795 _ =>
796 Err(
797 OfficeError::Xlsx(XlsxError::InvalidFormula {
798 formula: "Unexpected token".to_string(),
799 })
800 ),
801 }
802 }
803
804 fn current_token(&self) -> Result<&Token> {
806 self.tokens.get(self.current).ok_or_else(|| {
807 OfficeError::Xlsx(XlsxError::InvalidFormula {
808 formula: "Unexpected end of formula".to_string(),
809 })
810 })
811 }
812
813 fn advance(&mut self) {
815 if self.current < self.tokens.len() {
816 self.current += 1;
817 }
818 }
819
820 fn check_token(&self, token: &Token) -> bool {
822 if let Ok(current) = self.current_token() {
823 std::mem::discriminant(current) == std::mem::discriminant(token)
824 } else {
825 false
826 }
827 }
828
829 fn match_token(&mut self, token: &Token) -> bool {
831 if self.check_token(token) {
832 self.advance();
833 true
834 } else {
835 false
836 }
837 }
838
839 fn match_operator(&mut self, op: &str) -> bool {
841 if let Ok(Token::Operator(current_op)) = self.current_token() {
842 if current_op == op {
843 self.advance();
844 return true;
845 }
846 }
847 false
848 }
849
850 fn match_equality_operator(&mut self) -> Option<BinaryOperator> {
852 if let Ok(Token::Operator(op)) = self.current_token() {
853 let result = match op.as_str() {
854 "=" => Some(BinaryOperator::Equal),
855 "<>" => Some(BinaryOperator::NotEqual),
856 _ => None,
857 };
858
859 if result.is_some() {
860 self.advance();
861 }
862
863 result
864 } else {
865 None
866 }
867 }
868
869 fn match_comparison_operator(&mut self) -> Option<BinaryOperator> {
871 if let Ok(Token::Operator(op)) = self.current_token() {
872 let result = match op.as_str() {
873 "<" => Some(BinaryOperator::LessThan),
874 "<=" => Some(BinaryOperator::LessThanOrEqual),
875 ">" => Some(BinaryOperator::GreaterThan),
876 ">=" => Some(BinaryOperator::GreaterThanOrEqual),
877 "<>" => Some(BinaryOperator::NotEqual),
878 _ => None,
879 };
880
881 if result.is_some() {
882 self.advance();
883 }
884
885 result
886 } else {
887 None
888 }
889 }
890
891 fn match_addition_operator(&mut self) -> Option<BinaryOperator> {
893 if let Ok(Token::Operator(op)) = self.current_token() {
894 let result = match op.as_str() {
895 "+" => Some(BinaryOperator::Add),
896 "-" => Some(BinaryOperator::Subtract),
897 _ => None,
898 };
899
900 if result.is_some() {
901 self.advance();
902 }
903
904 result
905 } else {
906 None
907 }
908 }
909
910 fn match_multiplication_operator(&mut self) -> Option<BinaryOperator> {
912 if let Ok(Token::Operator(op)) = self.current_token() {
913 let result = match op.as_str() {
914 "*" => Some(BinaryOperator::Multiply),
915 "/" => Some(BinaryOperator::Divide),
916 _ => None,
917 };
918
919 if result.is_some() {
920 self.advance();
921 }
922
923 result
924 } else {
925 None
926 }
927 }
928
929 fn match_unary_operator(&mut self) -> Option<UnaryOperator> {
931 if let Ok(Token::Operator(op)) = self.current_token() {
932 let result = match op.as_str() {
933 "+" => Some(UnaryOperator::Plus),
934 "-" => Some(UnaryOperator::Minus),
935 "%" => Some(UnaryOperator::Percent),
936 "!" => Some(UnaryOperator::Factorial),
937 _ => None,
938 };
939
940 if result.is_some() {
941 self.advance();
942 }
943
944 result
945 } else {
946 None
947 }
948 }
949}
950
951pub fn parse_formula(formula: &str) -> Result<FormulaExpression> {
953 let formula_content = if formula.starts_with('=') { &formula[1..] } else { formula };
955
956 let mut parser = FormulaParser::new(formula_content)?;
957 parser.parse()
958}
959
960#[cfg(test)]
961mod tests {
962 use super::*;
963
964 #[test]
965 fn test_tokenize_simple() {
966 let tokens = FormulaParser::tokenize("1+2").unwrap();
967 assert_eq!(tokens.len(), 4); }
969
970 #[test]
971 fn test_parse_simple_addition() {
972 let expr = parse_formula("1+2").unwrap();
973 match expr {
974 FormulaExpression::BinaryOp { op: BinaryOperator::Add, .. } => {}
975 _ => panic!("Expected addition expression"),
976 }
977 }
978
979 #[test]
980 fn test_parse_cell_reference() {
981 let expr = parse_formula("A1").unwrap();
982 match expr {
983 FormulaExpression::CellRef(_) => {}
984 _ => panic!("Expected cell reference"),
985 }
986 }
987
988 #[test]
989 fn test_parse_function_call() {
990 let expr = parse_formula("SUM(A1:A10)").unwrap();
991 match expr {
992 FormulaExpression::Function { name, args } => {
993 assert_eq!(name, "SUM");
994 assert_eq!(args.len(), 1);
995 }
996 _ => panic!("Expected function call"),
997 }
998 }
999
1000 #[test]
1001 fn test_formula_value_conversions() {
1002 let num_val = FormulaValue::Number(42.0);
1003 assert_eq!(num_val.as_number().unwrap(), 42.0);
1004 assert_eq!(num_val.as_text(), "42");
1005
1006 let bool_val = FormulaValue::Boolean(true);
1007 assert_eq!(bool_val.as_boolean().unwrap(), true);
1008 assert_eq!(bool_val.as_number().unwrap(), 1.0);
1009 }
1010
1011 #[test]
1012 fn test_logical_or_operator() {
1013 let expr = parse_formula("TRUE|FALSE").unwrap();
1014 match expr {
1015 FormulaExpression::BinaryOp { op: BinaryOperator::LogicalOr, .. } => {}
1016 _ => panic!("Expected logical OR expression"),
1017 }
1018 }
1019
1020 #[test]
1021 fn test_logical_and_operator() {
1022 let expr = parse_formula("TRUE&FALSE").unwrap();
1023 match expr {
1024 FormulaExpression::BinaryOp { op: BinaryOperator::LogicalAnd, .. } => {}
1025 _ => panic!("Expected logical AND expression"),
1026 }
1027 }
1028
1029 #[test]
1030 fn test_boolean_constants() {
1031 let expr = parse_formula("TRUE").unwrap();
1032 match expr {
1033 FormulaExpression::Constant(FormulaValue::Boolean(true)) => {}
1034 _ => panic!("Expected TRUE constant"),
1035 }
1036
1037 let expr = parse_formula("FALSE").unwrap();
1038 match expr {
1039 FormulaExpression::Constant(FormulaValue::Boolean(false)) => {}
1040 _ => panic!("Expected FALSE constant"),
1041 }
1042 }
1043
1044 #[test]
1045 fn test_mixed_logical_operations() {
1046 let expr = parse_formula("TRUE|FALSE&TRUE").unwrap();
1047 match expr {
1049 FormulaExpression::BinaryOp { op: BinaryOperator::LogicalOr, left, right } => {
1050 match *left {
1051 FormulaExpression::Constant(FormulaValue::Boolean(true)) => {}
1052 _ => panic!("Expected TRUE constant"),
1053 }
1054 match *right {
1055 FormulaExpression::BinaryOp { op: BinaryOperator::LogicalAnd, .. } => {}
1056 _ => panic!("Expected logical AND expression"),
1057 }
1058 }
1059 _ => panic!("Expected logical OR expression"),
1060 }
1061 }
1062
1063 #[test]
1064 fn test_case_insensitive_boolean_constants() {
1065 let expr = parse_formula("true").unwrap();
1066 match expr {
1067 FormulaExpression::Constant(FormulaValue::Boolean(true)) => {}
1068 _ => panic!("Expected TRUE constant"),
1069 }
1070
1071 let expr = parse_formula("False").unwrap();
1072 match expr {
1073 FormulaExpression::Constant(FormulaValue::Boolean(false)) => {}
1074 _ => panic!("Expected FALSE constant"),
1075 }
1076 }
1077
1078 #[test]
1079 fn test_is_cell_reference() {
1080 assert!(FormulaParser::is_cell_reference("A1"));
1082 assert!(FormulaParser::is_cell_reference("Z999"));
1083 assert!(FormulaParser::is_cell_reference("AA1"));
1084 assert!(FormulaParser::is_cell_reference("AZ999"));
1085 assert!(FormulaParser::is_cell_reference("$A$1"));
1086 assert!(FormulaParser::is_cell_reference("$Z$999"));
1087 assert!(FormulaParser::is_cell_reference("A$1"));
1088 assert!(FormulaParser::is_cell_reference("$A1"));
1089 assert!(FormulaParser::is_cell_reference("AA$1"));
1090 assert!(FormulaParser::is_cell_reference("$AA1"));
1091
1092 assert!(!FormulaParser::is_cell_reference("A"));
1094 assert!(!FormulaParser::is_cell_reference("1"));
1095 assert!(!FormulaParser::is_cell_reference("$A"));
1096 assert!(!FormulaParser::is_cell_reference("A$"));
1097 assert!(!FormulaParser::is_cell_reference("$$A1"));
1098 assert!(!FormulaParser::is_cell_reference("A1$"));
1099 assert!(!FormulaParser::is_cell_reference("A1$1"));
1100 assert!(!FormulaParser::is_cell_reference("A$$1"));
1101 assert!(!FormulaParser::is_cell_reference("$A$1$"));
1102 assert!(!FormulaParser::is_cell_reference(""));
1103 assert!(!FormulaParser::is_cell_reference("AB CD"));
1104 }
1105
1106 #[test]
1107 fn test_compound_operators() {
1108 let expr = parse_formula("A1<=B1").unwrap();
1110 match expr {
1111 FormulaExpression::BinaryOp { op: BinaryOperator::LessThanOrEqual, .. } => {}
1112 _ => panic!("Expected less than or equal expression"),
1113 }
1114
1115 let expr = parse_formula("A1>=B1").unwrap();
1116 match expr {
1117 FormulaExpression::BinaryOp { op: BinaryOperator::GreaterThanOrEqual, .. } => {}
1118 _ => panic!("Expected greater than or equal expression"),
1119 }
1120
1121 let expr = parse_formula("A1<>B1").unwrap();
1122 match expr {
1123 FormulaExpression::BinaryOp { op: BinaryOperator::NotEqual, .. } => {}
1124 _ => panic!("Expected not equal expression"),
1125 }
1126 }
1127
1128 #[test]
1129 fn test_invalid_operator_combinations() {
1130 assert!(parse_formula("1<=<2").is_err());
1134 assert!(parse_formula("1>=>2").is_err());
1135 assert!(parse_formula("1&&>2").is_err());
1136 assert!(parse_formula("1||<2").is_err());
1137 assert!(parse_formula("TRUE||FALSE").is_err());
1138 assert!(parse_formula("TRUE&&TRUE").is_err());
1139 assert!(parse_formula("TRUE && TRUE").is_err());
1140 }
1141
1142 #[test]
1143 fn test_operator_spacing() {
1144 let expr = parse_formula("A1 <= B1").unwrap();
1146 match expr {
1147 FormulaExpression::BinaryOp { op: BinaryOperator::LessThanOrEqual, .. } => {}
1148 _ => panic!("Expected less than or equal expression"),
1149 }
1150 }
1151}