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 _ => {
269 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
270 ExcelError::new_value().with_message("IF condition must be boolean or number"),
271 )));
272 }
273 };
274
275 if b {
276 args[1].value()
277 } else if let Some(arg) = args.get(2) {
278 arg.value()
279 } else {
280 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
281 false,
282 )))
283 }
284 }
285}
286
287pub fn register_builtins() {
288 crate::function_registry::register_function(std::sync::Arc::new(TrueFn));
289 crate::function_registry::register_function(std::sync::Arc::new(FalseFn));
290 crate::function_registry::register_function(std::sync::Arc::new(AndFn));
291 crate::function_registry::register_function(std::sync::Arc::new(OrFn));
292 crate::function_registry::register_function(std::sync::Arc::new(IfFn));
293}
294
295#[cfg(test)]
298mod tests {
299 use super::*;
300 use crate::traits::ArgumentHandle;
301 use crate::{interpreter::Interpreter, test_workbook::TestWorkbook};
302 use formualizer_parse::LiteralValue;
303 use std::sync::{
304 Arc,
305 atomic::{AtomicUsize, Ordering},
306 };
307
308 #[derive(Debug)]
309 struct CountFn(Arc<AtomicUsize>);
310 impl Function for CountFn {
311 func_caps!(PURE);
312 fn name(&self) -> &'static str {
313 "COUNTING"
314 }
315 fn min_args(&self) -> usize {
316 0
317 }
318 fn eval<'a, 'b, 'c>(
319 &self,
320 _args: &'c [ArgumentHandle<'a, 'b>],
321 _ctx: &dyn FunctionContext<'b>,
322 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
323 self.0.fetch_add(1, Ordering::SeqCst);
324 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
325 true,
326 )))
327 }
328 }
329
330 #[derive(Debug)]
331 struct ErrorFn(Arc<AtomicUsize>);
332 impl Function for ErrorFn {
333 func_caps!(PURE);
334 fn name(&self) -> &'static str {
335 "ERRORFN"
336 }
337 fn min_args(&self) -> usize {
338 0
339 }
340 fn eval<'a, 'b, 'c>(
341 &self,
342 _args: &'c [ArgumentHandle<'a, 'b>],
343 _ctx: &dyn FunctionContext<'b>,
344 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
345 self.0.fetch_add(1, Ordering::SeqCst);
346 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
347 ExcelError::new_value(),
348 )))
349 }
350 }
351
352 fn interp(wb: &TestWorkbook) -> Interpreter<'_> {
353 wb.interpreter()
354 }
355
356 #[test]
357 fn test_true_false() {
358 let wb = TestWorkbook::new()
359 .with_function(std::sync::Arc::new(TrueFn))
360 .with_function(std::sync::Arc::new(FalseFn));
361
362 let ctx = interp(&wb);
363 let t = ctx.context.get_function("", "TRUE").unwrap();
364 let fctx = ctx.function_context(None);
365 assert_eq!(
366 t.eval(&[], &fctx).unwrap().into_literal(),
367 LiteralValue::Boolean(true)
368 );
369
370 let f = ctx.context.get_function("", "FALSE").unwrap();
371 assert_eq!(
372 f.eval(&[], &fctx).unwrap().into_literal(),
373 LiteralValue::Boolean(false)
374 );
375 }
376
377 #[test]
378 fn test_and_or() {
379 let wb = TestWorkbook::new()
380 .with_function(std::sync::Arc::new(AndFn))
381 .with_function(std::sync::Arc::new(OrFn));
382 let ctx = interp(&wb);
383 let fctx = ctx.function_context(None);
384
385 let and = ctx.context.get_function("", "AND").unwrap();
386 let or = ctx.context.get_function("", "OR").unwrap();
387 let dummy_ast = formualizer_parse::parser::ASTNode::new(
389 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
390 None,
391 );
392 let dummy_ast_false = formualizer_parse::parser::ASTNode::new(
393 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(false)),
394 None,
395 );
396 let dummy_ast_one = formualizer_parse::parser::ASTNode::new(
397 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
398 None,
399 );
400 let hs = vec![
401 ArgumentHandle::new(&dummy_ast, &ctx),
402 ArgumentHandle::new(&dummy_ast_one, &ctx),
403 ];
404 assert_eq!(
405 and.eval(&hs, &fctx).unwrap().into_literal(),
406 LiteralValue::Boolean(true)
407 );
408
409 let hs2 = vec![
410 ArgumentHandle::new(&dummy_ast_false, &ctx),
411 ArgumentHandle::new(&dummy_ast_one, &ctx),
412 ];
413 assert_eq!(
414 and.eval(&hs2, &fctx).unwrap().into_literal(),
415 LiteralValue::Boolean(false)
416 );
417 assert_eq!(
418 or.eval(&hs2, &fctx).unwrap().into_literal(),
419 LiteralValue::Boolean(true)
420 );
421 }
422
423 #[test]
424 fn and_short_circuits_on_false_without_evaluating_rest() {
425 let counter = Arc::new(AtomicUsize::new(0));
426 let wb = TestWorkbook::new()
427 .with_function(Arc::new(AndFn))
428 .with_function(Arc::new(CountFn(counter.clone())));
429 let ctx = interp(&wb);
430 let fctx = ctx.function_context(None);
431 let and = ctx.context.get_function("", "AND").unwrap();
432
433 let a_false = formualizer_parse::parser::ASTNode::new(
435 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(false)),
436 None,
437 );
438 let counting_call = formualizer_parse::parser::ASTNode::new(
439 formualizer_parse::parser::ASTNodeType::Function {
440 name: "COUNTING".into(),
441 args: vec![],
442 },
443 None,
444 );
445 let hs = vec![
446 ArgumentHandle::new(&a_false, &ctx),
447 ArgumentHandle::new(&counting_call, &ctx),
448 ];
449 let out = and.eval(&hs, &fctx).unwrap().into_literal();
450 assert_eq!(out, LiteralValue::Boolean(false));
451 assert_eq!(
452 counter.load(Ordering::SeqCst),
453 0,
454 "COUNTING should not be evaluated"
455 );
456 }
457
458 #[test]
459 fn or_short_circuits_on_true_without_evaluating_rest() {
460 let counter = Arc::new(AtomicUsize::new(0));
461 let wb = TestWorkbook::new()
462 .with_function(Arc::new(OrFn))
463 .with_function(Arc::new(CountFn(counter.clone())));
464 let ctx = interp(&wb);
465 let fctx = ctx.function_context(None);
466 let or = ctx.context.get_function("", "OR").unwrap();
467
468 let a_true = formualizer_parse::parser::ASTNode::new(
470 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
471 None,
472 );
473 let counting_call = formualizer_parse::parser::ASTNode::new(
474 formualizer_parse::parser::ASTNodeType::Function {
475 name: "COUNTING".into(),
476 args: vec![],
477 },
478 None,
479 );
480 let hs = vec![
481 ArgumentHandle::new(&a_true, &ctx),
482 ArgumentHandle::new(&counting_call, &ctx),
483 ];
484 let out = or.eval(&hs, &fctx).unwrap().into_literal();
485 assert_eq!(out, LiteralValue::Boolean(true));
486 assert_eq!(
487 counter.load(Ordering::SeqCst),
488 0,
489 "COUNTING should not be evaluated"
490 );
491 }
492
493 #[test]
494 fn or_range_arg_short_circuits_on_first_true_before_evaluating_next_arg() {
495 let counter = Arc::new(AtomicUsize::new(0));
496 let wb = TestWorkbook::new()
497 .with_function(Arc::new(OrFn))
498 .with_function(Arc::new(CountFn(counter.clone())));
499 let ctx = interp(&wb);
500 let fctx = ctx.function_context(None);
501 let or = ctx.context.get_function("", "OR").unwrap();
502
503 let arr = formualizer_parse::parser::ASTNode::new(
505 formualizer_parse::parser::ASTNodeType::Array(vec![
506 vec![formualizer_parse::parser::ASTNode::new(
507 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
508 None,
509 )],
510 vec![formualizer_parse::parser::ASTNode::new(
511 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(0)),
512 None,
513 )],
514 ]),
515 None,
516 );
517 let counting_call = formualizer_parse::parser::ASTNode::new(
518 formualizer_parse::parser::ASTNodeType::Function {
519 name: "COUNTING".into(),
520 args: vec![],
521 },
522 None,
523 );
524 let hs = vec![
525 ArgumentHandle::new(&arr, &ctx),
526 ArgumentHandle::new(&counting_call, &ctx),
527 ];
528 let out = or.eval(&hs, &fctx).unwrap().into_literal();
529 assert_eq!(out, LiteralValue::Boolean(true));
530 assert_eq!(
531 counter.load(Ordering::SeqCst),
532 0,
533 "COUNTING should not be evaluated"
534 );
535 }
536
537 #[test]
538 fn and_returns_first_error_when_no_decisive_false() {
539 let err_counter = Arc::new(AtomicUsize::new(0));
540 let wb = TestWorkbook::new()
541 .with_function(Arc::new(AndFn))
542 .with_function(Arc::new(ErrorFn(err_counter.clone())));
543 let ctx = interp(&wb);
544 let fctx = ctx.function_context(None);
545 let and = ctx.context.get_function("", "AND").unwrap();
546
547 let one = formualizer_parse::parser::ASTNode::new(
549 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
550 None,
551 );
552 let errcall = formualizer_parse::parser::ASTNode::new(
553 formualizer_parse::parser::ASTNodeType::Function {
554 name: "ERRORFN".into(),
555 args: vec![],
556 },
557 None,
558 );
559 let hs = vec![
560 ArgumentHandle::new(&one, &ctx),
561 ArgumentHandle::new(&errcall, &ctx),
562 ArgumentHandle::new(&one, &ctx),
563 ];
564 let out = and.eval(&hs, &fctx).unwrap().into_literal();
565 match out {
566 LiteralValue::Error(e) => assert_eq!(e.to_string(), "#VALUE!"),
567 _ => panic!("Expected error"),
568 }
569 assert_eq!(
570 err_counter.load(Ordering::SeqCst),
571 1,
572 "ERRORFN should be evaluated once"
573 );
574 }
575
576 #[test]
577 fn or_does_not_evaluate_error_after_true() {
578 let err_counter = Arc::new(AtomicUsize::new(0));
579 let wb = TestWorkbook::new()
580 .with_function(Arc::new(OrFn))
581 .with_function(Arc::new(ErrorFn(err_counter.clone())));
582 let ctx = interp(&wb);
583 let fctx = ctx.function_context(None);
584 let or = ctx.context.get_function("", "OR").unwrap();
585
586 let a_true = formualizer_parse::parser::ASTNode::new(
588 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
589 None,
590 );
591 let errcall = formualizer_parse::parser::ASTNode::new(
592 formualizer_parse::parser::ASTNodeType::Function {
593 name: "ERRORFN".into(),
594 args: vec![],
595 },
596 None,
597 );
598 let hs = vec![
599 ArgumentHandle::new(&a_true, &ctx),
600 ArgumentHandle::new(&errcall, &ctx),
601 ];
602 let out = or.eval(&hs, &fctx).unwrap().into_literal();
603 assert_eq!(out, LiteralValue::Boolean(true));
604 assert_eq!(
605 err_counter.load(Ordering::SeqCst),
606 0,
607 "ERRORFN should not be evaluated"
608 );
609 }
610}