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