Merge pull request #600 from MarekSpodBiedry:issue-564
This commit is contained in:
commit
e381a8fd01
|
|
@ -407,6 +407,7 @@ impl<'h> Evaluator<'h> {
|
|||
};
|
||||
}
|
||||
let v = self.value(x);
|
||||
let sys = self.host.workbook().date_system;
|
||||
Ev::V(map_unary(
|
||||
&v,
|
||||
&|n| match op {
|
||||
|
|
@ -416,6 +417,7 @@ impl<'h> Evaluator<'h> {
|
|||
UnOp::At | UnOp::Spill | UnOp::Trim(_) => Value::number(n),
|
||||
},
|
||||
op == UnOp::Plus,
|
||||
sys,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -502,7 +504,7 @@ impl<'h> Evaluator<'h> {
|
|||
let mut acc = self.value(left);
|
||||
while let Some((o, r)) = rest.pop() {
|
||||
let y = self.value(r);
|
||||
acc = binary_values(o, &acc, &y);
|
||||
acc = binary_values(o, &acc, &y, self.host.workbook().date_system);
|
||||
}
|
||||
Ev::V(acc)
|
||||
}
|
||||
|
|
@ -1593,12 +1595,12 @@ impl Acc {
|
|||
|
||||
// ---------------------------------------------------------------- operators
|
||||
|
||||
fn map_unary(v: &Value, f: &dyn Fn(f64) -> Value, plus: bool) -> Value {
|
||||
fn map_unary(v: &Value, f: &dyn Fn(f64) -> Value, plus: bool, sys: gridcraft_core::DateSystem) -> Value {
|
||||
match v {
|
||||
Value::Array(a) => Value::from(Array { rows: a.rows, cols: a.cols, data: a.data.iter().map(|x| map_unary(x, f, plus)).collect() }),
|
||||
Value::Array(a) => Value::from(Array { rows: a.rows, cols: a.cols, data: a.data.iter().map(|x| map_unary(x, f, plus, sys)).collect() }),
|
||||
Value::Error(e) => Value::Error(*e),
|
||||
Value::Text(_) if plus => v.clone(),
|
||||
other => match other.to_number() {
|
||||
other => match other.to_number_in(sys) {
|
||||
Ok(n) => f(n),
|
||||
Err(e) => Value::Error(e),
|
||||
},
|
||||
|
|
@ -1651,18 +1653,18 @@ fn zip3(c: &Array, t: &Value, f: &Value, g: impl Fn(&Value, &Value, &Value) -> V
|
|||
Value::from(Array { rows, cols, data })
|
||||
}
|
||||
|
||||
pub fn binary_values(op: BinOp, x: &Value, y: &Value) -> Value {
|
||||
zip2(x, y, |a, b| binary_scalar(op, a, b))
|
||||
pub fn binary_values(op: BinOp, x: &Value, y: &Value, sys: gridcraft_core::DateSystem) -> Value {
|
||||
zip2(x, y, |a, b| binary_scalar(op, a, b, sys))
|
||||
}
|
||||
|
||||
fn binary_scalar(op: BinOp, a: &Value, b: &Value) -> Value {
|
||||
fn binary_scalar(op: BinOp, a: &Value, b: &Value, sys: gridcraft_core::DateSystem) -> Value {
|
||||
if let Value::Error(e) = a {
|
||||
return Value::Error(*e);
|
||||
}
|
||||
if let Value::Error(e) = b {
|
||||
return Value::Error(*e);
|
||||
}
|
||||
let num = |f: fn(f64, f64) -> Value| match (a.to_number(), b.to_number()) {
|
||||
let num = |f: fn(f64, f64) -> Value| match (a.to_number_in(sys), b.to_number_in(sys)) {
|
||||
(Ok(x), Ok(y)) => f(x, y),
|
||||
(Err(e), _) | (_, Err(e)) => Value::Error(e),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -290,6 +290,26 @@ fn circular_references_without_iterative_calculation() {
|
|||
assert_eq!(t.get("A1"), Value::Error(CellError::Circ));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_text_coerces_in_the_workbook_date_system() {
|
||||
for (sys, serial) in [(gridcraft_core::DateSystem::D1900, 1462.0), (gridcraft_core::DateSystem::D1904, 0.0)] {
|
||||
let mut t = T::new();
|
||||
t.wb.date_system = sys;
|
||||
t.set("A1", "'1/1/1904");
|
||||
t.set("B1", "=A1+0");
|
||||
t.set("C1", "=VALUE(A1)");
|
||||
t.set("D1", "=-A1");
|
||||
t.set("E1", "=DATEVALUE(A1)");
|
||||
t.set("F1", "=NUMBERVALUE(A1)");
|
||||
assert_eq!(t.get("A1"), Value::from("1/1/1904"));
|
||||
assert_eq!(t.num("B1"), serial, "{sys:?}");
|
||||
assert_eq!(t.num("C1"), serial, "{sys:?}");
|
||||
assert_eq!(t.num("D1"), -serial, "{sys:?}");
|
||||
assert_eq!(t.num("E1"), serial, "{sys:?}");
|
||||
assert_eq!(t.num("F1"), serial, "{sys:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operators() {
|
||||
let mut t = T::new();
|
||||
|
|
|
|||
|
|
@ -41,7 +41,12 @@ pub fn parse_input(s: &str, sys: DateSystem) -> Parsed {
|
|||
|
||||
/// Numeric text as arithmetic coerces it ("1,000", "50%", "$3", "1/2/2020", "10:00").
|
||||
pub fn parse_number_text(s: &str) -> Option<f64> {
|
||||
parse_numeric(s.trim(), DateSystem::D1900).map(|(n, _)| n)
|
||||
parse_number_text_in(s, DateSystem::D1900)
|
||||
}
|
||||
|
||||
/// Like [`parse_number_text`], with date text read in the given workbook date system.
|
||||
pub fn parse_number_text_in(s: &str, sys: DateSystem) -> Option<f64> {
|
||||
parse_numeric(s.trim(), sys).map(|(n, _)| n)
|
||||
}
|
||||
|
||||
fn parse_numeric(t: &str, sys: DateSystem) -> Option<(f64, Option<&'static str>)> {
|
||||
|
|
|
|||
|
|
@ -224,13 +224,17 @@ impl Value {
|
|||
/// Coercion to a number as arithmetic does it: empty = 0, TRUE = 1, numeric text parses
|
||||
/// (including dates, times, percentages and currency), other text = `#VALUE!`.
|
||||
pub fn to_number(&self) -> Result<f64, CellError> {
|
||||
self.to_number_in(crate::DateSystem::D1900)
|
||||
}
|
||||
/// [`Value::to_number`] with date text read in the given workbook date system.
|
||||
pub fn to_number_in(&self, sys: crate::DateSystem) -> Result<f64, CellError> {
|
||||
match self {
|
||||
Value::Empty => Ok(0.0),
|
||||
Value::Number(n) => Ok(*n),
|
||||
Value::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }),
|
||||
Value::Text(t) => crate::parse::parse_number_text(t).ok_or(CellError::Value),
|
||||
Value::Text(t) => crate::parse::parse_number_text_in(t, sys).ok_or(CellError::Value),
|
||||
Value::Error(e) => Err(*e),
|
||||
Value::Array(a) => a.data.first().map(|v| v.to_number()).unwrap_or(Ok(0.0)),
|
||||
Value::Array(a) => a.data.first().map(|v| v.to_number_in(sys)).unwrap_or(Ok(0.0)),
|
||||
}
|
||||
}
|
||||
/// Coercion to text as `&` does it.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Text functions.
|
||||
|
||||
use gridcraft_core::parse::parse_number_text;
|
||||
use gridcraft_core::parse::parse_number_text_in;
|
||||
use gridcraft_core::{CellError, Value, number_to_text};
|
||||
|
||||
use crate::criteria::wildcard_find;
|
||||
|
|
@ -295,17 +295,17 @@ fn unicode(a: &[Arg], _c: &mut dyn Ctx) -> R<Value> {
|
|||
|
||||
/// VALUE: a blank cell reads as 0, but empty or blank text is not a number (observed:
|
||||
/// `VALUE("")` is `#VALUE!`).
|
||||
fn value(a: &[Arg], _c: &mut dyn Ctx) -> R<Value> {
|
||||
fn value(a: &[Arg], c: &mut dyn Ctx) -> R<Value> {
|
||||
match scalar(arg(a, 0)?) {
|
||||
Value::Number(n) => num_val(n),
|
||||
Value::Empty => num_val(0.0),
|
||||
Value::Error(e) => Err(e),
|
||||
Value::Text(t) => parse_number_text(&t).ok_or(CellError::Value).and_then(num_val),
|
||||
Value::Text(t) => parse_number_text_in(&t, c.date_system()).ok_or(CellError::Value).and_then(num_val),
|
||||
_ => Err(CellError::Value),
|
||||
}
|
||||
}
|
||||
|
||||
fn numbervalue(a: &[Arg], _c: &mut dyn Ctx) -> R<Value> {
|
||||
fn numbervalue(a: &[Arg], c: &mut dyn Ctx) -> R<Value> {
|
||||
let s = text(a, 0)?;
|
||||
let dec = if a.len() > 1 { text(a, 1)?.chars().next().ok_or(CellError::Value)? } else { '.' };
|
||||
let grp = if a.len() > 2 { text(a, 2)?.chars().next().ok_or(CellError::Value)? } else { ',' };
|
||||
|
|
@ -338,7 +338,7 @@ fn numbervalue(a: &[Arg], _c: &mut dyn Ctx) -> R<Value> {
|
|||
out.push(c);
|
||||
}
|
||||
}
|
||||
let mut n = parse_number_text(&out).ok_or(CellError::Value)?;
|
||||
let mut n = parse_number_text_in(&out, c.date_system()).ok_or(CellError::Value)?;
|
||||
for _ in 0..pct {
|
||||
n /= 100.0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue