Skip to main content

vla_lang/calc/
eval.rs

1//! The evaluator and the functions seam (KERNEL.7, 2026-10-08).
2//!
3//! One formula at a time: an [`Expr`] against an [`Env`], the grid as the
4//! cell being computed sees it, through a [`Ctx`] that carries the
5//! [`Library`] of functions. The library is the seam: a function is a
6//! Rust function registered under its upper-case name, strict (its
7//! arguments evaluated first, as `SUM`'s are) or a form (its arguments
8//! handed over unevaluated, as `IF`'s are, so that only the branch taken
9//! is computed). The language registers its own functions
10//! (`super::library`), the ones a cartridge rule needs on day one; a crate
11//! above registers more through the same calls, `frazaro-core` Excel's
12//! library by measurement, and an engine consumes the language's alone.
13//!
14//! What an operand is: a [`Value`], or an area, a rectangle of cells a
15//! reference names, which a function such as `SUM` walks and which a
16//! scalar operator reads as one cell when it is one cell. A range where
17//! one value is wanted is not computed in this slice ([`Reason`]), as the
18//! parser's page says. An error value is a value and propagates as Excel
19//! propagates it, the left operand's first; it never refuses anything.
20
21use std::borrow::Cow;
22use std::cell::Cell as Counter;
23use std::cmp::Ordering;
24use std::collections::BTreeMap;
25
26use crate::intrinsics::fold;
27use crate::rows::quoted;
28use crate::sheet::Workbook;
29
30use super::dense::Dense;
31use super::formula::{self, BinOp, Expr, RefExpr};
32use super::graph::{areas_at, cells_in, Area, Areas, CellId, Extent};
33use super::value::{ErrorKind, Value};
34
35/// Why a cell was not computed: the honest static label, named.
36#[derive(Clone, Debug, PartialEq, Eq)]
37pub enum Reason {
38    /// A function the library does not hold, by name.
39    Function(String),
40    /// A construct this slice does not read, as written.
41    Construct(String),
42    /// The cell is in a cycle, or reads one.
43    Cycle,
44}
45
46impl Reason {
47    /// The label as a datum: `(not-computed "VLOOKUP")`, `(not-computed cycle)`.
48    pub fn spell(&self) -> String {
49        match self {
50            Reason::Function(name) | Reason::Construct(name) => {
51                format!("(not-computed {})", quoted(name))
52            }
53            Reason::Cycle => "(not-computed cycle)".to_string(),
54        }
55    }
56}
57
58/// What a formula cell holds after evaluation.
59#[derive(Clone, Debug, PartialEq)]
60pub enum Computed {
61    Value(Value),
62    NotComputed(Reason),
63}
64
65impl Computed {
66    /// The datum a `value` or `calc` row prints.
67    pub fn spell(&self) -> String {
68        match self {
69            Computed::Value(v) => v.spell(),
70            Computed::NotComputed(r) => r.spell(),
71        }
72    }
73}
74
75/// An evaluated operand: one value, or the cells a reference names. `'e` is
76/// the life of the expression it was evaluated from, whose text labels an
77/// area (KERNEL.25).
78#[derive(Clone, Debug, PartialEq)]
79pub enum Arg<'e> {
80    Scalar(Value),
81    Area {
82        areas: Areas,
83        /// The reference as written, for a label: borrowed from the parse,
84        /// so that a reference evaluated costs no copy of its text, or owned
85        /// when the parse was the evaluation's own, a defined name's text.
86        written: Cow<'e, str>,
87        /// The offset the reference was evaluated at (KERNEL.22, a shape at
88        /// a cell other than its first), by which the label's text is moved
89        /// before it is shown; (0, 0) everywhere else.
90        moved: (i64, i64),
91    },
92}
93
94impl Arg<'_> {
95    /// The operand with its label owned, to outlive the parse it was
96    /// evaluated from.
97    pub fn into_owned(self) -> Arg<'static> {
98        match self {
99            Arg::Scalar(v) => Arg::Scalar(v),
100            Arg::Area {
101                areas,
102                written,
103                moved,
104            } => Arg::Area {
105                areas,
106                written: Cow::Owned(written.into_owned()),
107                moved,
108            },
109        }
110    }
111}
112
113/// The grid as the cell being computed sees it.
114pub trait Env {
115    /// The model.
116    fn model(&self) -> &Workbook;
117    /// The cell being computed.
118    fn here(&self) -> CellId;
119    /// A cell's value now: a constant as it stands, a formula's computed
120    /// value, `Empty` for a cell with nothing; a formula not computed is
121    /// the reason, which propagates.
122    fn read(&self, id: CellId) -> Result<Value, Reason>;
123    /// A sheet's used rectangle, cached by the caller.
124    fn extent(&self, sheet: usize) -> Extent;
125    /// The text of a defined name, resolved as `graph::Names::resolve`
126    /// resolves it.
127    fn name(&self, name: &str, scope: Option<usize>, home: usize) -> Option<&str>;
128    /// A sheet's values kept as an array over its extent, when the
129    /// environment keeps one (KERNEL.20: an engine's machine, for each
130    /// `.last` twin): each slot the value a read of its cell answers, or
131    /// `Value::Empty` for a cell holding none, which a walk skips as a walk
132    /// of the cells skips it; so a range of the sheet is walked by index and
133    /// never cell by cell. `None`, a sheet walked through its cells, unless
134    /// an environment says otherwise.
135    fn dense(&self, _sheet: usize) -> Option<&Dense> {
136        None
137    }
138
139    /// The cell being computed from now on, told by an evaluation of many
140    /// cells at once before it computes at each (KERNEL.20, `super::vector`),
141    /// so that [`Env::here`] answers it: an engine's machine keeps it as it
142    /// keeps the cell it evaluates alone. Nothing, unless an environment
143    /// says otherwise.
144    fn enter(&self, _id: CellId) {}
145}
146
147/// A strict function: its arguments evaluated, a value or a reason out.
148pub type Strict = fn(&Ctx<'_>, &[Arg<'_>]) -> Result<Value, Reason>;
149
150/// A form: its arguments as written, an operand or a reason out, the operand
151/// living as long as the arguments it was evaluated from.
152pub type Form = for<'e> fn(&Ctx<'_>, &'e [Expr]) -> Result<Arg<'e>, Reason>;
153
154/// One of the language's own functions that an evaluation of many cells at
155/// once computes by itself (`super::vector`), known by the name the
156/// language's library registers it under: `IF` (KERNEL.20); the aggregates
157/// and `NOT`, which it computes by the library's own rule and fold
158/// (KERNEL.26, `super::library::Fold`); and the language's other functions,
159/// which it computes by the library's own meaning of each (KERNEL.28). Any
160/// other registration of the name replaces it, so that a crate above that
161/// registers a meaning of its own has that meaning computed, cell by cell.
162#[derive(Clone, Copy, Debug, PartialEq, Eq)]
163pub(crate) enum Known {
164    /// `library::if_`.
165    If,
166    /// `SUM`, `MIN` and `MAX`: `library::Sum`, `Least` and `Most`.
167    Sum,
168    Min,
169    Max,
170    /// `AND` and `OR`: `library::All` and `Some_`.
171    And,
172    Or,
173    /// `NOT`: `library::negated`.
174    Not,
175    /// `ABS`, `INT` and `SIN`: `library::abs_of`, `int_of` and `sin_of`.
176    Abs,
177    Int,
178    Sin,
179    /// `MOD`: `library::mod_of`.
180    Mod,
181    /// `ROW` and `COLUMN`: `library::placed`.
182    Row,
183    Column,
184    /// `CHOOSE`: `library::chosen`.
185    Choose,
186}
187
188/// The functions a formula may call, by upper-case name: the seam. Every
189/// function registered is a function of its arguments and of the cell it is
190/// computed at, and of nothing else: no clock, no chance, no state of its
191/// own. An engine's machine computes a cell again only when a cell it reads
192/// has changed (KERNEL.20, `super::reach`), so a function that answered
193/// differently over the same arguments would be computed once and kept.
194#[derive(Default)]
195pub struct Library {
196    /// Each strict function, and whether it is one an evaluation of many
197    /// cells at once knows (KERNEL.26).
198    strict: BTreeMap<String, (Strict, Option<Known>)>,
199    /// Each form, and whether it is one an evaluation of many cells at once
200    /// knows (KERNEL.20).
201    forms: BTreeMap<String, (Form, Option<Known>)>,
202}
203
204impl Library {
205    /// A library with nothing in it.
206    pub fn empty() -> Library {
207        Library::default()
208    }
209
210    /// Register a strict function; a later registration of the same name
211    /// replaces the earlier.
212    pub fn register(&mut self, name: &str, f: Strict) {
213        let key = name.to_ascii_uppercase();
214        self.forms.remove(&key);
215        self.strict.insert(key, (f, None));
216    }
217
218    /// Register a form.
219    pub fn register_form(&mut self, name: &str, f: Form) {
220        let key = name.to_ascii_uppercase();
221        self.strict.remove(&key);
222        self.forms.insert(key, (f, None));
223    }
224
225    /// Register one of the language's own forms as one an evaluation of
226    /// many cells at once computes by itself (KERNEL.20).
227    pub(crate) fn register_known(&mut self, name: &str, f: Form, known: Known) {
228        let key = name.to_ascii_uppercase();
229        self.strict.remove(&key);
230        self.forms.insert(key, (f, Some(known)));
231    }
232
233    /// Register one of the language's own strict functions as one an
234    /// evaluation of many cells at once computes by itself, by the same rule
235    /// and fold (KERNEL.26).
236    pub(crate) fn register_known_strict(&mut self, name: &str, f: Strict, known: Known) {
237        let key = name.to_ascii_uppercase();
238        self.forms.remove(&key);
239        self.strict.insert(key, (f, Some(known)));
240    }
241
242    /// The known function registered under that name, without case: a form
243    /// or a strict function.
244    pub(crate) fn known(&self, name: &str) -> Option<Known> {
245        let key = key(name);
246        match self.forms.get(&*key) {
247            Some(&(_, known)) => known,
248            None => self.strict.get(&*key).and_then(|&(_, known)| known),
249        }
250    }
251
252    /// The strict function of that name, without case.
253    pub fn strict(&self, name: &str) -> Option<Strict> {
254        self.strict.get(&*key(name)).map(|&(f, _)| f)
255    }
256
257    /// The form of that name, without case.
258    pub fn form(&self, name: &str) -> Option<Form> {
259        self.forms.get(&*key(name)).map(|&(f, _)| f)
260    }
261
262    /// Whether the library holds a function of that name.
263    pub fn has(&self, name: &str) -> bool {
264        let wanted = key(name);
265        self.strict.contains_key(&*wanted) || self.forms.contains_key(&*wanted)
266    }
267
268    /// Every name the library holds, sorted.
269    pub fn names(&self) -> Vec<&str> {
270        let mut names: Vec<&str> = self
271            .strict
272            .keys()
273            .chain(self.forms.keys())
274            .map(String::as_str)
275            .collect();
276        names.sort_unstable();
277        names
278    }
279
280    /// How many functions the library holds.
281    pub fn len(&self) -> usize {
282        self.strict.len() + self.forms.len()
283    }
284
285    /// Whether the library holds none.
286    pub fn is_empty(&self) -> bool {
287        self.len() == 0
288    }
289}
290
291/// A name as the library keys it, in ASCII upper case: the name as it
292/// stands when it holds no ASCII small letter, as a parsed call's name
293/// never does (`formula.rs` upper-cases it), so that a lookup at every
294/// evaluation makes no string (KERNEL.25).
295fn key(name: &str) -> Cow<'_, str> {
296    if name.bytes().any(|b| b.is_ascii_lowercase()) {
297        Cow::Owned(name.to_ascii_uppercase())
298    } else {
299        Cow::Borrowed(name)
300    }
301}
302
303/// How deep a name may read a name before `#NAME?`.
304const NAME_DEPTH: u32 = 16;
305
306/// One cell's evaluation: the environment and the library.
307pub struct Ctx<'a> {
308    pub env: &'a dyn Env,
309    pub lib: &'a Library,
310    depth: Counter<u32>,
311    /// The offset of the cell being computed from the cell its formula was
312    /// read at: (0, 0) but in an engine's machine, which parses a shape once
313    /// and evaluates it at each of its cells (KERNEL.22).
314    offset: Counter<(i64, i64)>,
315}
316
317impl<'a> Ctx<'a> {
318    pub fn new(env: &'a dyn Env, lib: &'a Library) -> Ctx<'a> {
319        Ctx::at(env, lib, 0, 0)
320    }
321
322    /// An evaluation of a formula read at another cell, `d_row` rows up and
323    /// `d_col` columns left of the cell being computed: every reference the
324    /// formula writes is moved by that much, as a filled formula's are, and
325    /// the references inside a defined name's text are not, since a name
326    /// does not move with the cell that reads it (KERNEL.22).
327    pub fn at(env: &'a dyn Env, lib: &'a Library, d_row: i64, d_col: i64) -> Ctx<'a> {
328        Ctx {
329            env,
330            lib,
331            depth: Counter::new(0),
332            offset: Counter::new((d_row, d_col)),
333        }
334    }
335
336    /// An expression as an operand.
337    pub fn eval<'e>(&self, e: &'e Expr) -> Result<Arg<'e>, Reason> {
338        match e {
339            Expr::Number(v) => Ok(Arg::Scalar(Value::Number(*v))),
340            Expr::Text(t) => Ok(Arg::Scalar(Value::Text(t.clone()))),
341            Expr::Bool(b) => Ok(Arg::Scalar(Value::Bool(*b))),
342            Expr::Error(k) => Ok(Arg::Scalar(Value::Error(*k))),
343            Expr::Empty => Ok(Arg::Scalar(Value::Empty)),
344            Expr::Ref(r) => self.reference(r),
345            Expr::Name { name, sheet } => self.named(name, sheet.as_deref()),
346            Expr::Call(name, args) => self.call(name, args),
347            Expr::Neg(x) => {
348                let v = self.scalar(x)?;
349                Ok(Arg::Scalar(match v.number() {
350                    Ok(n) => finite(-n),
351                    Err(k) => Value::Error(k),
352                }))
353            }
354            // Unary plus returns its operand as it is, an area included.
355            Expr::Pos(x) => self.eval(x),
356            Expr::Percent(x) => {
357                let v = self.scalar(x)?;
358                Ok(Arg::Scalar(match v.number() {
359                    Ok(n) => finite(n / 100.0),
360                    Err(k) => Value::Error(k),
361                }))
362            }
363            // One operator, as it always was; a chain of them goes round a
364            // loop instead (VLA.1).
365            Expr::Binary(op, l, r) if !matches!(**l, Expr::Binary(..)) => {
366                let a = self.scalar(l)?;
367                let b = self.scalar(r)?;
368                Ok(Arg::Scalar(binary(*op, &a, &b)))
369            }
370            Expr::Binary(..) => self.chain(e),
371        }
372    }
373
374    /// A chain of binary operators (`1+2+3`, which Excel reads left to
375    /// right) is a left spine; it is evaluated as the recursion would, the
376    /// leftmost operand first and each link in turn, but with its right
377    /// operands on a stack, so that a formula as long as Excel holds, 4,096
378    /// links, needs no deeper stack than a short one (VLA.1). The first
379    /// sixteen are held in place, so that a chain such as a neighbour count
380    /// allocates nothing (KERNEL.25).
381    fn chain<'e>(&self, e: &'e Expr) -> Result<Arg<'e>, Reason> {
382        const NEAR: usize = 16;
383        let mut near: [(BinOp, &Expr); NEAR] = [(BinOp::Add, e); NEAR];
384        let mut far: Vec<(BinOp, &Expr)> = Vec::new();
385        let mut links = 0;
386        let mut left = e;
387        while let Expr::Binary(op, l, r) = left {
388            if links < NEAR {
389                near[links] = (*op, r);
390            } else {
391                far.push((*op, r));
392            }
393            links += 1;
394            left = l;
395        }
396        let mut acc = self.scalar(left)?;
397        for i in (0..links).rev() {
398            let (op, r) = if i < NEAR { near[i] } else { far[i - NEAR] };
399            let b = self.scalar(r)?;
400            acc = binary(op, &acc, &b);
401        }
402        Ok(Arg::Scalar(acc))
403    }
404
405    /// An expression as one value: a scalar, or a reference to one cell;
406    /// a reference to more is a construct this slice does not read.
407    pub fn scalar(&self, e: &Expr) -> Result<Value, Reason> {
408        let arg = self.eval(e)?;
409        self.one(&arg)
410    }
411
412    /// An operand as one value, by the same rule.
413    pub fn one(&self, arg: &Arg<'_>) -> Result<Value, Reason> {
414        match arg {
415            Arg::Scalar(v) => Ok(v.clone()),
416            Arg::Area {
417                areas,
418                written,
419                moved,
420            } => match &areas[..] {
421                [a] if a.is_cell() => self.env.read((a.sheet, a.top, a.left)),
422                _ if *moved == (0, 0) => Err(Reason::Construct(written.to_string())),
423                // The label a moved text would show: the cell's own reference.
424                _ => Err(Reason::Construct(crate::refers::shift_a1_references(
425                    written, moved.0, moved.1,
426                ))),
427            },
428        }
429    }
430
431    /// A condition as `IF`, `IFS` and `NOT` read one: the operand as one
432    /// value, then its truth value (`Value::truth`), a text read through a
433    /// reference coerced as a typed one is. Measured: Excel's save of the
434    /// subset fixture computes `IF(A6,1,2)` over the text `TRUE` as 1, and
435    /// over the text `x` as `#VALUE!` (`scripts/recalc/subset_saved.xlsx`,
436    /// `logic!C6` and `C3`). `AND` and `OR` read a reference as a range
437    /// instead, skipping its texts (`logic!C13`).
438    pub fn truth(&self, e: &Expr) -> Result<Result<bool, ErrorKind>, Reason> {
439        Ok(self.scalar(e)?.truth())
440    }
441
442    /// [`Ctx::truth`] over an evaluated operand.
443    pub fn truth_arg(&self, arg: &Arg<'_>) -> Result<Result<bool, ErrorKind>, Reason> {
444        Ok(self.one(arg)?.truth())
445    }
446
447    /// Every cell the areas hold, in area order then row-major, to `f`;
448    /// a cell nothing holds is skipped, as Excel skips it in a range. `f` is
449    /// a type of its own and not a pointer, so that an aggregate's fold is
450    /// compiled into the walk (KERNEL.25). A sheet the environment keeps as
451    /// an array is walked by index, the same values in the same order
452    /// (KERNEL.20).
453    pub fn each<F>(&self, areas: &[Area], mut f: F) -> Result<(), Reason>
454    where
455        F: FnMut(Value) -> Result<(), Reason>,
456    {
457        let model = self.env.model();
458        for a in areas {
459            if let Some(kept) = self.env.dense(a.sheet) {
460                kept.each(a, &mut f)?;
461                continue;
462            }
463            let Some(sheet) = model.sheets.get(a.sheet) else {
464                continue;
465            };
466            for ((row, col), _) in cells_in(sheet, self.env.extent(a.sheet), a) {
467                let v = self.env.read((a.sheet, row, col))?;
468                if v != Value::Empty {
469                    f(v)?;
470                }
471            }
472        }
473        Ok(())
474    }
475
476    pub(super) fn reference<'e>(&self, r: &'e RefExpr) -> Result<Arg<'e>, Reason> {
477        let home = self.env.here().0;
478        let (d_row, d_col) = self.offset.get();
479        match areas_at(self.env.model(), home, r, d_row, d_col) {
480            Ok(areas) => Ok(Arg::Area {
481                areas,
482                written: Cow::Borrowed(&r.written),
483                moved: (d_row, d_col),
484            }),
485            Err(k) => Ok(Arg::Scalar(Value::Error(k))),
486        }
487    }
488
489    /// A defined name's value: its text parsed here and evaluated, so that
490    /// an area it names carries its label owned, the parse being this
491    /// call's own.
492    fn named(&self, name: &str, sheet: Option<&str>) -> Result<Arg<'static>, Reason> {
493        let model = self.env.model();
494        let home = self.env.here().0;
495        let scope = match sheet {
496            None => None,
497            Some(s) => match model.find_sheet(s) {
498                Some(i) => Some(i),
499                None => return Ok(Arg::Scalar(Value::Error(ErrorKind::Ref))),
500            },
501        };
502        let Some(text) = self.env.name(name, scope, home) else {
503            return Ok(Arg::Scalar(Value::Error(ErrorKind::Name)));
504        };
505        if self.depth.get() >= NAME_DEPTH {
506            return Ok(Arg::Scalar(Value::Error(ErrorKind::Name)));
507        }
508        let parsed = formula::parse(text).map_err(Reason::Construct)?;
509        // A name's references are where the name says, whatever cell reads it.
510        let offset = self.offset.replace((0, 0));
511        self.depth.set(self.depth.get() + 1);
512        let result = self.eval(&parsed).map(Arg::into_owned);
513        self.depth.set(self.depth.get() - 1);
514        self.offset.set(offset);
515        result
516    }
517
518    fn call<'e>(&self, name: &str, args: &'e [Expr]) -> Result<Arg<'e>, Reason> {
519        if let Some(form) = self.lib.form(name) {
520            return form(self, args);
521        }
522        if let Some(f) = self.lib.strict(name) {
523            // One argument or two, the counts a call mostly has, held on the
524            // stack in a frame of their own (KERNEL.20), so that this frame,
525            // which evaluation stacks once a level of nesting, holds no array
526            // (`VLA.1`'s limits are pinned on a small stack); any other count
527            // in a vector. Left to right, stopping at the first not computed.
528            return match args {
529                [a] => self.strict_one(f, a),
530                [a, b] => self.strict_two(f, a, b),
531                _ => {
532                    let mut values = Vec::with_capacity(args.len());
533                    for a in args {
534                        values.push(self.eval(a)?);
535                    }
536                    f(self, &values)
537                }
538            }
539            .map(Arg::Scalar);
540        }
541        Err(Reason::Function(name.to_string()))
542    }
543
544    /// A strict call of one argument, the argument on the stack (KERNEL.20).
545    fn strict_one(&self, f: Strict, a: &Expr) -> Result<Value, Reason> {
546        let x = self.eval(a)?;
547        f(self, std::slice::from_ref(&x))
548    }
549
550    /// A strict call of two arguments, evaluated left to right, on the stack.
551    fn strict_two(&self, f: Strict, a: &Expr, b: &Expr) -> Result<Value, Reason> {
552        let pair = [self.eval(a)?, self.eval(b)?];
553        f(self, &pair)
554    }
555}
556
557/// A result no cell can hold is `#NUM!`.
558pub fn finite(v: f64) -> Value {
559    if v == 0.0 {
560        // No negative zero in a cell: Excel has none, and an empty sum is one.
561        Value::Number(0.0)
562    } else if v.is_finite() {
563        Value::Number(v)
564    } else {
565        Value::Error(ErrorKind::Num)
566    }
567}
568
569/// A binary operator over two values: the left operand's error first,
570/// then the right's; arithmetic over the numbers each coerces to, `&` over
571/// their texts, a comparison in Excel's order of kinds. Its arithmetic and
572/// its comparison of two numbers are [`arithmetic`] and [`compares`] over
573/// [`compare_numbers`], which an evaluation of many cells at once calls over
574/// its lanes of numbers (KERNEL.26), so that each rule is written once.
575pub fn binary(op: BinOp, a: &Value, b: &Value) -> Value {
576    if let Value::Error(k) = a {
577        return Value::Error(*k);
578    }
579    if let Value::Error(k) = b {
580        return Value::Error(*k);
581    }
582    match op {
583        BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div | BinOp::Pow => {
584            let x = match a.number() {
585                Ok(n) => n,
586                Err(k) => return Value::Error(k),
587            };
588            let y = match b.number() {
589                Ok(n) => n,
590                Err(k) => return Value::Error(k),
591            };
592            arithmetic(op, x, y)
593        }
594        BinOp::Concat => match (a.text(), b.text()) {
595            (Ok(s), Ok(t)) => Value::Text(s + &t),
596            (Err(k), _) | (_, Err(k)) => Value::Error(k),
597        },
598        _ => Value::Bool(compares(op, compare(a, b))),
599    }
600}
601
602/// An arithmetic operator over two numbers (`+`, `-`, `*`, `/`, `^`).
603#[inline]
604pub(crate) fn arithmetic(op: BinOp, x: f64, y: f64) -> Value {
605    match op {
606        BinOp::Add => finite(x + y),
607        BinOp::Sub => finite(x - y),
608        BinOp::Mul => finite(x * y),
609        BinOp::Div => {
610            if y == 0.0 {
611                Value::Error(ErrorKind::Div0)
612            } else {
613                finite(x / y)
614            }
615        }
616        _ => power(x, y),
617    }
618}
619
620/// Whether a comparison operator holds of how its operands compare.
621#[inline]
622pub(crate) fn compares(op: BinOp, ordering: Ordering) -> bool {
623    match op {
624        BinOp::Eq => ordering == Ordering::Equal,
625        BinOp::Ne => ordering != Ordering::Equal,
626        BinOp::Lt => ordering == Ordering::Less,
627        BinOp::Gt => ordering == Ordering::Greater,
628        BinOp::Le => ordering != Ordering::Greater,
629        _ => ordering != Ordering::Less,
630    }
631}
632
633/// How two numbers compare.
634#[inline]
635pub(crate) fn compare_numbers(x: f64, y: f64) -> Ordering {
636    x.partial_cmp(&y).unwrap_or(Ordering::Equal)
637}
638
639/// `^` as Excel has it: `0^0` is `#NUM!`, `0` to a negative power is
640/// `#DIV/0!`, a negative base to a fractional power is `#NUM!`.
641fn power(x: f64, y: f64) -> Value {
642    if x == 0.0 && y == 0.0 {
643        return Value::Error(ErrorKind::Num);
644    }
645    if x == 0.0 && y < 0.0 {
646        return Value::Error(ErrorKind::Div0);
647    }
648    finite(x.powf(y))
649}
650
651/// Excel's order of kinds in a comparison: every number is less than every
652/// text, every text less than every truth value; two texts compare without
653/// case; an empty cell is 0, `""` or FALSE, whichever the other side is.
654pub fn compare(a: &Value, b: &Value) -> Ordering {
655    use Value::*;
656    match (a, b) {
657        (Empty, Empty) => Ordering::Equal,
658        (Empty, Number(y)) => 0.0f64.partial_cmp(y).unwrap_or(Ordering::Equal),
659        (Number(x), Empty) => x.partial_cmp(&0.0).unwrap_or(Ordering::Equal),
660        (Empty, Text(t)) => {
661            if t.is_empty() {
662                Ordering::Equal
663            } else {
664                Ordering::Less
665            }
666        }
667        (Text(t), Empty) => {
668            if t.is_empty() {
669                Ordering::Equal
670            } else {
671                Ordering::Greater
672            }
673        }
674        (Empty, Bool(y)) => false.cmp(y),
675        (Bool(x), Empty) => x.cmp(&false),
676        (Number(x), Number(y)) => compare_numbers(*x, *y),
677        (Text(x), Text(y)) => fold(x).cmp(&fold(y)),
678        (Bool(x), Bool(y)) => x.cmp(y),
679        _ => rank(a).cmp(&rank(b)),
680    }
681}
682
683fn rank(v: &Value) -> u8 {
684    match v {
685        Value::Number(_) | Value::Empty => 0,
686        Value::Text(_) => 1,
687        Value::Bool(_) => 2,
688        Value::Error(_) => 3,
689    }
690}
691
692#[cfg(test)]
693mod tests {
694    use super::*;
695
696    #[test]
697    fn operators_coerce_as_excel_does() {
698        let n = |v: f64| Value::Number(v);
699        let t = |s: &str| Value::Text(s.to_string());
700        assert_eq!(binary(BinOp::Add, &t("5"), &n(1.0)), n(6.0));
701        assert_eq!(
702            binary(BinOp::Add, &t("x"), &n(1.0)),
703            Value::Error(ErrorKind::Value)
704        );
705        assert_eq!(binary(BinOp::Mul, &Value::Bool(true), &n(3.0)), n(3.0));
706        assert_eq!(binary(BinOp::Add, &Value::Empty, &n(1.0)), n(1.0));
707        assert_eq!(
708            binary(BinOp::Div, &n(1.0), &n(0.0)),
709            Value::Error(ErrorKind::Div0)
710        );
711        assert_eq!(
712            binary(BinOp::Div, &n(1.0), &Value::Empty),
713            Value::Error(ErrorKind::Div0)
714        );
715        assert_eq!(
716            binary(BinOp::Pow, &n(0.0), &n(0.0)),
717            Value::Error(ErrorKind::Num)
718        );
719        assert_eq!(
720            binary(BinOp::Pow, &n(0.0), &n(-1.0)),
721            Value::Error(ErrorKind::Div0)
722        );
723        assert_eq!(
724            binary(BinOp::Pow, &n(-8.0), &n(1.0 / 3.0)),
725            Value::Error(ErrorKind::Num)
726        );
727        assert_eq!(binary(BinOp::Pow, &n(2.0), &n(10.0)), n(1024.0));
728        assert_eq!(
729            binary(BinOp::Mul, &n(1e308), &n(10.0)),
730            Value::Error(ErrorKind::Num)
731        );
732        assert_eq!(binary(BinOp::Concat, &n(1.0), &n(2.0)), t("12"));
733        assert_eq!(
734            binary(BinOp::Concat, &Value::Bool(true), &Value::Empty),
735            t("TRUE")
736        );
737        assert_eq!(binary(BinOp::Concat, &n(0.1 + 0.2), &t("")), t("0.3"));
738        // The left error first.
739        assert_eq!(
740            binary(
741                BinOp::Add,
742                &Value::Error(ErrorKind::NA),
743                &Value::Error(ErrorKind::Div0)
744            ),
745            Value::Error(ErrorKind::NA)
746        );
747    }
748
749    #[test]
750    fn comparisons_follow_excel_s_order_of_kinds() {
751        let n = |v: f64| Value::Number(v);
752        let t = |s: &str| Value::Text(s.to_string());
753        let yes = Value::Bool(true);
754        assert_eq!(binary(BinOp::Eq, &t("ABC"), &t("abc")), yes);
755        assert_eq!(binary(BinOp::Lt, &n(1e9), &t("")), yes);
756        assert_eq!(binary(BinOp::Gt, &Value::Bool(false), &t("zzz")), yes);
757        assert_eq!(binary(BinOp::Eq, &Value::Empty, &n(0.0)), yes);
758        assert_eq!(binary(BinOp::Eq, &Value::Empty, &t("")), yes);
759        assert_eq!(binary(BinOp::Eq, &Value::Empty, &Value::Bool(false)), yes);
760        assert_eq!(binary(BinOp::Ne, &n(1.0), &t("1")), yes);
761        assert_eq!(binary(BinOp::Le, &n(2.0), &n(2.0)), yes);
762        assert_eq!(binary(BinOp::Ge, &n(1.0), &n(2.0)), Value::Bool(false));
763    }
764
765    #[test]
766    fn the_library_is_keyed_without_case_and_a_name_holds_one_kind() {
767        let mut lib = Library::empty();
768        fn one(_: &Ctx<'_>, _: &[Arg]) -> Result<Value, Reason> {
769            Ok(Value::Number(1.0))
770        }
771        fn two<'e>(_: &Ctx<'_>, _: &'e [Expr]) -> Result<Arg<'e>, Reason> {
772            Ok(Arg::Scalar(Value::Number(2.0)))
773        }
774        lib.register("one", one);
775        lib.register_form("Two", two);
776        assert!(lib.has("ONE") && lib.has("two"));
777        assert_eq!(lib.names(), vec!["ONE", "TWO"]);
778        assert!(lib.strict("one").is_some() && lib.form("one").is_none());
779        lib.register_form("one", two);
780        assert!(lib.strict("one").is_none() && lib.form("one").is_some());
781        assert_eq!(lib.len(), 2);
782    }
783
784    #[test]
785    fn a_name_is_upper_cased_only_when_that_would_change_it() {
786        // KERNEL.25: a parsed call's name, already in upper case, is looked
787        // up as it stands; any other has its ASCII letters upper-cased first,
788        // so a letter outside ASCII keeps its case, as it always has.
789        fn one(_: &Ctx<'_>, _: &[Arg]) -> Result<Value, Reason> {
790            Ok(Value::Number(1.0))
791        }
792        assert!(matches!(key("SUM"), Cow::Borrowed("SUM")));
793        assert!(matches!(key("ÄRGER"), Cow::Borrowed("ÄRGER")));
794        assert_eq!(key("Sum"), "SUM");
795        assert_eq!(key("ärger"), "äRGER");
796        let mut lib = Library::empty();
797        lib.register("Ärger", one);
798        lib.register("sum", one);
799        assert_eq!(lib.names(), vec!["SUM", "ÄRGER"]);
800        for (name, found) in [
801            ("SUM", true),
802            ("Sum", true),
803            ("sum", true),
804            ("ÄRGER", true),
805            ("Ärger", true),
806            ("ärger", false),
807            ("äRGER", false),
808        ] {
809            assert_eq!(lib.strict(name).is_some(), found, "{name}");
810            assert_eq!(lib.has(name), found, "{name}");
811            assert!(lib.form(name).is_none(), "{name}");
812        }
813    }
814}