1use super::utils::ARG_ANY_ONE;
4use crate::args::ArgSchema;
5use crate::function::Function;
6use crate::traits::{ArgumentHandle, FunctionContext};
7use formualizer_common::{ExcelError, LiteralValue};
8use formualizer_macros::func_caps;
9
10#[derive(Debug)]
13pub struct TrueFn;
14impl Function for TrueFn {
56 func_caps!(PURE);
57
58 fn name(&self) -> &'static str {
59 "TRUE"
60 }
61 fn min_args(&self) -> usize {
62 0
63 }
64
65 fn eval<'a, 'b, 'c>(
66 &self,
67 _args: &'c [ArgumentHandle<'a, 'b>],
68 _ctx: &dyn FunctionContext<'b>,
69 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
70 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
71 true,
72 )))
73 }
74}
75
76#[derive(Debug)]
79pub struct FalseFn;
80impl Function for FalseFn {
122 func_caps!(PURE);
123
124 fn name(&self) -> &'static str {
125 "FALSE"
126 }
127 fn min_args(&self) -> usize {
128 0
129 }
130
131 fn eval<'a, 'b, 'c>(
132 &self,
133 _args: &'c [ArgumentHandle<'a, 'b>],
134 _ctx: &dyn FunctionContext<'b>,
135 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
136 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
137 false,
138 )))
139 }
140}
141
142#[derive(Debug)]
145pub struct AndFn;
146impl Function for AndFn {
190 func_caps!(PURE, REDUCTION, BOOL_ONLY, SHORT_CIRCUIT);
191
192 fn name(&self) -> &'static str {
193 "AND"
194 }
195 fn min_args(&self) -> usize {
196 1
197 }
198 fn variadic(&self) -> bool {
199 true
200 }
201 fn arg_schema(&self) -> &'static [ArgSchema] {
202 &ARG_ANY_ONE[..]
203 }
204
205 fn eval<'a, 'b, 'c>(
206 &self,
207 args: &'c [ArgumentHandle<'a, 'b>],
208 _ctx: &dyn FunctionContext<'b>,
209 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
210 let mut first_error: Option<LiteralValue> = None;
211 for h in args {
212 let it = h.lazy_values_owned()?;
213 for v in it {
214 match v {
215 LiteralValue::Error(_) => {
216 if first_error.is_none() {
217 first_error = Some(v);
218 }
219 }
220 LiteralValue::Empty => {
221 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
222 false,
223 )));
224 }
225 LiteralValue::Boolean(b) => {
226 if !b {
227 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
228 false,
229 )));
230 }
231 }
232 LiteralValue::Number(n) => {
233 if n == 0.0 {
234 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
235 false,
236 )));
237 }
238 }
239 LiteralValue::Int(i) => {
240 if i == 0 {
241 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
242 false,
243 )));
244 }
245 }
246 _ => {
247 if first_error.is_none() {
249 first_error =
250 Some(LiteralValue::Error(ExcelError::new_value().with_message(
251 "AND expects logical/numeric inputs; text is not coercible",
252 )));
253 }
254 }
255 }
256 }
257 }
258 if let Some(err) = first_error {
259 return Ok(crate::traits::CalcValue::Scalar(err));
260 }
261 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
262 true,
263 )))
264 }
265}
266
267#[derive(Debug)]
270pub struct OrFn;
271impl Function for OrFn {
315 func_caps!(PURE, REDUCTION, BOOL_ONLY, SHORT_CIRCUIT);
316
317 fn name(&self) -> &'static str {
318 "OR"
319 }
320 fn min_args(&self) -> usize {
321 1
322 }
323 fn variadic(&self) -> bool {
324 true
325 }
326 fn arg_schema(&self) -> &'static [ArgSchema] {
327 &ARG_ANY_ONE[..]
328 }
329
330 fn eval<'a, 'b, 'c>(
331 &self,
332 args: &'c [ArgumentHandle<'a, 'b>],
333 _ctx: &dyn FunctionContext<'b>,
334 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
335 let mut first_error: Option<LiteralValue> = None;
336 for h in args {
337 let it = h.lazy_values_owned()?;
338 for v in it {
339 match v {
340 LiteralValue::Error(_) => {
341 if first_error.is_none() {
342 first_error = Some(v);
343 }
344 }
345 LiteralValue::Empty => {
346 }
348 LiteralValue::Boolean(b) => {
349 if b {
350 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
351 true,
352 )));
353 }
354 }
355 LiteralValue::Number(n) => {
356 if n != 0.0 {
357 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
358 true,
359 )));
360 }
361 }
362 LiteralValue::Int(i) => {
363 if i != 0 {
364 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
365 true,
366 )));
367 }
368 }
369 _ => {
370 if first_error.is_none() {
372 first_error =
373 Some(LiteralValue::Error(ExcelError::new_value().with_message(
374 "OR expects logical/numeric inputs; text is not coercible",
375 )));
376 }
377 }
378 }
379 }
380 }
381 if let Some(err) = first_error {
382 return Ok(crate::traits::CalcValue::Scalar(err));
383 }
384 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
385 false,
386 )))
387 }
388}
389
390#[derive(Debug)]
393pub struct IfFn;
394impl Function for IfFn {
438 func_caps!(PURE, SHORT_CIRCUIT);
439
440 fn name(&self) -> &'static str {
441 "IF"
442 }
443 fn min_args(&self) -> usize {
444 2
445 }
446 fn variadic(&self) -> bool {
447 true
448 }
449
450 fn arg_schema(&self) -> &'static [ArgSchema] {
451 use std::sync::LazyLock;
452 static ONE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| vec![ArgSchema::any()]);
454 &ONE[..]
455 }
456
457 fn eval<'a, 'b, 'c>(
458 &self,
459 args: &'c [ArgumentHandle<'a, 'b>],
460 _ctx: &dyn FunctionContext<'b>,
461 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
462 if args.len() < 2 || args.len() > 3 {
463 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
464 ExcelError::new_value()
465 .with_message(format!("IF expects 2 or 3 arguments, got {}", args.len())),
466 )));
467 }
468
469 let condition = args[0].value()?.into_literal();
470 let b = match condition {
471 LiteralValue::Boolean(b) => b,
472 LiteralValue::Number(n) => n != 0.0,
473 LiteralValue::Int(i) => i != 0,
474 LiteralValue::Empty => false,
475 _ => {
476 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
477 ExcelError::new_value().with_message("IF condition must be boolean or number"),
478 )));
479 }
480 };
481
482 if b {
483 args[1].value()
484 } else if let Some(arg) = args.get(2) {
485 arg.value()
486 } else {
487 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
488 false,
489 )))
490 }
491 }
492}
493
494pub fn register_builtins() {
495 crate::function_registry::register_function(std::sync::Arc::new(TrueFn));
496 crate::function_registry::register_function(std::sync::Arc::new(FalseFn));
497 crate::function_registry::register_function(std::sync::Arc::new(AndFn));
498 crate::function_registry::register_function(std::sync::Arc::new(OrFn));
499 crate::function_registry::register_function(std::sync::Arc::new(IfFn));
500}
501
502#[cfg(test)]
505mod tests {
506 use super::*;
507 use crate::traits::ArgumentHandle;
508 use crate::{interpreter::Interpreter, test_workbook::TestWorkbook};
509 use formualizer_parse::LiteralValue;
510 use std::sync::{
511 Arc,
512 atomic::{AtomicUsize, Ordering},
513 };
514
515 #[derive(Debug)]
516 struct CountFn(Arc<AtomicUsize>);
517 impl Function for CountFn {
518 func_caps!(PURE);
519 fn name(&self) -> &'static str {
520 "COUNTING"
521 }
522 fn min_args(&self) -> usize {
523 0
524 }
525 fn eval<'a, 'b, 'c>(
526 &self,
527 _args: &'c [ArgumentHandle<'a, 'b>],
528 _ctx: &dyn FunctionContext<'b>,
529 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
530 self.0.fetch_add(1, Ordering::SeqCst);
531 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
532 true,
533 )))
534 }
535 }
536
537 #[derive(Debug)]
538 struct ErrorFn(Arc<AtomicUsize>);
539 impl Function for ErrorFn {
540 func_caps!(PURE);
541 fn name(&self) -> &'static str {
542 "ERRORFN"
543 }
544 fn min_args(&self) -> usize {
545 0
546 }
547 fn eval<'a, 'b, 'c>(
548 &self,
549 _args: &'c [ArgumentHandle<'a, 'b>],
550 _ctx: &dyn FunctionContext<'b>,
551 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
552 self.0.fetch_add(1, Ordering::SeqCst);
553 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
554 ExcelError::new_value(),
555 )))
556 }
557 }
558
559 fn interp(wb: &TestWorkbook) -> Interpreter<'_> {
560 wb.interpreter()
561 }
562
563 #[test]
564 fn test_true_false() {
565 let wb = TestWorkbook::new()
566 .with_function(std::sync::Arc::new(TrueFn))
567 .with_function(std::sync::Arc::new(FalseFn));
568
569 let ctx = interp(&wb);
570 let t = ctx.context.get_function("", "TRUE").unwrap();
571 let fctx = ctx.function_context(None);
572 assert_eq!(
573 t.eval(&[], &fctx).unwrap().into_literal(),
574 LiteralValue::Boolean(true)
575 );
576
577 let f = ctx.context.get_function("", "FALSE").unwrap();
578 assert_eq!(
579 f.eval(&[], &fctx).unwrap().into_literal(),
580 LiteralValue::Boolean(false)
581 );
582 }
583
584 #[test]
585 fn test_and_or() {
586 let wb = TestWorkbook::new()
587 .with_function(std::sync::Arc::new(AndFn))
588 .with_function(std::sync::Arc::new(OrFn));
589 let ctx = interp(&wb);
590 let fctx = ctx.function_context(None);
591
592 let and = ctx.context.get_function("", "AND").unwrap();
593 let or = ctx.context.get_function("", "OR").unwrap();
594 let dummy_ast = formualizer_parse::parser::ASTNode::new(
596 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
597 None,
598 );
599 let dummy_ast_false = formualizer_parse::parser::ASTNode::new(
600 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(false)),
601 None,
602 );
603 let dummy_ast_one = formualizer_parse::parser::ASTNode::new(
604 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
605 None,
606 );
607 let hs = vec![
608 ArgumentHandle::new(&dummy_ast, &ctx),
609 ArgumentHandle::new(&dummy_ast_one, &ctx),
610 ];
611 assert_eq!(
612 and.eval(&hs, &fctx).unwrap().into_literal(),
613 LiteralValue::Boolean(true)
614 );
615
616 let hs2 = vec![
617 ArgumentHandle::new(&dummy_ast_false, &ctx),
618 ArgumentHandle::new(&dummy_ast_one, &ctx),
619 ];
620 assert_eq!(
621 and.eval(&hs2, &fctx).unwrap().into_literal(),
622 LiteralValue::Boolean(false)
623 );
624 assert_eq!(
625 or.eval(&hs2, &fctx).unwrap().into_literal(),
626 LiteralValue::Boolean(true)
627 );
628 }
629
630 #[test]
631 fn and_short_circuits_on_false_without_evaluating_rest() {
632 let counter = Arc::new(AtomicUsize::new(0));
633 let wb = TestWorkbook::new()
634 .with_function(Arc::new(AndFn))
635 .with_function(Arc::new(CountFn(counter.clone())));
636 let ctx = interp(&wb);
637 let fctx = ctx.function_context(None);
638 let and = ctx.context.get_function("", "AND").unwrap();
639
640 let a_false = formualizer_parse::parser::ASTNode::new(
642 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(false)),
643 None,
644 );
645 let counting_call = formualizer_parse::parser::ASTNode::new(
646 formualizer_parse::parser::ASTNodeType::Function {
647 name: "COUNTING".into(),
648 args: vec![],
649 },
650 None,
651 );
652 let hs = vec![
653 ArgumentHandle::new(&a_false, &ctx),
654 ArgumentHandle::new(&counting_call, &ctx),
655 ];
656 let out = and.eval(&hs, &fctx).unwrap().into_literal();
657 assert_eq!(out, LiteralValue::Boolean(false));
658 assert_eq!(
659 counter.load(Ordering::SeqCst),
660 0,
661 "COUNTING should not be evaluated"
662 );
663 }
664
665 #[test]
666 fn or_short_circuits_on_true_without_evaluating_rest() {
667 let counter = Arc::new(AtomicUsize::new(0));
668 let wb = TestWorkbook::new()
669 .with_function(Arc::new(OrFn))
670 .with_function(Arc::new(CountFn(counter.clone())));
671 let ctx = interp(&wb);
672 let fctx = ctx.function_context(None);
673 let or = ctx.context.get_function("", "OR").unwrap();
674
675 let a_true = formualizer_parse::parser::ASTNode::new(
677 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
678 None,
679 );
680 let counting_call = formualizer_parse::parser::ASTNode::new(
681 formualizer_parse::parser::ASTNodeType::Function {
682 name: "COUNTING".into(),
683 args: vec![],
684 },
685 None,
686 );
687 let hs = vec![
688 ArgumentHandle::new(&a_true, &ctx),
689 ArgumentHandle::new(&counting_call, &ctx),
690 ];
691 let out = or.eval(&hs, &fctx).unwrap().into_literal();
692 assert_eq!(out, LiteralValue::Boolean(true));
693 assert_eq!(
694 counter.load(Ordering::SeqCst),
695 0,
696 "COUNTING should not be evaluated"
697 );
698 }
699
700 #[test]
701 fn or_range_arg_short_circuits_on_first_true_before_evaluating_next_arg() {
702 let counter = Arc::new(AtomicUsize::new(0));
703 let wb = TestWorkbook::new()
704 .with_function(Arc::new(OrFn))
705 .with_function(Arc::new(CountFn(counter.clone())));
706 let ctx = interp(&wb);
707 let fctx = ctx.function_context(None);
708 let or = ctx.context.get_function("", "OR").unwrap();
709
710 let arr = formualizer_parse::parser::ASTNode::new(
712 formualizer_parse::parser::ASTNodeType::Array(vec![
713 vec![formualizer_parse::parser::ASTNode::new(
714 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
715 None,
716 )],
717 vec![formualizer_parse::parser::ASTNode::new(
718 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(0)),
719 None,
720 )],
721 ]),
722 None,
723 );
724 let counting_call = formualizer_parse::parser::ASTNode::new(
725 formualizer_parse::parser::ASTNodeType::Function {
726 name: "COUNTING".into(),
727 args: vec![],
728 },
729 None,
730 );
731 let hs = vec![
732 ArgumentHandle::new(&arr, &ctx),
733 ArgumentHandle::new(&counting_call, &ctx),
734 ];
735 let out = or.eval(&hs, &fctx).unwrap().into_literal();
736 assert_eq!(out, LiteralValue::Boolean(true));
737 assert_eq!(
738 counter.load(Ordering::SeqCst),
739 0,
740 "COUNTING should not be evaluated"
741 );
742 }
743
744 #[test]
745 fn and_returns_first_error_when_no_decisive_false() {
746 let err_counter = Arc::new(AtomicUsize::new(0));
747 let wb = TestWorkbook::new()
748 .with_function(Arc::new(AndFn))
749 .with_function(Arc::new(ErrorFn(err_counter.clone())));
750 let ctx = interp(&wb);
751 let fctx = ctx.function_context(None);
752 let and = ctx.context.get_function("", "AND").unwrap();
753
754 let one = formualizer_parse::parser::ASTNode::new(
756 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
757 None,
758 );
759 let errcall = formualizer_parse::parser::ASTNode::new(
760 formualizer_parse::parser::ASTNodeType::Function {
761 name: "ERRORFN".into(),
762 args: vec![],
763 },
764 None,
765 );
766 let hs = vec![
767 ArgumentHandle::new(&one, &ctx),
768 ArgumentHandle::new(&errcall, &ctx),
769 ArgumentHandle::new(&one, &ctx),
770 ];
771 let out = and.eval(&hs, &fctx).unwrap().into_literal();
772 match out {
773 LiteralValue::Error(e) => assert_eq!(e.to_string(), "#VALUE!"),
774 _ => panic!("Expected error"),
775 }
776 assert_eq!(
777 err_counter.load(Ordering::SeqCst),
778 1,
779 "ERRORFN should be evaluated once"
780 );
781 }
782
783 #[test]
784 fn or_does_not_evaluate_error_after_true() {
785 let err_counter = Arc::new(AtomicUsize::new(0));
786 let wb = TestWorkbook::new()
787 .with_function(Arc::new(OrFn))
788 .with_function(Arc::new(ErrorFn(err_counter.clone())));
789 let ctx = interp(&wb);
790 let fctx = ctx.function_context(None);
791 let or = ctx.context.get_function("", "OR").unwrap();
792
793 let a_true = formualizer_parse::parser::ASTNode::new(
795 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
796 None,
797 );
798 let errcall = formualizer_parse::parser::ASTNode::new(
799 formualizer_parse::parser::ASTNodeType::Function {
800 name: "ERRORFN".into(),
801 args: vec![],
802 },
803 None,
804 );
805 let hs = vec![
806 ArgumentHandle::new(&a_true, &ctx),
807 ArgumentHandle::new(&errcall, &ctx),
808 ];
809 let out = or.eval(&hs, &fctx).unwrap().into_literal();
810 assert_eq!(out, LiteralValue::Boolean(true));
811 assert_eq!(
812 err_counter.load(Ordering::SeqCst),
813 0,
814 "ERRORFN should not be evaluated"
815 );
816 }
817
818 #[test]
819 fn if_treats_empty_condition_as_false() {
820 let wb = TestWorkbook::new().with_function(Arc::new(IfFn));
821 let ctx = interp(&wb);
822 let fctx = ctx.function_context(None);
823 let iff = ctx.context.get_function("", "IF").unwrap();
824
825 let cond_empty = formualizer_parse::parser::ASTNode::new(
826 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Empty),
827 None,
828 );
829 let when_true = formualizer_parse::parser::ASTNode::new(
830 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(10)),
831 None,
832 );
833 let when_false = formualizer_parse::parser::ASTNode::new(
834 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(20)),
835 None,
836 );
837
838 let args = vec![
839 ArgumentHandle::new(&cond_empty, &ctx),
840 ArgumentHandle::new(&when_true, &ctx),
841 ArgumentHandle::new(&when_false, &ctx),
842 ];
843
844 assert_eq!(
845 iff.eval(&args, &fctx).unwrap().into_literal(),
846 LiteralValue::Int(20)
847 );
848 }
849}