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;
14
15impl Function for TrueFn {
16 func_caps!(PURE);
17
18 fn name(&self) -> &'static str {
19 "TRUE"
20 }
21 fn min_args(&self) -> usize {
22 0
23 }
24
25 fn eval<'a, 'b, 'c>(
26 &self,
27 _args: &'c [ArgumentHandle<'a, 'b>],
28 _ctx: &dyn FunctionContext<'b>,
29 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
30 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
31 true,
32 )))
33 }
34}
35
36#[derive(Debug)]
39pub struct FalseFn;
40
41impl Function for FalseFn {
42 func_caps!(PURE);
43
44 fn name(&self) -> &'static str {
45 "FALSE"
46 }
47 fn min_args(&self) -> usize {
48 0
49 }
50
51 fn eval<'a, 'b, 'c>(
52 &self,
53 _args: &'c [ArgumentHandle<'a, 'b>],
54 _ctx: &dyn FunctionContext<'b>,
55 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
56 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
57 false,
58 )))
59 }
60}
61
62#[derive(Debug)]
65pub struct AndFn;
66
67impl Function for AndFn {
68 func_caps!(PURE, REDUCTION, BOOL_ONLY, SHORT_CIRCUIT);
69
70 fn name(&self) -> &'static str {
71 "AND"
72 }
73 fn min_args(&self) -> usize {
74 1
75 }
76 fn variadic(&self) -> bool {
77 true
78 }
79 fn arg_schema(&self) -> &'static [ArgSchema] {
80 &ARG_ANY_ONE[..]
81 }
82
83 fn eval<'a, 'b, 'c>(
84 &self,
85 args: &'c [ArgumentHandle<'a, 'b>],
86 _ctx: &dyn FunctionContext<'b>,
87 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
88 let mut first_error: Option<LiteralValue> = None;
89 for h in args {
90 let it = h.lazy_values_owned()?;
91 for v in it {
92 match v {
93 LiteralValue::Error(_) => {
94 if first_error.is_none() {
95 first_error = Some(v);
96 }
97 }
98 LiteralValue::Empty => {
99 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
100 false,
101 )));
102 }
103 LiteralValue::Boolean(b) => {
104 if !b {
105 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
106 false,
107 )));
108 }
109 }
110 LiteralValue::Number(n) => {
111 if n == 0.0 {
112 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
113 false,
114 )));
115 }
116 }
117 LiteralValue::Int(i) => {
118 if i == 0 {
119 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
120 false,
121 )));
122 }
123 }
124 _ => {
125 if first_error.is_none() {
127 first_error =
128 Some(LiteralValue::Error(ExcelError::new_value().with_message(
129 "AND expects logical/numeric inputs; text is not coercible",
130 )));
131 }
132 }
133 }
134 }
135 }
136 if let Some(err) = first_error {
137 return Ok(crate::traits::CalcValue::Scalar(err));
138 }
139 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
140 true,
141 )))
142 }
143}
144
145#[derive(Debug)]
148pub struct OrFn;
149
150impl Function for OrFn {
151 func_caps!(PURE, REDUCTION, BOOL_ONLY, SHORT_CIRCUIT);
152
153 fn name(&self) -> &'static str {
154 "OR"
155 }
156 fn min_args(&self) -> usize {
157 1
158 }
159 fn variadic(&self) -> bool {
160 true
161 }
162 fn arg_schema(&self) -> &'static [ArgSchema] {
163 &ARG_ANY_ONE[..]
164 }
165
166 fn eval<'a, 'b, 'c>(
167 &self,
168 args: &'c [ArgumentHandle<'a, 'b>],
169 _ctx: &dyn FunctionContext<'b>,
170 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
171 let mut first_error: Option<LiteralValue> = None;
172 for h in args {
173 let it = h.lazy_values_owned()?;
174 for v in it {
175 match v {
176 LiteralValue::Error(_) => {
177 if first_error.is_none() {
178 first_error = Some(v);
179 }
180 }
181 LiteralValue::Empty => {
182 }
184 LiteralValue::Boolean(b) => {
185 if b {
186 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
187 true,
188 )));
189 }
190 }
191 LiteralValue::Number(n) => {
192 if n != 0.0 {
193 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
194 true,
195 )));
196 }
197 }
198 LiteralValue::Int(i) => {
199 if i != 0 {
200 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
201 true,
202 )));
203 }
204 }
205 _ => {
206 if first_error.is_none() {
208 first_error =
209 Some(LiteralValue::Error(ExcelError::new_value().with_message(
210 "OR expects logical/numeric inputs; text is not coercible",
211 )));
212 }
213 }
214 }
215 }
216 }
217 if let Some(err) = first_error {
218 return Ok(crate::traits::CalcValue::Scalar(err));
219 }
220 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
221 false,
222 )))
223 }
224}
225
226#[derive(Debug)]
229pub struct IfFn;
230
231impl Function for IfFn {
232 func_caps!(PURE, SHORT_CIRCUIT);
233
234 fn name(&self) -> &'static str {
235 "IF"
236 }
237 fn min_args(&self) -> usize {
238 2
239 }
240 fn variadic(&self) -> bool {
241 true
242 }
243
244 fn arg_schema(&self) -> &'static [ArgSchema] {
245 use std::sync::LazyLock;
246 static ONE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| vec![ArgSchema::any()]);
248 &ONE[..]
249 }
250
251 fn eval<'a, 'b, 'c>(
252 &self,
253 args: &'c [ArgumentHandle<'a, 'b>],
254 _ctx: &dyn FunctionContext<'b>,
255 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
256 if args.len() < 2 || args.len() > 3 {
257 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
258 ExcelError::new_value()
259 .with_message(format!("IF expects 2 or 3 arguments, got {}", args.len())),
260 )));
261 }
262
263 let condition = args[0].value()?.into_literal();
264 let b = match condition {
265 LiteralValue::Boolean(b) => b,
266 LiteralValue::Number(n) => n != 0.0,
267 LiteralValue::Int(i) => i != 0,
268 LiteralValue::Empty => false,
269 _ => {
270 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
271 ExcelError::new_value().with_message("IF condition must be boolean or number"),
272 )));
273 }
274 };
275
276 if b {
277 args[1].value()
278 } else if let Some(arg) = args.get(2) {
279 arg.value()
280 } else {
281 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
282 false,
283 )))
284 }
285 }
286}
287
288pub fn register_builtins() {
289 crate::function_registry::register_function(std::sync::Arc::new(TrueFn));
290 crate::function_registry::register_function(std::sync::Arc::new(FalseFn));
291 crate::function_registry::register_function(std::sync::Arc::new(AndFn));
292 crate::function_registry::register_function(std::sync::Arc::new(OrFn));
293 crate::function_registry::register_function(std::sync::Arc::new(IfFn));
294}
295
296#[cfg(test)]
299mod tests {
300 use super::*;
301 use crate::traits::ArgumentHandle;
302 use crate::{interpreter::Interpreter, test_workbook::TestWorkbook};
303 use formualizer_parse::LiteralValue;
304 use std::sync::{
305 Arc,
306 atomic::{AtomicUsize, Ordering},
307 };
308
309 #[derive(Debug)]
310 struct CountFn(Arc<AtomicUsize>);
311 impl Function for CountFn {
312 func_caps!(PURE);
313 fn name(&self) -> &'static str {
314 "COUNTING"
315 }
316 fn min_args(&self) -> usize {
317 0
318 }
319 fn eval<'a, 'b, 'c>(
320 &self,
321 _args: &'c [ArgumentHandle<'a, 'b>],
322 _ctx: &dyn FunctionContext<'b>,
323 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
324 self.0.fetch_add(1, Ordering::SeqCst);
325 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
326 true,
327 )))
328 }
329 }
330
331 #[derive(Debug)]
332 struct ErrorFn(Arc<AtomicUsize>);
333 impl Function for ErrorFn {
334 func_caps!(PURE);
335 fn name(&self) -> &'static str {
336 "ERRORFN"
337 }
338 fn min_args(&self) -> usize {
339 0
340 }
341 fn eval<'a, 'b, 'c>(
342 &self,
343 _args: &'c [ArgumentHandle<'a, 'b>],
344 _ctx: &dyn FunctionContext<'b>,
345 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
346 self.0.fetch_add(1, Ordering::SeqCst);
347 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
348 ExcelError::new_value(),
349 )))
350 }
351 }
352
353 fn interp(wb: &TestWorkbook) -> Interpreter<'_> {
354 wb.interpreter()
355 }
356
357 #[test]
358 fn test_true_false() {
359 let wb = TestWorkbook::new()
360 .with_function(std::sync::Arc::new(TrueFn))
361 .with_function(std::sync::Arc::new(FalseFn));
362
363 let ctx = interp(&wb);
364 let t = ctx.context.get_function("", "TRUE").unwrap();
365 let fctx = ctx.function_context(None);
366 assert_eq!(
367 t.eval(&[], &fctx).unwrap().into_literal(),
368 LiteralValue::Boolean(true)
369 );
370
371 let f = ctx.context.get_function("", "FALSE").unwrap();
372 assert_eq!(
373 f.eval(&[], &fctx).unwrap().into_literal(),
374 LiteralValue::Boolean(false)
375 );
376 }
377
378 #[test]
379 fn test_and_or() {
380 let wb = TestWorkbook::new()
381 .with_function(std::sync::Arc::new(AndFn))
382 .with_function(std::sync::Arc::new(OrFn));
383 let ctx = interp(&wb);
384 let fctx = ctx.function_context(None);
385
386 let and = ctx.context.get_function("", "AND").unwrap();
387 let or = ctx.context.get_function("", "OR").unwrap();
388 let dummy_ast = formualizer_parse::parser::ASTNode::new(
390 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
391 None,
392 );
393 let dummy_ast_false = formualizer_parse::parser::ASTNode::new(
394 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(false)),
395 None,
396 );
397 let dummy_ast_one = formualizer_parse::parser::ASTNode::new(
398 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
399 None,
400 );
401 let hs = vec![
402 ArgumentHandle::new(&dummy_ast, &ctx),
403 ArgumentHandle::new(&dummy_ast_one, &ctx),
404 ];
405 assert_eq!(
406 and.eval(&hs, &fctx).unwrap().into_literal(),
407 LiteralValue::Boolean(true)
408 );
409
410 let hs2 = vec![
411 ArgumentHandle::new(&dummy_ast_false, &ctx),
412 ArgumentHandle::new(&dummy_ast_one, &ctx),
413 ];
414 assert_eq!(
415 and.eval(&hs2, &fctx).unwrap().into_literal(),
416 LiteralValue::Boolean(false)
417 );
418 assert_eq!(
419 or.eval(&hs2, &fctx).unwrap().into_literal(),
420 LiteralValue::Boolean(true)
421 );
422 }
423
424 #[test]
425 fn and_short_circuits_on_false_without_evaluating_rest() {
426 let counter = Arc::new(AtomicUsize::new(0));
427 let wb = TestWorkbook::new()
428 .with_function(Arc::new(AndFn))
429 .with_function(Arc::new(CountFn(counter.clone())));
430 let ctx = interp(&wb);
431 let fctx = ctx.function_context(None);
432 let and = ctx.context.get_function("", "AND").unwrap();
433
434 let a_false = formualizer_parse::parser::ASTNode::new(
436 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(false)),
437 None,
438 );
439 let counting_call = formualizer_parse::parser::ASTNode::new(
440 formualizer_parse::parser::ASTNodeType::Function {
441 name: "COUNTING".into(),
442 args: vec![],
443 },
444 None,
445 );
446 let hs = vec![
447 ArgumentHandle::new(&a_false, &ctx),
448 ArgumentHandle::new(&counting_call, &ctx),
449 ];
450 let out = and.eval(&hs, &fctx).unwrap().into_literal();
451 assert_eq!(out, LiteralValue::Boolean(false));
452 assert_eq!(
453 counter.load(Ordering::SeqCst),
454 0,
455 "COUNTING should not be evaluated"
456 );
457 }
458
459 #[test]
460 fn or_short_circuits_on_true_without_evaluating_rest() {
461 let counter = Arc::new(AtomicUsize::new(0));
462 let wb = TestWorkbook::new()
463 .with_function(Arc::new(OrFn))
464 .with_function(Arc::new(CountFn(counter.clone())));
465 let ctx = interp(&wb);
466 let fctx = ctx.function_context(None);
467 let or = ctx.context.get_function("", "OR").unwrap();
468
469 let a_true = formualizer_parse::parser::ASTNode::new(
471 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
472 None,
473 );
474 let counting_call = formualizer_parse::parser::ASTNode::new(
475 formualizer_parse::parser::ASTNodeType::Function {
476 name: "COUNTING".into(),
477 args: vec![],
478 },
479 None,
480 );
481 let hs = vec![
482 ArgumentHandle::new(&a_true, &ctx),
483 ArgumentHandle::new(&counting_call, &ctx),
484 ];
485 let out = or.eval(&hs, &fctx).unwrap().into_literal();
486 assert_eq!(out, LiteralValue::Boolean(true));
487 assert_eq!(
488 counter.load(Ordering::SeqCst),
489 0,
490 "COUNTING should not be evaluated"
491 );
492 }
493
494 #[test]
495 fn or_range_arg_short_circuits_on_first_true_before_evaluating_next_arg() {
496 let counter = Arc::new(AtomicUsize::new(0));
497 let wb = TestWorkbook::new()
498 .with_function(Arc::new(OrFn))
499 .with_function(Arc::new(CountFn(counter.clone())));
500 let ctx = interp(&wb);
501 let fctx = ctx.function_context(None);
502 let or = ctx.context.get_function("", "OR").unwrap();
503
504 let arr = formualizer_parse::parser::ASTNode::new(
506 formualizer_parse::parser::ASTNodeType::Array(vec![
507 vec![formualizer_parse::parser::ASTNode::new(
508 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
509 None,
510 )],
511 vec![formualizer_parse::parser::ASTNode::new(
512 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(0)),
513 None,
514 )],
515 ]),
516 None,
517 );
518 let counting_call = formualizer_parse::parser::ASTNode::new(
519 formualizer_parse::parser::ASTNodeType::Function {
520 name: "COUNTING".into(),
521 args: vec![],
522 },
523 None,
524 );
525 let hs = vec![
526 ArgumentHandle::new(&arr, &ctx),
527 ArgumentHandle::new(&counting_call, &ctx),
528 ];
529 let out = or.eval(&hs, &fctx).unwrap().into_literal();
530 assert_eq!(out, LiteralValue::Boolean(true));
531 assert_eq!(
532 counter.load(Ordering::SeqCst),
533 0,
534 "COUNTING should not be evaluated"
535 );
536 }
537
538 #[test]
539 fn and_returns_first_error_when_no_decisive_false() {
540 let err_counter = Arc::new(AtomicUsize::new(0));
541 let wb = TestWorkbook::new()
542 .with_function(Arc::new(AndFn))
543 .with_function(Arc::new(ErrorFn(err_counter.clone())));
544 let ctx = interp(&wb);
545 let fctx = ctx.function_context(None);
546 let and = ctx.context.get_function("", "AND").unwrap();
547
548 let one = formualizer_parse::parser::ASTNode::new(
550 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
551 None,
552 );
553 let errcall = formualizer_parse::parser::ASTNode::new(
554 formualizer_parse::parser::ASTNodeType::Function {
555 name: "ERRORFN".into(),
556 args: vec![],
557 },
558 None,
559 );
560 let hs = vec![
561 ArgumentHandle::new(&one, &ctx),
562 ArgumentHandle::new(&errcall, &ctx),
563 ArgumentHandle::new(&one, &ctx),
564 ];
565 let out = and.eval(&hs, &fctx).unwrap().into_literal();
566 match out {
567 LiteralValue::Error(e) => assert_eq!(e.to_string(), "#VALUE!"),
568 _ => panic!("Expected error"),
569 }
570 assert_eq!(
571 err_counter.load(Ordering::SeqCst),
572 1,
573 "ERRORFN should be evaluated once"
574 );
575 }
576
577 #[test]
578 fn or_does_not_evaluate_error_after_true() {
579 let err_counter = Arc::new(AtomicUsize::new(0));
580 let wb = TestWorkbook::new()
581 .with_function(Arc::new(OrFn))
582 .with_function(Arc::new(ErrorFn(err_counter.clone())));
583 let ctx = interp(&wb);
584 let fctx = ctx.function_context(None);
585 let or = ctx.context.get_function("", "OR").unwrap();
586
587 let a_true = formualizer_parse::parser::ASTNode::new(
589 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
590 None,
591 );
592 let errcall = formualizer_parse::parser::ASTNode::new(
593 formualizer_parse::parser::ASTNodeType::Function {
594 name: "ERRORFN".into(),
595 args: vec![],
596 },
597 None,
598 );
599 let hs = vec![
600 ArgumentHandle::new(&a_true, &ctx),
601 ArgumentHandle::new(&errcall, &ctx),
602 ];
603 let out = or.eval(&hs, &fctx).unwrap().into_literal();
604 assert_eq!(out, LiteralValue::Boolean(true));
605 assert_eq!(
606 err_counter.load(Ordering::SeqCst),
607 0,
608 "ERRORFN should not be evaluated"
609 );
610 }
611
612 #[test]
613 fn if_treats_empty_condition_as_false() {
614 let wb = TestWorkbook::new().with_function(Arc::new(IfFn));
615 let ctx = interp(&wb);
616 let fctx = ctx.function_context(None);
617 let iff = ctx.context.get_function("", "IF").unwrap();
618
619 let cond_empty = formualizer_parse::parser::ASTNode::new(
620 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Empty),
621 None,
622 );
623 let when_true = formualizer_parse::parser::ASTNode::new(
624 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(10)),
625 None,
626 );
627 let when_false = formualizer_parse::parser::ASTNode::new(
628 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(20)),
629 None,
630 );
631
632 let args = vec![
633 ArgumentHandle::new(&cond_empty, &ctx),
634 ArgumentHandle::new(&when_true, &ctx),
635 ArgumentHandle::new(&when_false, &ctx),
636 ];
637
638 assert_eq!(
639 iff.eval(&args, &fctx).unwrap().into_literal(),
640 LiteralValue::Int(20)
641 );
642 }
643}